Documentação
Aplicações
Última atualização em 22. 2. 2022 por Mark Fric
Execução programática de testes estratégicos de retaguarda
Conteúdo da página
Este exemplo mostrará como executar os testes de estratégia de forma programática, chamando nosso motor de testes de retaguarda. O exemplo é feito em forma de snippet de Análise Personalizada.
O código de backtesting pode ser teoricamente incluído em qualquer outro trecho, você só precisa perceber que os backtests são lentos, então definitivamente não é recomendado executar backtests em colunas de banco de dados e outros trechos similares que têm que ser rápidos.
Você pode baixar o trecho completo como anexo neste artigo, seu código fonte completo também será publicado no final do artigo.
Descreveremos as partes mais importantes do backtest passo a passo. O backtest em si é implementado no método de Análise Personalizada processDatabank()onde realiza o backktest personalizado da primeira estratégia no banco de dados.
Usando o BacktestEngine para fazer o backtest
O teste de retrocesso é relativamente simples, você só precisa inicializar BacktestEngine e depois chamar seu método runBacktest() e então getResults() para obter o resultado mais atrás.
// criar simulador de negociação (mais tarde usado para o motor backtest) ITradingSimulator simulador = novo MetaTrader4Simulator(); // precisão de teste do conjunto para o simulador simulador.setTestPrecision(Precisions.getPrecision(Precisions.PRECISION_BASE_TF)); // criar o motor de retaguarda usando o simulador BacktestEngine BacktestEngine = novo BacktestEngine(simulador); backtestEngine.setSingleThreaded(true); // adicionar configurações de backtest ao motor backtestEngine.addSetup(configurações); // ------------------------ // executar o backtest - isto irá executar o backtest real usando os ajustes configurados acima // Dependendo das configurações, pode levar algum tempo. // Quando terminado, ele retornará um novo objeto ResultsGroup com o resultado do backtest. // Em caso de erro, uma Exceção é lançada com a descrição do erro ResultadosResultadoGrupo backktestResultadoRG = backtestEngine.runBacktest().getResultado();
Primeiro criamos um simulador de negociação de determinado tipo (MT4, MT5, Tradestation, etc.) e estabelecemos sua precisão de retaguarda.
Então criaremos BacktestEngine usando este simulador. Temos que adicionar configurações que descrevem outros parâmetros de backtest ao motor usando addSetup() - as próprias configurações serão explicadas posteriormente.
A última coisa é chamar runBacktest().getResults() para executar o backtest real e obter seus resultados.
Criando configurações para o backtest
O BacktestEngine deve ser configurado com configurações - um objeto SettingsMap. É um mapa de pares de chaves -> valores, onde é possível definir vários parâmetros do backtest.
SettingsMap settings = novas configuraçõesMap(); // preparar a configuração do gráfico para o backtest - você deve especificar o nome dos dados, intervalo, etc ChartSetup chartSetup = novo ChartSetup( "História", // isto é constante "EURUSD_M1", // nome do símbolo, deve corresponder ao nome no Gerenciador de dados TimeframeManager.TF_H1, // Timeframe SQTimeOld.toLong(2008, 4, 20), // data de SQTimeOld.toLong(2009, 6, 29), // data até 3,5, // spread Sessão.Forex_247 // sessão ); settings.set(SettingsKeys.BacktestChart, chartSetup); // preparar outras configurações de backtest - isto irá colocar outras peças opcionais/necessárias no mapa de configurações prepararBacktestSettings(configurações, estratégiaToRetest);
Você pode ver que nós criamos primeiro o ConfiguraçõesMapa objeto. Definimos então ChartSetup - é uma configuração de dados usada para backtest, e é adicionada às configurações chamando settings.set(SettingsKeys.BacktestChart, chartSetup).
A última chamada é o método PrepararConfigurações de Ensaios(...) que definirá o resto das configurações para manter o código mais simples e legível.
Método de preparaçãoBacktestSettings()
É o método que define outros valores de configuração opcionais e necessários para as configurações de retaguarda - como estratégia, gerenciamento de dinheiro, capital inicial, opções comerciais e assim por diante.
vazio privado prepararPacktestSettings(ConfiguraçõesConfiguraçõesMapa,ResultadosEstratégia do GrupoToRetest) lança Exceção { // cria objeto de estratégia a partir da estratégia XML que é armazenada na fonte ResultsGroup String strategyName = strategyToRetest.getName(); Element elStrategy = strategyToRetest.getStrategyXml(); if(elStrategy == nulo) { // o resultado não tem nenhuma estratégia, não pode ser testado lançar nova Exceção ("O resultado não tem estratégia, não pode ser testado!"); } StrategyBase strategy = StrategyBase.createXmlStrategy(elStrategy.clone(), strategyName); settings.set(SettingsKeys.StrategyObject, strategyName); settings.set(SettingsKeys.MinimumDistance, 0); // estabelecer capital inicial e gestão de dinheiro settings.set(SettingsKeys.InitialCapital, 100000d); settings.set(SettingsKeys.MoneyManagement, MoneyManagementMethodsList.create("FixedSize", 0.1)); // Nota - você pode criar um método Money Managemtn diferente especificando seu nome (snippet) // e parâmetros em sua ordem exata, por exemplo: // MoneyManagementMethodsList.create("RiskFixedPctOfAccount", 5, 100, 0.1, 0.5)) // criar e definir as opções comerciais necessárias TradingOptions = criarTradingOptions(); settings.set(SettingsKeys.TradingOptions, opções); }
O código é comentado e você deve ser capaz de entender o que ele faz. Há novamente um sub-método separado criarOpções de Negociação() usado para manter o código mais organizado. Este método não será explicado aqui, ele é comentado no código e retorna uma lista de opções comerciais que devem ser aplicadas ao backtest.
Trabalhando com o resultado do backtest
Quando seu backktest terminar com sucesso você terá um novo ResultadosGrupo objeto com o resultado do backtest. Em geral, você pode fazer duas coisas com ele:
- Leia suas métricas (número de negócios, lucro líquido, Sharpe, etc.) para determinar se você quer filtrar a estratégia
- Salvar o resultado para algum banco de dados ou para arquivar
Ambas as coisas são bastante simples:
// 1. obter os valores métricos e compará-los / filtrá-los de alguma forma, por exemplo: 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); // agora faça algo com estes valores Log.info("Comércios: {}, lucro: {}", negócios, lucro); // 2. você pode salvar o novo backtest em um banco de dados ou arquivo Projeto SQProject = ProjectEngine.get(projectName); if(project === nulo) { lançar nova Exceção ("Projeto '"+projetoNome+"'' não pode ser carregado!"); } Banco de dados alvoDB = project.getDatabanks().get("Resultados"); if(targetDB == nulo) { lançar nova Exceção ("O banco de dados alvo 'Resultados' não foi encontrado!"); } // adicionar a nova estratégia+backtest a este banco de dados e atualizar a grade do banco de dados targetDB.add(backtestResultRG, true);
Código fonte completo de CAStrategyTestByProgramming snippet:
pacote SQ.CustomAnalysis; importação SQ.TradingOptions.*; import com.strategyquant.datalib.timeframeManager; import com.strategyquant.datalib.consts.Precisions; import com.strategyquant.datalib.session.session.Session; import com.strategyquant.lib.SettingsMap; import com.strategyquant.lib.time.SQTimeOld; import com.strategyquant.tradinglib.*; importar com.strategyquant.tradinglib.engine.BacktestEngine; import com.strategyquant.tradinglib.moneymanagement.MoneyManagementMethodsList; importar com.strategyquant.tradinglib.options.TradingOptions; import com.strategyquant.tradinglib.simulator.Engines; importar com.strategyquant.tradinglib.simulator.ITradingSimulator; importar com.strategyquant.tradinglib.simulator.impl.*; importar com.strategyquant.tradinglib.project.projectEngine; importar com.strategyquant.tradinglib.project.SQProject; import org.jdom2.Element; importar org.slf4j.Logger; importação org.slf4j.LoggerFactory; importação java.util.ArrayList; classe pública CAStrategyTestByProgramming estende o CustomAnalysisMethod { logger final estático público = LoggerFactory.getLogger("CAStrategyTestByProgramming"); //------------------------------------------------------------------------ //------------------------------------------------------------------------ //------------------------------------------------------------------------ public CAStrategyTestByProgramming() { super("CAStrategyTestByProgramming", TYPE_PROCESS_DATABANK); } //------------------------------------------------------------------------ @Override filtro booleano públicoEstratégia(String projectName, String task, String databankName, ResultsGroup rg) lança Exceção { retornar falso; } //------------------------------------------------------------------------ @Override public ArrayList processDatabank(String projectName, String task, String databankName, ArrayList databankRG) lança Exceção { if(databankRG.size() == 0) { banco de dados de retornoRG; } // obter a primeira estratégia em banco de dados para realizar o reteste ResultsGroup strategyToRetest = databankRG.get(0); // nós armazenaremos todas as configurações para novos backtest neste Mapa de Configurações SettingsMap settings = novas configuraçõesMap(); // preparar a configuração do gráfico para o backtest - você deve especificar o nome dos dados, intervalo, etc ChartSetup chartSetup = novo ChartSetup( "História", // isto é constante "EURUSD_M1", // nome do símbolo, deve corresponder ao nome no Gerenciador de dados TimeframeManager.TF_H1, // Timeframe SQTimeOld.toLong(2008, 4, 20), // data de SQTimeOld.toLong(2009, 6, 29), // data até 3,5, // spread Sessão.Forex_247 // sessão ); settings.set(SettingsKeys.BacktestChart, chartSetup); // preparar outras configurações de backtest - isto irá colocar outras peças opcionais/necessárias no mapa de configurações prepararBacktestSettings(configurações, estratégiaToRetest); // criar simulador de negociação (mais tarde usado para o motor backtest) ITradingSimulator simulador = novo MetaTrader4Simulator(); // simuladores disponíveis: //MetaTrader4Simulator() //MetaTrader5SimulatorHedging(OrderExecutionTypes.EXCHANGE) //MetaTrader5SimulatorNetting(OrderExecutionTypes.EXCHANGE) //TradestationSimulator() //MultiChartsSimulator() //JForexSimulator() // precisão de teste do conjunto para o simulador simulador.setTestPrecision(Precisions.getPrecision(Precisions.PRECISION_BASE_TF)); // Precisões disponíveis (dependendo também dos dados - você não pode usar o tick precisão se não tiver dados tick): //PRECISION_SELECTED_TF = "Somente o tempo selecionado (mais rápido)"; //PRECISION_BASE_TF = "1 minuto de simulação do tick de dados (lento)"; //PRECISION_TICK_CUSTOM_SPREADS = "Real Tick - propagação personalizada (mais lento)"; //PRECISION_TICK_REAL_SPREADS = "Real Tick - espalhamento real (mais lento)"; //PRECISION_OPEN_PRICES = "Comércio em Bar Aberto"; // criar o motor de retaguarda usando o simulador BacktestEngine BacktestEngine = novo BacktestEngine(simulador); backtestEngine.setSingleThreaded(true); // adicionar configurações de backtest ao motor backtestEngine.addSetup(configurações); // ------------------------ // executar o backtest - isto irá executar o backtest real usando os ajustes configurados acima // Dependendo das configurações, pode levar algum tempo. // Quando terminado, ele retornará um novo objeto ResultsGroup com o resultado do backtest. // Em caso de erro, uma Exceção é lançada com a descrição do erro ResultadosResultadoGrupo backktestResultadoRG = backtestEngine.runBacktest().getResultado(); // obter detalhes dos resultados - aqui você pode acessar todos os resultados em ResultsGroup // e fazer algo com eles. // Em geral, você pode fazer duas coisas: // 1. obter os valores métricos e compará-los / filtrá-los de alguma forma, por exemplo: 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); // agora faça algo com estes valores // Por exemplo - você pode usar o resultado deste backtest personalizado para filtrar esta estratégia em particular // do banco de dados - removendo-o da matriz do banco de dadosRG Log.info("Comércios: {}, lucro: {}", negócios, lucro); // 2. você pode salvar o novo backtest em um banco de dados ou arquivo Projeto SQProject = ProjectEngine.get(projectName); if(project === nulo) { lançar nova Exceção ("Projeto '"+projetoNome+"'' não pode ser carregado!"); } Banco de dados alvoDB = project.getDatabanks().get("Resultados"); if(targetDB == nulo) { lançar nova Exceção ("O banco de dados alvo 'Resultados' não foi encontrado!"); } // adicionar a nova estratégia+backtest a este banco de dados e atualizar a grade do banco de dados targetDB.add(backtestResultRG, true); // método deve retornar uma lista de estratégias originais em banco de dados que passaram por este filtro banco de dados de retornoRG; } //------------------------------------------------------------------------ vazio privado prepararPacktestSettings(ConfiguraçõesConfiguraçõesMapa,ResultadosEstratégia do GrupoToRetest) lança Exceção { // cria objeto de estratégia a partir da estratégia XML que é armazenada na fonte ResultsGroup String strategyName = strategyToRetest.getName(); Element elStrategy = strategyToRetest.getStrategyXml(); if(elStrategy == nulo) { // o resultado não tem nenhuma estratégia, não pode ser testado lançar nova Exceção ("O resultado não tem estratégia, não pode ser testado!"); } StrategyBase strategy = StrategyBase.createXmlStrategy(elStrategy.clone(), strategyName); settings.set(SettingsKeys.StrategyObject, strategyName); settings.set(SettingsKeys.MinimumDistance, 0); // estabelecer capital inicial e gestão de dinheiro settings.set(SettingsKeys.InitialCapital, 100000d); settings.set(SettingsKeys.MoneyManagement, MoneyManagementMethodsList.create("FixedSize", 0.1)); // Nota - você pode criar um método Money Managemtn diferente especificando seu nome (snippet) // e parâmetros em sua ordem exata, por exemplo: // MoneyManagementMethodsList.create("RiskFixedPctOfAccount", 5, 100, 0.1, 0.5)) // criar e definir as opções comerciais necessárias TradingOptions = criarTradingOptions(); settings.set(SettingsKeys.TradingOptions, opções); } //------------------------------------------------------------------------ tradingOptions privados criam TradingOptions() { Opções TradingOptions = novas opções TradingOptions(); // todas as opções comerciais são definidas como snippets // em SQ.TradingOptions.* (visível em CodeEditor) // abaixo é um exemplo de poucos deles aplicados Opção ExitAtEndOfDay = nova opção ExitAtEndOfDay(); opção.ExitAtEndOfDay = verdadeiro; option.EODExitTime = 0; options.add(opção); Opção ExitOnFriday2 = nova ExitOnFriday(); opção2.ExitOnFriday = verdadeiro; opção2.FridayExitTime = 0; options.add(opção2); LimitTimeRange opção3 = novo LimitTimeRange(); opção3.LimitTimeRange(); LimitTimeRange() = verdadeiro; opção3.SignalTimeRangeFrom = 700; opção3.SignalTimeRangeTo = 1900; option3.ExitAtEndOfRange = true; options.add(opção3); MinMaxSLPT opçãoMmSLPT = novo MinMaxSLPT(); optionMmSLPT.MinimumSLPT = 50; optionMmSLPT.MaximumSLPT = 100; optionMmSLPT.MinimumPT = 50; optionMmSLPT.MaximumPT = 100; options.add(optionMmSLPT); opções de retorno; } }
Este artigo foi útil? O artigo foi útil O artigo não foi útil
Excelente!!! Muito boa idéia e muito útil !!!! Eu não sabia que poderíamos pedir à SQX para fazer um backtest de Java.
Podemos até mesmo selecionar o motor, a estratégia # e o período. Vou utilizá-lo !!!!
O SQX é realmente incrível
Com um exemplo como este, estou aprendendo muito, obrigado equipe SQX !!!!!
Estou recebendo um pequeno erro".Importação falhou. Arquivo esperado com extensão .sxp” ?
Eu tenho o V 135.368".
Estranho, o arquivo tem a extensão sxpTenho este erro quando o importo...
O que devo fazer para evitar este erro?