Documentación
Aplicaciones
Última actualización el 22. 2. 2022 por Mark Fric
Ejecución programática de pruebas retrospectivas de estrategias
Contenido de la página
Este ejemplo mostrará cómo ejecutar backtests de estrategia programáticamente llamando a nuestro motor de backtesting. El ejemplo está hecho en forma de fragmento de análisis personalizado.
El código de backtesting puede incluirse teóricamente en cualquier otro snippet, sólo tiene que darse cuenta de que los backtests son lentos, por lo que definitivamente no se recomienda ejecutar backtests en columnas de bancos de datos y otros snippets similares que tienen que ser rápidos.
Puede descargar el fragmento completo como archivo adjunto en este artículo, su código fuente completo también se publicará al final del artículo.
Describiremos las partes más importantes del backtest paso a paso. El backtest en sí se implementa en el método de análisis personalizado procesarBancoDeDatos()donde realiza un backtest personalizado de la primera estrategia del banco de datos.
Uso de BacktestEngine para ejecutar backtest
Ejecutar backtest es relativamente sencillo, sólo es necesario inicializar BacktestEngine y luego llamar a su método runBacktest() y después getResults() para obtener el resultado del backtest.
// crear simulador de trading (posteriormente utilizado como motor de backtest) ITradingSimulator simulator = new MetaTrader4Simulator(); // establecer la precisión de prueba para el simulador simulator.setTestPrecision(Precisions.getPrecision(Precisions.PRECISION_BASE_TF)); // crear el motor de backtest utilizando el simulador BacktestEngine backtestEngine = new BacktestEngine(simulator); backtestEngine.setSingleThreaded(true); // añadir configuración de backtest al motor backtestEngine.addSetup(settings); // ------------------------ // ejecutar backtest - esto ejecutará el backtest real utilizando los ajustes configurados anteriormente // Dependiendo de la configuración puede tardar un poco. // Cuando termine, devolverá un nuevo objeto ResultsGroup con el resultado del backtest. // En caso de error se lanza una Excepción con la descripción del error ResultsGroup backtestResultRG = backtestEngine.runBacktest().getResults();
Primero creamos un simulador de trading de un tipo determinado (MT4, MT5, Tradestation, etc.) y establecemos su precisión de backtest.
Entonces crearemos BacktestEngine utilizando este simulador. Tenemos que añadir ajustes que describan otros parámetros de backtest al motor utilizando addSetup() - los propios ajustes se explicarán más adelante.
Lo último es llamar runBacktest().getResults() para ejecutar el backtest real y obtener sus resultados.
Creación de ajustes para el backtest
BacktestEngine debe ser configurado con ajustes - un objeto SettingsMap. Es un mapa de pares clave -> valor, donde se pueden establecer varios parámetros del backtest.
SettingsMap ajustes = new SettingsMap(); // preparar la configuración del gráfico para el backtest - debe especificar el nombre de los datos, rango, etc ChartSetup chartSetup = new ChartSetup( "History", // esto es constante "EURUSD_M1", // nombre del símbolo, debe coincidir con el nombre en el Gestor de Datos TimeframeManager.TF_H1, // timeframe SQTimeOld.toLong(2008, 4, 20), // fecha desde SQTimeOld.toLong(2009, 6, 29), // fecha hasta 3.5, // margen Session.Forex_247 // sesión ); settings.set(SettingsKeys.BacktestChart, chartSetup); // preparar otros ajustes de backtest - esto pondrá otras partes opcionales/requeridas en el mapa de ajustes prepareBacktestSettings(settings, strategyToRetest);
Puedes ver que primero creamos el AjustesMapa objeto. A continuación definimos ChartSetup - es una configuración de los datos utilizados para el backtest, y se añade a la configuración llamando a settings.set(SettingsKeys.BacktestChart, chartSetup).
La última llamada es el método prepareBacktestSettings(...) que establecerá el resto de la configuración para mantener el código más simple y legible.
Método prepareBacktestSettings()
Es el método que establece otros valores de configuración opcionales y obligatorios para los ajustes de backtest, como la estrategia, la gestión del dinero, el capital inicial, las opciones de negociación, etc.
private void prepareBacktestSettings(SettingsMap settings,ResultsGroup strategyToRetest) throws Exception { // crea un objeto de estrategia a partir del XML de estrategia almacenado en el ResultsGroup de origen String strategyName = strategyToRetest.getName(); Elemento elStrategy = strategyToRetest.getStrategyXml(); if(elStrategy == null) { // el resultado no tiene ninguna estrategia, no se puede probar throw new Exception("¡El resultado no tiene ninguna estrategia, no se puede probar!"); } StrategyBase strategy = StrategyBase.createXmlStrategy(elStrategy.clone(), strategyName); settings.set(SettingsKeys.StrategyObject, strategy); settings.set(SettingsKeys.MinimumDistance, 0); // establecer el capital inicial y la gestión del dinero settings.set(SettingsKeys.CapitalInicial, 100000d); settings.set(SettingsKeys.MoneyManagement, MoneyManagementMethodsList.create("FixedSize", 0.1)); // Nota - puede crear un método MoneyManagemtn diferente especificando su nombre (snippet) // y los parámetros en su orden exacto, por ejemplo: // MoneyManagementMethodsList.create("RiskFixedPctOfAccount", 5, 100, 0.1, 0.5)) // crear y establecer las opciones de negociación necesarias TradingOptions options = createTradingOptions(); settings.set(SettingsKeys.TradingOptions, options); }
El código está comentado y debería ser capaz de entender lo que hace. Hay de nuevo un submétodo separado createTradingOptions() utilizado para mantener el código más organizado. Este método no se explicará aquí, está comentado en el código y devuelve una lista de opciones de negociación que deben aplicarse al backtest.
Trabajar con el resultado del backtest
Cuando su backtest haya finalizado con éxito obtendrá un nuevo Grupo de resultados con el resultado del backtest. A continuación, en general, puede hacer dos cosas con él:
- Lea sus métricas (número de operaciones, beneficio neto, Sharpe, etc.) para determinar si desea filtrar la estrategia
- Guardar el resultado en algún banco de datos o en un archivo
Ambas cosas son bastante sencillas:
// 1. Obtener los valores métricos y compararlos / filtrarlos de alguna manera, ejemplo: int operaciones = 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); // ahora haga algo con estos valores Log.info("Operaciones: {}, beneficio: {}", operaciones, beneficio); // 2. Puede guardar el nuevo backtest en un banco de datos o archivo SQProject proyecto = ProjectEngine.get(projectName); if(proyecto == null) { throw new Exception("¡No se puede cargar el proyecto '"+nombredelproyecto+"'!"); } Banco de datos targetDB = project.getDatabanks().get("Resultados"); if(targetDB == null) { throw new Exception("¡No se ha encontrado el banco de datos 'Resultados'!"); } // añada la nueva estrategia+backtest a este banco de datos y actualice la cuadrícula del banco de datos targetDB.add(backtestResultRG, true);
Código fuente completo del fragmento CAStrategyTestByProgramming:
paquete 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 CAStrategyTestPorProgramación() { 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; } // obtener la primera estrategia del banco de datos para volver a probarla ResultsGroup strategyToRetest = databankRG.get(0); // almacenaremos todos los ajustes para el nuevo backtest en este SettingsMap SettingsMap settings = new SettingsMap(); // preparar la configuración del gráfico para el backtest - debe especificar el nombre de los datos, rango, etc ChartSetup chartSetup = new ChartSetup( "History", // esto es constante "EURUSD_M1", // nombre del símbolo, debe coincidir con el nombre en el Gestor de Datos TimeframeManager.TF_H1, // timeframe SQTimeOld.toLong(2008, 4, 20), // fecha desde SQTimeOld.toLong(2009, 6, 29), // fecha hasta 3.5, // margen Session.Forex_247 // sesión ); settings.set(SettingsKeys.BacktestChart, chartSetup); // preparar otros ajustes de backtest - esto pondrá otras partes opcionales/requeridas en el mapa de ajustes prepareBacktestSettings(settings, strategyToRetest); // crear simulador de negociación (utilizado posteriormente para el motor de backtest) ITradingSimulator simulator = new MetaTrader4Simulator(); // simuladores disponibles: //MetaTrader4Simulator() //MetaTrader5SimulatorHedging(OrderExecutionTypes.EXCHANGE) //MetaTrader5SimulatorNetting(OrderExecutionTypes.EXCHANGE) //TradestationSimulator() //Simulador MultiCharts() //JForexSimulator() //establecer la precisión de prueba para el simulador simulator.setTestPrecision(Precisions.getPrecision(Precisions.PRECISION_BASE_TF)); // Precisiones disponibles (dependiendo también de los datos - no se puede utilizar la precisión de tick si no se tienen datos de tick): //PRECISION_SELECTED_TF = "Sólo marco temporal seleccionado (el más rápido)"; //PRECISION_BASE_TF = "Simulación de ticks de datos de 1 minuto (lento)"; //PRECISION_TICK_CUSTOM_SPREADS = "Tick real - spread personalizado (más lento)"; //PRECISION_TICK_REAL_SPREADS = "Tick real - spread real (más lento)"; //PRECISION_OPEN_PRICES = "Operar en apertura de barra"; // crear motor de backtest usando simulador BacktestEngine backtestEngine = new BacktestEngine(simulador); backtestEngine.setSingleThreaded(true); // añadir configuración de backtest al motor backtestEngine.addSetup(settings); // ------------------------ // ejecutar backtest - esto ejecutará el backtest real utilizando los ajustes configurados anteriormente // Dependiendo de la configuración puede tardar un poco. // Cuando termine, devolverá un nuevo objeto ResultsGroup con el resultado del backtest. // En caso de error se lanza una Excepción con la descripción del error ResultsGroup backtestResultRG = backtestEngine.runBacktest().getResults(); // obtener los detalles de los resultados del backtest - aquí puede acceder a todos los resultados en ResultsGroup // y hacer algo con ellos. // Generalmente puedes hacer dos cosas: // 1. Obtener los valores métricos y compararlos / filtrarlos de alguna manera, ejemplo: 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); // ahora haz algo con estos valores // Por ejemplo - puede utilizar el resultado de este backtest personalizado para filtrar esta estrategia en particular // del banco de datos, eliminándola de la matriz databankRG Log.info("Operaciones: {}, beneficio: {}", operaciones, beneficio); // 2. Puede guardar el nuevo backtest en un banco de datos o archivo SQProject proyecto = ProjectEngine.get(projectName); if(proyecto == null) { throw new Exception("¡No se puede cargar el proyecto '"+nombredelproyecto+"'!"); } Banco de datos targetDB = project.getDatabanks().get("Resultados"); if(targetDB == null) { throw new Exception("¡No se ha encontrado el banco de datos 'Resultados'!"); } // añada la nueva estrategia+backtest a este banco de datos y actualice la cuadrícula del banco de datos targetDB.add(backtestResultRG, true); // el método debe devolver una lista de estrategias originales en el banco de datos que hayan pasado este filtro return databankRG; } //------------------------------------------------------------------------ private void prepareBacktestSettings(SettingsMap settings,ResultsGroup strategyToRetest) throws Exception { // crea un objeto de estrategia a partir del XML de estrategia almacenado en el ResultsGroup de origen String strategyName = strategyToRetest.getName(); Elemento elStrategy = strategyToRetest.getStrategyXml(); if(elStrategy == null) { // el resultado no tiene ninguna estrategia, no se puede probar throw new Exception("¡El resultado no tiene ninguna estrategia, no se puede probar!"); } StrategyBase strategy = StrategyBase.createXmlStrategy(elStrategy.clone(), strategyName); settings.set(SettingsKeys.StrategyObject, strategy); settings.set(SettingsKeys.MinimumDistance, 0); // establecer el capital inicial y la gestión del dinero settings.set(SettingsKeys.CapitalInicial, 100000d); settings.set(SettingsKeys.MoneyManagement, MoneyManagementMethodsList.create("FixedSize", 0.1)); // Nota - puede crear un método MoneyManagemtn diferente especificando su nombre (snippet) // y los parámetros en su orden exacto, por ejemplo: // MoneyManagementMethodsList.create("RiskFixedPctOfAccount", 5, 100, 0.1, 0.5)) // crear y establecer las opciones de negociación necesarias TradingOptions options = createTradingOptions(); settings.set(SettingsKeys.TradingOptions, options); } //------------------------------------------------------------------------ private TradingOptions createTradingOptions() { TradingOptions opciones = new TradingOptions(); // todas las opciones de negociación se definen como fragmentos // en SQ.TradingOptions.* (visible en CodeEditor) // a continuación se muestra un ejemplo de algunas de ellas aplicadas ExitAtEndOfDay option = new ExitAtEndOfDay(); option.ExitAtEndOfDay = true; option.EODExitTime = 0; options.add(option); ExitOnFriday option2 = new ExitOnFriday(); option2.SalidaViernes = true; option2.HoraSalidaViernes = 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.LímiteMínimo = 50; optionMmSLPT.MaximumSL = 100; optionMmSLPT.MínimoPT = 50; optionMmSLPT.MaximumPT = 100; options.add(optionMmSLPT); devolver opciones; } }
¿Le ha resultado útil este artículo? El artículo era útil El artículo no era útil
¡¡¡Excelente !!! ¡¡¡¡Muy buena idea y muy útil !!!! No sabía que podíamos pedir a SQX que ejecutara un backtest desde Java.
Incluso podemos seleccionar el motor, la estrategia # y el período. ¡¡¡¡Voy a utilizarlo !!!!
SQX es realmente increíble
¡¡¡¡¡Con ejemplos como este, estoy aprendiendo mucho , Gracias equipo SQX !!!!!
Me aparece un pequeño error "Import failed.Archivo esperado con extensión .sxp” ?
Tengo V 135.368".
Extraño, el archivo tiene la extensión sxp.tengo este error cuando lo importo
¿Qué debo hacer para evitar este error?