Avançar para o conteúdo

Três erros, três testes aprovados, nenhum detetado

Gráfico de 4 horas do perpétuo ETHUSDT com duas janelas de retrospeção desenhadas lado a lado, uma de 23 barras e outra de 22, ou seja, o comprimento que o código Pine calcula e o comprimento que a reconstrução em Python calculou a partir da mesma fórmula.
A mesma retrospeção, calculada duas vezes. O Pine arredonda 22,5 afastando-se do zero e obtém 23. O Python 3 arredonda para o número par e obtém 22. Nada, em nenhuma das duas linguagens, o avisa de que a convenção mudou.

Se alguma vez transpôs um indicador para Python para lhe fazer um backtest, ou confiou num script porque vinha com uma verificação, é da lacuna que essas duas coisas deixam aberta que aqui se fala.

Reconstruímos três dos nossos próprios indicadores para que um backtest pudesse correr sobre eles. As três reconstruções tinham lá dentro uma constante errada. As três passaram a sua validação. Nenhuma foi apanhada por um teste.

Porquê reconstruir sequer um indicador

Um script Pine corre no seu gráfico. Não percorre seis anos de histórico em ciclo, e não responde se um sinal compensa realmente. Para fazer essa pergunta, reconstrói-se em algo que se possa correr milhares de vezes.

É nessa reconstrução que um indicador deixa de ser, sem alarido, o mesmo indicador.

O erro que mais custou, e é uma barra

Um dos comprimentos dentro do Volume Oracle é derivado do período:

target bars   = 36 hours × 60 / 240 minutes = 9
regime length = round(9 × 2.5) = round(22.5)

O math.round do Pine resolve os empates afastando-se do zero. Dá 23. O round() do Python 3 usa o arredondamento bancário, que resolve os empates para o número par. Dá 22.

Ambas são implementações corretas do arredondamento. São simplesmente convenções diferentes, e nada o avisa de que passou de uma para a outra.

Todas as retrospeções derivadas desse número se moveram com ele. O resultado na linha mais bem sustentada de uma varredura de 36 sinais:

versãovantagem a 48 htaxa de acerton
reconstruído, comprimento 22+0,423 %511216
correto, comprimento 23+0,260 %501223

Um corte de 38 % por causa de uma convenção de arredondamento.

Depois registámos a lição de forma errada

A nota que escrevemos dizia que acontecia apenas em 4H. Foi escrita a partir do único período que estava então em teste, e assim ficou durante dois meses.

A correr a mesma aritmética em todos os períodos:

períodocomprimento de regimecomprimento de fluxoresultado
5m45 contra 4523 contra 22diverge
2H45 contra 4523 contra 22diverge
4H23 contra 2212 contra 11diverge
8H53 contra 5227 contra 26diverge
1D18 contra 189 contra 9empata, concorda
3D, 1W15 contra 158 contra 8empata, concorda

Quatro em dezasseis, não um.

Dessa tabela caem duas coisas que raciocínio nenhum produziu.

Um empate não chega. 1D, 3D e 1W caem todos exatamente em ,5 e concordam. O arredondamento bancário só move um empate cuja resposta arredondada para cima é ímpar, por isso round(17.5) dá 18 de qualquer das formas. Cerca de metade de todos os empates são inofensivos, e é precisamente por isso que verificar um por amostragem não prova nada.

A divergência pode chegar um passo mais tarde. Em 5m e 2H o primeiro comprimento concorda em 45. A linha seguinte, round(45 × 0.5), volta a ser 22,5, e essa divide-se. Se verificar a primeira constante e seguir em frente, dá por bons os dois períodos sem razão.

Os outros dois, em breve

Harmonic normaliza um oscilador recortando-o para uma banda móvel. No código fonte, cada barra é recortada pela sua própria banda. A reconstrução recortava uma janela inteira de 200 barras usando a banda da barra atual, o que é uma função diferente. Diferença medida em 11.497 barras: 0,47 pontos em média. Um erro de transcrição real sem efeito material.

Esse, ainda por cima, inflacionámo-lo antes de o medir. Foi anunciado, só de ler o código, como sendo quase de certeza o desvio de 9 %. O número era 0,47. Um erro real e um erro material são afirmações diferentes, e mede-se a grandeza antes de dar nome ao impacto.

Plutus Flow faz a média do volume ao longo de 50 barras para detetar picos. A reconstrução usou 20, porque era esse o número que julgávamos saber. Efeito: menos de um décimo de ponto percentual, sem mudanças de sinal. O veredito não se mexeu, mas o número estava errado e agora está certo, que é o único critério que conta.

A parte que é, de facto, a lição

Eis o que a validação de cada reconstrução comunicou na altura.

reconstruçãoa sua própria verificaçãoo que isso significava
Volume Oracle3 em 3 antes, 3 em 3 depoisnão conseguiu distingui-las
Harmonic6 em 8 antes, 5 em 8 depoispontuou mais alto a versão com o erro
Plutus Flownunca foi executada

É na linha do meio que vale a pena demorar-se. A validação preferiu o código errado. Se tivéssemos deixado a verificação decidir, ter-nos-ia convencido a voltar ao erro.

Uma validação que compara um único valor de saída não consegue detetar uma constante de entrada errada. A saída é um crivo demasiado grosseiro. Muitos conjuntos de parâmetros errados e diferentes produzem hoje a mesma etiqueta de regime, o mesmo estado de cruzamento, a mesma cor no gráfico. Diferem em barras para as quais não está a olhar neste momento, e é exatamente aí que vive um backtest.

O que fazer em vez disso

Imprima todos os parâmetros derivados. Todos os comprimentos, todos os limiares, todos os multiplicadores. Compare-os com o código fonte linha a linha, antes de classificar o que quer que seja.

Mantenha a verificação da saída. Apanha outra classe de erros e é necessária. Apenas está muito longe de ser suficiente, e estes três passaram-lhe à frente sem serem incomodados.

O âmbito honesto

O código Pine estava correto nos três casos. O que estava errado era o aparelho construído para o testar, e essa é a versão mais incómoda do problema: o instrumento derivou, e a autoverificação do próprio instrumento dizia que não.

O número de 38 % é uma linha, uma varredura, um período, dentro da amostra, e está citado a partir do nosso próprio registo em vez de recalculado. O resultado do arredondamento é reproduzível com um único comando, e foi ao corrigi-lo que se descobriu que a nossa própria nota sobre o assunto estava errada havia dois meses.


Medição passada das nossas próprias ferramentas, descrita a posteriori. Nada aqui é uma recomendação nem uma indicação de resultados futuros.