
Meu bom amigo Ran vem observando esse indicador há muito tempo e o recomendou a mim antes do Ano Novo para discutir se ele poderia ser convertido em uma forma quantitativa. Infelizmente, sofri de procrastinação e não o ajudei a realizar seu desejo até agora. Na verdade, meu entendimento de algoritmos fez um grande progresso recentemente. Pretendo escrever um tradutor de pinho um dia. Tudo pode ser python. . Ok, sem mais delongas, vamos apresentar esta lendária super tendência. .
Nova geração de sistema de negociação inteligente da CMC Markets - Supertrend
Aqui está um artigo apresentando esse sistema.

Na nova geração do sistema de negociação inteligente da CMC Markets, selecione “Super Trend Line” nos indicadores técnicos para acessá-lo. Conforme mostrado na figura, você pode ajustar a “cor e a espessura” dos sinais ascendentes e descendentes de acordo com suas preferências. Então, o que é o indicador de supertendência? Antes de entender a fórmula do indicador supertrend, é necessário entender o ATR porque o supertrend usa valores de ATR para calcular os valores do indicador.
O algoritmo principal também é apresentado na imagem abaixo.

À primeira vista, a descrição principal é um canal de HL2 (preço médio da linha K) multiplicado por n vezes ATR. Crie uma nova tendência.
Mas o artigo é bastante breve. Não há um algoritmo detalhado. Então pensei na melhor comunidade Tradingview.
Não é de surpreender. Com certeza, está lá.

A julgar pelo gráfico, ele é bastante consistente com a tendência. Mas infelizmente é apenas um sinal de alerta.
O código não parece muito longo, então vamos traduzi-lo e testá-lo. ! (っ•̀ω•́)っ✎⁾⁾!
O código completo do pinheiro é como acima. .
Aqui criamos uma nova estratégia no FMZ e a chamamos de SuperTrade

Em seguida, definimos dois parâmetros Factor e Pd

Para simplificar melhor a operação do código e torná-lo mais fácil de entender, precisamos usar o pacote de expansão de dados avançado do Pythonpandas
Durante o almoço, perguntei à professora Mengmeng se a FMZ apoia esta biblioteca. Verifiquei à tarde e realmente funcionou. A professora Mengmeng é realmente incrível.
1. Precisamos importar a biblioteca de tempo da biblioteca pandas 2. Configure o contrato trimestral na função principal (principalmente executando OKEX) 3. Defina um loop doTicker() para testar uma vez a cada 15 minutos. Execute o código em um ciclo de 15 minutos Em seguida, escrevemos a estratégia principal em 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. Precisamos recuperar o OHCLV da linha K, então use GetRecords() 5. Importamos os dados recuperados para o pandas M15 = pd.DataFrame(records) 6. Precisamos modificar a tag do cabeçalho da tabela. M15.colunas =[‘time’,‘open’,‘high’,‘low’,‘close’,‘volume’,‘OpenInterest’] Na verdade, ele apenas muda as primeiras letras de “open”, “high”, “low” e “close” para minúsculas, para que seja mais fácil escrever código mais tarde sem alternar entre maiúsculas e minúsculas.
def doTicker(records):
M15 = pd.DataFrame(records)
M15.columns = ['time','open','high','low','close','volume','OpenInterest']
7. Adicione uma coluna hl2 ao conjunto de dados hl2=(high+low)/2
#HL2
M15['hl2']=(M15['high']+M15['low'])/2
8. Em seguida, vamos calcular o ATR Como o cálculo do ATR requer a importação de um comprimento variável, seu valor é Pd
Em seguida, nos referimos ao manual da linguagem Mai, e as etapas do algoritmo da média de volatilidade verdadeira do ATR são as seguintes: TR : MAX(MAX((HIGH-LOW),ABS(REF(CLOSE,1)-HIGH)),ABS(REF(CLOSE,1)-LOW)); ATR : RMA(TR,N)
O valor TR é a maior das três diferenças a seguir. 1. A flutuação entre o preço mais alto e o preço mais baixo do dia de negociação atual MÁXIMA-MÍNIMA 2. A flutuação entre o preço de fechamento do dia de negociação anterior e o preço mais alto do dia de negociação atual (REF(CLOSE,1)-HIGH) 3. A flutuação entre o preço de fechamento do dia de negociação anterior e o preço mais baixo do dia de negociação atual (REF(CLOSE,1)-LOW) Então TR: MAX(MAX((ALTO-BAIXO),ABS(REF(FECHAR,1)-ALTO)),ABS(REF(FECHAR,1)-BAIXO));
Em cálculos Python
M15['prev_close']=M15['close'].shift(1)
Primeiro, configure um prev_close para obter os dados de close na linha anterior, ou seja, mova close para a direita em 1 grade para formar um novo parâmetro
ranges= [M15['high'] - M15['low'],M15['high']-M15['prev_close'],M15['low']-M15['prev_close']]
Em seguida, defina uma variável intermediária para registrar a matriz de três valores de comparação de TR. (ALTO-BAIXO)(alto-anterior_fechamento)(baixo-anterior_fechamento)
M15['tr'] = pd.DataFrame(ranges).T.abs().max(axis=1)
Definimos uma nova coluna chamada TR no conjunto de dados. O valor de TR é o valor absoluto máximo da variável intermediária. Usamos as funções abs() e max().
alpha = (1.0 / length) if length > 0 else 0.5
M15['atr']=M15['tr'].ewm(alpha=alpha, min_periods=length).mean()
Por fim, precisamos calcular o valor de ATR, ATR:RMA (TR, N). Descobriu-se que o algoritmo RMA é, na verdade, uma variante de valor fixo do algoritmo EMA. N é a variável que importamos, onde o parâmetro padrão para ATR é 14. Aqui importamos alfa = o recíproco do comprimento.
===
Em seguida, use o algoritmo EWM para calcular a EMA O processo completo de cálculo do ATR é o seguinte
#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 Comece a calcular Up e Dn
M15['Up']=M15['hl2']-(Factor*M15['atr'])
M15['Dn']=M15['hl2']+(Factor*M15['atr'])
Up=hl2 -(Factor * atr) Dn=hl2 +(Factor * atr) Não é simples?
O seguinte é o segmento de código principal das linhas 15-21 na 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
O objetivo principal deste parágrafo é expressar, Se em uma fase de alta, (linha inferior) TrendUp = max(Up,TrendUp[1]) Se estiver em fase de queda, (linha superior) TrendDown=min(Dn,TrendDown[1]) Ou seja, em uma tendência, o valor do ATR vem utilizando uma técnica similar à estratégia Bandit Bollinger. Continue estreitando o outro lado do canal
Aqui, cada cálculo de TrendUp e TrendDown precisa ser autoiterado. Ou seja, cada passo deve ser calculado com base no passo anterior. Então precisamos percorrer o conjunto de dados.
Aqui, primeiro precisamos criar novos campos TrendUp, TrendDown, Trend e linecolor para o conjunto de dados. E dê a eles um valor inicial Em seguida, use a sintaxe fillna(0) para preencher os dados com valores nulos nos resultados calculados anteriormente com 0
M15['TrendUp']=0.0
M15['TrendDown']=0.0
M15['Trend']=1
M15['Tsl']=0.0
M15['linecolor']='Homily'
M15 = M15.fillna(0)
Iniciar um loop for Usando operações ternárias do Python em loops
for x in range(len(M15)):
Calculando TrendUp TrendUp = MAX(Up,TrendUp[-1]) if close[-1]>TrendUp[-1] else Up O significado geral é que se o fechamento anterior > o TrendUp anterior, se for verdadeiro, pegue o valor máximo de Up e o TrendUp anterior, se não for verdadeiro, pegue o valor Up e passe para o TrendUp atual
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]
Da mesma forma, calcule 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]
Abaixo está o sinalizador para calcular a direção do controle. Simplifiquei o pseudocódigo Trend= 1 if (close > TrendDown[-1]) else (x) x = -1 if (close< TrendUp[-1]) else Trend[-1]
O significado é que se o preço de fechamento > o TrendDown anterior, então pegue 1 (altista), caso contrário, pegue x Se o preço de fechamento for menor que o TrendUp anterior, então tome -1 (curto). Se não, tome o Trend anterior (o que significa que ele permanece inalterado). Traduzido em linguagem gráfica, significa romper o trilho superior para mudar a bandeira para alta, romper o trilho inferior para mudar a bandeira para baixa, e os outros tempos permanecem inalterados.
M15['Tsl'].values[x] = M15['TrendUp'].values[x] if (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x]
Calcular Tsl e Linecolor Tsl= rendUp if (Trend==1) else TrendDown Tsl é usado para representar o valor do SuperTrend no gráfico. Isso significa que quando você estiver otimista, marque a faixa inferior no gráfico, e quando estiver pessimista, marque a faixa superior no gráfico. linecolor= ‘green’ if (Trend==1) else ‘red’ O significado de linecolor é se você estiver otimista, marque a linha verde, se estiver pessimista, marque a cor vazia (usado principalmente para exibição do 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'
As linhas 23 a 30 a seguir são principalmente para plotagem, o que não será explicado em detalhes aqui.
Por fim, existem 2 linhas de código para controle de sinal de compra e venda No Tradingview, significa dar um sinal após reverter a Bandeira. Converta instruções condicionais para python. Se a bandeira de tendência anterior mudar de -1 para 1, significa que a resistência superior foi quebrada. Abra longo Se a bandeira de tendência anterior mudar de 1 para -1, significa que o suporte descendente foi quebrado. Abra uma posição curta.
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)
O código completo para esta seção é o seguinte:
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)


Ajustei a estrutura geral do código. E mescle as instruções de ordens longas e curtas relacionadas à estratégia. Aqui está o código completo
'''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);
Link de estratégia pública: https://www.fmz.com/strategy/200625
Selecionamos dados do ano passado para backtesting. Use o contrato trimestral da OKEX com um ciclo de 15 minutos. Os parâmetros definidos são, Factor=3 Pd=45 vol=100 (100 ingressos por pedido) O retorno anualizado é de aproximadamente 33%. Em geral, o recuo não é muito grande. A principal razão para isso é a falha do 312, que teve um impacto significativo no sistema. Se não houvesse 312, os retornos seriam melhores.

SuperTrend é um sistema de negociação muito bom
O princípio principal do sistema SuperTrend é usar a estratégia de rompimento do canal ATR (semelhante ao canal Kent) Mas a principal mudança está no uso da estratégia de estreitamento de Bandit Bollinger, ou princípio Donchian reverso. Os canais superior e inferior estão constantemente se estreitando durante a operação de mercado. Para conseguir a operação de avanço e rotação do canal. (Uma vez rompido o canal, os trilhos superiores e inferiores retornam aos seus valores iniciais)
Eu tracei o TrendUp TrendDn separadamente no TradingView
Isso ajudará você a entender melhor essa estratégia
Claro num piscar de olhos

Há também uma versão js no github. Não entendo muito bem de js, mas a julgar pela instrução if, parece haver algum problema. O endereço éhttps://github.com/Dodo33/gekko-supertrend-strategy/blob/master/Supertrend.js
Finalmente encontrei a versão original. Foi publicado em 2013.05.29 Escrito por Rajandran R Código C++ publicado no fórum Mt4https://www.mql5.com/en/code/viewcode/10851/128437/Non_Repainting_SuperTrend.mq4 Eu entendo aproximadamente o significado de C++ e vou reescrevê-lo quando tiver oportunidade.
Espero que todos possam aprender a essência disso. É difícil. ~!