Backtesting von FX Systre mit Python (1)

Einführung

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.

Backtesting Systre mit Python

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.

Erfassung historischer FX-Daten

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.

HistData.com

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

Erstellung beliebiger Zeitrahmendaten

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

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.

Technische Indikatoren erstellen

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

Anzeige der technischen Indikatoren

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.

chart_y.png

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.

chart_m.png

Es ist sehr praktisch.

Handelsregeln für das gleitende durchschnittliche Kreuzungssystem

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.

Backtest ausführen

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.

Bewertung des Handelssystems

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)

equity.png

Anzeige des Handelsdiagramms

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.

trade.png

Da es sich um ein Transferhandelssystem handelt, können Sie sehen, dass die Long-Position und die Short-Position abwechselnd angezeigt werden.

Zusammenfassung

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

Backtesting von FX Systre mit Python (1)
Backtesting von FX Systre mit Python (2)
Optimierung der FX-Systolenparameter in Python
Quadtree in Python --2
CURL in Python
Metaprogrammierung mit Python
Python 3.3 mit Anaconda
Geokodierung in Python
SendKeys in Python
Metaanalyse in Python
Unittest in Python
[FX] Hit oanda-API mit Python mit Docker
Zwietracht in Python
DCI in Python
Quicksort in Python
nCr in Python
N-Gramm in Python
Programmieren mit Python
Plink in Python
Konstante in Python
SQLite in Python
Schritt AIC in Python
LINE-Bot [0] in Python
CSV in Python
Reverse Assembler mit Python
Reflexion in Python
Konstante in Python
nCr in Python.
Format in Python
Scons in Python 3
Puyopuyo in Python
Python in Virtualenv
PPAP in Python
Reflexion in Python
Chemie mit Python
Hashbar in Python
DirectLiNGAM in Python
LiNGAM in Python
In Python reduzieren
In Python flach drücken
Zeigen Sie FX (Forex) Daten Candle Stick in Python an
Sortierte Liste in Python
Täglicher AtCoder # 36 mit Python
Clustertext in Python
AtCoder # 2 jeden Tag mit Python
Täglicher AtCoder # 32 in Python
Täglicher AtCoder # 6 in Python
Bearbeiten Sie Schriftarten in Python
Singleton-Muster in Python
Dateioperationen in Python
Lesen Sie DXF mit Python
Täglicher AtCoder # 53 in Python
Tastenanschlag in Python
Verwenden Sie config.ini mit Python
Täglicher AtCoder # 33 in Python
Löse ABC168D in Python
Logistische Verteilung in Python
Täglicher AtCoder # 7 in Python