Type/to search
0
Follow
48
Followers
SuperTrend V.1 — система супертрендовых линий
Original
Created 2020-04-20 22:10:36  Updated 2024-12-12 21:03:30
 28
 12168

img

1. Происхождение истории

Мой хороший друг Ран давно наблюдает за этим показателем и порекомендовал его мне перед Новым годом, чтобы обсудить, можно ли преобразовать его в количественную форму.
К сожалению, я страдал от прокрастинации и не помог ему осуществить его желание до сих пор. На самом деле, мое понимание алгоритмов в последнее время значительно продвинулось.
Я планирую когда-нибудь написать переводчик с сосны. Все может быть на Python. .
Итак, без лишних слов, позвольте нам представить эту легендарную супертрендовую линию. .

2. Введение в систему

Новое поколение интеллектуальной торговой системы CMC Markets - Supertrend
Вот статья, знакомящая с этой системой.
img

В новом поколении интеллектуальной торговой системы CMC Markets выберите «Super Trend Line» в технических индикаторах, чтобы вызвать ее.
Как показано на рисунке, вы можете настроить «цвет и толщину» восходящих и нисходящих сигналов в соответствии со своими предпочтениями.
Так что же такое индикатор супертренда? Прежде чем понимать формулу индикатора supertrend, необходимо понять ATR, поскольку supertrend использует значения ATR для расчета значений индикатора.

Основной алгоритм также представлен на рисунке ниже.
img

На первый взгляд, основное описание представляет собой канал HL2 (средняя цена K-линии), умноженный на n раз ATR. Сделайте прорыв тренда.
Но статья довольно краткая. Подробного алгоритма нет. Затем я подумал о лучшем сообществе Tradingview.
Неудивительно. Конечно же, он там есть.
img

Судя по графику, это вполне соответствует тенденции. Но, к сожалению, это всего лишь сигнал тревоги.

3. Изучите исходный код.

Код не выглядит слишком длинным, поэтому давайте переведем его и попробуем. ! (っ•̀ω•́)っ✎⁾⁾!
img
Полный код сосны такой же, как и выше. .

4. Преобразование кода

Здесь мы создаем новую стратегию в FMZ и называем ее SuperTrade.
img

Далее задаем два параметра Factor и Pd
img

Чтобы еще больше упростить работу кода и сделать его более понятным, нам нужно использовать расширенный пакет расширения данных Python.pandas

Во время обеда я спросил учителя Мэнмэна, поддерживает ли FMZ эту библиотеку. Я проверил это днем, и это действительно сработало.
Учитель Мэнмэн действительно потрясающий.

1. Нам нужно импортировать библиотеку времени библиотеки pandas
2. Настройте квартальный контракт в основной функции (в основном работающей на OKEX)
3. Установите цикл doTicker() для тестирования каждые 15 минут.
Запустите код в 15-минутном цикле.
Далее мы пишем основную стратегию в doTicker().

import pandas as pd import time def main(): exchange.SetContractType("quarter") preTime = 0 Log(exchange.GetAccount()) while True: records = exchange.GetRecords(PERIOD_M15) if records and records[-2].Time > preTime: preTime = records[-2].Time doTicker(records[:-1]) Sleep(1000 *60)

4. Нам нужно получить OHCLV K-линии, поэтому используйте GetRecords()
5. Импортируем полученные данные в pandas M15 = pd.DataFrame(records)
6. Нам необходимо изменить тег заголовка таблицы. M15.столбцы =['time','open','high','low','close','volume','OpenInterest']
Фактически, он просто изменяет первые буквы слов «open», «high», «low» и «close» на строчные, чтобы впоследствии было проще писать код, не чередуя заглавные и строчные буквы.

def doTicker(records): M15 = pd.DataFrame(records) M15.columns = ['time','open','high','low','close','volume','OpenInterest']

7. Добавьте столбец hl2 в набор данных hl2=(high+low)/2

#HL2 M15['hl2']=(M15['high']+M15['low'])/2

8. Далее, давайте рассчитаем ATR
Поскольку для расчета ATR требуется импорт переменной длины, ее значение равно Pd

Затем мы обращаемся к руководству по языку Mai, и шаги алгоритма расчета средней истинной волатильности ATR следующие:
TR : MAX(MAX((HIGH-LOW),ABS(REF(CLOSE,1)-HIGH)),ABS(REF(CLOSE,1)-LOW));
ATR : RMA(TR,N)

Значение TR является наибольшим из следующих трех различий.

  1. Колебание между самой высокой ценой и самой низкой ценой текущего торгового дня HIGH-LOW
  2. Колебание между ценой закрытия предыдущего торгового дня и максимальной ценой текущего торгового дня (REF(CLOSE,1)-HIGH)
  3. Колебание между ценой закрытия предыдущего торгового дня и самой низкой ценой текущего торгового дня (REF(CLOSE,1)-LOW)
    Итак, TR: MAX(MAX((HIGH-LOW),ABS(REF(CLOSE,1)-HIGH)),ABS(REF(CLOSE,1)-LOW));

В вычислениях Python

M15['prev_close']=M15['close'].shift(1)

Сначала настройте prev_close, чтобы получить данные close в предыдущей строке, то есть переместите close вправо на 1 позицию сетки, чтобы сформировать новый параметр.

ranges= [M15['high'] - M15['low'],M15['high']-M15['prev_close'],M15['low']-M15['prev_close']]

Далее определим промежуточную переменную для записи массива из трех сравнительных значений TR. (ВЫСОКИЙ-НИЗКИЙ)(высокий-предыдущий_закрытый)(низкий-предыдущий_закрытый)

M15['tr'] = pd.DataFrame(ranges).T.abs().max(axis=1)

Мы определяем новый столбец с именем TR в наборе данных. Значение TR — это максимальное абсолютное значение промежуточной переменной. Мы используем функции abs() и max().

alpha = (1.0 / length) if length > 0 else 0.5 M15['atr']=M15['tr'].ewm(alpha=alpha, min_periods=length).mean()

Наконец, нам нужно вычислить значение ATR, ATR: RMA (TR, N). Установлено, что алгоритм RMA на самом деле является вариантом алгоритма EMA с фиксированным значением.
N — это импортированная нами переменная, где параметр по умолчанию для ATR равен 14. Здесь мы импортируем альфа = величину, обратную длине.

===

Затем используйте алгоритм EWM для расчета EMA.
Полный процесс расчета ATR выглядит следующим образом:

#ATR(PD) length=Pd M15['prev_close']=M15['close'].shift(1) ranges= [M15['high'] - M15['low'],M15['high']-M15['prev_close'],M15['low']-M15['prev_close']] M15['tr'] = pd.DataFrame(ranges).T.abs().max(axis=1) alpha = (1.0 / length) if length > 0 else 0.5 M15['atr']=M15['tr'].ewm(alpha=alpha, min_periods=length).mean()

9 Начните расчет Up и Dn

M15['Up']=M15['hl2']-(Factor*M15['atr']) M15['Dn']=M15['hl2']+(Factor*M15['atr'])

Up=hl2 -(Factor * atr)
Dn=hl2 +(Factor * atr)
Разве это не просто?

Ниже приведен основной фрагмент кода строк 15-21 в TV.

TrendUp=close[1]>TrendUp[1]? max(Up,TrendUp[1]) : Up TrendDown=close[1]<TrendDown[1]? min(Dn,TrendDown[1]) : Dn Trend = close > TrendDown[1] ? 1: close< TrendUp[1]? -1: nz(Trend[1],1) Tsl = Trend==1? TrendUp: TrendDown linecolor = Trend == 1 ? green : red

Основная цель этого параграфа — выразить,
Если в бычьей фазе, (нижняя линия) TrendUp = max(Up,TrendUp[1])
Если он находится в стадии падения, (верхняя линия) TrendDown=min(Dn,TrendDown[1])
То есть, в тренде значение ATR использовало технику, похожую на стратегию Bandit Bollinger.
Продолжайте сужать другую сторону канала.

Здесь каждый расчет TrendUp и TrendDown должен быть самоитерированным.
То есть каждый шаг должен рассчитываться на основе предыдущего шага.
Поэтому нам нужно выполнить цикл по всему набору данных.

Здесь нам сначала необходимо создать новые поля TrendUp, TrendDown, Trend и linecolor для набора данных. И дайте им начальное значение
Затем используйте синтаксис fillna(0), чтобы заполнить данные нулевыми значениями в ранее рассчитанных результатах с 0.

M15['TrendUp']=0.0 M15['TrendDown']=0.0 M15['Trend']=1 M15['Tsl']=0.0 M15['linecolor']='Homily' M15 = M15.fillna(0)

Начать цикл for
Использование тернарных операций Python в циклах

for x in range(len(M15)):

Расчет тренда вверх
TrendUp = MAX(Up,TrendUp[-1]) if close[-1]>TrendUp[-1] else Up
Общий смысл в том, что если предыдущее закрытие > предыдущего TrendUp, то если это правда, то берется максимальное значение Up и предыдущего TrendUp, если это не правда, то берется значение Up и передается текущему TrendUp.

M15['TrendUp'].values[x] = max(M15['Up'].values[x],M15['TrendUp'].values[x-1]) if (M15['close'].values[x-1]>M15['TrendUp'].values[x-1]) else M15['Up'].values[x]

Аналогично рассчитайте TrendDown
TrendDown=min(Dn,TrendDown[-1]) if close[-1]<TrendDown[-1] else Dn
Общий смысл в том, что если предыдущее закрытие < предыдущего TrendDown, то если это правда, то берется минимальное значение между Dn и предыдущим TrendDown, если это не правда, то берется значение Dn и передается в текущий TrendDown.

M15['TrendDown'].values[x] = min(M15['Dn'].values[x],M15['TrendDown'].values[x-1]) if (M15['close'].values[x-1]<M15['TrendDown'].values[x-1]) else M15['Dn'].values[x]

Ниже представлен флаг для расчета направления управления. Я упростил псевдокод
Trend= 1 if (close > TrendDown[-1]) else (x)
x = -1 if (close< TrendUp[-1]) else Trend[-1]

Смысл в том, что если цена закрытия > предыдущего тренда вниз, то берем 1 (бычий), если нет, то берем x
Если цена закрытия меньше предыдущего TrendUp, то берем -1 (короткая позиция). Если нет, то берем предыдущий Trend (то есть он остается неизменным).
В переводе на графический язык это означает, что прорыв верхней границы переключает флаг на бычий, прорыв нижней границы переключает флаг на медвежий, а все остальное время остается неизменным.

M15['Tsl'].values[x] = M15['TrendUp'].values[x] if (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x]

Рассчитать Tsl и цвет линии
Tsl= rendUp if (Trend==1) else TrendDown
Tsl используется для представления значения SuperTrend на графике. Это означает, что когда вы настроены по-бычьи, отметьте нижнюю дорожку на графике, а когда вы настроены по-медвежьи, отметьте верхнюю дорожку на графике.
linecolor= 'green' if (Trend==1) else 'red'
Значение параметра linecolor: если вы настроены по-бычьи, отметьте зеленую линию, если вы настроены по-медвежьи, отметьте пустой цвет (в основном используется для отображения в Tradingview)

M15['Tsl'].values[x] = M15['TrendUp'].values[x] if (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x] M15['linecolor'].values[x]= 'green' if ( M15['Trend'].values[x]==1) else 'red'

Следующие строки 23–30 в основном предназначены для построения графика, который не будет здесь подробно объясняться.

Наконец, есть 2 строки кода для управления сигналами покупки и продажи.
В Tradingview это означает подачу сигнала после разворота Флага.
Преобразовать условные операторы в Python.
Если предыдущий флаг тренда изменился с -1 на 1, это означает, что верхнее сопротивление было пробито. Открывайте длинную позицию
Если предыдущий флаг тренда изменился с 1 на -1, это означает, что нисходящая поддержка была пробита. Открываем короткую позицию.

if(M15['Trend'].values[-1] == 1 and M15['Trend'].values[-2] == -1): Log('SuperTrend V.1 Alert Long',"Create Order Buy) if(M15['Trend'].values[-1] == -1 and M15['Trend'].values[-2] == 1): Log('SuperTrend V.1 Alert Long',"Create Order Sell)

Полный код этого раздела выглядит следующим образом:

M15['TrendUp']=0.0 M15['TrendDown']=0.0 M15['Trend']=1 M15['Tsl']=0.0 M15['linecolor']='Homily' M15 = M15.fillna(0) for x in range(len(M15)): M15['TrendUp'].values[x] = max(M15['Up'].values[x],M15['TrendUp'].values[x-1]) if (M15['close'].values[x-1]>M15['TrendUp'].values[x-1]) else M15['Up'].values[x] M15['TrendDown'].values[x] = min(M15['Dn'].values[x],M15['TrendDown'].values[x-1]) if (M15['close'].values[x-1]<M15['TrendDown'].values[x-1]) else M15['Dn'].values[x] M15['Trend'].values[x] = 1 if (M15['close'].values[x] > M15['TrendDown'].values[x-1]) else ( -1 if (M15['close'].values[x]< M15['TrendUp'].values[x-1])else M15['Trend'].values[x-1] ) M15['Tsl'].values[x] = M15['TrendUp'].values[x] if (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x] M15['linecolor'].values[x]= 'green' if ( M15['Trend'].values[x]==1) else 'red' if(M15['Trend'].values[-1] == 1 and M15['Trend'].values[-2] == -1): Log('SuperTrend V.1 Alert Long',"Create Order Buy) Log('Tsl=',Tsl) if(M15['Trend'].values[-1] == -1 and M15['Trend'].values[-2] == 1): Log('SuperTrend V.1 Alert Long',"Create Order Sell) Log('Tsl=',Tsl)

img

img

5. Все коды

Я скорректировал общую структуру кода.
И объедините в стратегию инструкции по длинным и коротким ордерам.
Вот полный код

'''backtest start: 2019-05-01 00:00:00 end: 2020-04-21 00:00:00 period: 15m exchanges: [{"eid":"Futures_OKCoin","currency":"BTC_USD"}] ''' import pandas as pd import time def main(): exchange.SetContractType("quarter") preTime = 0 Log(exchange.GetAccount()) while True: records = exchange.GetRecords(PERIOD_M15) if records and records[-2].Time > preTime: preTime = records[-2].Time doTicker(records[:-1]) Sleep(1000 *60) def doTicker(records): #Log('onTick',exchange.GetTicker()) M15 = pd.DataFrame(records) #Factor=3 #Pd=7 M15.columns = ['time','open','high','low','close','volume','OpenInterest'] #HL2 M15['hl2']=(M15['high']+M15['low'])/2 #ATR(PD) length=Pd M15['prev_close']=M15['close'].shift(1) ranges= [M15['high'] - M15['low'],M15['high']-M15['prev_close'],M15['low']-M15['prev_close']] M15['tr'] = pd.DataFrame(ranges).T.abs().max(axis=1) alpha = (1.0 / length) if length > 0 else 0.5 M15['atr']=M15['tr'].ewm(alpha=alpha, min_periods=length).mean() M15['Up']=M15['hl2']-(Factor*M15['atr']) M15['Dn']=M15['hl2']+(Factor*M15['atr']) M15['TrendUp']=0.0 M15['TrendDown']=0.0 M15['Trend']=1 M15['Tsl']=0.0 M15['linecolor']='Homily' M15 = M15.fillna(0) for x in range(len(M15)): M15['TrendUp'].values[x] = max(M15['Up'].values[x],M15['TrendUp'].values[x-1]) if (M15['close'].values[x-1]>M15['TrendUp'].values[x-1]) else M15['Up'].values[x] M15['TrendDown'].values[x] = min(M15['Dn'].values[x],M15['TrendDown'].values[x-1]) if (M15['close'].values[x-1]<M15['TrendDown'].values[x-1]) else M15['Dn'].values[x] M15['Trend'].values[x] = 1 if (M15['close'].values[x] > M15['TrendDown'].values[x-1]) else ( -1 if (M15['close'].values[x]< M15['TrendUp'].values[x-1])else M15['Trend'].values[x-1] ) M15['Tsl'].values[x] = M15['TrendUp'].values[x] if (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x] M15['linecolor'].values[x]= 'Long' if ( M15['Trend'].values[x]==1) else 'Short' linecolor=M15['linecolor'].values[-2] close=M15['close'].values[-2] Tsl=M15['Tsl'].values[-2] if(M15['Trend'].values[-1] == 1 and M15['Trend'].values[-2] == -1): Log('SuperTrend V.1 Alert Long','Create Order Buy') Log('Tsl=',Tsl) position = exchange.GetPosition() if len(position) > 0: Amount=position[0]["Amount"] exchange.SetDirection("closesell") exchange.Buy(_C(exchange.GetTicker).Sell*1.01, Amount); exchange.SetDirection("buy") exchange.Buy(_C(exchange.GetTicker).Sell*1.01, vol); if(M15['Trend'].values[-1] == -1 and M15['Trend'].values[-2] == 1): Log('SuperTrend V.1 Alert Long','Create Order Sell') Log('Tsl=',Tsl) position = exchange.GetPosition() if len(position) > 0: Amount=position[0]["Amount"] exchange.SetDirection("closebuy") exchange.Sell(_C(exchange.GetTicker).Buy*0.99,Amount); exchange.SetDirection("sell") exchange.Sell(_C(exchange.GetTicker).Buy*0.99, vol*2);

Ссылка на публичную стратегию: https://www.fmz.com/strategy/200625

6. Тестирование на исторических данных и резюме

Для бэктестинга мы выбрали данные за прошлый год.
Используйте квартальный контракт OKEX с 15-минутным циклом.
Установлены следующие параметры:
Factor=3
Pd=45
vol=100 (100 билетов в заказе)
Годовая доходность составляет около 33%.
В целом откат не очень большой.
Основной причиной этого стала катастрофа рейса 312, которая оказала значительное влияние на систему.
Если бы не было 312, отдача была бы лучше.

img

6. Заключительные мысли

SuperTrend — очень хорошая торговая система

Основной принцип системы SuperTrend заключается в использовании стратегии прорыва канала ATR (аналогично каналу Кента).
Но главное изменение заключается в использовании стратегии сужения «Бандит Боллинджер» или обратного принципа Дончиана.
Верхний и нижний каналы постоянно сужаются в процессе работы рынка.
Для осуществления прорыва канала и поворотной операции. (После прорыва канала верхние и нижние рельсы возвращаются к своим первоначальным значениям)

Я построил график dn TrendUp TrendDn отдельно на TradingView
Это поможет вам лучше понять эту стратегию.

Ясно с первого взгляда
img

Также есть версия js на github. Я не очень хорошо понимаю js, но судя по оператору if, тут есть какая-то проблема.
Адрес:https://github.com/Dodo33/gekko-supertrend-strategy/blob/master/Supertrend.js

Наконец мне удалось найти оригинальную версию.
Опубликовано 29.05.2013
Автор: Раджандран Р.
Код C++ опубликован на форуме Mt4https://www.mql5.com/en/code/viewcode/10851/128437/Non_Repainting_SuperTrend.mq4
Я примерно понимаю значение C++ и перепишу его, когда появится возможность.

Надеюсь, каждый сможет извлечь из этого суть.
Это трудно. ~!

Related Recommendations
Comment
All comments (25)

    YYDS!

    5 years ago

    如果直接使用这个策略在OK交易所交易需要怎么连接交易所,小白一个不会python,看不明白

    6 years ago

    这里如果312那波行情没吃到的话应该参数还有很大的调整空间,因为supertrend主要就是抓趋势单,312是不应该错过的。另外期待lz的pine翻译器早日问世

    6 years ago

    可惜各种周期和参数,回测效果都不怎么好, 不知道其它人怎么优化的?

    6 years ago

    可以了,弄好了,感谢您的付出

    6 years ago

    用不了呢,显示这个:Traceback (most recent call last): File "<string>", line 1473, in Run File "<string>", line 8, in <module> ImportError: No module named pandas

    6 years ago

    意思是缺少pandas包 你的系统可能需要pip install pandas

    6 years ago

    请问这是怎么处理的呢?万分感谢

    6 years ago

    pine的翻译器,期待

    6 years ago

    没啥文化只能说一声 牛逼!

    6 years ago

    “估摸着某一天写一个pine的翻译器。一切皆可python。”—— 牛,好些人看好这个!

    6 years ago

    啊哈哈,谢谢老板

    6 years ago

    期待期待,pine真的看不太懂,教程也很少

    6 years ago

    回测引擎的代码是否可以开源呢,我想实现复现一下 然后用svm找出最好的参数

    6 years ago

    这个系统好像也曾经是收益率前十的期货策略。长期坚持做下去是能赚钱的。

    6 years ago

    恩啊。学习精髓。

    6 years ago

    你好,请教下,PD就是 ATR的长度值吧? 比如 ATR(14) ,就是 PD赋值14了吧?

    6 years ago

    是的,完全正确

    6 years ago

    好的,谢谢!!!
    顺手mq4也收走了,谢谢。。 o(∩_∩)o

    6 years ago

    感谢梦梦老师哈

    6 years ago

    碰巧我也写了个JS版本的。

    6 years ago

    求JS版!

    6 years ago

    一会儿,公开。

    6 years ago

    赞的,!

    6 years ago
  • 1
iPhone Download
Forums
PINE Language
© 2015 - ∞ INVENTOR PTE LTD (SG)