O Sr. Ran, meu bom amigo, tem observado este indicador há muito tempo e recomendou-mo antes do dia de Ano Novo para discutir se ele pode ser convertido em quantificação. É uma pena que o procrastinador tenha adiado até agora para ajudá-lo a cumprir tal desejo. Estima-se que um dia vou escrever um tradutor para a língua Pine. Tudo pode ser Python. Bem, sem muita tolice, vamos apresentar a lendária linha de super tendência.
Na nova geração de sistema de negociação inteligente no CMC Markets, podemos selecionar
Dê uma breve olhada. Ele descreve principalmente o canal onde HL2 (k-line preço médio) mais n vezes de ATR. Faça uma ruptura de tendência. Mas o artigo é simples, sem algoritmos detalhados, então eu pensei na comunidade mais incrível, TradingView. De facto, está mesmo lá.
Olhando para o gráfico, está em linha com a tendência, infelizmente, é apenas um sinal de alarme de alerta.
O código não é muito longo, então vamos tentar traduzi-lo.!
O código completo do pinheiro é como acima.
Aqui criamos uma nova estratégia no FMZ, chamá-lo de SuperTrend
Em seguida, vamos definir dois parâmetros, Fator e Pd
A fim de simplificar melhor a operação de código e facilitar a compreensão, precisamos usar o pacote de expansão de dados avançado do python
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):
M15 = pd.DataFrame(records)
M15.columns = ['time','open','high','low','close','volume','OpenInterest']
#HL2
M15['hl2']=(M15['high']+M15['low'])/2
Em seguida, referimos-nos ao manual do MyLanguage, e os passos do algoritmo do valor médio da amplitude de flutuação real ATR são os seguintes: TR: MAX ((MAX((HIGH-LOW),ABS ((REF ((CLOSE,1)-HIGH)),ABS ((REF ((CLOSE,1)-LOW)); ATR: RMA(TR,N)
O valor TR é o máximo das três diferenças seguintes:
No cálculo do Python
M15['prev_close']=M15['close'].shift(1)
Precisamos configurar um prev_close para recuperar os dados de fechar na linha anterior, ou seja, mover fechar à direita por uma grade para formar um novo parâmetro
ranges= [M15['high'] - M15['low'],M15['high']-M15['prev_close'],M15['low']-M15['prev_close']]
Em seguida, definimos uma variável intermediária que registra uma matriz de 3 valores contrastantes para TR. (HIGH-LOW) (high-prev_close) (low-prev_close)
M15['tr'] = pd.DataFrame(ranges).T.abs().max(axis=1)
Definimos uma nova coluna no conjunto de dados e nomeamos como TR. O valor de TR é o maior valor absoluto da variável intermediária, usando 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()
Finalmente, precisamos calcular o valor de ATR, ATR: RMA (TR, N). O parâmetro padrão do ATR é 14.
===
Então o algoritmo ewm é usado para calcular o 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 Começar a calcular Up e Dn
M15['Up']=M15['hl2']-(Factor*M15['atr'])
M15['Dn']=M15['hl2']+(Factor*M15['atr'])
Up=hl2 - ((Fator * atr) Dn=hl2 +(Fator * atr) Não é simples?
Aqui está a secção do código principal das linhas 15-21 da 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 ponto principal deste parágrafo é expressar que, Se estiver na fase de alta, (linha inferior) TrendUp=max (Up, TrendUp [1]) Se estiver no estágio de queda, (linha superior) TrendDown=min (Dn, TrendDown [1]) Isto é, em uma tendência, o valor ATR tem usado uma tecnologia semelhante à estratégia Bandit Bollinger. Continua a estreitar o outro lado do canal.
Aqui, cada cálculo de TrendUp e TrendDown requer auto-iteração. Ou seja, cada etapa deve ser calculada de acordo com a etapa anterior. Por conseguinte, o conjunto de dados deve ser iterado no loop.
Primeiro, criamos novos campos TrendUp, TrendDown, Trend, linecolor para o conjunto de dados. Em seguida, usamos a gramática fillna (0) para preencher os dados com valor nulo no resultado calculado 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)
Ativar um loop for Usando a operação ternária Python no loop
for x in range(len(M15)):
Calcular a tendência TrendUp = MAX(Up,TrendUp[-1]) se fechado[-1]>TrendUp[-1] caso contrário Up Isso significa aproximadamente que se o fechamento anterior> anterior TrendUp é verdadeiro, o valor máximo entre Up e o TrendUp anterior será tomado; caso contrário, o valor Up será tomado e passado 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 a tendência para baixo TrendDown=min(Dn,TrendDown[-1]) se fechado[-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]
A seguir está a bandeira para calcular a direção de controle. Tendência= 1 se (fechar > Tendência Baixa[-1]) caso contrário (x) x = -1 se (fechado< Tendência de subida[-1]) caso contrário Tendência[-1]
O significado é que se o preço de fechamento>a tendência anterior, tomar o valor de 1 (bullish). Se o preço de fechamento for inferior ao anterior TrendUp, tome o valor de -1 (baixista). Para traduzir em linguagem de imagem é que uma quebra da bandeira de transição da pista superior para alta; e uma quebra da bandeira de transição da pista inferior para baixa.
M15['Tsl'].values[x] = M15['TrendUp'].values[x] if (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x]
Calcular Tsl e Linecolor
Tzl= RendUp se (Tendência==1) caso contrário TrendDown
Tsl é o valor usado para representar a SuperTendência na imagem. Significa marcar a faixa descendente na imagem quando estamos em alta, e marcar a faixa superior na imagem quando estamos em baixa.
linecolor=
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 próximas 23-30 linhas de código são principalmente desenhos de gráficos, que não são explicados aqui.
Finalmente, há 2 linhas de código para controlar o sinal de compra e venda Em Tradeview, significa que o sinal é dado após inverter a bandeira Converte a instrução condicional para Python. Se o último sinal de tendência mudar de -1 para 1, significa que a resistência superior foi ultrapassada e a posição longa aberta. Se a última bandeira de tendência mudar de 1 para -1, significa que o suporte para baixo foi excedido e a posição curta aberta.
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 é 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 eu fundei as instruções de ordem relacionadas a ir longo e ir curto na 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);
Endereço da estratégia pública:https://www.fmz.com/strategy/200625
Selecionámos os dados do ano passado para testes de retrospectiva. Usamos o contrato trimestral OKEX por um período de 15 minutos. Os parâmetros definidos são: Fator = 3 Pd=45 Vol=100 (100 contratos por encomenda) O rendimento anual é de cerca de 33%. De um modo geral, a retirada não é muito grande. A queda acentuada de 312 teve um impacto relativamente grande no sistema, Se não houver 312, os retornos devem ser melhores.
SuperTrend é um sistema de negociação muito bom
O principal princípio do sistema SuperTrend é adotar a estratégia de ruptura do canal ATR (semelhante ao canal de Kent) No entanto, a sua alteração deve-se principalmente ao uso da estratégia de estreitamento do Bandit Bollinger, ou ao inverso do princípio de Donchian. Na operação de mercado, os canais superior e inferior são continuamente estreitados. A fim de conseguir o funcionamento da direção de ruptura do canal (uma vez que o canal atravessa, as faixas superior e inferior retornarão ao valor inicial)
Eu traço, dn, TrendUp e TrendDn separadamente no TradeView, O que facilita a melhor compreensão da estratégia. Veja-se claramente de uma olhada:
Além disso, há uma versão do js no github. Eu não sou bom no js, mas parece que há algo errado com a instrução if. O endereço:https://github.com/Dodo33/gekko-supertrend-strategy/blob/master/Supertrend.js
Finalmente, encontrei a versão original. Publicado em 29 de maio de 2013 O autor é Rajandran R. O código C++ foi publicado no fórum Mt4:https://www.mql5.com/en/code/viewcode/10851/128437/Non_Repainting_SuperTrend.mq4Eu entendi grosseiramente o significado de C++, e vou reescrevê-lo quando tiver oportunidade.
Espero que possa aprender a essência dela. É difícil!