M. Ran, mon bon ami, observe cet indicateur depuis longtemps et me l'a recommandé avant le jour du Nouvel An pour discuter de sa conversion en quantification. C'est dommage que le procrastinateur ait retardé jusqu'à maintenant pour l'aider à réaliser un tel souhait. En fait, ma compréhension des algorithmes s'est rapidement améliorée récemment. Il est estimé qu'un jour j'écrirai un traducteur pour la langue Pine. Tout peut être Python. Eh bien, sans trop de bêtises, présentons la légendaire ligne de super tendance.
Dans la nouvelle génération de système de trading intelligent dans CMC Markets, nous pouvons sélectionner
Prenez un bref coup d'œil. Il décrit principalement le canal où HL2 (k-line prix moyen) plus n fois de ATR. Mais l'article est simple, pas d'algorithme détaillé, alors j'ai pensé à la communauté la plus incroyable, TradingView. En effet, il est vraiment là.
Si l'on regarde le graphique, c'est en ligne avec la tendance, malheureusement, c'est seulement un signal d'alarme d'alerte.
Le code n'est pas trop long, alors essayons de le traduire.!
Le code complet du pin est comme ci-dessus.
Ici, nous créons une nouvelle stratégie sur FMZ, appelons-la SuperTrend
Ensuite, nous allons définir deux paramètres, Facteur et Pd
Afin de mieux simplifier l'opération de code et de faciliter la compréhension, nous devons utiliser le package d'expansion de données avancée de 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
Ensuite, nous nous référons au manuel de MyLanguage, et les étapes de l'algorithme de la valeur moyenne de l'amplitude de fluctuation réelle ATR sont les suivantes: TR: MAX ((MAX (((HIGH-LOW),ABS ((REF ((CLOSE,1) -HIGH)),ABS ((REF ((CLOSE,1) -LOW)); ATR: RMA ((TR,N)
La valeur TR est le maximum des trois différences suivantes:
Dans le calcul de Python
M15['prev_close']=M15['close'].shift(1)
Nous avons besoin de configurer un prev_close pour récupérer les données de close dans la ligne précédente, c'est-à-dire, se déplacer près à droite par une grille pour former un nouveau paramètre
ranges= [M15['high'] - M15['low'],M15['high']-M15['prev_close'],M15['low']-M15['prev_close']]
Ensuite, nous définissons une variable intermédiaire qui enregistre un tableau de 3 valeurs contrastées pour TR. (HIGH-LOW) (high-prev_close) (low-prev_close)
M15['tr'] = pd.DataFrame(ranges).T.abs().max(axis=1)
Nous définissons une nouvelle colonne dans l'ensemble de données et l'appelons TR. La valeur de TR est la plus grande valeur absolue de la variable intermédiaire, en utilisant les fonctions abs () et max ()
alpha = (1.0 / length) if length > 0 else 0.5
M15['atr']=M15['tr'].ewm(alpha=alpha, min_periods=length).mean()
Enfin, nous devons calculer la valeur de ATR, ATR: RMA (TR, N). Il s'avère que l'algorithme RMA est une variante de valeur fixe de l'algorithme EMA en fait. N est la variable que nous importons. le paramètre par défaut de ATR est 14. ici nous importons le réciproque de alpha = longueur.
===
Ensuite, l'algorithme ewm est utilisé pour calculer l'EMA Le processus complet de calcul de l'ATR est le suivant:
#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 Commencez à calculer Up et Dn
M15['Up']=M15['hl2']-(Factor*M15['atr'])
M15['Dn']=M15['hl2']+(Factor*M15['atr'])
Le facteur d'accélération de l'échantillon est le facteur d'accélération de l'échantillon Dn=hl2 +(Facteur * atr) Ce n'est pas simple?
Voici la section du code de base des lignes 15-21 de 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
L'objectif principal de ce paragraphe est d'exprimer que: Si elle est dans la phase haussière, (ligne inférieure) TrendUp=max (Up, TrendUp [1]) S'il est dans la phase de chute, (ligne supérieure) TrendDown=min (Dn, TrendDown [1]) C'est-à-dire que, dans une tendance, la valeur ATR utilise une technologie similaire à la stratégie Bandit Bollinger. Continuez à réduire l'autre côté du canal.
Ici, chaque calcul de TrendUp et TrendDown nécessite une auto-itération. C'est-à-dire que chaque étape doit être calculée selon l'étape précédente. Par conséquent, l'ensemble de données doit être itéré dans la boucle.
Tout d'abord, nous créons de nouveaux champs TrendUp, TrendDown, Trend, linecolor pour l'ensemble de données. Ensuite, nous utilisons la grammaire fillna (0) pour remplir les données avec valeur nulle dans le résultat calculé précédemment avec 0
M15['TrendUp']=0.0
M15['TrendDown']=0.0
M15['Trend']=1
M15['Tsl']=0.0
M15['linecolor']='Homily'
M15 = M15.fillna(0)
Activer une boucle for Utilisation de l'opération ternaire python dans la boucle
for x in range(len(M15)):
Calculer la tendance à la hausse TrendUp = MAX(Up,TrendUp[-1]) si près[-1]>TrendUp[-1] autrement En haut Cela signifie à peu près que si la précédente clôture>la précédente tendance est vraie, la valeur maximale entre Up et la tendance précédente sera prise; sinon, la valeur Up sera prise et transmise à la tendance actuelle
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]
De même, calculer le TrendDown Tendance vers le bas=min(Dn,Tendance vers le bas[-1]) si proche[-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]
Voici le drapeau pour le calcul de la direction de commande. Tendance = 1 si (close > TrendDown[-1]) autrement (x) x = -1 si (close< TrendUp[-1]) autrement Tendance[-1]
La signification est que si le prix de clôture > la tendance descendante précédente, prenez la valeur de 1 (bullish). Si le prix de clôture est inférieur au précédent TrendUp, prenez la valeur de -1 (baisse). Pour traduire en langage d'image, c'est une rupture du drapeau de transition de la piste supérieure pour la hausse; et une rupture du drapeau de transition de la piste inférieure pour la baisse.
M15['Tsl'].values[x] = M15['TrendUp'].values[x] if (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x]
Calculer Tsl et Linecolor
Tsl= rendUp si (Tendance==1) autrement TendanceDown
Tsl est la valeur utilisée pour représenter la SuperTrend sur l'image. Cela signifie marquer la piste vers le bas sur l'image lorsque nous sommes en hausse, et marquer la piste supérieure sur l'image lorsque nous sommes en baisse.
lignecolor=
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'
Les 23 à 30 lignes de code suivantes sont principalement des dessins graphiques, qui ne sont pas expliqués ici.
Enfin, il y a 2 lignes de code pour contrôler le signal d'achat et de vente Dans Tradeview, cela signifie que le signal est donné après inversion du drapeau Convertissez l'instruction conditionnelle en python. Si le dernier indicateur de tendance passe de -1 à 1, cela signifie que la résistance supérieure est dépassée et que la position longue est ouverte. Si le dernier indicateur de tendance change de 1 à -1, cela signifie que le support à la baisse est dépassé et que la position à découvert est ouverte.
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)
Le code complet est le suivant:
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)
J'ai ajusté la structure globale du code. Et j'ai fusionné les instructions d'ordre liées à aller long et court dans la stratégie. Voici le code complet:
'''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);
Adresse de la stratégie publique:https://www.fmz.com/strategy/200625
Nous avons sélectionné les données de l'année dernière pour le backtesting. Nous utilisons le contrat trimestriel OKEX pour une période de 15 minutes. Les paramètres définis sont: Facteur = 3 Pd = 45 Vol=100 (100 contrats par commande) Le rendement annualisé est d'environ 33%. D'une manière générale, le retrait n'est pas très important. La forte diminution de 312 a eu un impact relativement important sur le système, S'il n'y a pas de 312, les rendements devraient être meilleurs.
SuperTrend est un très bon système de trading.
Le principe principal du système SuperTrend est d'adopter une stratégie de percée du canal ATR (similaire au canal Kent) Cependant, son changement est principalement dû à l'utilisation de la stratégie de rétrécissement du Bandit Bollinger, ou l'inverse du principe de Donchian. Dans l'opération de marché, les canaux supérieur et inférieur sont constamment rétrécis. Afin d'obtenir le fonctionnement de la direction de rupture du canal (une fois que le canal est traversé, les voies supérieure et inférieure reviendront à la valeur initiale)
Je trace, dn, TrendUp et TrendDn séparément sur le TradeView, Ce qui facilite la compréhension de la stratégie. On voit bien ça en un coup d'œil:
En outre, il y a une version de js sur github. Je ne suis pas bon en js, mais il semble qu'il y ait quelque chose qui ne va pas avec l'instruction if. L'adresse est:https://github.com/Dodo33/gekko-supertrend-strategy/blob/master/Supertrend.js
Finalement, j'ai retrouvé la version originale. Il a été publié le 29 mai 2013 L'auteur est Rajandran R. Le code C++ a été publié sur le forum Mt4:https://www.mql5.com/en/code/viewcode/10851/128437/Non_Repainting_SuperTrend.mq4J'ai grossièrement compris le sens de C++, et je vais le réécrire quand j'en aurai l'occasion.
J'espère que vous pourrez en apprendre l'essence. C'est difficile!