Um bug causou 86% das nossas perdas em trading
Nosso primeiro bot de trading perdeu ₩5,1 milhões em simulação, em 190 saídas. Quando dividimos as perdas pelo motivo de cada saída, um único caminho de código respondeu por 86,5% delas. Depois, o mesmo bug nos enganou uma segunda vez.
Como isto foi feito: o código, as execuções da pesquisa e os primeiros rascunhos deste texto foram produzidos por um assistente de IA que trabalhou sob a direção do operador; o operador tomou todas as decisões e conferiu os números. Toda a negociação foi simulada (paper trading): preços reais, sem dinheiro de verdade. Nada aqui é recomendação de investimento.
Perdas por motivo de saída
| Motivo da saída | Saídas | Resultado, KRW | Média | Taxa de acerto | Permanência mediana |
|---|---|---|---|---|---|
| Saída por alarme sem confirmação | 157 | −4.416.442 | −0,37% | 10,2% | 12 s |
| Tempo de permanência esgotado | 18 | −244.623 | −0,26% | 38,9% | 2 h |
| Saída por alarme confirmada | 6 | −684.389 | −2,10% | 0% | 14 min |
| Trailing stop | 5 | +613.519 | +2,33% | 100% | 18 min |
| Stop loss | 2 | −386.990 | −3,69% | 0% | 23 min |
| Outros | 2 | +13.194 | — | — | — |
157 das 190 saídas vieram de uma única regra: um alarme que vendia imediatamente, sem confirmação, sem tempo mínimo de permanência e sem verificar se o preço de fato havia se movido. Essas saídas tiveram uma permanência mediana de 12 segundos. O custo mediano de ida e volta delas, medido no mesmo registro, foi de 0,43%, então perdiam dinheiro mesmo quando o preço não se movia: 97 delas, 62%, terminaram com o preço a menos de 0,35% da entrada e mesmo assim deram prejuízo.
As saídas feitas em até 30 segundos após a entrada, 114 operações, responderam sozinhas por 63% de todas as perdas.
O que o código fazia
No mesmo arquivo, outros dois alarmes já exigiam uma etapa de confirmação antes de vender. Este havia ficado de fora. Um módulo irmão tinha sido corrigido para o mesmo problema semanas antes; este não. Alteramos o código para que um alarme sem confirmação só possa vender depois de um tempo mínimo de permanência de 20 segundos, de uma perda real maior que o custo de ida e volta e de seis segundos de confirmação, enquanto as saídas de segurança rígidas, o stop loss e a saída por crash, continuaram sendo acionadas imediatamente.
Em um replay sobre o registro, a nova regra teria bloqueado 143 das 157 saídas por alarme. Não apresentamos isso como ganho. Bloquear uma saída significa apenas não pagar a ida e volta naquele momento; o que o preço teria feito depois, se tivéssemos mantido a posição, não pode ser provado a partir deste registro.
O segundo erro
A auditoria também mostrou que as operações da janela de sinal de 3 minutos tinham perdido ₩4,42 milhões, enquanto a janela de 1 hora tinha dado lucro, e recomendamos operar menos a janela de 3 minutos. Um dia depois, retiramos a recomendação. 157 das 158 operações da janela de 3 minutos eram exatamente essas saídas por alarme. Sem elas, a janela de 3 minutos teve uma operação. Tínhamos comparado dois caminhos de código e chamado isso de diferença entre tempos gráficos.
A mesma segunda auditoria encontrou uma falha relacionada: seis saídas por alarme confirmado haviam vendido todas antes de chegar ao próprio stop loss, em média a −2,0%, e cinco das seis moedas estavam mais altas quinze minutos depois. Os alarmes estavam passando por cima do plano de risco. Passamos a impedi-los de vender antes da linha do stop.
O que tiramos disso
- Divida as perdas por motivo de saída e por caminho de código antes de culpar a estratégia. Um único bug pode parecer exatamente uma estratégia ruim.
- Um padrão encontrado nos dados pode ser o bug disfarçado. Verifique se os grupos que você compara passaram pelo mesmo código.
- Não mude uma política com base em 8 a 12 operações.
- Guarde as evidências: apenas 32 das 190 saídas ainda tinham o registro de preços posterior à saída, porque um log de 50 entradas havia sobrescrito o restante. Aumentamos o limite para 200.
Corrigir o bug eliminou um bug, não o problema de fundo: a vantagem da estratégia por operação continuava menor que os seus custos. As seis semanas inteiras.