Dokumentation

Anwendungen

Zuletzt aktualisiert am 22. 2. 2022 von Mark Fric

Programmatische Durchführung von Strategie-Backtests

In diesem Beispiel wird gezeigt, wie man Strategie-Backtests programmatisch ausführen kann, indem man unsere Backtesting-Engine aufruft. Das Beispiel ist in Form eines benutzerdefinierten Analyse-Snippets erstellt.

Der Backtesting-Code kann theoretisch in jedes andere Snippet eingefügt werden, Sie müssen sich nur darüber im Klaren sein, dass Backtests langsam sind. Es ist also definitiv nicht empfehlenswert, Backtests in Datenbankspalten und ähnlichen anderen Snippets durchzuführen, die schnell sein müssen.

Sie können das komplette Snippet als Anhang zu diesem Artikel herunterladen, der vollständige Quellcode wird ebenfalls am Ende des Artikels veröffentlicht.

 

Wir werden die wichtigsten Teile des Backtests Schritt für Schritt beschreiben. Der Backtest selbst ist in der Methode Custom Analysis implementiert processDatabank()wo er einen benutzerdefinierten Backtest der allerersten Strategie in der Datenbank durchführt.

 

BacktestEngine zur Durchführung von Backtests verwenden

Die Durchführung eines Backtests ist relativ einfach, Sie müssen nur folgende Daten initialisieren BacktestEngine und rufen dann dessen Methode runBacktest() und dann getResults(), um das Backtest-Ergebnis zu erhalten.

// Erstellen eines Handelssimulators (später für die Backtest-Engine verwendet)
ITradingSimulator simulator = new MetaTrader4Simulator();

// Testgenauigkeit für den Simulator festlegen
simulator.setTestPrecision(Precisions.getPrecision(Precisions.PRECISION_BASE_TF));

// BacktestEngine unter Verwendung des Simulators erstellen
BacktestEngine backtestEngine = new BacktestEngine(simulator);
backtestEngine.setSingleThreaded(true);

// Hinzufügen von Backtest-Einstellungen zur Engine
backtestEngine.addSetup(einstellungen);

// ------------------------
// Backtest ausführen - damit wird der eigentliche Backtest unter Verwendung der oben konfigurierten Einstellungen ausgeführt
// Je nach den Einstellungen kann dies eine Weile dauern.
// Nach Abschluss wird ein neues ResultsGroup-Objekt mit dem Backtest-Ergebnis zurückgegeben.
// Im Falle eines Fehlers wird eine Exception mit der Beschreibung des Fehlers geworfen
ResultsGroup backtestResultRG = backtestEngine.runBacktest().getResults();

 

Zunächst erstellen wir einen Handelssimulator eines bestimmten Typs (MT4, MT5, Tradestation usw.) und stellen dessen Backtest-Präzision ein.

Dann erstellen wir BacktestEngine unter Verwendung dieses Simulators. Wir müssen der Engine Einstellungen hinzufügen, die andere Backtest-Parameter beschreiben, indem wir addSetup() - Die Einstellungen selbst werden später erklärt.

Der letzte Punkt ist der Anruf runBacktest().getResults() um den eigentlichen Backtest durchzuführen und die Ergebnisse zu erhalten.

 

Einstellungen für Backtest erstellen

BacktestEngine muss mit Einstellungen konfiguriert werden - einem SettingsMap-Objekt. Dabei handelt es sich um eine Karte mit Schlüssel-Werte-Paaren, in der Sie verschiedene Parameter des Backtests einstellen können.

SettingsMap settings = new SettingsMap();

// Chart-Setup für Backtest vorbereiten - Sie müssen den Datennamen, den Bereich usw. angeben
ChartSetup chartSetup = new ChartSetup(
        "Historie", // dies ist eine Konstante
        "EURUSD_M1", // Symbolname, muss mit dem Namen im Datenmanager übereinstimmen
        TimeframeManager.TF_H1, // Zeitrahmen
        SQTimeOld.toLong(2008, 4, 20), // Datum von
        SQTimeOld.toLong(2009, 6, 29), // Datum bis
        3.5, // Spanne
        Session.Forex_247 // Sitzung
);
settings.set(SettingsKeys.BacktestChart, chartSetup);


// andere Backtest-Einstellungen vorbereiten - dadurch werden andere optionale/erforderliche Teile in die Settings-Map eingefügt
prepareBacktestSettings(settings, strategyToRetest);

Sie können sehen, dass wir zuerst die EinstellungenKarte Objekt. Wir definieren dann ChartSetup - Es handelt sich um eine Konfiguration von Daten, die für Backtests verwendet werden, und sie wird zu den Einstellungen hinzugefügt, indem man settings.set(SettingsKeys.BacktestChart, chartSetup).

Der letzte Aufruf ist die Methode prepareBacktestSettings(...) die den Rest der Einstellungen festlegt, um den Code einfacher und lesbarer zu halten.

 

Methode prepareBacktestSettings()

Es ist die Methode, die andere optionale und erforderliche Konfigurationswerte für die Backtest-Einstellungen festlegt - wie Strategie, Geldmanagement, Anfangskapital, Handelsoptionen und so weiter.

private void prepareBacktestSettings(SettingsMap settings,ResultsGroup strategyToRetest) throws Exception {
    // erstellt Strategieobjekt aus Strategie-XML, das in der Quell-Ergebnisgruppe gespeichert ist
    String strategyName = strategyToRetest.getName();
    Element elStrategy = strategyToRetest.getStrategyXml();
    if(elStrategy == null) {
        // Ergebnis hat keine Strategie, kann nicht getestet werden
        throw new Exception("Ergebnis hat keine Strategie, kann nicht getestet werden!");
    }
    StrategyBase strategy = StrategyBase.createXmlStrategy(elStrategy.clone(), strategyName);
    settings.set(SettingsKeys.StrategyObject, strategy);


    settings.set(SettingsKeys.MinimumDistance, 0);

    // Anfangskapital und Geldmanagement einstellen
    settings.set(SettingsKeys.InitialCapital, 100000d);
    settings.set(SettingsKeys.MoneyManagement, MoneyManagementMethodsList.create("FixedSize", 0.1));
    // Hinweis: Sie können eine andere MoneyManagement-Methode erstellen, indem Sie den (Schnipsel-)Namen
    // und Parameter in der exakten Reihenfolge angeben, zum Beispiel:
    // MoneyManagementMethodsList.create("RiskFixedPctOfAccount", 5, 100, 0.1, 0.5))


    // Erforderliche Handelsoptionen erstellen und einstellen
    TradingOptions options = createTradingOptions();
    settings.set(SettingsKeys.TradingOptions, options);
}

Der Code ist kommentiert, und Sie sollten in der Lage sein zu verstehen, was er tut. Es gibt wieder eine separate Untermethode createTradingOptions() verwendet, um den Code übersichtlicher zu gestalten. Diese Methode wird hier nicht erklärt, sie ist im Code kommentiert und gibt eine Liste von Handelsoptionen zurück, die für den Backtest gelten sollen.

 

Arbeiten mit Backtest-Ergebnis

Wenn Ihr Backtest erfolgreich abgeschlossen wurde, erhalten Sie eine neue ErgebnisGruppe Objekt mit Backtest-Ergebnis. Sie können dann im Allgemeinen zwei Dinge damit tun:

  1. Lesen Sie die Metriken (Anzahl der Trades, Nettogewinn, Sharpe usw.), um festzustellen, ob Sie die Strategie herausfiltern möchten.
  2. Speichern des Ergebnisses in einer Datenbank oder in einer Datei

Beides ist ganz einfach:

// 1. die Metrikwerte abrufen und sie auf irgendeine Weise vergleichen/filtern, Beispiel:
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);
	// nun etwas mit diesen Werten tun
Log.info("Trades: {}, Gewinn: {}", Trades, Gewinn);


// 2. den neuen Backtest in einer Datenbank oder Datei speichern
SQProject project = ProjectEngine.get(projectName);
if(projekt == null) {
    throw new Exception("Projekt '"+Projektname+"' kann nicht geladen werden!");
}

Databank targetDB = project.getDatabanks().get("Results");
if(targetDB == null) {
    throw new Exception("Zieldatenbank 'Ergebnisse' wurde nicht gefunden!");
}

// Hinzufügen der neuen Strategie+Backtest zu dieser Datenbank und Aktualisieren des Datenbankgitters
targetDB.add(backtestResultRG, true);

 

 

Vollständiger Quellcode des CAStrategyTestByProgramming-Snippets:

Paket 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.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 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 CAStrategyTestByProgrammierung() {
        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;
        }

        // holt die erste Strategie in der Datenbank, um einen erneuten Test damit durchzuführen
        ErgebnisGruppe strategyToRetest = databankRG.get(0);

        // wir speichern alle Einstellungen für den neuen Backtest in dieser SettingsMap
        SettingsMap settings = new SettingsMap();

        // Bereiten Sie das Chart-Setup für den Backtest vor - Sie müssen den Datennamen, den Bereich, etc. angeben
        ChartSetup chartSetup = new ChartSetup(
                "Historie", // dies ist eine Konstante
                "EURUSD_M1", // Symbolname, muss mit dem Namen im Datenmanager übereinstimmen
                TimeframeManager.TF_H1, // Zeitrahmen
                SQTimeOld.toLong(2008, 4, 20), // Datum von
                SQTimeOld.toLong(2009, 6, 29), // Datum bis
                3.5, // Spanne
                Session.Forex_247 // Sitzung
        );
        settings.set(SettingsKeys.BacktestChart, chartSetup);


        // andere Backtest-Einstellungen vorbereiten - dadurch werden andere optionale/erforderliche Teile in die Settings-Map eingefügt
        prepareBacktestSettings(settings, strategyToRetest);


        // Handelssimulator erstellen (wird später für die Backtest-Engine verwendet)
        ITradingSimulator simulator = new MetaTrader4Simulator();
        // verfügbare Simulatoren:
        //MetaTrader4Simulator()
        //MetaTrader5SimulatorHedging(OrderExecutionTypes.EXCHANGE)
        //MetaTrader5SimulatorNetting(OrderExecutionTypes.EXCHANGE)
        //TradestationSimulator()
        //MultiChartsSimulator()
        //JForexSimulator()


        // Testgenauigkeit für den Simulator festlegen
        simulator.setTestPrecision(Precisions.getPrecision(Precisions.PRECISION_BASE_TF));
        // Verfügbare Genauigkeit (hängt auch von den Daten ab - Sie können keine Tick-Präzision verwenden, wenn Sie keine Tick-Daten haben):
        //PRECISION_SELECTED_TF = "Nur ausgewählter Zeitrahmen (schnellster)";
        //PRECISION_BASE_TF = "1 Minute Daten-Tick-Simulation (langsam)";
        //PRECISION_TICK_CUSTOM_SPREADS = "Real Tick - benutzerdefinierter Spread (am langsamsten)";
        //PRECISION_TICK_REAL_SPREADS = "Real Tick - realer Spread (am langsamsten)";
        //PRECISION_OPEN_PRICES = "Trade On Bar Open";


        // BacktestEngine mit Simulator erstellen
        BacktestEngine backtestEngine = new BacktestEngine(simulator);
        backtestEngine.setSingleThreaded(true);

        // Hinzufügen von Backtest-Einstellungen zur Engine
        backtestEngine.addSetup(einstellungen);


        // ------------------------
        // Backtest ausführen - damit wird der eigentliche Backtest unter Verwendung der oben konfigurierten Einstellungen ausgeführt
        // Abhängig von den Einstellungen kann dies eine Weile dauern.
        // Nach Beendigung wird ein neues ResultsGroup-Objekt mit dem Backtest-Ergebnis zurückgegeben.
        // Im Falle eines Fehlers wird eine Exception mit der Beschreibung des Fehlers geworfen
        ResultsGroup backtestResultRG = backtestEngine.runBacktest().getResults();

        // holt Details zu den Backtest-Ergebnissen - hier können Sie auf alle Ergebnisse in ResultsGroup zugreifen
        // und etwas mit ihnen machen.


        // Sie können im Allgemeinen zwei Dinge tun:
        // 1. die Metrikwerte abrufen und sie auf irgendeine Weise vergleichen / filtern, Beispiel:
        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);
        // Machen Sie nun etwas mit diesen Werten
        // Zum Beispiel können Sie das Ergebnis dieses benutzerdefinierten Backtests verwenden, um diese bestimmte Strategie herauszufiltern
        // aus der Datenbank herausfiltern, indem sie aus dem Array databankRG entfernt wird
        Log.info("Trades: {}, Gewinn: {}", Trades, Gewinn);



        // 2. der neue Backtest kann in einer Datenbank oder Datei gespeichert werden
        SQProject project = ProjectEngine.get(projectName);
        if(projekt == null) {
            throw new Exception("Projekt '"+Projektname+"' kann nicht geladen werden!");
        }

        Databank targetDB = project.getDatabanks().get("Results");
        if(targetDB == null) {
            throw new Exception("Zieldatenbank 'Ergebnisse' wurde nicht gefunden!");
        }

        // Hinzufügen der neuen Strategie+Backtest zu dieser Datenbank und Aktualisieren des Datenbankgitters
        targetDB.add(backtestResultRG, true);


        // Methode muss eine Liste der ursprünglichen Strategien in der Datenbank zurückgeben, die diesen Filter bestanden haben
        return databankRG;
    }

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

    private void prepareBacktestSettings(SettingsMap settings,ResultsGroup strategyToRetest) throws Exception {
        // erstellt Strategieobjekt aus Strategie-XML, das in der Quelle ResultsGroup gespeichert ist
        String strategyName = strategyToRetest.getName();
        Element elStrategy = strategyToRetest.getStrategyXml();
        if(elStrategy == null) {
            // Ergebnis hat keine Strategie, kann nicht getestet werden
            throw new Exception("Ergebnis hat keine Strategie, kann nicht getestet werden!");
        }
        StrategyBase strategy = StrategyBase.createXmlStrategy(elStrategy.clone(), strategyName);
        settings.set(SettingsKeys.StrategyObject, strategy);


        settings.set(SettingsKeys.MinimumDistance, 0);

        // Anfangskapital und Geldmanagement einstellen
        settings.set(SettingsKeys.InitialCapital, 100000d);
        settings.set(SettingsKeys.MoneyManagement, MoneyManagementMethodsList.create("FixedSize", 0.1));
        // Hinweis - Sie können eine andere MoneyManagement-Methode erstellen, indem Sie ihren (Schnipsel-)Namen
        // und Parameter in der exakten Reihenfolge angeben, zum Beispiel:
        // MoneyManagementMethodsList.create("RiskFixedPctOfAccount", 5, 100, 0.1, 0.5))


        // Erforderliche Handelsoptionen erstellen und einstellen
        TradingOptions options = createTradingOptions();
        settings.set(SettingsKeys.TradingOptions, options);
    }

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

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

        // alle Handelsoptionen werden als Snippets definiert
        // in SQ.TradingOptions.* (sichtbar im CodeEditor)
        // Nachstehend ein Beispiel für die Anwendung einiger dieser Optionen
        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);

        Optionen zurückgeben;
    }
}

 

 

War dieser Artikel hilfreich? Der Artikel war nützlich Der Artikel war nicht nützlich

Abonnieren
Benachrichtigen Sie mich bei
3 Kommentare
Älteste
Neuestes Meistgewählt
Inline-Rückmeldungen
Alle Kommentare anzeigen
Emmanuel
25. 2. 2022 9:46 am

Ausgezeichnet !!! Sehr gute Idee und sehr nützlich !!!! Ich wusste nicht, dass wir SQX bitten können, einen Backtest von Java aus durchzuführen.

Wir können sogar den Motor, die Strategie # und den Zeitraum auswählen. Ich werde es verwenden !!!!

SQX ist wirklich erstaunlich

Emmanuel
25. 2. 2022 9:47 am

Mit Beispielen wie diesem lerne ich eine Menge, danke SQX-Team !!!!!

Emmanuel
25. 2. 2022 9:55 Uhr

Ich erhalte eine kleine Fehlermeldung "Import fehlgeschlagen.Erwartete Datei mit der Erweiterung .sxp” ?
Ich habe V 135.368."
Seltsam, die Datei hat die Erweiterung sxpIch habe diesen Fehler, wenn ich es importiere
Was sollte ich tun, um diesen Fehler zu vermeiden?

Verwandte Beiträge