Backtesting von FX Systre mit Python Nachdem wir den Backtesting-Code geschrieben haben, versuchen wir, die Parameter der Systole zu optimieren. Die Optimierung des Handelssystems bedeutet nicht, heutzutage populäres Deep Learning durchzuführen, sondern einfach die Werte der Parameter der technischen Indikatoren zu ändern, um den mit dem höchsten Bewertungswert zu finden. Dies dient zum Üben der Python-Programmierung.
Backtesting von FX Systre mit Python Bereiten Sie wie bei die historischen Daten von FX vor. Nach wie vor werde ich die Daten für das Stunden-Chart 2015 von EUR / USD erstellen.
import numpy as np
import pandas as pd
import indicators as ind #indicators.Import von 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) #7 Stunden Offset
ohlc = ind.TF_ohlc(dataM1, 'H') #Erstellung von Stundendaten
Verwenden Sie für Indikatoren.py die auf GitHub aufgeführte.
Verwenden Sie dieselbe Backtest-Funktion wie beim letzten Mal. Berechnen Sie Handelsergebnisse und Gewinn / Verlust unter Einbeziehung historischer Daten und Handelssignale.
def Backtest(ohlc, BuyEntry, SellEntry, BuyExit, SellExit, lots=0.1, spread=2):
Open = ohlc['Open'].values #Offener Preis
Point = 0.0001 #Wert von 1pip
if(Open[0] > 50): Point = 0.01 #1 Pip-Wert des Kreuzkreises
Spread = spread*Point #Ausbreitung
Lots = lots*100000 #Tatsächliches Handelsvolumen
N = len(ohlc) #FX-Datengröße
BuyExit[N-2] = SellExit[N-2] = True #Endlich Zwangsausgang
BuyPrice = SellPrice = 0.0 #Verkaufspreis
LongTrade = np.zeros(N) #Handelsinformationen kaufen
ShortTrade = np.zeros(N) #Handelsinformationen verkaufen
LongPL = np.zeros(N) #Gewinn / Verlust der Kaufposition
ShortPL = np.zeros(N) #Gewinn / Verlust der Verkaufsposition
for i in range(1,N):
if BuyEntry[i-1] and BuyPrice == 0: #Einstiegssignal kaufen
BuyPrice = Open[i]+Spread
LongTrade[i] = BuyPrice #Kaufposition offen
elif BuyExit[i-1] and BuyPrice != 0: #Ausgangssignal kaufen
ClosePrice = Open[i]
LongTrade[i] = -ClosePrice #Kaufposition geschlossen
LongPL[i] = (ClosePrice-BuyPrice)*Lots #Gewinn- und Verlustrechnung
BuyPrice = 0
if SellEntry[i-1] and SellPrice == 0: #Eingangssignal verkaufen
SellPrice = Open[i]
ShortTrade[i] = SellPrice #Verkaufsposition offen
elif SellExit[i-1] and SellPrice != 0: #Ausgangssignal verkaufen
ClosePrice = Open[i]+Spread
ShortTrade[i] = -ClosePrice #Verkaufsposition geschlossen
ShortPL[i] = (SellPrice-ClosePrice)*Lots #Gewinn- und Verlustrechnung
SellPrice = 0
return pd.DataFrame({'Long':LongTrade, 'Short':ShortTrade}, index=ohlc.index),\
pd.DataFrame({'Long':LongPL, 'Short':ShortPL}, index=ohlc.index)
Verwenden Sie zur Bewertung des Systems die folgenden Funktionen, um den Gesamtgewinn / -verlust, die Anzahl der Transaktionen, den durchschnittlichen Gewinn / Verlust, den Gewinnfaktor, den maximalen Drawdown und den Wiederherstellungsfaktor zu berechnen. Dies entspricht fast der Ausgabe der MetaTrader-Optimierung.
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()
#Gesamtgewinn und -verlust
Profit = GrossProfit+GrossLoss
#Anzahl der Transaktionen
Trades = LongTrades+ShortTrades
#Durchschnittlicher Gewinn / Verlust
if Trades==0: Average = 0
else: Average = Profit/Trades
#Gewinnfaktor
if GrossLoss==0: PF=100
else: PF = -GrossProfit/GrossLoss
#Maximaler Drawdown
Equity = (LongPL+ShortPL).cumsum()
MDD = (Equity.cummax()-Equity).max()
#Wiederherstellungsfaktor
if MDD==0: RF=100
else: RF = Profit/MDD
return np.array([Profit, Trades, Average, PF, MDD, RF])
Im vorherigen Backtest wurde der Zeitraum für den langfristigen gleitenden Durchschnitt auf 30 und der Zeitraum für den kurzfristigen gleitenden Durchschnitt auf 10 festgelegt. Bei dieser Optimierung werden wir diese beiden Zeiträume jedoch ändern.
Die Änderungsperiode beträgt 10 bis 50 für den langfristigen gleitenden Durchschnitt und 5 bis 30 für den kurzfristigen gleitenden Durchschnitt. Fügen Sie es wie folgt in das Array ein.
SlowMAperiod = np.arange(10, 51) #Bereich der langfristigen gleitenden Durchschnittsperiode
FastMAperiod = np.arange(5, 31) #Bereich des kurzfristigen gleitenden Durchschnittszeitraums
Es gibt 41 und 26 Möglichkeiten für jede Periode, aber die Kombination der beiden Perioden beträgt $ 41 \ mal 26 = 1066 $.
Optimieren Sie durch Ersetzen des Periodenbereichs dieses Parameters. Mit zunehmender Anzahl von Periodenkombinationen kann die Berechnungszeit nicht ignoriert werden. Daher müssen unnötige Berechnungen so weit wie möglich vermieden werden.
Berechnen Sie vorerst die Zeitreihen von 41 und 26 gleitenden Durchschnitten im Voraus. Dann werden für 1066 Kombinationen Kauf- / Verkaufssignale erzeugt, zurückgetestet und ausgewertet, und Parameterwerte und Bewertungswerte werden ausgegeben. Ein Beispiel für den Code lautet wie folgt.
def Optimize(ohlc, SlowMAperiod, FastMAperiod):
SlowMA = np.empty([len(SlowMAperiod), len(ohlc)]) #Langfristiger gleitender Durchschnitt
for i in range(len(SlowMAperiod)):
SlowMA[i] = ind.iMA(ohlc, SlowMAperiod[i])
FastMA = np.empty([len(FastMAperiod), len(ohlc)]) #Kurzfristiger gleitender Durchschnitt
for i in range(len(FastMAperiod)):
FastMA[i] = ind.iMA(ohlc, FastMAperiod[i])
N = len(SlowMAperiod)*len(FastMAperiod)
Eval = np.empty([N, 6]) #Bewertungsgegenstand
Slow = np.empty(N) #Langfristiger gleitender Durchschnittszeitraum
Fast = np.empty(N) #Kurzfristiger gleitender Durchschnittszeitraum
def shift(x, n=1): return np.concatenate((np.zeros(n), x[:-n])) #Schaltfunktion
k = 0
for i in range(len(SlowMAperiod)):
for j in range(len(FastMAperiod)):
#Einstiegssignal kaufen
BuyEntry = (FastMA[j] > SlowMA[i]) & (shift(FastMA[j]) <= shift(SlowMA[i]))
#Eingangssignal verkaufen
SellEntry = (FastMA[j] < SlowMA[i]) & (shift(FastMA[j]) >= shift(SlowMA[i]))
#Ausgangssignal kaufen
BuyExit = SellEntry.copy()
#Ausgangssignal verkaufen
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)
Ich war besorgt über die Berechnungszeit, aber mit der 1,8-GHz-CPU des Core i5-3337U dauerte es ungefähr 12 Sekunden. Ich habe versucht, die gleichen Bedingungen mit MetaTrader 5 zu optimieren, aber es hat fast 50 Sekunden gedauert, daher denke ich, dass es für Python einigermaßen praktisch war.
Sie können den optimalen Parameterwert finden, indem Sie die Optimierungsergebnisse nach Ihren bevorzugten Elementen sortieren. Wenn Sie beispielsweise nach dem Gesamtgewinn / -verlust sortieren, ist dies wie folgt.
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
Daraus ergeben sich für die langfristige gleitende Durchschnittsperiode 27 Werte für die Parameter, die den Gesamtgewinn / -verlust maximieren, und für die kurzfristige gleitende Durchschnittsperiode 8.
Als Test sieht die in diesem Zeitraum erneut getestete Asset-Kurve folgendermaßen aus:
Hört sich gut an. Es ist jedoch selbstverständlich, dass ein solches Ergebnis durch Optimierung der Parameter erzielt werden kann, und es ist nicht sehr erfreulich. Es ist nur eine Enttäuschung, einen weiteren Zeitraum zu testen.
Dieses Mal ist es in Ordnung, schnellere Ergebnisse als der Backtest von MetaTrader zu erzielen. MetaTrader kann auch in Tick-Einheiten Backtesting durchführen, aber ich denke, es wird viel Zeit in Python dauern. Es ist noch ein langer Weg.
Recommended Posts