Voltámos a executar um backtest guardado e obtivemos uma curva de capital diferente. O mesmo código da estratégia, o mesmo intervalo de datas, o mesmo símbolo. O saldo final diferia 1.8%, e três operações tinham mudado 1 barra.
Isso basta para tornar difícil confiar num resultado de investigação. Se um colega, uma versão futura de si ou um serviço de paper trading não conseguir reproduzir a execução, não é possível saber se uma alteração melhorou a estratégia ou se apenas mudou a experiência. Eis a sequência que seguimos para encontrar a divergência.
Dia 1: Definimos por escrito o que significava «a mesma execução»
O nosso primeiro erro foi tratar um ficheiro de estratégia como se fosse a experiência completa. Não era. A execução também dependia dos dados de entrada, da versão do motor, do calendário, dos metadados dos instrumentos e das definições de execução. O código descrevia apenas uma parte do cálculo.
Criámos um manifesto da execução antes de alterar o que quer que fosse. Registámos o commit da estratégia, os identificadores dos instantâneos de dados, o intervalo de datas, a plataforma, a tabela de comissões, a fonte de funding, o modelo de execução das ordens e as versões do software. Também guardámos as ordens e as execuções resultantes, porque uma curva de capital, por si só, não mostra onde duas execuções começaram a divergir.
| Artefacto | O que registar | Porque é importante |
|---|---|---|
| Dados de mercado | ID do instantâneo, versão do esquema, ajustamentos | Os fornecedores corrigem o histórico e revêm as operações societárias |
| Execução | Escalão de comissões, série de funding, definições de execução e impacto | As predefinições e os pressupostos relativos à conta alteram os resultados |
| Ambiente de execução | Commit do código, versões do motor e das dependências | As bibliotecas podem alterar a ordenação, o arredondamento ou os indicadores |
| Resultado | Ordens, execuções, posições e métricas | Mostra onde as execuções começam a divergir |
Dia 2: Comparamos as operações, não o Sharpe
As métricas resumidas eram uma distração. Ambas as execuções tinham um Sharpe quase igual, mas os registos de execução das ordens revelaram a primeira divergência numa liquidação de funding. Uma execução aplicou a taxa à posição aberta no momento da liquidação; a outra usou a posição após o rebalanceamento desse mesmo instante.
O código da estratégia não tinha mudado. A ordenação dos eventos no motor, sim. Uma pequena atualização de versão tornou explícita uma sequência que antes dependia da ordem em que dois eventos calhavam ser ordenados.
Corrigimos o contrato da execução para especificar a ordem: aplicar o funding à posição mantida até à liquidação e, em seguida, processar as decisões da estratégia para esse instante. A convenção exata pode variar consoante a plataforma e o motor. Deixá-la implícita é o erro.
Dia 3: Um ficheiro de dados «igual» afinal era diferente
Depois de fixarmos a ordem dos eventos, as divergências restantes concentravam-se em algumas operações sobre ações. O fornecedor tinha corrigido um ajustamento histórico de desdobramento de ações. O nosso ficheiro tinha o mesmo nome e o mesmo número de linhas de antes, o que nos levara a pensar que não tinha mudado.
Agora, calculamos uma impressão digital de cada instantâneo de dados imutável e guardamos junto dele a política de ajustamento. Um hash indica-nos se os bytes mudaram; não explica porquê. Por isso, o manifesto também inclui a fonte, a hora de obtenção e a versão da transformação. No caso de dados sujeitos a revisão, esses detalhes fazem parte do resultado.
Um backtest reprodutível tem de responder à pergunta: «Que versão do passado viu?»
Dia 4: Encontrámos uma predefinição discreta
A última diferença era uma comissão maker definida como 0 porque a configuração da estratégia não incluía esse campo. Um motor mais recente aplicava a comissão predefinida da conta. Essa única predefinição alterou as operações marginais o suficiente para explicar a maior parte da diferença no saldo final.
Tornámos explícitas as definições com impacto económico e configurámos o motor para incluir a configuração resolvida no registo da execução. As predefinições são práticas enquanto exploramos. São uma base fraca para comparar resultados ao longo do tempo.
O que faríamos de forma diferente da próxima vez
Passámos meio dia a comparar métricas agregadas antes de procurar a primeira execução divergente. Não comecem por aí. Ordenem os dois registos de eventos por data e hora e comparem a primeira divergência; as diferenças seguintes resultam muitas vezes dessa causa inicial.
Também deixaríamos de acreditar que uma imagem de contentor torna, por si só, uma execução reprodutível. Fixa grande parte do ambiente de software, mas não um ficheiro de dados externo, uma tabela de comissões obtida em tempo de execução ou o histórico revisto de um fornecedor.
Quando um backtest muda, guardem os manifestos e registos de ambas as execuções e, em seguida, corrijam uma origem de divergência de cada vez. O resultado útil não é apenas uma curva que se consegue voltar a gerar. É um registo que explica que dados e pressupostos a produziram, e porque poderá a próxima execução ser diferente.
← Todos os artigos


