Reiniciámos uma estratégia paper a meio de uma operação e obtivemos uma sequência de ordens diferente da que tinha produzido antes do reinício. Os mesmos dados de mercado, a mesma versão da estratégia, o mesmo saldo da conta. O cálculo do sinal coincidiu. A memória da estratégia sobre a posição e as ordens pendentes, não.
Investigámos a divergência ao longo de um dia de reprodução. A lição útil foi simples: para o seu software, um reinício é um evento de mercado. Se não testar como a estratégia reconstrói o seu estado, um backtest limpo pode ocultar um sistema paper que se esquece do que tem.
09:10 — Escolhemos uma posição sem complicações
A estratégia de teste negociava um contrato perpétuo líquido: entrava longo quando uma média móvel curta cruzava acima de uma mais longa e saía no cruzamento inverso. Iniciámos a reprodução com uma pequena posição já aberta e uma ordem limite reduce-only pendente para a reduzir. Assim, a recuperação tinha de reconstruir 2 factos: o que detínhamos e o que já tínhamos pedido à plataforma para fazer.
No ponto de controlo, a conta tinha 0.04 contratos. A ordem aberta era de 0.01. O processo da estratégia tinha ambos os valores em cache na memória, mas o processo de arranque só obtinha a posição. Tratava a ordem em cache como se já não existisse.
09:25 — Surgiu a primeira ordem duplicada
Após o reinício, a estratégia detetou a posição longa existente, executou a lógica do sinal e submeteu outra ordem de redução de 0.01. A plataforma paper tinha agora 2 ordens ativas. Nenhuma era, por si só, uma ordem errada. Em conjunto, podiam vender o dobro da quantidade pretendida se ambas fossem executadas.
De início, culpámos o ciclo do sinal. O problema não estava no ciclo; estava na captura de estado incompleta. A estratégia perguntava: «Que posição tenho?», mas nunca perguntava: «Que ordens continuam ativas?».
| Estado após o reinício | O que o processo julgava ter | O que a conta tinha |
|---|---|---|
| Posição | Longa 0.04 | Longa 0.04 |
| Ordens de redução abertas | Nenhuma | 2 de 0.01 cada |
| Exposição pretendida após uma execução | Longa 0.03 | Podia ficar longa 0.02 |
10:00 — Corrigimos a recuperação e depois encontrámos um problema de sincronização
Alterámos o arranque para reconstruir o estado a partir das posições da conta e das ordens abertas antes de permitir novas decisões. Isso eliminou a duplicação. Depois, tornámos a desconexão menos previsível: uma ordem foi executada enquanto a estratégia estava offline e a notificação da execução chegou depois da reconexão.
A captura da conta já refletia a execução. A notificação atrasada reduziu então a posição local uma segunda vez. Durante alguns segundos, a estratégia julgou ter 0.02 contratos quando a conta tinha 0.03. O reequilíbrio seguinte baseou-se num défice que não existia.
Acrescentámos regras de reconciliação: usar a captura da conta como ponto de partida, recorrer a identificadores de eventos para ignorar execuções já refletidas nessa captura e não submeter ordens até terminar a sincronização inicial. Uma notificação pode chegar atrasada ou duplicada. A recuperação tem de lidar com ambas as situações.
13:40 — A reprodução detetou uma divergência discreta
Reproduzimos a mesma evolução de preços na execução original e na execução reiniciada. Comparar o P&L final não teria revelado o problema: as 2 versões terminaram com a mesma posição depois de o mercado inverter. A comparação dos eventos de ordens tornou-o evidente.
Registámos cada decisão juntamente com o estado que consultou: posição, ordens abertas, identificador da última execução processada, valor do sinal e versão da estratégia. Assim, foi possível explicar o primeiro evento divergente. Uma execução detetou uma ordem ativa; a outra encontrou uma lista vazia. Mais tarde, uma delas processou uma execução duas vezes.
Um saldo final igual não prova que o comportamento foi igual. Compare a sequência de decisões e ordens, sobretudo nos momentos de recuperação.
16:20 — O que faríamos de forma diferente da próxima vez
Passámos demasiado tempo a reproduzir os dados de preços antes de verificar as transições de estado da conta. Da próxima vez, injetaríamos primeiro os casos de falha e manteríamos a evolução do mercado quase plana. Assim, o erro do software fica fácil de identificar, sem que um movimento volátil complique o diagnóstico.
- Reiniciar com uma posição e uma ordem parcialmente executada.
- Desligar após a submissão e voltar a ligar antes de chegar a notificação da execução.
- Entregar o mesmo evento de execução 2 vezes e confirmar que o estado só se altera uma vez.
- Impedir novas ordens até as posições e as ordens abertas estarem reconciliadas.
- Comparar os registos de decisões e ordens entre as execuções contínuas e as reiniciadas.
Também aprendemos a guardar uma captura de recuperação juntamente com a versão da estratégia e o registo de eventos. Isso permitiu reproduzir a falha em minutos, sem depender da memória de alguém para reconstituir a sequência exata de reconexão.
Uma estratégia paper que só se comporta corretamente enquanto o seu processo permanece ativo ainda não passou por um ensaio completo. Reinicie-a a meio de uma posição, faça chegar execuções com atraso e inspecione todas as ordens que enviar a seguir. O objetivo não é provar que nunca falha. É tornar a recuperação visível antes de a conta paper lhe ensinar a mesma lição de surpresa.
← Todos os artigos


