Es gibt viele Bibliotheken, die Systre mit Python backtesten, aber es ist für Leute, die über MetaTrader eingegeben haben, schwierig zu verstehen. Deshalb habe ich den Code für das Backtesting nach dem Üben von Python geschrieben.
In der ersten Version habe ich jedoch geschrieben, dass es zuerst funktioniert, daher war es ziemlich verschwenderisch und die Ausführungsgeschwindigkeit war langsam, also habe ich diesmal versucht, es ein wenig zu verbessern.
Bei Aktienkursen gibt es viele Dinge, die direkt von Yahoo! usw. heruntergeladen werden können. Bei FX können jedoch Daten mit mehreren Zeitrahmen wie 5 Minuten oder 15 Minuten verwendet werden, also 1 Minute als Basisdaten. Ich möchte Fußdaten.
In diesem Fall sind die Daten auch groß, daher halte ich es für bequemer, die heruntergeladenen Daten im Voraus zu lesen. Laden Sie es hier als Beispieldaten von der folgenden Site herunter.
Es gibt verschiedene Formate für dieselben Daten, aber ich werde die Daten im generischen ASCII-Format herunterladen, da ich die Pandas-Funktion verwende.
Lesen wir nun die heruntergeladenen 1-Minuten-EUR / USD-Daten für 2015'DAT_ASCII_EURUSD_M1_2015.csv 'mit der Pandas-Funktion. Wenn dies jedoch unverändert bleibt, beginnt der Marktpreis zu Beginn der Woche am Sonntag, sodass ich ihn um 7 Stunden verzögere und am Montag ab Mitternacht beginne.
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) #7 Stunden Offset
Erstellen Sie als Nächstes Daten für einen beliebigen Zeitrahmen aus den 1-Minuten-Daten. Auch dies können Sie ganz einfach mit den Pandas-Funktionen resample ()
und ohlc ()
tun.
#Eine Funktion, die vierbeinige Daten für einen von tf angegebenen Zeitrahmen aus df-Daten erstellt
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') #Erstellung von Stundendaten
Das für tf
angegebene Schlüsselwort lautet
'T'
'H'
'D'
'W'
'M'
Schreiben Sie also für 15 Minuten einfach "15T". Auf diese Weise können Sie sogar Minuten Daten erstellen. Hier habe ich 1-Stunden-Daten gemacht.
Die in den Handelsregeln von Systre verwendeten technischen Indikatoren werden auf GitHub erstellt. Sie können es verwenden, indem Sie nur Indikatoren abrufen. Ich werde den Quellcode weglassen, aber ich habe es vermieden, Pandas für interne Iterationen zu verwenden, und Numba verwendet, daher denke ich, dass es keine zeitaufwändigen Funktionen mehr gibt. ~~ Aufgrund des Algorithmus dauert die parabolische SAR jedoch einige Zeit. ~~
Beispielsweise kann ein gleitender Durchschnitt von 10 bar und ein gleitender Durchschnitt von 30 bar wie folgt geschrieben werden:
import indicators as ind #indicators.Import von py
FastMA = ind.iMA(ohlc, 10) #Kurzfristiger gleitender Durchschnitt
SlowMA = ind.iMA(ohlc, 30) #Langfristiger gleitender Durchschnitt
Sie können die Plotfunktion von Pandas verwenden, um den erstellten technischen Index im Diagramm anzuzeigen. Da Sie ihn jedoch möglicherweise vergrößern oder verkleinern möchten, wird er häufig auf marktbezogenen Websites HighCharts angezeigt. Verwenden wir die in /) angezeigte Python-Bibliothek.
Hier verwende ich die einfach zu installierenden pandas-highcharts.
Installation ist
pip install pandas-highcharts
Ist in Ordnung. Der Code zur Anzeige des Schlusskurses von FX und der beiden gleitenden Durchschnitte lautet wie folgt.
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)
Wenn Sie dies tun, sehen Sie ein Diagramm wie dieses.
Wenn Sie einen Wert von einem Jahr anzeigen, überlappen sich die gleitenden Durchschnitte kaum. Wenn Sie also richtig hineinzoomen, sieht es so aus und Sie können den Unterschied in den gleitenden Durchschnitten erkennen.
Es ist sehr praktisch.
Als Beispiel für ein Handelssystem nehmen wir das klassische gleitende Durchschnittskreuzungssystem. Die Kauf- und Verkaufsregeln sind
So einfach ist das. Dieses Signal ist ein Einstiegssignal, und das Ausstiegssignal zum Schließen einer Position ist, dass der Kaufausgang mit dem Verkaufseingang und der Verkaufsausgang mit dem Kaufeintrag identisch ist. Dies ist der sogenannte Transferhandel.
#Einstiegssignal kaufen
BuyEntry = ((FastMA > SlowMA) & (FastMA.shift() <= SlowMA.shift())).values
#Eingangssignal verkaufen
SellEntry = ((FastMA < SlowMA) & (FastMA.shift() >= SlowMA.shift())).values
#Ausgangssignal kaufen
BuyExit = SellEntry.copy()
#Ausgangssignal verkaufen
SellExit = BuyEntry.copy()
Hier ist jedes Signal ein boolartiges Array von numpy. Wenn Sie normal darüber nachdenken, würden Sie das Signal wahrscheinlich beurteilen, während Sie die Zeitreihendaten drehen. Im Fall von Python ist es jedoch schneller, sie gemeinsam in einem Array zu verarbeiten. Fügen Sie das Signal also in ein Array ein.
Wir werden endlich einen Backtest durchführen. Geben Sie historische Daten und das obige Handelssignal ein und geben Sie den tatsächlichen Handelspreis und den Gewinn / Verlust als Zeitreihendaten aus.
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)
Trade, PL = Backtest(ohlc, BuyEntry, SellEntry, BuyExit, SellExit)
In der vorherigen Version war dieser Teil in mehrere Phasen unterteilt, aber diesmal habe ich versucht, ihn zusammenzufassen. Ursprünglich wäre es gut, wenn wir nur mit Signalen handeln könnten, aber es gibt Fälle, in denen Signale ausgegeben werden, selbst wenn es Positionen gibt, also haben wir auch die Existenz von Positionen überprüft.
In der Ausgabe dieser Funktion speichert Trade
den Kauf- / Verkaufspreis als positiven Wert und den Abrechnungspreis als negativen Wert für jeden Kauf und Verkauf. Realisierte Gewinne und Verluste zum Zeitpunkt der Abrechnung werden in "PL" gespeichert.
Mit den Informationen zu "Handel" und "PL" können Sie das Handelssystem grob bewerten. Zum Beispiel sieht es so aus.
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('Anzahl der Kaufgeschäfte=', LongTrades)
print('Anzahl der gewinnenden Trades=', LongWinTrades)
print('Maximaler Gewinnhandel=', LongPL.max())
print('Durchschnittlicher Gewinnhandel=', round(LongPL.clip_lower(0).sum()/LongWinTrades, 2))
print('Anzahl der negativen Trades=', LongLoseTrades)
print('Maximaler negativer Handel=', LongPL.min())
print('Durchschnittlicher negativer Handel=', round(LongPL.clip_upper(0).sum()/LongLoseTrades, 2))
print('Gewinnrate=', 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('Anzahl der Verkaufsgeschäfte=', ShortTrades)
print('Anzahl der gewinnenden Trades=', ShortWinTrades)
print('Maximaler Gewinnhandel=', ShortPL.max())
print('Durchschnittlicher Gewinnhandel=', round(ShortPL.clip_lower(0).sum()/ShortWinTrades, 2))
print('Anzahl der negativen Trades=', ShortLoseTrades)
print('Maximaler negativer Handel=', ShortPL.min())
print('Durchschnittlicher negativer Handel=', round(ShortPL.clip_upper(0).sum()/ShortLoseTrades, 2))
print('Gewinnrate=', round(ShortWinTrades/ShortTrades*100, 2), '%\n')
Trades = LongTrades + ShortTrades
WinTrades = LongWinTrades+ShortWinTrades
LoseTrades = LongLoseTrades+ShortLoseTrades
print('Gesamtzahl der Trades=', Trades)
print('Anzahl der gewinnenden Trades=', WinTrades)
print('Maximaler Gewinnhandel=', max(LongPL.max(), ShortPL.max()))
print('Durchschnittlicher Gewinnhandel=', round((LongPL.clip_lower(0).sum()+ShortPL.clip_lower(0).sum())/WinTrades, 2))
print('Anzahl der negativen Trades=', LoseTrades)
print('Maximaler negativer Handel=', min(LongPL.min(), ShortPL.min()))
print('Durchschnittlicher negativer Handel=', round((LongPL.clip_upper(0).sum()+ShortPL.clip_upper(0).sum())/LoseTrades, 2))
print('Gewinnrate=', 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('Gesamtgewinn=', round(GrossProfit, 2))
print('Gesamtverlust=', round(GrossLoss, 2))
print('Gesamtgewinn und -verlust=', round(Profit, 2))
print('Gewinnfaktor=', round(-GrossProfit/GrossLoss, 2))
print('Durchschnittlicher Gewinn / Verlust=', round(Profit/Trades, 2))
print('Maximaler Drawdown=', round(MDD, 2))
print('Wiederherstellungsfaktor=', round(Profit/MDD, 2))
return Equity
Equity = BacktestReport(Trade, PL)
Anzahl der Kaufgeschäfte= 113
Anzahl der gewinnenden Trades= 39
Maximaler Gewinnhandel= 440.4
Durchschnittlicher Gewinnhandel= 82.57
Anzahl der negativen Trades= 74
Maximaler negativer Handel= -169.4
Durchschnittlicher negativer Handel= -43.89
Gewinnrate= 34.51 %
Anzahl der Verkaufsgeschäfte= 113
Anzahl der gewinnenden Trades= 49
Maximaler Gewinnhandel= 327.6
Durchschnittlicher Gewinnhandel= 78.71
Anzahl der negativen Trades= 64
Maximaler negativer Handel= -238.5
Durchschnittlicher negativer Handel= -43.85
Gewinnrate= 43.36 %
Gesamtzahl der Trades= 226
Anzahl der gewinnenden Trades= 88
Maximaler Gewinnhandel= 440.4
Durchschnittlicher Gewinnhandel= 80.42
Anzahl der negativen Trades= 138
Maximaler negativer Handel= -238.5
Durchschnittlicher negativer Handel= -43.87
Gewinnrate= 38.94 %
Gesamtgewinn= 7077.0
Gesamtverlust= -6054.4
Gesamtgewinn und -verlust= 1022.6
Gewinnfaktor= 1.17
Durchschnittlicher Gewinn / Verlust= 4.52
Maximaler Drawdown= 1125.4
Wiederherstellungsfaktor= 0.91
Hier wird jedes Element angezeigt, aber beim maschinellen Lernen oder Optimieren muss nur der Bewertungswert berechnet werden.
Oft möchten Sie die Asset-Kurve als Systembewertung sehen. In der obigen Funktion wird die Asset-Kurve als "Eigenkapital" ausgegeben. Wenn Sie also das ursprüngliche Asset hinzufügen und ein Diagramm erstellen, sieht es wie folgt aus.
Initial = 10000 #Anfangsvermögen
display_charts(pd.DataFrame({'Equity':Equity+Initial}), chart_type="stock", title="Asset-Kurve", figsize=(640,480), grid=True)
Lassen Sie uns abschließend anzeigen, wo Sie in der Tabelle gekauft und verkauft haben. Es ist in Ordnung, nur die Punkte anzuzeigen, die Sie gekauft und verkauft haben, aber ich weiß nicht, wie das in HighCharts geht. Deshalb werde ich versuchen, die geöffneten und die geschlossenen Punkte mit einer Linie zu verbinden.
Lassen Sie uns die "Handels" -Informationen in eine Zeile mit dem folgenden Code konvertieren.
def PositionLine(trade):
PosPeriod = 0 #Positionszeitraum
Position = False #Vorhandensein oder Nichtvorhandensein einer Position
Line = trade.copy()
for i in range(len(Line)):
if trade[i] > 0: Position = True
elif Position: PosPeriod += 1 #Zählen Sie die Dauer einer Position
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 #Interpolieren Sie die Dauer der Position
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="Handelschart", figsize=(640,480), grid=True)
Es wird in High Charts auf die gleiche Weise angezeigt. Versuchen Sie daher, entsprechend zu zoomen.
Da es sich um ein Transferhandelssystem handelt, können Sie sehen, dass die Long-Position und die Short-Position abwechselnd angezeigt werden.
Ich habe den Code in Python geschrieben, um das System zu testen, sodass die Handelsregeln nur durch technische Indikatoren beschrieben werden können. Die Bibliothek, die Diagramme von Python bis HighCharts anzeigen kann, war sehr praktisch. Es gibt noch Raum für Verbesserungen, daher möchte ich weiter schreiben, wenn ich Lust dazu habe.
Die Fortsetzung ist hier. Backtesting FX Systre mit Python (2)
Der in diesem Artikel veröffentlichte Code wurde unten hochgeladen. MT5IndicatorsPy/EA_sample.ipynb
Recommended Posts