Backtesting FX System avec Python Maintenant que nous avons écrit le code de backtesting, essayons d'optimiser les paramètres de la systole. Optimiser le système de trading ne signifie pas faire du deep learning, qui est populaire de nos jours, mais simplement changer les valeurs des paramètres des indicateurs techniques pour trouver celui avec la valeur d'évaluation la plus élevée. C'est pour pratiquer la programmation Python.
Backtesting FX System avec Python Comme avec, préparez les données historiques de FX. Comme auparavant, je vais faire les données pour le graphique horaire 2015 de l'EUR / USD.
import numpy as np
import pandas as pd
import indicators as ind #indicators.Importation de py
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
ohlc = ind.TF_ohlc(dataM1, 'H') #Création de données horaires
Pour indicateurs.py, utilisez celui répertorié sur GitHub.
Utilisez la même fonction de backtest que la dernière fois. Calculez les résultats commerciaux et les profits / pertes en incluant des données historiques et des signaux commerciaux.
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)
Pour évaluer le système, utilisez les fonctions suivantes pour calculer le résultat total, le nombre de transactions, le profit / perte moyen, le facteur de profit, le prélèvement maximum et le facteur de recouvrement. C'est presque le même que la sortie de l'optimisation MetaTrader.
def BacktestReport(Trade, PL):
LongPL = PL['Long']
ShortPL = PL['Short']
LongTrades = np.count_nonzero(Trade['Long'])//2
ShortTrades = np.count_nonzero(Trade['Short'])//2
GrossProfit = LongPL.clip_lower(0).sum()+ShortPL.clip_lower(0).sum()
GrossLoss = LongPL.clip_upper(0).sum()+ShortPL.clip_upper(0).sum()
#Total des profits et pertes
Profit = GrossProfit+GrossLoss
#Nombre de transactions
Trades = LongTrades+ShortTrades
#Bénéfice / perte moyen
if Trades==0: Average = 0
else: Average = Profit/Trades
#Facteur de profit
if GrossLoss==0: PF=100
else: PF = -GrossProfit/GrossLoss
#Tirage maximum
Equity = (LongPL+ShortPL).cumsum()
MDD = (Equity.cummax()-Equity).max()
#Facteur de récupération
if MDD==0: RF=100
else: RF = Profit/MDD
return np.array([Profit, Trades, Average, PF, MDD, RF])
Dans le précédent test rétrospectif, la période de moyenne mobile à long terme était fixée à 30 et la période de moyenne mobile à court terme était fixée à 10, mais dans cette optimisation, nous modifierons ces deux périodes.
La période de changement est de 10 à 50 pour la moyenne mobile à long terme et de 5 à 30 pour la moyenne mobile à court terme. Mettez-le dans le tableau comme suit.
SlowMAperiod = np.arange(10, 51) #Gamme de période moyenne mobile à long terme
FastMAperiod = np.arange(5, 31) #Gamme de période moyenne mobile à court terme
Il y a 41 et 26 façons pour chaque période, mais la combinaison des deux périodes est de 41 $ \ fois 26 = 1066 $.
Optimisez en remplaçant la plage de périodes de ce paramètre. À mesure que le nombre de combinaisons de périodes augmente, le temps de calcul ne peut pas être ignoré, il est donc nécessaire d'éliminer autant que possible les calculs inutiles.
Pour le moment, calculez à l'avance les séries chronologiques de 41 et 26 moyennes mobiles. Ensuite, pour 1 066 combinaisons, des signaux d'achat / vente sont générés, testés en amont et évalués, et les valeurs de paramètres et les valeurs d'évaluation sont sorties. Un exemple de code est le suivant.
def Optimize(ohlc, SlowMAperiod, FastMAperiod):
SlowMA = np.empty([len(SlowMAperiod), len(ohlc)]) #Moyenne mobile à long terme
for i in range(len(SlowMAperiod)):
SlowMA[i] = ind.iMA(ohlc, SlowMAperiod[i])
FastMA = np.empty([len(FastMAperiod), len(ohlc)]) #Moyenne mobile à court terme
for i in range(len(FastMAperiod)):
FastMA[i] = ind.iMA(ohlc, FastMAperiod[i])
N = len(SlowMAperiod)*len(FastMAperiod)
Eval = np.empty([N, 6]) #Élément d'évaluation
Slow = np.empty(N) #Période moyenne mobile à long terme
Fast = np.empty(N) #Période moyenne mobile à court terme
def shift(x, n=1): return np.concatenate((np.zeros(n), x[:-n])) #Fonction Shift
k = 0
for i in range(len(SlowMAperiod)):
for j in range(len(FastMAperiod)):
#Acheter le signal d'entrée
BuyEntry = (FastMA[j] > SlowMA[i]) & (shift(FastMA[j]) <= shift(SlowMA[i]))
#Vendre le signal d'entrée
SellEntry = (FastMA[j] < SlowMA[i]) & (shift(FastMA[j]) >= shift(SlowMA[i]))
#Acheter un signal de sortie
BuyExit = SellEntry.copy()
#Vendre un signal de sortie
SellExit = BuyEntry.copy()
#Backtest
Trade, PL = Backtest(ohlc, BuyEntry, SellEntry, BuyExit, SellExit)
Eval[k] = BacktestReport(Trade, PL)
Slow[k] = SlowMAperiod[i]
Fast[k] = FastMAperiod[j]
k += 1
return pd.DataFrame({'Slow':Slow, 'Fast':Fast, 'Profit': Eval[:,0], 'Trades':Eval[:,1],
'Average':Eval[:,2],'PF':Eval[:,3], 'MDD':Eval[:,4], 'RF':Eval[:,5]},
columns=['Slow','Fast','Profit','Trades','Average','PF','MDD','RF'])
result = Optimize(ohlc, SlowMAperiod, FastMAperiod)
Je m'inquiétais du temps de calcul, mais cela a pris environ 12 secondes avec le processeur Core i5-3337U 1,8 GHz. J'ai essayé d'optimiser les mêmes conditions avec MetaTrader 5, mais cela a pris près de 50 secondes, donc je pense que c'était raisonnablement pratique pour Python.
Vous pouvez trouver la valeur de paramètre optimale en triant les résultats d'optimisation par vos éléments préférés. Par exemple, si vous triez par résultat total, ce sera comme suit.
result.sort_values('Profit', ascending=False).head(20)
Slow Fast Profit Trades Average PF MDD RF
445 27.0 8.0 2507.1 264.0 9.496591 1.423497 485.1 5.168213
470 28.0 7.0 2486.0 260.0 9.561538 1.419642 481.2 5.166251
446 27.0 9.0 2263.3 252.0 8.981349 1.376432 624.7 3.623019
444 27.0 7.0 2171.4 272.0 7.983088 1.341276 504.7 4.302358
471 28.0 8.0 2102.3 250.0 8.409200 1.359030 540.3 3.890986
497 29.0 8.0 2093.3 242.0 8.650000 1.365208 603.8 3.466876
495 29.0 6.0 2063.5 256.0 8.060547 1.342172 620.6 3.325008
498 29.0 9.0 2053.5 238.0 8.628151 1.362451 686.5 2.991260
546 31.0 5.0 1959.4 254.0 7.714173 1.344256 529.7 3.699075
520 30.0 5.0 1940.3 276.0 7.030072 1.313538 681.7 2.846267
496 29.0 7.0 1931.5 248.0 7.788306 1.322891 611.3 3.159660
422 26.0 11.0 1903.4 248.0 7.675000 1.309702 708.7 2.685763
523 30.0 8.0 1903.0 232.0 8.202586 1.327680 823.9 2.309746
524 30.0 9.0 1875.8 234.0 8.016239 1.328598 908.6 2.064495
573 32.0 6.0 1820.8 242.0 7.523967 1.320688 639.8 2.845889
420 26.0 9.0 1819.1 258.0 7.050775 1.282035 667.0 2.727286
572 32.0 5.0 1808.2 256.0 7.063281 1.313564 522.9 3.458023
598 33.0 5.0 1799.6 248.0 7.256452 1.317183 613.2 2.934768
419 26.0 8.0 1777.4 274.0 6.486861 1.273817 552.7 3.215849
434 26.0 23.0 1739.6 368.0 4.727174 1.241049 1235.5 1.408013
À partir de là, les valeurs des paramètres qui maximisent le profit / perte total sont de 27 pour la période de moyenne mobile à long terme et de 8 pour la période de moyenne mobile à court terme.
À titre de test, la courbe d'actifs testée en amont pendant cette période ressemble à ceci:
Ça m'a l'air bien. Cependant, il est naturel qu'un tel résultat puisse être obtenu en optimisant les paramètres, et ce n'est pas très agréable. Vous êtes simplement déçu par un backtesting pendant une autre période.
Cette fois, il est normal d'obtenir des résultats plus rapides que le backtest de MetaTrader. MetaTrader peut également effectuer un backtest en unités de ticks, mais je pense que cela prendra beaucoup de temps pour le faire en Python. Il reste encore un long chemin à parcourir.
Recommended Posts