[Python] Essayez d'optimiser les paramètres de systole FX avec un algorithme génétique

[Python] Essayez d'optimiser les paramètres de la systole FX avec une recherche aléatoire

C'est une continuation de. Implémentons un algorithme génétique (GA) au lieu d'une recherche aléatoire.

Préparation

La création des données horaires est la même que la dernière fois.

import numpy as np
import pandas as pd
import indicators as ind #indicators.Importation de 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) #Décalage de 7 heures
ohlc = ind.TF_ohlc(dataM1, 'H') #Création de données horaires

Vous aurez besoin des indicateurs.py et backtest.py téléchargés sur GitHub. Pour backtest.py, il y a une légère correction dans BacktestReport.

Système de trading à optimiser

Cette fois, afin d'augmenter la combinaison de valeurs de paramètres à optimiser, nous ajouterons un signal de tassement au système d'intersection de deux moyennes mobiles. Le signal de paiement est défini comme suit.

Dans ce système, il y a 3 paramètres. Cette fois, nous rechercherons chaque paramètre dans la plage suivante.

SlowMAperiod = np.arange(7, 151) #Gamme de période moyenne mobile à long terme
FastMAperiod = np.arange(5, 131) #Gamme de période moyenne mobile à court terme
ExitMAperiod = np.arange(3, 111) #Gamme de période moyenne mobile pour le règlement

Il existe environ 2 millions de combinaisons. Il est possible de tout frapper, mais cela prendra plusieurs heures.

Routine principale

La routine principale de l'algorithme génétique est presque la même que la recherche aléatoire précédente. Remplacez simplement la recherche aléatoire de paramètres par le traitement génétique décrit ci-dessous. De plus, le signal d'achat et de vente ajoute un signal de règlement comme dans la règle ci-dessus.

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

    SlowMA = np.empty([len(Prange[0]), len(ohlc)]) #Moyenne mobile à long terme
    for i in range(len(Prange[0])):
        SlowMA[i] = ind.iMA(ohlc, Prange[0][i])

    FastMA = np.empty([len(Prange[1]), len(ohlc)]) #Moyenne mobile à court terme
    for i in range(len(Prange[1])):
        FastMA[i] = ind.iMA(ohlc, Prange[1][i])
    
    ExitMA = np.empty([len(Prange[2]), len(ohlc)]) #Moyenne mobile pour le paiement
    for i in range(len(Prange[2])):
        ExitMA[i] = ind.iMA(ohlc, Prange[2][i])
    
    Close = ohlc['Close'].values #le dernier prix
    
    M = 20 #Nombre d'individus
    Eval = np.zeros([M, 6])  #Élément d'évaluation
    Param = InitParam(Prange, M) #Initialisation des paramètres
    gens = 0 #Nombre de générations
    while gens < 100:
        for k in range(M):
            i0 = Param[k,0]
            i1 = Param[k,1]
            i2 = Param[k,2]
            #Acheter le signal d'entrée
            BuyEntry = (FastMA[i1] > SlowMA[i0]) & (shift(FastMA[i1]) <= shift(SlowMA[i0]))
            #Vendre un signal d'entrée
            SellEntry = (FastMA[i1] < SlowMA[i0]) & (shift(FastMA[i1]) >= shift(SlowMA[i0]))
            #Acheter un signal de sortie
            BuyExit = (Close < ExitMA[i2]) & (shift(Close) >= shift(ExitMA[i2]))
            #Vendre un signal de sortie
            SellExit = (Close > ExitMA[i2]) & (shift(Close) <= shift(ExitMA[i2]))
            #Backtest
            Trade, PL = Backtest(ohlc, BuyEntry, SellEntry, BuyExit, SellExit) 
            Eval[k] = BacktestReport(Trade, PL)
        #Changement de génération
        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'])

Un autre changement par rapport à la dernière fois est que la plage des trois paramètres «SlowMAperiod», «FastMAperiod» et «ExitMAperiod »est regroupée dans une liste appelée« Prange »et transmise à chaque fonction. En faisant cela, même si le nombre de paramètres augmente, il peut être manipulé tel quel.

Traitement génétique

Dans la fonction ci-dessus, les fonctions ajoutées pour GA sont ʻInitParam () et ʻEvolution (). Premièrement, ʻInitParam () `est l'initialisation des paramètres de chaque individu.

from numpy.random import randint,choice

#Initialisation des paramètres
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 () `contient un traitement génétique comme suit:

#Traitement génétique
def Evolution(Param, Eval, Prange):
    #Sélectionnez la roulette avec stockage élite
    #1 point de passage
    #Génération de quartier
    #mutation
    return Param

Chaque processus est expliqué ci-dessous.

Sélectionnez la roulette avec stockage élite

Tout d'abord, sélectionnez l'individu à laisser pour la prochaine génération parmi les individus actuels. Il existe plusieurs façons de le sélectionner, mais il existe une fonction Numpy qui est utile pour la sélection de la roulette, alors utilisons-la. La sélection à la roulette est une méthode de sélection probabiliste des individus à laisser pour la prochaine génération en fonction du degré d'adaptabilité, qui est la valeur d'évaluation du test arrière. Plus l'adaptabilité est élevée, plus il est facile de rester.

La fonction utilisée cette fois est une fonction appelée numpy.random.choice (), qui sélectionne aléatoirement le nombre requis dans la liste, mais si vous ajoutez une liste de probabilités de sélection appelée p à l'argument optionnel, cela Il choisira en fonction de la probabilité. C'est la sélection de roulette elle-même. Le code ressemble à ceci:

    #Sélectionnez la roulette avec stockage élite
    Param = Param[np.argsort(Eval)[::-1]] #Trier
    R = Eval-min(Eval)
    R = R/sum(R)
    idx = choice(len(Eval), size=len(Eval), replace=True, p=R)
    idx[0] = 0 #Sauvegarde élite
    Param = Param[idx]

Cependant, il n'est pas pratique que la probabilité soit négative, elle a donc été corrigée pour que la valeur minimale d'adaptabilité soit de 0. De plus, si vous ne sélectionnez que la roulette, vous ne serez peut-être pas malencontreusement sélectionné même si l'adaptabilité est élevée, nous trions donc l'adaptabilité afin que l'individu le plus élevé (élite) reste toujours dans la génération suivante.

1 point de passage

Ensuite, les gènes sont croisés. Il s'agit de sélectionner deux individus et d'échanger une partie de leurs informations génétiques entre eux. Il existe plusieurs méthodes de croisement, mais ici j'ai choisi l'un des paramètres et j'ai échangé l'avant et l'arrière.

    #1 point de passage
    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]))

Encore une fois, utilisez «choice ()» pour générer une séquence de nombres aléatoires pour le nombre d'individus se croisant. En ajoutant replace = False, vous pouvez obtenir une séquence unique de nombres aléatoires. Ensuite, en sélectionnant deux individus comme «x» et en échangeant les données dans la dernière moitié de l'intersection «p», un croisement est réalisé.

Génération de quartier

En GA normale, l'évolution est simulée par sélection, croisement et mutation, mais si le point de croisement est limité à la rupture du paramètre comme cette fois, il sera plein des mêmes individus avant que vous ne le sachiez. L'évolution s'arrêtera. Cependant, s'il y a beaucoup de mutations, ce sera proche d'une recherche aléatoire, donc ce n'est pas très efficace. Par conséquent, cette fois, nous changerons certains des paramètres en +1 ou -1. Il s'agit d'une solution dite de voisinage, souvent utilisée dans les algorithmes de recherche locaux.

    #Génération de quartier
    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])

Sélectionnez un individu qui produit un quartier ainsi qu'une croix. Ensuite, décidez quel paramètre changer avec un nombre aléatoire et changez ce paramètre de 1.

mutation

Enfin, effectuez la mutation. Il existe plusieurs façons de procéder, mais certains des paramètres de l'individu sélectionné sont réécrits avec de nouveaux nombres aléatoires. Dans le cas de GA, les mutations sont importantes pour sortir de la solution locale, mais si vous les utilisez beaucoup, le caractère aléatoire augmentera, donc ici nous en définirons environ deux.

    #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]))

Résultat d'exécution

Exécutons un algorithme génétique en utilisant la fonction définie ci-dessus.

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

GA utilise également des nombres aléatoires, les résultats seront donc différents à chaque fois. Voici un exemple des résultats, montrant la plus grande adaptabilité pour chaque génération. Puisqu'il est enregistré en tant qu'élite, la haute adaptabilité sera mise à jour séquentiellement.

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

Les individus qui sont restés dans la dernière génération sont les suivants.

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

Je n'ai pas étudié la solution optimale à ce problème, donc je ne sais pas, mais je pense que ce résultat est raisonnablement élevé. En premier lieu, le but de GA n'est pas de trouver la solution optimale, mais de trouver la solution quasi optimale dans un temps plus court que le round-robin.

En fait, trouver la solution optimale avec les paramètres Systre ne signifie pas que le système produira les mêmes résultats sur des périodes de temps différentes. Donc, si vous obtenez une solution décente dans 2000 essais sur 2 millions de combinaisons, tout devrait bien se passer.

Recommended Posts

[Python] Essayez d'optimiser les paramètres de systole FX avec un algorithme génétique
[Python] Essayez d'optimiser les paramètres de la systole FX avec une recherche aléatoire
Essayez de résoudre le problème du voyageur de commerce avec un algorithme génétique (code Python)
Système de trading automatique FX réalisé avec python et algorithme génétique Partie 1
Rechercher le labyrinthe avec l'algorithme python A *
Essayez le scraping HTML avec la bibliothèque Python
Essayez de dessiner une carte avec python + cartopy 0.18.0
Essayez de résoudre le problème du voyageur de commerce avec un algorithme génétique (théorie)
Essayez de dessiner une courbe de vie avec python
Essayez de créer un code de "décryptage" en Python
Essayez de créer un groupe de dièdre avec Python
Essayez de résoudre le problème du voyageur de commerce avec un algorithme génétique (résultat de l'exécution)
Algorithme A * (édition Python)
Essayez d'incorporer Python dans un programme C ++ avec pybind11
Essayez d'exécuter python dans l'environnement Django créé avec pipenv
Un exemple de python pour apprendre XOR avec un algorithme génétique sur un réseau neuronal
Essayez de programmer avec un shell!
Trouver une solution au problème N-Queen avec un algorithme génétique (2)
Essayez la sortie Python avec Haxe 3.2
Essayez d'ouvrir une sous-fenêtre avec PyQt5 et Python
Backtesting FX Systre avec Python (2)
Faites une loterie avec Python
[Python3] Méthode Dikstra avec 14 lignes
Essayez d'exécuter Python avec Try Jupyter
Essayez la reconnaissance faciale avec Python
Créer un répertoire avec python
Trouver une solution au problème N-Queen avec un algorithme génétique (1)
Résolution du problème d'horaire des infirmières (optimisation des équipes) avec un algorithme génétique
Trouvez la valeur optimale de la fonction à l'aide d'un algorithme génétique (partie 2)
Essayez de créer un environnement python avec Visual Studio Code et WSL
Essayez d'extraire une chaîne de caractères d'une image avec Python3
Essayez d'ajouter un mur à votre fichier IFC avec IfcOpenShell python
[Python] Qu'est-ce qu'une instruction with?
Résoudre ABC163 A ~ C avec Python
Manuel de graphisme Python avec Matplotlib.
Faisons une interface graphique avec python.
Essayez d'exploiter Facebook avec Python
Essayez la décomposition de valeurs singulières avec Python
Créez un environnement virtuel avec Python!
Ecrire des algorithmes A * (A-star) en Python
J'ai fait une loterie avec Python.
Créer un environnement virtuel avec Python 3
Résoudre ABC168 A ~ C avec Python
Créer un système de recommandation avec python
Essayez la reconnaissance faciale avec python + OpenCV
[Python] Générer un mot de passe avec Slackbot
Résoudre ABC162 A ~ C avec Python
Résoudre ABC167 A ~ C avec Python
Implémentation d'un algorithme simple en Python 2
Résoudre ABC158 A ~ C avec Python
Faisons un graphe avec python! !!
Essayez la simulation de contrôle de fréquence avec Python
Implémentation de la méthode Dyxtra par python
Exécutez un algorithme simple en Python
Premiers pas avec les algorithmes génétiques Python
[Python] Hériter d'une classe avec des variables de classe
J'ai créé un démon avec Python
Ecrire un script batch avec Python3.5 ~
Calculer l'itinéraire le plus court d'un graphe avec la méthode Dyxtra et Python
Essayez de créer un logiciel de capture aussi précis que possible avec python (2)