Il existe de nombreuses bibliothèques qui testent Systre avec Python, mais il est difficile pour les personnes qui sont entrées depuis MetaTrader de comprendre, alors j'ai écrit le code pour le backtest après avoir pratiqué Python.
Système de backtesting avec Python
Cependant, dans la première version, j'ai écrit que cela fonctionne en premier, donc c'était assez coûteux et la vitesse d'exécution était lente, alors j'ai essayé de l'améliorer un peu cette fois.
Dans le cas des cours boursiers, il y a beaucoup de choses qui peuvent être téléchargées directement à partir de Yahoo! Etc., mais dans le cas de FX, des données de plusieurs périodes de temps telles que 5 minutes ou 15 minutes peuvent être utilisées, donc 1 minute comme données de base. Je veux des données sur les pieds.
Dans ce cas, les données sont également volumineuses, donc je pense qu'il est plus pratique de lire les données téléchargées à l'avance. Ici, téléchargez-le comme exemple de données à partir du site suivant.
Il existe plusieurs formats pour les mêmes données, mais je vais télécharger les données au format générique ASCII car j'utilise la fonction pandas.
Maintenant, lisons les données téléchargées sur 1 minute EUR / USD pour 2015'DAT_ASCII_EURUSD_M1_2015.csv 'avec la fonction pandas. Cependant, si cela reste tel quel, le prix du marché en début de semaine commencera le dimanche, donc je vais le retarder de 7 heures et commencer à minuit le lundi.
import numpy as np
import pandas as pd
dataM1 = pd.read_csv('DAT_ASCII_EURUSD_M1_2015.csv', sep=';',
                     names=('Time','Open','High','Low','Close', ''),
                     index_col='Time', parse_dates=True)
dataM1.index += pd.offsets.Hour(7) #Décalage de 7 heures
Ensuite, créez des données pour n'importe quel intervalle de temps à partir des données d'une minute. Encore une fois, vous pouvez facilement le faire en utilisant les fonctions pandas resample (), ʻohlc () `.
#Une fonction qui crée des données à quatre pattes pour une période spécifiée par tf à partir de données df
def TF_ohlc(df, tf):
    x = df.resample(tf).ohlc()
    O = x['Open']['open']
    H = x['High']['high']
    L = x['Low']['low']
    C = x['Close']['close']
    ret = pd.DataFrame({'Open': O, 'High': H, 'Low': L, 'Close': C},
                       columns=['Open','High','Low','Close'])
    return ret.dropna()
ohlc = TF_ohlc(dataM1, 'H') #Création de données horaires
Le mot-clé spécifié pour tf est
Donc, pendant 15 minutes, écrivez simplement `` 15T ''. Avec cela, vous pouvez créer même des minutes de données. Ici, j'ai fait des données d'une heure.
Les indicateurs techniques utilisés dans les règles de trading de Systre sont créés sur GitHub. Vous pouvez l'utiliser en récupérant uniquement les indicateurs.py. Je vais omettre le code source, mais j'ai évité d'utiliser des pandas pour les itérations internes et utilisé Numba, donc je pense qu'il n'y a plus de fonctions qui prennent du temps. ~~ Cependant, le SAR parabolique prend un certain temps en raison de l'algorithme. ~~
Par exemple, une moyenne mobile de 10 barres et une moyenne mobile de 30 barres peuvent être écrites comme suit:
import indicators as ind #indicators.Importation de py
FastMA = ind.iMA(ohlc, 10) #Moyenne mobile à court terme
SlowMA = ind.iMA(ohlc, 30) #Moyenne mobile à long terme
Vous pouvez utiliser la fonction de tracé des pandas pour afficher l'index technique créé sur le graphique, mais comme vous voudrez peut-être l'agrandir ou le réduire, vous le voyez souvent sur les sites liés au marché HighCharts. Utilisons la bibliothèque Python affichée dans /).
Ici, je vais utiliser le facile à installer pandas-highcharts.
L'installation est
pip install pandas-highcharts
Est OK. Le code pour afficher le cours de clôture du FX et les deux moyennes mobiles est le suivant.
from pandas_highcharts.display import display_charts
df = pd.DataFrame({'Close': ohlc['Close'], 'FastMA': FastMA, 'SlowMA': SlowMA})
display_charts(df, chart_type="stock", title="MA cross", figsize=(640,480), grid=True)
Lorsque vous faites cela, vous verrez un graphique comme celui-ci.

Si vous affichez la valeur d'un an, les moyennes mobiles se chevaucheront à peine, donc si vous effectuez un zoom avant correctement, cela ressemblera à ceci, et vous pourrez voir la différence dans les moyennes mobiles.

C'est assez pratique.
Comme exemple de système de trading, nous prendrons le système classique de croisement de moyenne mobile. Les règles d'achat et de vente sont
C'est aussi simple que ça. Ce signal est un signal d'entrée, et le signal de sortie pour fermer une position est que la sortie d'achat est la même que l'entrée de vente et la sortie de vente est la même que l'entrée d'achat. C'est ce qu'on appelle le trading de transfert.
#Acheter le signal d'entrée
BuyEntry = ((FastMA > SlowMA) & (FastMA.shift() <= SlowMA.shift())).values
#Vendre un signal d'entrée
SellEntry = ((FastMA < SlowMA) & (FastMA.shift() >= SlowMA.shift())).values
#Acheter un signal de sortie
BuyExit = SellEntry.copy()
#Vendre un signal de sortie
SellExit = BuyEntry.copy()
Ici, chaque signal est un tableau de type booléen de numpy. Si vous y réfléchissez normalement, vous jugeriez probablement le signal en tournant les données de la série chronologique, mais dans le cas de Python, il est plus rapide de les traiter collectivement dans un tableau, alors mettez le signal dans un tableau.
Nous allons enfin effectuer un backtest. Entrez les données historiques et le signal de négociation ci-dessus, et sortez le prix de négociation réel et le profit / perte sous forme de données de série chronologique.
def Backtest(ohlc, BuyEntry, SellEntry, BuyExit, SellExit, lots=0.1, spread=2):
    Open = ohlc['Open'].values #Prix ouvert
    Point = 0.0001 #Valeur de 1pip
    if(Open[0] > 50): Point = 0.01 #1 valeur pip du cercle croisé
    Spread = spread*Point #Propagé
    Lots = lots*100000 #Volume d'échange réel
    N = len(ohlc) #Taille des données FX
    BuyExit[N-2] = SellExit[N-2] = True #Enfin sortie forcée
    BuyPrice = SellPrice = 0.0 #Prix de vente
    
    LongTrade = np.zeros(N) #Acheter des informations commerciales
    ShortTrade = np.zeros(N) #Vendre des informations commerciales
    
    LongPL = np.zeros(N) #Gain / perte de position d'achat
    ShortPL = np.zeros(N) #Gain / perte de position de vente
    for i in range(1,N):
        if BuyEntry[i-1] and BuyPrice == 0: #Acheter le signal d'entrée
            BuyPrice = Open[i]+Spread
            LongTrade[i] = BuyPrice #Achat de position ouverte
        elif BuyExit[i-1] and BuyPrice != 0: #Acheter un signal de sortie
            ClosePrice = Open[i]
            LongTrade[i] = -ClosePrice #Position d'achat fermée
            LongPL[i] = (ClosePrice-BuyPrice)*Lots #Règlement des profits et pertes
            BuyPrice = 0
        if SellEntry[i-1] and SellPrice == 0: #Vendre un signal d'entrée
            SellPrice = Open[i]
            ShortTrade[i] = SellPrice #Position de vente ouverte
        elif SellExit[i-1] and SellPrice != 0: #Vendre un signal de sortie
            ClosePrice = Open[i]+Spread
            ShortTrade[i] = -ClosePrice #Position de vente fermée
            ShortPL[i] = (SellPrice-ClosePrice)*Lots #Règlement des profits et pertes
            SellPrice = 0
    return pd.DataFrame({'Long':LongTrade, 'Short':ShortTrade}, index=ohlc.index),\
            pd.DataFrame({'Long':LongPL, 'Short':ShortPL}, index=ohlc.index)
        
Trade, PL = Backtest(ohlc, BuyEntry, SellEntry, BuyExit, SellExit)
Dans la version précédente, cette partie était divisée en plusieurs étapes, mais cette fois j'ai essayé de la résumer. À l'origine, ce serait bien si nous ne pouvions négocier qu'avec des signaux, mais il y a des cas où des signaux sont émis même s'il y a des positions, nous avons donc également vérifié l'existence de positions.
Dans la sortie de cette fonction, «Commerce» stocke le prix d'achat / de vente comme une valeur positive et le prix de règlement comme une valeur négative pour chaque achat et vente. Les gains et pertes réalisés au moment du règlement sont stockés dans «PL».
Avec les informations sur «Commerce» et «PL», vous pouvez évaluer approximativement le système commercial. Par exemple, cela ressemble à ceci.
def BacktestReport(Trade, PL):
    LongPL = PL['Long']
    LongTrades = np.count_nonzero(Trade['Long'])//2
    LongWinTrades = np.count_nonzero(LongPL.clip_lower(0))
    LongLoseTrades = np.count_nonzero(LongPL.clip_upper(0))
    print('Nombre de transactions d'achat=', LongTrades)
    print('Nombre de trades gagnants=', LongWinTrades)
    print('Échange de gains maximum=', LongPL.max())
    print('Échange de gains moyen=', round(LongPL.clip_lower(0).sum()/LongWinTrades, 2))
    print('Nombre de transactions négatives=', LongLoseTrades)
    print('Échange négatif maximum=', LongPL.min())
    print('Commerce négatif moyen=', round(LongPL.clip_upper(0).sum()/LongLoseTrades, 2))
    print('Taux de réussite=', round(LongWinTrades/LongTrades*100, 2), '%\n')
    ShortPL = PL['Short']
    ShortTrades = np.count_nonzero(Trade['Short'])//2
    ShortWinTrades = np.count_nonzero(ShortPL.clip_lower(0))
    ShortLoseTrades = np.count_nonzero(ShortPL.clip_upper(0))
    print('Nombre de transactions de vente=', ShortTrades)
    print('Nombre de trades gagnants=', ShortWinTrades)
    print('Échange de gains maximum=', ShortPL.max())
    print('Échange de gains moyen=', round(ShortPL.clip_lower(0).sum()/ShortWinTrades, 2))
    print('Nombre de transactions négatives=', ShortLoseTrades)
    print('Échange négatif maximum=', ShortPL.min())
    print('Commerce négatif moyen=', round(ShortPL.clip_upper(0).sum()/ShortLoseTrades, 2))
    print('Taux de réussite=', round(ShortWinTrades/ShortTrades*100, 2), '%\n')
    Trades = LongTrades + ShortTrades
    WinTrades = LongWinTrades+ShortWinTrades
    LoseTrades = LongLoseTrades+ShortLoseTrades
    print('Nombre total de transactions=', Trades)
    print('Nombre de trades gagnants=', WinTrades)
    print('Échange de gains maximum=', max(LongPL.max(), ShortPL.max()))
    print('Échange de gains moyen=', round((LongPL.clip_lower(0).sum()+ShortPL.clip_lower(0).sum())/WinTrades, 2))
    print('Nombre de transactions négatives=', LoseTrades)
    print('Échange négatif maximum=', min(LongPL.min(), ShortPL.min()))
    print('Commerce négatif moyen=', round((LongPL.clip_upper(0).sum()+ShortPL.clip_upper(0).sum())/LoseTrades, 2))
    print('Taux de réussite=', round(WinTrades/Trades*100, 2), '%\n')
    GrossProfit = LongPL.clip_lower(0).sum()+ShortPL.clip_lower(0).sum()
    GrossLoss = LongPL.clip_upper(0).sum()+ShortPL.clip_upper(0).sum()
    Profit = GrossProfit+GrossLoss
    Equity = (LongPL+ShortPL).cumsum()
    MDD = (Equity.cummax()-Equity).max()
    print('Bénéfice total=', round(GrossProfit, 2))
    print('Perte totale=', round(GrossLoss, 2))
    print('Total des profits et pertes=', round(Profit, 2))
    print('Facteur de profit=', round(-GrossProfit/GrossLoss, 2))
    print('Bénéfice / perte moyen=', round(Profit/Trades, 2))
    print('Tirage maximum=', round(MDD, 2))
    print('Facteur de récupération=', round(Profit/MDD, 2))
    return Equity
Equity = BacktestReport(Trade, PL)
Nombre de transactions d'achat= 113
Nombre de trades gagnants= 39
Échange de gains maximum= 440.4
Échange de gains moyen= 82.57
Nombre de transactions négatives= 74
Échange négatif maximum= -169.4
Commerce négatif moyen= -43.89
Taux de réussite= 34.51 %
Nombre de transactions de vente= 113
Nombre de trades gagnants= 49
Échange de gains maximum= 327.6
Échange de gains moyen= 78.71
Nombre de transactions négatives= 64
Échange négatif maximum= -238.5
Commerce négatif moyen= -43.85
Taux de réussite= 43.36 %
Nombre total de transactions= 226
Nombre de trades gagnants= 88
Échange de gains maximum= 440.4
Échange de gains moyen= 80.42
Nombre de transactions négatives= 138
Échange négatif maximum= -238.5
Commerce négatif moyen= -43.87
Taux de réussite= 38.94 %
Bénéfice total= 7077.0
Perte totale= -6054.4
Total des profits et pertes= 1022.6
Facteur de profit= 1.17
Bénéfice / perte moyen= 4.52
Tirage maximum= 1125.4
Facteur de récupération= 0.91
Ici, chaque élément est affiché, mais lors de l'exécution de l'apprentissage automatique ou de l'optimisation, il est uniquement nécessaire de calculer la valeur d'évaluation.
Vous voudrez souvent voir la courbe des actifs comme une évaluation du système. Dans la fonction ci-dessus, la courbe d'actif est sortie en tant que ʻEquity`, donc si vous ajoutez les actifs initiaux et créez un graphique, ce sera comme suit.
Initial = 10000 #Actifs initiaux
display_charts(pd.DataFrame({'Equity':Equity+Initial}), chart_type="stock", title="Courbe des actifs", figsize=(640,480), grid=True)

Enfin, montrons où vous avez acheté et vendu sur le graphique. Il est normal d'afficher uniquement les points que vous avez achetés et vendus, mais je ne sais pas comment le faire dans HighCharts, alors je vais essayer de relier les points qui se sont ouverts et les points qui ont fermé la position avec une ligne.
Convertissons les informations Trade en une ligne avec le code suivant.
def PositionLine(trade):
    PosPeriod = 0 #Période de poste
    Position = False #Présence ou absence de poste
    Line = trade.copy()
    for i in range(len(Line)):
        if trade[i] > 0: Position = True 
        elif Position: PosPeriod += 1 #Compter la durée d'un poste
        if trade[i] < 0:
            if PosPeriod > 0:
                Line[i] = -trade[i]
                diff = (Line[i]-Line[i-PosPeriod])/PosPeriod
                for j in range(i-1, i-PosPeriod, -1):
                    Line[j] = Line[j+1]-diff #Interpoler la durée du poste
                PosPeriod = 0
                Position = False
        if trade[i] == 0 and not Position: Line[i] = 'NaN'
    return Line
df = pd.DataFrame({'Open': ohlc['Open'],
                   'Long': PositionLine(Trade['Long'].values),
                   'Short': PositionLine(Trade['Short'].values)})
display_charts(df, chart_type="stock", title="Graphique du commerce", figsize=(640,480), grid=True)
Il sera affiché dans les graphiques élevés de la même manière, essayez donc de zoomer correctement.

Puisqu'il s'agit d'un système de transfert, vous pouvez voir que la position longue et la position courte apparaissent alternativement.
J'ai écrit le code en Python pour backtester le système afin que les règles de trading ne puissent être décrites que par des indicateurs techniques. La bibliothèque qui peut afficher des graphiques de Python à HighCharts était assez pratique. Il y a encore place à l'amélioration, alors j'aimerais continuer à écrire quand j'en ai envie.
La suite est ici. Backtesting FX System avec Python (2)
Le code publié dans cet article a été téléchargé ci-dessous. MT5IndicatorsPy/EA_sample.ipynb
Recommended Posts