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

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

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

Судя по графику, это вполне соответствует тенденции. Но, к сожалению, это всего лишь сигнал тревоги.
Код не выглядит слишком длинным, поэтому давайте переведем его и попробуем. ! (っ•̀ω•́)っ✎⁾⁾!
Полный код сосны такой же, как и выше. .
Здесь мы создаем новую стратегию в FMZ и называем ее SuperTrade.

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

Чтобы еще больше упростить работу кода и сделать его более понятным, нам нужно использовать расширенный пакет расширения данных 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]
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)


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

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

Также есть версия 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++ и перепишу его, когда появится возможность.
Надеюсь, каждый сможет извлечь из этого суть. Это трудно. ~!