Documentation

Applications

Dernière mise à jour le 22. 2. 2022 par Mark Fric

Exécution programmée de backtests de stratégie

Cet exemple montre comment exécuter des backtests de stratégie de manière programmatique en appelant notre moteur de backtesting. L'exemple est présenté sous la forme d'un extrait d'analyse personnalisée.

Le code de backtesting peut théoriquement être inclus dans n'importe quel autre extrait, mais il faut savoir que les backtests sont lents et qu'il n'est donc pas recommandé d'exécuter des backtests dans des colonnes de banques de données ou d'autres extraits similaires qui doivent être rapides.

Vous pouvez télécharger l'extrait complet en pièce jointe à cet article, son code source complet sera également publié à la fin de l'article.

 

Nous décrirons les parties les plus importantes du backtest étape par étape. Le backtest lui-même est implémenté dans la méthode d'analyse personnalisée. processDatabank()où il effectue un backtest personnalisé de la toute première stratégie de la banque de données.

 

Utilisation de BacktestEngine pour exécuter un backtest

L'exécution d'un backtest est relativement simple, il suffit d'initialiser BacktestEngine puis appeler sa méthode runBacktest() et ensuite getResults() pour obtenir le résultat du backtest.

// créer un simulateur de trading (utilisé plus tard pour le moteur de backtest)
ITradingSimulator simulator = new MetaTrader4Simulator() ;

// définir la précision de test pour le simulateur
simulator.setTestPrecision(Precisions.getPrecision(Precisions.PRECISION_BASE_TF)) ;

// créer un moteur de backtest à l'aide du simulateur
BacktestEngine backtestEngine = new BacktestEngine(simulator) ;
backtestEngine.setSingleThreaded(true) ;

// ajouter les paramètres de backtest au moteur
backtestEngine.addSetup(settings) ;

// ------------------------
// exécuter le backtest - ceci lancera le backtest réel en utilisant les paramètres configurés ci-dessus
// En fonction des paramètres, cela peut prendre un certain temps.
// Une fois terminé, il renvoie un nouvel objet ResultsGroup contenant les résultats du backtest.
// En cas d'erreur, une exception est levée avec la description de l'erreur.
ResultsGroup backtestResultRG = backtestEngine.runBacktest().getResults() ;

 

Nous créons d'abord un simulateur de trading d'un type donné (MT4, MT5, Tradestation, etc.) et définissons la précision de son backtest.

Ensuite, nous créerons BacktestEngine en utilisant ce simulateur. Nous devons ajouter des paramètres qui décrivent d'autres paramètres de backtest au moteur en utilisant addSetup() - Les réglages eux-mêmes seront expliqués plus loin.

La dernière chose à faire est d'appeler runBacktest().getResults() pour exécuter le backtest réel et obtenir ses résultats.

 

Création de paramètres pour le backtest

Le BacktestEngine doit être configuré avec des paramètres - un objet SettingsMap. Il s'agit d'une carte de paires clé -> valeur, dans laquelle vous pouvez définir divers paramètres du backtest.

SettingsMap settings = new SettingsMap() ;

// préparer la configuration du graphique pour le backtest - vous devez spécifier le nom des données, la plage, etc.
ChartSetup chartSetup = new ChartSetup(
        "History", // ceci est constant
        "EURUSD_M1", // nom du symbole, doit correspondre au nom dans le gestionnaire de données
        TimeframeManager.TF_H1, // cadre temporel
        SQTimeOld.toLong(2008, 4, 20), // date du
        SQTimeOld.toLong(2009, 6, 29), // date du
        3.5, // écart
        Session.Forex_247 // session
) ;
settings.set(SettingsKeys.BacktestChart, chartSetup) ;


// préparer d'autres paramètres de backtest - ceci placera d'autres parties optionnelles/obligatoires dans la carte des paramètres
prepareBacktestSettings(settings, strategyToRetest) ;

Vous pouvez voir que nous créons d'abord le Carte des paramètres objet. Nous définissons ensuite ChartSetup - il s'agit d'une configuration des données utilisées pour le backtest, et elle est ajoutée aux paramètres en appelant settings.set(SettingsKeys.BacktestChart, chartSetup).

Le dernier appel est la méthode prepareBacktestSettings(...) qui définira le reste des paramètres afin de simplifier le code et de le rendre plus lisible.

 

Méthode prepareBacktestSettings()

C'est la méthode qui définit d'autres valeurs de configuration facultatives et obligatoires pour les paramètres du backtest - comme la stratégie, le money management, le capital initial, les options de trading, etc.

private void prepareBacktestSettings(SettingsMap settings,ResultsGroup strategyToRetest) throws Exception {
    // crée un objet de stratégie à partir du XML de stratégie qui est stocké dans le ResultsGroup source
    String strategyName = strategyToRetest.getName() ;
    Element elStrategy = strategyToRetest.getStrategyXml() ;
    if(elStrategy == null) {
        // le résultat n'a pas de stratégie, il ne peut pas être testé
        throw new Exception("Le résultat n'a pas de stratégie, ne peut pas être testé !") ;
    }
    StrategyBase strategy = StrategyBase.createXmlStrategy(elStrategy.clone(), strategyName) ;
    settings.set(SettingsKeys.StrategyObject, strategy) ;


    settings.set(SettingsKeys.MinimumDistance, 0) ;

    // définir le capital initial et la gestion de l'argent
    settings.set(SettingsKeys.InitialCapital, 100000d) ;
    settings.set(SettingsKeys.MoneyManagement, MoneyManagementMethodsList.create("FixedSize", 0.1)) ;
    // Note - vous pouvez créer une méthode de gestion de l'argent différente en spécifiant son nom (extrait)
    // et les paramètres dans leur ordre exact, par exemple :
    // MoneyManagementMethodsList.create("RiskFixedPctOfAccount", 5, 100, 0.1, 0.5))


    // créer et définir les options de négociation requises
    Options de trading options = createTradingOptions() ;
    settings.set(SettingsKeys.TradingOptions, options) ;
}

Le code est commenté et vous devriez pouvoir comprendre ce qu'il fait. Il existe à nouveau une sous-méthode distincte createTradingOptions() utilisée pour mieux organiser le code. Cette méthode ne sera pas expliquée ici, elle est commentée dans le code et renvoie une liste d'options de trading qui devraient s'appliquer au backtest.

 

Travailler avec les résultats d'un backtest

Lorsque votre backtest s'est terminé avec succès, vous obtenez un nouveau fichier Groupe de résultats avec le résultat du backtest. Vous pouvez alors généralement faire deux choses avec cet objet :

  1. Lisez ses paramètres (nombre de transactions, bénéfice net, Sharpe, etc.) pour déterminer si vous souhaitez filtrer la stratégie.
  2. Enregistrer le résultat dans une banque de données ou dans un fichier

Les deux choses sont très simples :

// 1. obtenir les valeurs métriques et les comparer / les filtrer d'une manière ou d'une autre, par exemple :
int trades = backtestResultRG.portfolio().stats(Directions.Both, PlTypes.Money, SampleTypes.FullSample).getInt(StatsKey.NUMBER_OF_TRADES) ;
double profit = backtestResultRG.portfolio().stats(Directions.Both, PlTypes.Money, SampleTypes.FullSample).getDouble(StatsKey.NET_PROFIT) ;
	// maintenant, faites quelque chose avec ces valeurs
Log.info("Trades : {}, profit : {}", trades, profit) ;


// Vous pouvez enregistrer le nouveau backtest dans une banque de données ou un fichier.
SQProject project = ProjectEngine.get(projectName) ;
if(project == null) {
    throw new Exception("Project '"+projectName+"' cannot be loaded !") ;
}

Databank targetDB = project.getDatabanks().get("Results") ;
if(targetDB == null) {
    throw new Exception("La banque de données cible 'Résultats' n'a pas été trouvée !") ;
}

// ajouter la nouvelle stratégie+backtest à cette banque de données et rafraîchir la grille de la banque de données
targetDB.add(backtestResultRG, true) ;

 

 

Code source complet de l'extrait CAStrategyTestByProgramming :

package SQ.CustomAnalysis ;

import SQ.TradingOptions.* ;
import com.strategyquant.datalib.TimeframeManager ;
import com.strategyquant.datalib.consts.Precisions ;
import com.strategyquant.datalib.session.Session ;
import com.strategyquant.lib.SettingsMap ;
import com.strategyquant.lib.time.SQTimeOld ;
import com.strategyquant.tradinglib.* ;
import com.strategyquant.tradinglib.engine.BacktestEngine ;
import com.strategyquant.tradinglib.moneymanagement.MoneyManagementMethodsList ;
import com.strategyquant.tradinglib.options.TradingOptions ;
import com.strategyquant.tradinglib.simulator.Engines ;
import com.strategyquant.tradinglib.simulator.ITradingSimulator ;
import com.strategyquant.tradinglib.simulator.impl.* ;
import com.strategyquant.tradinglib.project.ProjectEngine ;
import com.strategyquant.tradinglib.project.SQProject ;
import org.jdom2.Element ;
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;

import java.util.ArrayList ;

public class CAStrategyTestByProgramming extends CustomAnalysisMethod {
    public static final Logger Log = LoggerFactory.getLogger("CAStrategyTestByProgramming") ;

    //------------------------------------------------------------------------
    //------------------------------------------------------------------------
    //------------------------------------------------------------------------

    public CAStrategyTestByProgramming() {
        super("CAStrategyTestByProgramming", TYPE_PROCESS_DATABANK) ;
    }
    
    //------------------------------------------------------------------------
    
    @Override
    public boolean filterStrategy(String projectName, String task, String databankName, ResultsGroup rg) throws Exception {
        return false ;
    }
    
    
    //------------------------------------------------------------------------
    
    @Override
    public ArrayList processDatabank(String projectName, String task, String databankName, ArrayList databankRG) throws Exception {
        if(databankRG.size() == 0) {
            return databankRG ;
        }

        // récupérer la première stratégie de la banque de données pour effectuer un nouveau test sur celle-ci
        ResultsGroup strategyToRetest = databankRG.get(0) ;

        // nous allons stocker tous les paramètres du nouveau backtest dans ce SettingsMap
        SettingsMap settings = new SettingsMap() ;

        // préparer la configuration du graphique pour le backtest - vous devez spécifier le nom des données, la plage, etc.
        ChartSetup chartSetup = new ChartSetup(
                "History", // ceci est constant
                "EURUSD_M1", // nom du symbole, doit correspondre au nom dans le gestionnaire de données
                TimeframeManager.TF_H1, // cadre temporel
                SQTimeOld.toLong(2008, 4, 20), // date du
                SQTimeOld.toLong(2009, 6, 29), // date du
                3.5, // écart
                Session.Forex_247 // session
        ) ;
        settings.set(SettingsKeys.BacktestChart, chartSetup) ;


        // préparer d'autres paramètres de backtest - ceci placera d'autres parties optionnelles/obligatoires dans la carte des paramètres
        prepareBacktestSettings(settings, strategyToRetest) ;


        // créer un simulateur de trading (utilisé ultérieurement pour le moteur de backtest)
        ITradingSimulator simulator = new MetaTrader4Simulator() ;
        // simulateurs disponibles :
        //MetaTrader4Simulator()
        //MetaTrader5SimulatorHedging(OrderExecutionTypes.EXCHANGE)
        //MetaTrader5SimulatorNetting(OrderExecutionTypes.EXCHANGE)
        //TradestationSimulator()
        //MultiChartsSimulator()
        //JForexSimulator()


        // définit la précision de test pour le simulateur
        simulator.setTestPrecision(Precisions.getPrecision(Precisions.PRECISION_BASE_TF)) ;
        // Précisions disponibles (en fonction également des données - vous ne pouvez pas utiliser la précision du tic-tac si vous n'avez pas de données de tic-tac) :
        //PRECISION_SELECTED_TF = "Période sélectionnée uniquement (la plus rapide)" ;
        //PRECISION_BASE_TF = "Simulation de tick de données de 1 minute (lent)" ;
        //PRECISION_TICK_CUSTOM_SPREADS = "Real Tick - custom spread (le plus lent)" ;
        //PRECISION_TICK_REAL_SPREADS = "Real Tick - spread réel (le plus lent)" ;
        //PRECISION_OPEN_PRICES = "Trade On Bar Open" ;


        // créer un moteur de backtest à l'aide du simulateur
        BacktestEngine backtestEngine = new BacktestEngine(simulator) ;
        backtestEngine.setSingleThreaded(true) ;

        // ajouter les paramètres de backtest au moteur
        backtestEngine.addSetup(settings) ;


        // ------------------------
        // exécuter le backtest - ceci lancera le backtest réel en utilisant les paramètres configurés ci-dessus
        // En fonction des paramètres, cela peut prendre un certain temps.
        // Lorsqu'il est terminé, il renvoie un nouvel objet ResultsGroup contenant les résultats du backtest.
        // En cas d'erreur, une exception est levée avec la description de l'erreur.
        ResultsGroup backtestResultRG = backtestEngine.runBacktest().getResults() ;

        // obtenir les détails des résultats du backtest - ici vous pouvez accéder à tous les résultats dans ResultsGroup
        // et faire quelque chose avec eux.


        Vous pouvez généralement faire deux choses : // 1. obtenir les valeurs des métriques et les comparer / filtrer d'une manière ou d'une autre :
        // 1. obtenir les valeurs métriques et les comparer / les filtrer d'une manière ou d'une autre, par exemple :
        int trades = backtestResultRG.portfolio().stats(Directions.Both, PlTypes.Money, SampleTypes.FullSample).getInt(StatsKey.NUMBER_OF_TRADES) ;
        double profit = backtestResultRG.portfolio().stats(Directions.Both, PlTypes.Money, SampleTypes.FullSample).getDouble(StatsKey.NET_PROFIT) ;
        // maintenant, faites quelque chose avec ces valeurs
        // Par exemple, vous pouvez utiliser le résultat de ce backtest personnalisé pour filtrer cette stratégie particulière.
        // de la banque de données - en la supprimant du tableau databankRG
        Log.info("Trades : {}, profit : {}", trades, profit) ;



        // Vous pouvez enregistrer le nouveau backtest dans une banque de données ou un fichier.
        SQProject project = ProjectEngine.get(projectName) ;
        if(project == null) {
            throw new Exception("Project '"+projectName+"' cannot be loaded !") ;
        }

        Databank targetDB = project.getDatabanks().get("Results") ;
        if(targetDB == null) {
            throw new Exception("La banque de données cible 'Résultats' n'a pas été trouvée !") ;
        }

        // ajouter la nouvelle stratégie+backtest à cette banque de données et rafraîchir la grille de la banque de données
        targetDB.add(backtestResultRG, true) ;


        // la méthode doit renvoyer une liste des stratégies originales de la banque de données qui ont passé ce filtre
        return databankRG ;
    }

    //------------------------------------------------------------------------

    private void prepareBacktestSettings(SettingsMap settings,ResultsGroup strategyToRetest) throws Exception {
        // crée un objet de stratégie à partir du XML de stratégie qui est stocké dans le ResultsGroup source
        String strategyName = strategyToRetest.getName() ;
        Element elStrategy = strategyToRetest.getStrategyXml() ;
        if(elStrategy == null) {
            // le résultat n'a pas de stratégie, il ne peut pas être testé
            throw new Exception("Le résultat n'a pas de stratégie, ne peut pas être testé !") ;
        }
        StrategyBase strategy = StrategyBase.createXmlStrategy(elStrategy.clone(), strategyName) ;
        settings.set(SettingsKeys.StrategyObject, strategy) ;


        settings.set(SettingsKeys.MinimumDistance, 0) ;

        // définir le capital initial et la gestion de l'argent
        settings.set(SettingsKeys.InitialCapital, 100000d) ;
        settings.set(SettingsKeys.MoneyManagement, MoneyManagementMethodsList.create("FixedSize", 0.1)) ;
        // Note - vous pouvez créer une méthode de gestion de l'argent différente en spécifiant son nom (extrait)
        // et les paramètres dans leur ordre exact, par exemple :
        // MoneyManagementMethodsList.create("RiskFixedPctOfAccount", 5, 100, 0.1, 0.5))


        // créer et définir les options de négociation requises
        Options de trading options = createTradingOptions() ;
        settings.set(SettingsKeys.TradingOptions, options) ;
    }

    //------------------------------------------------------------------------

    private TradingOptions createTradingOptions() {
        TradingOptions options = new TradingOptions() ;

        // toutes les options de trading sont définies sous forme de snippets
        // dans SQ.TradingOptions.* (visible dans CodeEditor)
        // voici un exemple d'application de quelques-unes d'entre elles
        Option ExitAtEndOfDay = new ExitAtEndOfDay() ;
        option.ExitAtEndOfDay = true ;
        option.EODExitTime = 0 ;
        options.add(option) ;

        ExitOnFriday option2 = new ExitOnFriday() ;
        option2.ExitOnFriday = true ;
        option2.FridayExitTime = 0 ;
        options.add(option2) ;

        LimitTimeRange option3 = new LimitTimeRange() ;
        option3.LimitTimeRange = true ;
        option3.SignalTimeRangeFrom = 700 ;
        option3.SignalTimeRangeTo = 1900 ;
        option3.ExitAtEndOfRange = true ;
        options.add(option3) ;

        MinMaxSLPT optionMmSLPT = new MinMaxSLPT() ;
        optionMmSLPT.MinimumSL = 50 ;
        optionMmSLPT.MaximumSL = 100 ;
        optionMmSLPT.MinimumPT = 50 ;
        optionMmSLPT.MaximumPT = 100 ;
        options.add(optionMmSLPT) ;

        return options ;
    }
}

 

 

Cet article a-t-il été utile ? L'article était utile L'article n'était pas utile

S'abonner
Notification pour
3 Commentaires
Le plus ancien
Le plus récent Le plus populaire
Commentaires en ligne
Afficher tous les commentaires
Emmanuel
25. 2. 2022 9:46 am

Excellent ! !! Très bonne idée et très utile ! !!! Je ne savais pas qu'on pouvait demander à SQX d'exécuter un backtest à partir de Java.

Nous pouvons même sélectionner le moteur, la stratégie # et la période. Je vais l'utiliser ! !!!

SQX est vraiment incroyable

Emmanuel
25. 2. 2022 9:47 am

Avec des exemples comme celui-ci, j'apprends beaucoup, merci à l'équipe de SQX ! !!!!

Emmanuel
25. 2. 2022 9:55 am

J'obtiens une petite erreur "L'importation a échoué.Fichier attendu avec extension .sxp” ?
J'ai V 135.368".
Bizarre, le fichier a l'extension sxpJ'ai cette erreur lorsque je l'importe
Que dois-je faire pour éviter cette erreur ?

Postes connexes