Reiniciamos uma estratégia paper no meio de uma operação e obtivemos uma sequência de ordens diferente da que ela havia produzido antes da reinicialização. Os mesmos dados de mercado, a mesma versão da estratégia, o mesmo saldo da conta. O cálculo do sinal bateu. A memória da estratégia sobre a posição e as ordens pendentes, não.
Rastreamos a divergência ao longo de um dia de trabalho com replay. A lição útil foi simples: para o seu software, uma reinicialização é um evento de mercado. Se você não testar como a estratégia recompõe o estado, um backtest limpo pode esconder um sistema paper que esquece o que tem em carteira.
09:10 — Escolhemos uma posição sem grandes surpresas
A estratégia de teste negociava um contrato perpétuo líquido: entrava comprado quando uma média móvel curta cruzava acima de uma mais longa e saía no cruzamento inverso. Iniciamos o replay com uma pequena posição já aberta e uma ordem limite reduce-only em aberto para reduzi-la. Assim, a recuperação precisava reconstruir 2 fatos: o que tínhamos em carteira e o que já havíamos pedido à corretora para fazer.
No ponto de verificação, a conta tinha 0.04 contratos. A ordem aberta era de 0.01. O processo da estratégia havia armazenado ambos os valores na memória, mas, ao iniciar, só buscava a posição. Tratou a ordem em cache como se tivesse desaparecido.
09:25 — Surgiu a primeira ordem duplicada
Após a reinicialização, a estratégia detectou a posição comprada existente, executou a lógica do sinal e enviou outra ordem de redução de 0.01. Agora, a corretora paper tinha 2 ordens ativas. Nenhuma delas era ruim por si só. Juntas, poderiam vender o dobro do volume pretendido se ambas fossem executadas.
A princípio, culpamos o loop do sinal. O problema não era o loop, e sim o retrato incompleto do estado. A estratégia perguntou: “Que posição eu tenho?”, mas não perguntou: “Quais ordens ainda estão ativas?”
| Estado após a reinicialização | O que o processo acreditava | O que a conta tinha |
|---|---|---|
| Posição | Comprada 0.04 | Comprada 0.04 |
| Ordens de redução abertas | Nenhuma | 2 de 0.01 cada |
| Exposição pretendida após uma execução | Comprada 0.03 | Poderia cair para comprada 0.02 |
10:00 — Corrigimos a recuperação e encontramos um problema de sincronização
Alteramos a inicialização para reconstruir o estado a partir das posições e ordens abertas da conta antes de permitir novas decisões. Isso eliminou a duplicação. Depois, simulamos uma desconexão menos organizada: uma ordem foi executada enquanto a estratégia estava offline, e a notificação da execução chegou após a reconexão.
O snapshot da conta já refletia a execução. A notificação atrasada então reduziu a posição local pela segunda vez. Por alguns segundos, a estratégia acreditou ter 0.02 contratos, quando a conta tinha 0.03. O rebalanceamento seguinte se baseou em uma falta de posição fantasma.
Adicionamos regras de reconciliação: usar o snapshot da conta como ponto de partida, recorrer aos identificadores de evento para ignorar execuções já refletidas nele e não enviar ordens até que a sincronização inicial termine. Uma notificação pode chegar atrasada ou em duplicidade. A recuperação precisa lidar com os dois casos.
13:40 — O replay detectou uma divergência discreta
Reproduzimos a mesma trajetória 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 que o mercado reverteu. A comparação dos eventos de ordem expôs a divergência.
Registramos cada decisão junto com o estado que ela consultou: posição, ordens abertas, identificador da última execução processada, valor do sinal e versão da estratégia. Assim, ficou claro o motivo do primeiro evento divergente. Uma execução encontrou uma ordem ativa; a outra, uma lista vazia. Mais tarde, uma delas aplicou a mesma execução 2 vezes.
Um saldo final igual não comprova que o comportamento foi igual. Compare a sequência de decisões e ordens, especialmente nos momentos de recuperação.
16:20 — O que faríamos diferente da próxima vez
Passamos tempo demais reproduzindo os dados de preço 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 trajetória do mercado quase estável. Assim, fica fácil identificar o erro de software, sem que um movimento volátil complique o diagnóstico.
- Reinicie com uma posição e uma ordem parcialmente executada.
- Desconecte após o envio e reconecte antes da chegada da notificação de execução.
- Entregue o mesmo evento de execução 2 vezes e verifique se ele altera o estado apenas uma vez.
- Bloqueie novas ordens até que as posições e as ordens abertas tenham sido reconciliadas.
- Compare os registros de decisões e ordens entre execuções ininterruptas e reiniciadas.
Também aprendemos a salvar um snapshot de recuperação junto com a versão da estratégia e o registro de eventos. Isso tornou possível reproduzir a falha em minutos, sem depender de alguém se lembrar da sequência exata de reconexão.
Uma estratégia paper que só se comporta corretamente enquanto o processo continua ativo ainda não passou por um ensaio completo. Reinicie-a no meio de uma posição, faça as execuções chegarem atrasadas e inspecione cada ordem enviada em seguida. O objetivo não é provar que ela nunca falha. É deixar a recuperação visível antes que a conta paper precise ensinar a mesma lição de surpresa.
← Todos os artigos


