SuperTrend V.1 -- Système de lignes de super tendance

Auteur:Je ne sais pas., Créé: 2022-12-01 11:36:33, Mis à jour: 2023-09-11 20:04:38

img

I. Origine du récit

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.

II. Introduction du système

Dans la nouvelle génération de système de trading intelligent dans CMC Markets, nous pouvons sélectionner Super Trend Line parmi les indicateurs techniques à utiliser, Nous pouvons ajuster la couleur et l'épaisseur des signaux croissants et décroissants selon nos propres préférences. Avant de comprendre la formule de l'indicateur de super tendance, il est nécessaire de comprendre l'ATR, car la super tendance adopte la valeur ATR pour calculer la valeur de l'indicateur. Les principaux algorithmes sont également décrits dans la figure suivante:

img

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à.

img

Si l'on regarde le graphique, c'est en ligne avec la tendance, malheureusement, c'est seulement un signal d'alarme d'alerte.

III. Apprendre le code source

Le code n'est pas trop long, alors essayons de le traduire.!

img

Le code complet du pin est comme ci-dessus.

VI. Conversion des codes

Ici, nous créons une nouvelle stratégie sur FMZ, appelons-la SuperTrend

img

Ensuite, nous allons définir deux paramètres, Facteur et Pd

img

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, Pandas (https://pandas.pydata.org/) FMZ soutient cette bibliothèque maintenant.

  1. Nous devons importer la bibliothèque des pandas et la bibliothèque du temps
  2. Dans la fonction principale, définir l'utilisation de contrats trimestriels (principalement pour okex)
  3. Réglez un cycle doTicker ((() pour détecter une fois toutes les 15 minutes. Exécutez le code sur une période de 15 minutes Ensuite, nous écrivons la stratégie principale dans 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)
  1. Nous avons besoin de récupérer le OHCLV de K-line, donc nous utilisons GetRecords ((()
  2. Nous importons les données récupérées à panda M15 = pd.DataFrame ((enregistrements)
  3. Nous devons modifier l'étiquette d'en-tête de la table. M15.colonnes = [time,open,high,low,close,volume,OpenInterest] En fait, il s'agit de changer les lettres initiales de open, high, low et close en minuscules, de sorte que notre écriture de code ultérieure n'ait pas besoin de changer de majuscule en minuscule.
def doTicker(records):
    M15 = pd.DataFrame(records)
    M15.columns = ['time','open','high','low','close','volume','OpenInterest']  
  1. Ajouter une colonne à l'ensemble de données hl2 hl2=(haut+bas)/2
#HL2
M15['hl2']=(M15['high']+M15['low'])/2
  1. Alors calculons ATR Parce que le calcul ATR doit importer une longueur variable, dont la valeur est Pd

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:

  1. La fluctuation entre le prix le plus élevé et le prix le plus bas de la journée de négociation en cours HIGH-LOW
  2. La fluctuation entre le prix de clôture de la journée de négociation précédente et le prix le plus élevé de la journée de négociation en cours REF (CLOSE, 1) - HIGH
  3. La fluctuation entre le prix de clôture de la journée de négociation précédente et le prix le plus bas de la journée de négociation en cours REF (CLOSE, 1) - LOW Donc TR: MAX (max) (max) (low),ABS (ref) (close,1) (high),ABS (ref) (close,1) (low)

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= vert si (Tendance==1) autrement rouge La signification de linecolor est de marquer la ligne verte si nous sommes en hausse, et marquer la couleur vide si nous sommes en baisse (principalement à des fins d'affichage Tradeview)

        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)

img img

V. Code complet

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

VI. Tests de retour et résumé

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.

img

VII. Écrire à la fin

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:

img

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!


Relationnée

Plus de