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