Optimisation des paramètres de systole FX en Python

Backtesting FX System avec Python Maintenant que nous avons écrit le code de backtesting, essayons d'optimiser les paramètres de la systole. Optimiser le système de trading ne signifie pas faire du deep learning, qui est populaire de nos jours, mais simplement changer les valeurs des paramètres des indicateurs techniques pour trouver celui avec la valeur d'évaluation la plus élevée. C'est pour pratiquer la programmation Python.

Préparation

Backtesting FX System avec Python Comme avec, préparez les données historiques de FX. Comme auparavant, je vais faire les données pour le graphique horaire 2015 de l'EUR / USD.

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

Pour indicateurs.py, utilisez celui répertorié sur GitHub.

Backtest et son évaluation

Utilisez la même fonction de backtest que la dernière fois. Calculez les résultats commerciaux et les profits / pertes en incluant des données historiques et des signaux commerciaux.

def Backtest(ohlc, BuyEntry, SellEntry, BuyExit, SellExit, lots=0.1, spread=2):
    Open = ohlc['Open'].values #Prix ouvert
    Point = 0.0001 #Valeur de 1pip
    if(Open[0] > 50): Point = 0.01 #1 valeur pip du cercle croisé
    Spread = spread*Point #Propagé
    Lots = lots*100000 #Volume d'échange réel
    N = len(ohlc) #Taille des données FX
    BuyExit[N-2] = SellExit[N-2] = True #Enfin sortie forcée
    BuyPrice = SellPrice = 0.0 #Prix de vente
    
    LongTrade = np.zeros(N) #Acheter des informations commerciales
    ShortTrade = np.zeros(N) #Vendre des informations commerciales
    
    LongPL = np.zeros(N) #Gain / perte de position d'achat
    ShortPL = np.zeros(N) #Gain / perte de position de vente

    for i in range(1,N):
        if BuyEntry[i-1] and BuyPrice == 0: #Acheter le signal d'entrée
            BuyPrice = Open[i]+Spread
            LongTrade[i] = BuyPrice #Achat de position ouverte
        elif BuyExit[i-1] and BuyPrice != 0: #Acheter un signal de sortie
            ClosePrice = Open[i]
            LongTrade[i] = -ClosePrice #Position d'achat fermée
            LongPL[i] = (ClosePrice-BuyPrice)*Lots #Règlement des profits et pertes
            BuyPrice = 0

        if SellEntry[i-1] and SellPrice == 0: #Vendre un signal d'entrée
            SellPrice = Open[i]
            ShortTrade[i] = SellPrice #Position de vente ouverte
        elif SellExit[i-1] and SellPrice != 0: #Vendre un signal de sortie
            ClosePrice = Open[i]+Spread
            ShortTrade[i] = -ClosePrice #Position de vente fermée
            ShortPL[i] = (SellPrice-ClosePrice)*Lots #Règlement des profits et pertes
            SellPrice = 0

    return pd.DataFrame({'Long':LongTrade, 'Short':ShortTrade}, index=ohlc.index),\
            pd.DataFrame({'Long':LongPL, 'Short':ShortPL}, index=ohlc.index)

Pour évaluer le système, utilisez les fonctions suivantes pour calculer le résultat total, le nombre de transactions, le profit / perte moyen, le facteur de profit, le prélèvement maximum et le facteur de recouvrement. C'est presque le même que la sortie de l'optimisation MetaTrader.

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()
    #Total des profits et pertes
    Profit = GrossProfit+GrossLoss
    #Nombre de transactions
    Trades = LongTrades+ShortTrades
    #Bénéfice / perte moyen
    if Trades==0: Average = 0
    else: Average = Profit/Trades
    #Facteur de profit
    if GrossLoss==0: PF=100
    else: PF = -GrossProfit/GrossLoss
    #Tirage maximum
    Equity = (LongPL+ShortPL).cumsum()
    MDD = (Equity.cummax()-Equity).max()
    #Facteur de récupération
    if MDD==0: RF=100
    else: RF = Profit/MDD
    return np.array([Profit, Trades, Average, PF, MDD, RF])

Paramètres à optimiser et leur plage

Dans le précédent test rétrospectif, la période de moyenne mobile à long terme était fixée à 30 et la période de moyenne mobile à court terme était fixée à 10, mais dans cette optimisation, nous modifierons ces deux périodes.

La période de changement est de 10 à 50 pour la moyenne mobile à long terme et de 5 à 30 pour la moyenne mobile à court terme. Mettez-le dans le tableau comme suit.

SlowMAperiod = np.arange(10, 51) #Gamme de période moyenne mobile à long terme
FastMAperiod = np.arange(5, 31)  #Gamme de période moyenne mobile à court terme

Il y a 41 et 26 façons pour chaque période, mais la combinaison des deux périodes est de 41 $ \ fois 26 = 1066 $.

optimisation

Optimisez en remplaçant la plage de périodes de ce paramètre. À mesure que le nombre de combinaisons de périodes augmente, le temps de calcul ne peut pas être ignoré, il est donc nécessaire d'éliminer autant que possible les calculs inutiles.

Pour le moment, calculez à l'avance les séries chronologiques de 41 et 26 moyennes mobiles. Ensuite, pour 1 066 combinaisons, des signaux d'achat / vente sont générés, testés en amont et évalués, et les valeurs de paramètres et les valeurs d'évaluation sont sorties. Un exemple de code est le suivant.

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

    FastMA = np.empty([len(FastMAperiod), len(ohlc)]) #Moyenne mobile à court terme
    for i in range(len(FastMAperiod)):
        FastMA[i] = ind.iMA(ohlc, FastMAperiod[i])
    
    N = len(SlowMAperiod)*len(FastMAperiod)
    Eval = np.empty([N, 6]) #Élément d'évaluation
    Slow = np.empty(N) #Période moyenne mobile à long terme
    Fast = np.empty(N) #Période moyenne mobile à court terme
    def shift(x, n=1): return np.concatenate((np.zeros(n), x[:-n])) #Fonction Shift
    k = 0
    for i in range(len(SlowMAperiod)):
        for j in range(len(FastMAperiod)):
            #Acheter le signal d'entrée
            BuyEntry = (FastMA[j] > SlowMA[i]) & (shift(FastMA[j]) <= shift(SlowMA[i]))
            #Vendre le signal d'entrée
            SellEntry = (FastMA[j] < SlowMA[i]) & (shift(FastMA[j]) >= shift(SlowMA[i]))
            #Acheter un signal de sortie
            BuyExit = SellEntry.copy()
            #Vendre un signal de sortie
            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)

Je m'inquiétais du temps de calcul, mais cela a pris environ 12 secondes avec le processeur Core i5-3337U 1,8 GHz. J'ai essayé d'optimiser les mêmes conditions avec MetaTrader 5, mais cela a pris près de 50 secondes, donc je pense que c'était raisonnablement pratique pour Python.

Résultats d'optimisation

Vous pouvez trouver la valeur de paramètre optimale en triant les résultats d'optimisation par vos éléments préférés. Par exemple, si vous triez par résultat total, ce sera comme suit.

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

À partir de là, les valeurs des paramètres qui maximisent le profit / perte total sont de 27 pour la période de moyenne mobile à long terme et de 8 pour la période de moyenne mobile à court terme.

À titre de test, la courbe d'actifs testée en amont pendant cette période ressemble à ceci: chart.png

Ça m'a l'air bien. Cependant, il est naturel qu'un tel résultat puisse être obtenu en optimisant les paramètres, et ce n'est pas très agréable. Vous êtes simplement déçu par un backtesting pendant une autre période.

Cette fois, il est normal d'obtenir des résultats plus rapides que le backtest de MetaTrader. MetaTrader peut également effectuer un backtest en unités de ticks, mais je pense que cela prendra beaucoup de temps pour le faire en Python. Il reste encore un long chemin à parcourir.

Recommended Posts

Optimisation des paramètres de systole FX en Python
Backtesting FX Systre avec Python (1)
Python en optimisation
Utiliser le magasin de paramètres en Python
Backtesting FX Systre avec Python (2)
Résoudre les problèmes d'optimisation avec Python
Transférer les valeurs des paramètres en Python
Implémentation de l'estimation des paramètres HMM en python
GPyOpt, un package d'optimisation bayésienne en Python
[FX] Hit oanda-API avec Python en utilisant Docker
J'ai essayé d'utiliser l'optimisation bayésienne de Python
Quadtree en Python --2
CURL en Python
Géocodage en python
SendKeys en Python
Méta-analyse en Python
Unittest en Python
Époque en Python
Discord en Python
Allemand en Python
DCI en Python
tri rapide en python
nCr en python
N-Gram en Python
Programmation avec Python
Plink en Python
Constante en Python
FizzBuzz en Python
Sqlite en Python
Étape AIC en Python
LINE-Bot [0] en Python
CSV en Python
Assemblage inversé avec Python
Réflexion en Python
Constante en Python
nCr en Python.
format en python
Scons en Python 3
Puyopuyo en python
python dans virtualenv
PPAP en Python
Quad-tree en Python
Réflexion en Python
Chimie avec Python
Hashable en Python
DirectLiNGAM en Python
LiNGAM en Python
Aplatir en Python
Aplatir en python
Afficher la bougie de données FX (forex) en Python
Liste triée en Python
AtCoder # 36 quotidien avec Python
Texte de cluster en Python
AtCoder # 2 tous les jours avec Python
Daily AtCoder # 32 en Python
Daily AtCoder # 6 en Python
Daily AtCoder # 18 en Python
Modifier les polices en Python
Motif singleton en Python