Documentação

Aplicações

Última atualização em 22. 2. 2022 por Mark Fric

Execução programática de testes estratégicos de retaguarda

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:

  1. Leia suas métricas (número de negócios, lucro líquido, Sharpe, etc.) para determinar se você quer filtrar a estratégia
  2. 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

Assine
Notificação de
3 Comentários
Mais antigo
Novidades Mais Votados
Feedbacks em linha
Ver todos os comentários
Emmanuel
25. 2. 2022 9:46 am

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

Emmanuel
25. 2. 2022 9:47 am

Com um exemplo como este, estou aprendendo muito, obrigado equipe SQX !!!!!

Emmanuel
25. 2. 2022 9:55 am

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?

Postos relacionados