[Python] Versuchen Sie, die FX-Systolenparameter mit einem genetischen Algorithmus zu optimieren

[Python] Versuchen Sie, die FX-Systolenparameter mit einer zufälligen Suche zu optimieren

Es ist eine Fortsetzung von. Lassen Sie uns einen genetischen Algorithmus (GA) anstelle einer zufälligen Suche implementieren.

Vorbereitung

Die Erstellung von Stundendaten erfolgt wie beim letzten Mal.

import numpy as np
import pandas as pd
import indicators as ind #indicators.Import von py
from backtest import Backtest,BacktestReport

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

Sie benötigen Indikatoren.py und backtest.py, die auf GitHub hochgeladen wurden. Für backtest.py gibt es eine leichte Korrektur in BacktestReport.

Handelssystem zu optimieren

Dieses Mal fügen wir dem Schnittpunktsystem zweier gleitender Durchschnitte ein Signal zur Abrechnung hinzu, um die Kombination der zu optimierenden Parameterwerte zu erhöhen. Das Zahlungssignal ist wie folgt definiert.

In diesem System gibt es 3 Parameter. Dieses Mal werden wir jeden Parameter im folgenden Bereich durchsuchen.

SlowMAperiod = np.arange(7, 151) #Bereich der langfristigen gleitenden Durchschnittsperiode
FastMAperiod = np.arange(5, 131) #Bereich des kurzfristigen gleitenden Durchschnittszeitraums
ExitMAperiod = np.arange(3, 111) #Bereich des gleitenden Durchschnittszeitraums für die Abrechnung

Es gibt ungefähr 2 Millionen Kombinationen. Es ist möglich, alle zu treffen, aber es wird mehrere Stunden dauern.

Hauptroutine

Die Hauptroutine des genetischen Algorithmus ist fast dieselbe wie bei der vorherigen Zufallssuche. Ersetzen Sie einfach die zufällige Suche nach Parametern durch die unten beschriebene genetische Verarbeitung. Zusätzlich fügt das Kauf- und Verkaufssignal ein Abwicklungssignal wie in der obigen Regel hinzu.

def Optimize(ohlc, Prange):
    def shift(x, n=1): return np.concatenate((np.zeros(n), x[:-n])) #Schaltfunktion

    SlowMA = np.empty([len(Prange[0]), len(ohlc)]) #Langfristiger gleitender Durchschnitt
    for i in range(len(Prange[0])):
        SlowMA[i] = ind.iMA(ohlc, Prange[0][i])

    FastMA = np.empty([len(Prange[1]), len(ohlc)]) #Kurzfristiger gleitender Durchschnitt
    for i in range(len(Prange[1])):
        FastMA[i] = ind.iMA(ohlc, Prange[1][i])
    
    ExitMA = np.empty([len(Prange[2]), len(ohlc)]) #Mobiler Durchschnitt für die Zahlung
    for i in range(len(Prange[2])):
        ExitMA[i] = ind.iMA(ohlc, Prange[2][i])
    
    Close = ohlc['Close'].values #Schlusskurs
    
    M = 20 #Anzahl der Personen
    Eval = np.zeros([M, 6])  #Bewertungsgegenstand
    Param = InitParam(Prange, M) #Parameterinitialisierung
    gens = 0 #Anzahl der Generationen
    while gens < 100:
        for k in range(M):
            i0 = Param[k,0]
            i1 = Param[k,1]
            i2 = Param[k,2]
            #Einstiegssignal kaufen
            BuyEntry = (FastMA[i1] > SlowMA[i0]) & (shift(FastMA[i1]) <= shift(SlowMA[i0]))
            #Eingangssignal verkaufen
            SellEntry = (FastMA[i1] < SlowMA[i0]) & (shift(FastMA[i1]) >= shift(SlowMA[i0]))
            #Ausgangssignal kaufen
            BuyExit = (Close < ExitMA[i2]) & (shift(Close) >= shift(ExitMA[i2]))
            #Ausgangssignal verkaufen
            SellExit = (Close > ExitMA[i2]) & (shift(Close) <= shift(ExitMA[i2]))
            #Backtest
            Trade, PL = Backtest(ohlc, BuyEntry, SellEntry, BuyExit, SellExit) 
            Eval[k] = BacktestReport(Trade, PL)
        #Generationswechsel
        Param = Evolution(Param, Eval[:,0], Prange)
        gens += 1
        print(gens, Eval[0,0])
    Slow = Prange[0][Param[:,0]]
    Fast = Prange[1][Param[:,1]]
    Exit = Prange[2][Param[:,2]]
    return pd.DataFrame({'Slow':Slow, 'Fast':Fast, 'Exit':Exit, 'Profit': Eval[:,0], 'Trades':Eval[:,1],
                         'Average':Eval[:,2],'PF':Eval[:,3], 'MDD':Eval[:,4], 'RF':Eval[:,5]},
                         columns=['Slow','Fast','Exit','Profit','Trades','Average','PF','MDD','RF'])

Eine weitere Änderung gegenüber dem letzten Mal besteht darin, dass der Bereich der drei Parameter "SlowMAperiod", "FastMAperiod" und "ExitMAperiod" in einer Liste mit dem Namen "Prange" zusammengefasst und an jede Funktion übergeben wird. Selbst wenn die Anzahl der Parameter zunimmt, kann dies auf diese Weise so behandelt werden, wie es ist.

Genetische Verarbeitung

In der obigen Funktion sind die für GA hinzugefügten Funktionen "InitParam ()" und "Evolution ()". Erstens ist InitParam () die Initialisierung der Parameter jedes Einzelnen.

from numpy.random import randint,choice

#Parameterinitialisierung
def InitParam(Prange, M):
    Param = randint(len(Prange[0]), size=M)
    for i in range(1,len(Prange)):
        Param = np.vstack((Param, randint(len(Prange[i]), size=M)))
    return Param.T

Evolution () enthält einige genetische Prozesse wie folgt:

#Genetische Verarbeitung
def Evolution(Param, Eval, Prange):
    #Wählen Sie Roulette mit Elite-Speicher
    #1 Punkt Kreuzung
    #Nachbarschaftsgeneration
    #Mutation
    return Param

Jeder Prozess wird unten erklärt.

Wählen Sie Roulette mit Elite-Speicher

Wählen Sie zunächst aus den aktuellen Personen die Person aus, die für die nächste Generation übrig bleiben soll. Es gibt verschiedene Möglichkeiten, es auszuwählen, aber es gab eine Numpy-Funktion, die für die Roulette-Auswahl nützlich war. Verwenden wir sie also. Die Roulette-Auswahl ist eine Methode zur probabilistischen Auswahl von Personen, die für die nächste Generation übrig bleiben sollen, entsprechend dem Grad der Anpassungsfähigkeit, der der Bewertungswert des Rückentests ist. Je höher die Anpassungsfähigkeit, desto leichter bleibt es.

Die diesmal verwendete Funktion ist eine Funktion namens "numpy.random.choice ()", die zufällig die erforderliche Anzahl aus der Liste auswählt. Wenn Sie jedoch dem optionalen Argument eine Liste von Auswahlwahrscheinlichkeiten mit dem Namen "p" hinzufügen, ist dies der Fall Es wird nach der Wahrscheinlichkeit wählen. Dies ist die Roulette-Auswahl selbst. Der Code sieht folgendermaßen aus:

    #Wählen Sie Roulette mit Elite-Speicher
    Param = Param[np.argsort(Eval)[::-1]] #Sortieren
    R = Eval-min(Eval)
    R = R/sum(R)
    idx = choice(len(Eval), size=len(Eval), replace=True, p=R)
    idx[0] = 0 #Elite speichern
    Param = Param[idx]

Es ist jedoch unpraktisch, wenn die Wahrscheinlichkeit negativ ist, weshalb sie so korrigiert wurde, dass der Mindestwert für die Anpassungsfähigkeit 0 ist. Wenn Sie nur Roulette auswählen, werden Sie möglicherweise auch bei hoher Anpassungsfähigkeit nicht unglücklich ausgewählt. Daher sortieren wir die Anpassungsfähigkeit so, dass die höchste Person (Elite) immer in der nächsten Generation verbleibt.

1 Punkt Kreuzung

Als nächstes werden die Gene gekreuzt. Dies dient dazu, zwei Individuen auszuwählen und einen Teil ihrer genetischen Informationen miteinander auszutauschen. Es gibt verschiedene Methoden zum Überqueren, aber hier habe ich einen der Parameter ausgewählt und die Vorder- und Rückseite ausgetauscht.

    #1 Punkt Kreuzung
    N = 10
    idx = choice(np.arange(1,len(Param)), size=N, replace=False)
    for i in range(0,N,2):
        ix = idx[i:i+2]
        p = randint(1,len(Prange))
        Param[ix] = np.hstack((Param[ix][:,:p], Param[ix][:,p:][::-1]))

Verwenden Sie erneut "choice ()", um eine Folge von Zufallszahlen für die Anzahl sich überschneidender Personen zu generieren. Durch Hinzufügen von "replace = False" erhalten Sie eine eindeutige Folge von Zufallszahlen. Dann wird die Kreuzung realisiert, indem zwei Personen als "ix" ausgewählt und die Daten in der zweiten Hälfte des Kreuzungspunkts "p" ausgetauscht werden.

Nachbarschaftsgeneration

Bei normaler GA wird die Evolution durch Auswahl, Kreuzung und Mutation simuliert. Wenn der Kreuzungspunkt jedoch wie diesmal auf die Unterbrechung des Parameters beschränkt ist, ist er voll von denselben Personen, bevor Sie ihn kennen. Die Evolution wird aufhören. Wenn es jedoch viele Mutationen gibt, kommt es einer zufälligen Suche nahe, so dass es nicht sehr effizient ist. Daher werden wir diesmal einige der Parameter auf +1 oder -1 ändern. Es handelt sich um eine sogenannte Nachbarschaftslösung, die häufig in lokalen Suchalgorithmen verwendet wird.

    #Nachbarschaftsgeneration
    N = 10
    idx = choice(np.arange(1,len(Param)), size=N, replace=False)
    diff = choice([-1,1], size=N).reshape(N,1)
    for i in range(N):
        p = randint(len(Prange))
        Param[idx[i]][p:p+1] = (Param[idx[i]][p]+diff[i]+len(Prange[p]))%len(Prange[p])

Wählen Sie eine Person aus, die sowohl eine Nachbarschaft als auch ein Kreuz erzeugt. Entscheiden Sie dann, welcher Parameter mit einer Zufallszahl geändert werden soll, und ändern Sie diesen Parameter um 1.

Mutation

Führen Sie schließlich die Mutation durch. Es gibt verschiedene Möglichkeiten, dies zu tun, aber einige der Parameter der ausgewählten Person werden mit neuen Zufallszahlen neu geschrieben. Im Fall von GA sind Mutationen wichtig, um aus der lokalen Lösung herauszukommen. Wenn Sie sie jedoch häufig verwenden, nimmt die Zufälligkeit zu, sodass wir hier etwa zwei festlegen.

    #Mutation
    N = 2
    idx = choice(np.arange(1,len(Param)), size=N, replace=False)
    for i in range(N):
        p = randint(len(Prange))
        Param[idx[i]][p:p+1] = randint(len(Prange[p]))

Ausführungsergebnis

Lassen Sie uns einen genetischen Algorithmus mit der oben definierten Funktion ausführen.

result = Optimize(ohlc, [SlowMAperiod, FastMAperiod, ExitMAperiod])
result.sort_values('Profit', ascending=False)

GA verwendet auch Zufallszahlen, sodass die Ergebnisse jedes Mal anders sind. Das Folgende ist ein Beispiel für die Ergebnisse, die die höchste Anpassungsfähigkeit für jede Generation zeigen. Da es als Elite gespeichert ist, wird die hohe Anpassungsfähigkeit nacheinander aktualisiert.

1 -94.9
2 958.2
3 958.2
4 958.2
5 1030.3
6 1030.3
7 1030.3
8 1454.0
9 1550.9
10 1550.9
11 1850.8
12 1850.8
13 1850.8
14 1850.8
15 1850.8
16 1850.8
17 2022.5
18 2076.5
19 2076.5
20 2076.5
:
61 2076.5
62 2076.5
63 2076.5
64 2076.5
65 2076.5
66 2316.2
67 2316.2
68 2316.2
69 2316.2
70 2316.2
:
95 2316.2
96 2316.2
97 2316.2
98 2316.2
99 2316.2
100 2316.2

Die Individuen, die in der letzten Generation geblieben sind, sind wie folgt.

Slow Fast Exit Profit Trades Average PF MDD RF
0 126 17 107 2316.2 75.0 30.882667 2.306889 387.1 5.983467
18 126 15 107 2316.2 75.0 30.882667 2.306889 387.1 5.983467
8 105 18 106 2210.2 76.0 29.081579 2.247080 387.1 5.709636
17 126 18 108 2130.9 75.0 28.412000 2.158098 424.9 5.015062
10 126 18 107 2078.4 79.0 26.308861 1.980794 448.3 4.636181
9 127 18 107 2074.5 73.0 28.417808 2.184819 371.3 5.587126
6 126 15 7 2030.3 76.0 26.714474 2.007143 415.7 4.884051
16 126 14 107 2024.9 76.0 26.643421 2.100489 424.9 4.765592
5 126 17 107 1954.7 74.0 26.414865 1.917441 448.3 4.360250
13 126 17 105 1878.7 79.0 23.781013 1.888694 414.2 4.535732
2 127 18 107 1872.4 75.0 24.965333 1.878813 448.3 4.176667
12 126 17 101 1869.6 76.0 24.600000 2.063300 420.4 4.447193
11 92 15 107 1859.5 73.0 25.472603 2.006223 358.8 5.182553
14 125 14 108 1843.1 84.0 21.941667 1.811938 473.6 3.891681
4 124 14 107 1839.8 75.0 24.530667 1.975245 420.4 4.376308
3 42 19 107 1796.8 75.0 23.957333 1.912405 410.7 4.374970
1 125 15 106 1614.7 81.0 19.934568 1.711729 386.9 4.173430
19 104 18 107 1583.7 94.0 16.847872 1.654746 393.4 4.025674
7 125 17 106 1421.7 81.0 17.551852 1.629015 574.4 2.475104
15 92 16 107 539.8 103.0 5.240777 1.150513 605.1 0.892084

Ich habe nicht die optimale Lösung für dieses Problem untersucht, daher weiß ich es nicht, aber ich denke, dieses Ergebnis ist ziemlich hoch. Erstens besteht der Zweck von GA nicht darin, die optimale Lösung zu finden, sondern die quasi-optimale Lösung in kürzerer Zeit als Round-Robin zu finden.

Das Finden der optimalen Lösung mit Systre-Parametern bedeutet nicht, dass das System über verschiedene Zeiträume dieselben Ergebnisse liefert. Wenn Sie also in 2000 Versuchen aus 2 Millionen Kombinationen eine anständige Lösung erhalten, sollte es Ihnen gut gehen.

Recommended Posts

[Python] Versuchen Sie, die FX-Systolenparameter mit einem genetischen Algorithmus zu optimieren
[Python] Versuchen Sie, die FX-Systolenparameter durch zufällige Suche zu optimieren
Versuchen Sie, das Problem des Handlungsreisenden mit einem genetischen Algorithmus (Python-Code) zu lösen.
Automatisches FX-Handelssystem mit Python und genetischem Algorithmus Teil 1
Durchsuche das Labyrinth mit dem Python A * -Algorithmus
Versuchen Sie HTML-Scraping mit der Python-Bibliothek
Versuchen Sie, eine Karte mit Python + Cartopy 0.18.0 zu zeichnen
Versuchen Sie, das Problem des Handlungsreisenden mit einem genetischen Algorithmus zu lösen (Theorie)
Versuchen Sie, mit Python eine Lebenskurve zu zeichnen
Versuchen Sie, in Python einen "Entschlüsselungs" -Code zu erstellen
Versuchen Sie, mit Python eine Diedergruppe zu bilden
Versuchen Sie, das Problem des Handlungsreisenden mit einem genetischen Algorithmus zu lösen (Ausführungsergebnis)
Ein * Algorithmus (Python Edition)
Versuchen Sie, Python mit pybind11 in ein C ++ - Programm einzubetten
Versuchen Sie, Python in der mit pipenv erstellten Django-Umgebung auszuführen
Eine Python-Probe zum Lernen von XOR mit einem genetischen Algorithmus in einem neuronalen Netz
Versuchen Sie, mit einer Shell zu programmieren!
Suche nach einer Lösung für das N-Queen-Problem mit einem genetischen Algorithmus (2)
Probieren Sie die Python-Ausgabe mit Haxe 3.2 aus
Versuchen Sie, ein Unterfenster mit PyQt5 und Python zu öffnen
Backtesting von FX Systre mit Python (2)
Machen Sie eine Lotterie mit Python
[Python3] Dikstra-Methode mit 14 Zeilen
Versuchen Sie, Python mit Try Jupyter auszuführen
Versuchen Sie die Gesichtserkennung mit Python
Erstellen Sie ein Verzeichnis mit Python
Suche nach einer Lösung für das N-Queen-Problem mit einem genetischen Algorithmus (1)
Lösung des Planungsproblems der Krankenschwester (Schichtoptimierung) mit einem genetischen Algorithmus
Finden Sie den optimalen Wert der Funktion mit einem genetischen Algorithmus (Teil 2)
Versuchen Sie, eine Python-Umgebung mit Visual Studio Code & WSL zu erstellen
Versuchen Sie, mit Python3 eine Zeichenfolge aus einem Bild zu extrahieren
Versuchen Sie, Ihrer IFC-Datei mit IfcOpenShell Python eine Wand hinzuzufügen
[Python] Was ist eine with-Anweisung?
Löse ABC163 A ~ C mit Python
Python-Grafikhandbuch mit Matplotlib.
Lassen Sie uns eine GUI mit Python erstellen.
Versuchen Sie, Facebook mit Python zu betreiben
Versuchen Sie die Singularwertzerlegung mit Python
Erstellen Sie eine virtuelle Umgebung mit Python!
Schreiben Sie A * (A-Stern) -Algorithmen in Python
Ich habe mit Python eine Lotterie gemacht.
Erstellen einer virtuellen Umgebung mit Python 3
Löse ABC168 A ~ C mit Python
Erstellen Sie ein Empfehlungssystem mit Python
Versuchen Sie die Gesichtserkennung mit Python + OpenCV
[Python] Generiere ein Passwort mit Slackbot
Löse ABC162 A ~ C mit Python
Löse ABC167 A ~ C mit Python
Implementierung eines einfachen Algorithmus in Python 2
Löse ABC158 A ~ C mit Python
Lassen Sie uns ein Diagramm mit Python erstellen! !!
Versuchen Sie die Frequenzsteuerungssimulation mit Python
Implementierung der Dyxtra-Methode durch Python
Führen Sie einen einfachen Algorithmus in Python aus
Erste Schritte mit genetischen Python-Algorithmen
[Python] Erbt eine Klasse mit Klassenvariablen
Ich habe mit Python einen Daemon erstellt
Schreiben Sie ein Batch-Skript mit Python3.5 ~
Berechnen Sie die kürzeste Route eines Diagramms mit der Dyxtra-Methode und Python
Versuchen Sie, mit Python (2) eine Erfassungssoftware zu erstellen, die so genau wie möglich ist.