Documentazione

Applicazioni

Ultimo aggiornamento il 22. 2. 2022 da Mark Fric

Esecuzione programmatica dei backtest delle strategie

Questo esempio mostra come eseguire i backtest delle strategie in modo programmatico, chiamando il nostro motore di backtesting. L'esempio è realizzato sotto forma di snippet di analisi personalizzata.

Il codice di backtesting può essere teoricamente incluso in qualsiasi altro snippet, ma è sufficiente rendersi conto che i backtest sono lenti, quindi è assolutamente sconsigliato eseguire backtest in colonne di banche dati e altri snippet simili che devono essere veloci.

È possibile scaricare lo snippet completo come allegato a questo articolo; il codice sorgente completo sarà pubblicato anche alla fine dell'articolo.

 

Descriviamo passo per passo le parti più importanti del backtest. Il backtest stesso è implementato nel metodo Analisi personalizzata elaboraBancaDati()dove viene eseguito un backtest personalizzato della prima strategia presente nella banca dati.

 

Utilizzo di BacktestEngine per l'esecuzione di backtest

L'esecuzione del backtest è relativamente semplice: è sufficiente inizializzare BacktestEngine e poi chiamare il suo metodo runBacktest() e poi getResults() per ottenere il risultato del backtest.

// creare un simulatore di trading (utilizzato in seguito per il motore di backtest)
ITradingSimulator simulator = new MetaTrader4Simulator();

// imposta la precisione di prova per il simulatore
simulator.setTestPrecision(Precisions.getPrecision(Precisions.PRECISION_BASE_TF));

// creare il motore di backtest utilizzando il simulatore
BacktestEngine backtestEngine = new BacktestEngine(simulatore);
backtestEngine.setSingleThreaded(true);

// aggiungere le impostazioni di backtest al motore
backtestEngine.addSetup(settings);

// ------------------------
// Eseguire il backtest - questo eseguirà il backtest vero e proprio utilizzando le impostazioni configurate in precedenza.
// A seconda delle impostazioni, potrebbe volerci un po' di tempo.
// Al termine, restituirà un nuovo oggetto ResultsGroup con i risultati del backtest.
// In caso di errore viene lanciata un'Eccezione con la descrizione dell'errore
ResultsGroup backtestResultRG = backtestEngine.runBacktest().getResults();

 

Per prima cosa creiamo un simulatore di trading di un determinato tipo (MT4, MT5, Tradestation, ecc.) e impostiamo la precisione del backtest.

Poi creeremo BacktestEngine utilizzando questo simulatore. È necessario aggiungere al motore le impostazioni che descrivono altri parametri del backtest utilizzando addSetup() - le impostazioni stesse saranno spiegate in seguito.

L'ultima cosa è chiamare runBacktest().getResults() per eseguire il backtest vero e proprio e ottenerne i risultati.

 

Creazione delle impostazioni per il backtest

BacktestEngine deve essere configurato con le impostazioni - un oggetto SettingsMap. Si tratta di una mappa di coppie chiave -> valore, in cui è possibile impostare vari parametri del backtest.

SettingsMap settings = new SettingsMap();

// preparare il setup del grafico per il backtest - è necessario specificare il nome dei dati, l'intervallo, ecc.
ChartSetup chartSetup = new ChartSetup(
        "History", // questa è una costante
        "EURUSD_M1", // nome del simbolo, deve corrispondere al nome nel Data Manager
        TimeframeManager.TF_H1, // timeframe
        SQTimeOld.toLong(2008, 4, 20), // data a partire da
        SQTimeOld.toLong(2009, 6, 29), // data al
        3.5, // spread
        Session.Forex_247 // sessione
);
settings.set(SettingsKeys.BacktestChart, chartSetup);


// preparare le altre impostazioni del backtest - questo metterà altre parti opzionali/richieste nella mappa delle impostazioni
prepareBacktestSettings(settings, strategyToRetest);

Si può notare che prima si crea il file ImpostazioniMappa oggetto. Definiamo quindi Impostazione dei grafici - è una configurazione dei dati utilizzati per il backtest e viene aggiunta alle impostazioni chiamando settings.set(SettingsKeys.BacktestChart, chartSetup).

L'ultima chiamata è il metodo prepareBacktestSettings(...) che imposterà il resto delle impostazioni per mantenere il codice più semplice e leggibile.

 

Metodo prepareBacktestSettings()

È il metodo che imposta altri valori di configurazione facoltativi e obbligatori per le impostazioni del backtest, come la strategia, la gestione del denaro, il capitale iniziale, le opzioni di trading e così via.

private void prepareBacktestSettings(SettingsMap settings,ResultsGroup strategyToRetest) throws Exception {
    // crea l'oggetto strategia dall'XML della strategia memorizzato nel ResultsGroup di origine
    String strategyName = strategyToRetest.getName();
    Element elStrategy = strategyToRetest.getStrategyXml();
    if(elStrategy == null) {
        // il risultato non ha alcuna strategia, non può essere testato
        throw new Exception("Il risultato non ha alcuna strategia, non può essere testato!");
    }
    StrategyBase strategy = StrategyBase.createXmlStrategy(elStrategy.clone(), strategyName);
    settings.set(SettingsKeys.StrategyObject, strategy);


    settings.set(SettingsKeys.MinimumDistance, 0);

    // impostare il capitale iniziale e la gestione del denaro
    settings.set(SettingsKeys.InitialCapital, 100000d);
    settings.set(SettingsKeys.MoneyManagement, MoneyManagementMethodsList.create("FixedSize", 0.1));
    // Nota: è possibile creare un metodo di gestione del denaro diverso specificandone il nome (snippet) e i parametri nell'ordine esatto.
    // e i parametri nel loro ordine esatto, ad esempio:
    // MoneyManagementMethodsList.create("RiskFixedPctOfAccount", 5, 100, 0.1, 0.5))


    // creare e impostare le opzioni di trading richieste
    TradingOptions options = createTradingOptions();
    settings.set(SettingsKeys.TradingOptions, options);
}

Il codice è commentato e si dovrebbe essere in grado di capire cosa fa. C'è di nuovo un sotto-metodo separato creareOpzioni di trading() utilizzato per mantenere il codice più organizzato. Questo metodo non sarà spiegato qui, è commentato nel codice e restituisce un elenco di opzioni di trading da applicare al backtest.

 

Lavorare con i risultati del backtest

Quando il backtest è terminato con successo, si otterrà un nuovo file Gruppo di risultati con il risultato del backtest. In genere, si possono fare due cose con esso:

  1. Leggete le sue metriche (numero di operazioni, profitto netto, Sharpe, ecc.) per determinare se volete filtrare la strategia.
  2. Salvare il risultato in una banca dati o in un file.

Entrambe le cose sono abbastanza semplici:

// 1. Ottenere i valori delle metriche e confrontarli / filtrarli in qualche modo, ad esempio:
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);
	// ora facciamo qualcosa con questi valori
Log.info("Operazioni: {}, profitto: {}", operazioni, profitto);


// 2. Si può salvare il nuovo backtest in una banca dati o in un file
SQProject project = ProjectEngine.get(projectName);
if(project == null) {
    throw new Exception("Il progetto '"+projectName+"' non può essere caricato!");
}

Databank targetDB = project.getDatabanks().get("Results");
if(targetDB == null) {
    throw new Exception("La banca dati di destinazione 'Risultati' non è stata trovata!");
}

// Aggiungiamo la nuova strategia+backtest a questa banca dati e aggiorniamo la griglia della banca dati.
targetDB.add(backtestResultRG, true);

 

 

Codice sorgente completo dello snippet CAStrategyTestByProgramming:

pacchetto SQ.CustomAnalysis;

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

importare 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;
        }

        // ottenere la prima strategia nella banca dati per eseguire il retest su di essa
        ResultsGroup strategyToRetest = databankRG.get(0);

        // memorizzeremo tutte le impostazioni per il nuovo backtest in questa SettingsMap
        SettingsMap settings = new SettingsMap();

        // prepariamo l'impostazione del grafico per il backtest - è necessario specificare il nome dei dati, l'intervallo, ecc.
        ChartSetup chartSetup = new ChartSetup(
                "History", // questa è una costante
                "EURUSD_M1", // nome del simbolo, deve corrispondere al nome nel Data Manager
                TimeframeManager.TF_H1, // timeframe
                SQTimeOld.toLong(2008, 4, 20), // data a partire da
                SQTimeOld.toLong(2009, 6, 29), // data al
                3.5, // spread
                Session.Forex_247 // sessione
        );
        settings.set(SettingsKeys.BacktestChart, chartSetup);


        // preparare le altre impostazioni del backtest - questo metterà altre parti opzionali/richieste nella mappa delle impostazioni
        prepareBacktestSettings(settings, strategyToRetest);


        // creare il simulatore di trading (utilizzato successivamente per il motore di backtest)
        ITradingSimulator simulator = new MetaTrader4Simulator();
        // simulatori disponibili:
        //MetaTrader4Simulator()
        //MetaTrader5SimulatorHedging(OrderExecutionTypes.EXCHANGE)
        //MetaTrader5SimulatorNetting(OrderExecutionTypes.EXCHANGE)
        //TradestationSimulator()
        //MultiChartsSimulator()
        //JForexSimulator()


        // imposta la precisione di prova per il simulatore
        simulator.setTestPrecision(Precisions.getPrecision(Precisions.PRECISION_BASE_TF));
        // Precisioni disponibili (a seconda anche dei dati - non è possibile utilizzare la precisione tick se non si dispone di dati tick):
        //PRECISION_SELECTED_TF = "Solo il timeframe selezionato (il più veloce)";
        //PRECISION_BASE_TF = "Simulazione di tick di dati a 1 minuto (lento)";
        //PRECISION_TICK_CUSTOM_SPREADS = "Tick reali - spread personalizzato (il più lento)";
        //PRECISION_TICK_REAL_SPREADS = "Tick reali - spread reale (più lento)";
        //PRECISION_OPEN_PRICES = "Negoziazione all'apertura della barra";


        // creare un motore di backtest utilizzando il simulatore
        BacktestEngine backtestEngine = new BacktestEngine(simulatore);
        backtestEngine.setSingleThreaded(true);

        // aggiungere le impostazioni di backtest al motore
        backtestEngine.addSetup(settings);


        // ------------------------
        // Eseguire il backtest - questo eseguirà il backtest vero e proprio utilizzando le impostazioni configurate in precedenza.
        // A seconda delle impostazioni, potrebbe volerci un po' di tempo.
        // Al termine, restituirà un nuovo oggetto ResultsGroup con i risultati del backtest.
        // In caso di errore viene lanciata un'Eccezione con la descrizione dell'errore
        ResultsGroup backtestResultRG = backtestEngine.runBacktest().getResults();

        // ottenere i dettagli dei risultati del backtest - qui è possibile accedere a tutti i risultati in ResultsGroup
        // e fare qualcosa con essi.


        // In genere si possono fare due cose:
        // 1. Ottenere i valori delle metriche e confrontarli / filtrarli in qualche modo, ad esempio:
        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);
        // Ora fate qualcosa con questi valori
        // Per esempio, è possibile utilizzare il risultato di questo backtest personalizzato per filtrare questa particolare strategia
        // dalla banca dati, rimuovendola dall'array databankRG
        Log.info("Operazioni: {}, profitto: {}", operazioni, profitto);



        // 2. Si può salvare il nuovo backtest in un database o in un file
        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 banca dati di destinazione 'Risultati' non è stata trovata!");
        }

        // Aggiungiamo la nuova strategia+backtest a questa banca dati e aggiorniamo la griglia della banca dati.
        targetDB.add(backtestResultRG, true);


        // il metodo deve restituire un elenco di strategie originali nella banca dati che hanno superato questo filtro
        restituire databankRG;
    }

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

    private void prepareBacktestSettings(SettingsMap settings,ResultsGroup strategyToRetest) throws Exception {
        // crea l'oggetto strategia dall'XML della strategia memorizzato nel ResultsGroup di origine
        String strategyName = strategyToRetest.getName();
        Element elStrategy = strategyToRetest.getStrategyXml();
        if(elStrategy == null) {
            // il risultato non ha alcuna strategia, non può essere testato
            throw new Exception("Il risultato non ha alcuna strategia, non può essere testato!");
        }
        StrategyBase strategy = StrategyBase.createXmlStrategy(elStrategy.clone(), strategyName);
        settings.set(SettingsKeys.StrategyObject, strategy);


        settings.set(SettingsKeys.MinimumDistance, 0);

        // impostare il capitale iniziale e la gestione del denaro
        settings.set(SettingsKeys.InitialCapital, 100000d);
        settings.set(SettingsKeys.MoneyManagement, MoneyManagementMethodsList.create("FixedSize", 0.1));
        // Nota: è possibile creare un metodo di gestione del denaro diverso specificandone il nome (snippet) e i parametri nell'ordine esatto.
        // e i parametri nel loro ordine esatto, ad esempio:
        // MoneyManagementMethodsList.create("RiskFixedPctOfAccount", 5, 100, 0.1, 0.5))


        // creare e impostare le opzioni di trading richieste
        TradingOptions options = createTradingOptions();
        settings.set(SettingsKeys.TradingOptions, options);
    }

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

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

        // tutte le opzioni di trading sono definite come snippet
        // in SQ.TradingOptions.* (visibile nel CodeEditor)
        // di seguito è riportato un esempio di applicazione di alcune di esse
        ExitAtEndOfDay option = 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);

        restituire le opzioni;
    }
}

 

 

Questo articolo è stato utile? L'articolo è stato utile L'articolo non è stato utile

Abbonarsi
Notificami
3 Commenti
Il più vecchio
Più recente I più votati
Feedback in linea
Visualizza tutti i commenti
Emmanuel
25. 2. 2022 9:46

Eccellente!!! Ottima idea e molto utile !!!! Non sapevo che si potesse chiedere a SQX di eseguire un backtest da Java.

Possiamo anche selezionare il motore, la strategia # e il periodo. Lo utilizzerò !!!!

SQX è davvero incredibile

Emmanuel
25. 2. 2022 9:47

Con esempi come questo, sto imparando molto, grazie al team SQX !!!!!

Emmanuel
25. 2. 2022 9:55

Ricevo un piccolo errore "Importazione fallita.File atteso con estensione .sxp” ?
Ho V 135.368".
Strano, il file ha l'estensione sxpHo questo errore quando lo importo
Cosa devo fare per evitare questo errore?

Messaggi correlati