Compra em 84,120. Stop em 84,036, alvo em 84,271. Chega o candle do minuto seguinte: abertura 84,118, máxima 84,290, mínima 84,010, fechamento 84,240.
Os dois níveis estão dentro desse candle. O stop foi atingido e o alvo foi atingido, e os quatro números do OHLC não contêm absolutamente nenhuma informação sobre qual veio primeiro. Mesmo assim, seu backtest retornou um número. Em algum ponto do loop, uma linha decidiu — provavelmente uma linha que você não considerou uma premissa de modelagem quando a escreveu.
Essa é a maior fonte isolada de desempenho fictício que vejo em estratégias de curto prazo, à frente de taxas e slippage, porque não parece uma premissa. Parece apenas infraestrutura do código.
Erro número 1: deixar a cadeia de if decidir
O formato mais comum:
if bar.high >= target:
exit(target, "tp")
elif bar.low <= stop:
exit(stop, "sl")
Ninguém escolheu “resolver os alvos antes dos stops”. A verificação do alvo só foi digitada primeiro porque esse é o caminho feliz e era nele que você estava pensando. Inverta os dois ramos e a curva de patrimônio muda; isso por si só já deveria mostrar que o P&L da estratégia é, em parte, uma propriedade do seu editor.
Rodei uma estratégia de reversão à média propositalmente comum em BTCUSDT perp, com 3 meses de candles de 1 minuto e 4,812 operações, stop de 0.10% e alvo de 0.18% em relação à entrada. Mesmos sinais, mesmas taxas; só mudou a convenção para desempatar.
Um oitavo das operações determina o resultado inteiro. Essa é a matemática de um par de stop e alvo apertados: as operações ambíguas são aquelas em que o preço foi para os dois lados, ou seja, a maioria das interessantes, e cada uma vale toda a distância entre stop e alvo, dependendo do cara ou coroa. 12.6% das operações × 0.28% de amplitude equivalem a 3.5% do giro bruto de notional por unidade da amostra, muito acima da vantagem real da estratégia.
A taxa depende do tamanho do candle em relação ao espaçamento entre seus níveis e piora rapidamente quando você aumenta o intervalo dos candles. Mesmo stop e alvo, mesmos sinais, reamostrados:
| Intervalo do candle | Operações com os dois níveis dentro de um candle | Sharpe (alvo primeiro) |
|---|---|---|
| 1s | 0.3% | 0.91 |
| 1m | 12.6% | 2.31 |
| 5m | 34% | 3.60 |
| 15m | 49% | 4.42 |
| 1h | 71% | 5.88 |
Veja o que essa tabela está dizendo de fato. Candles mais longos melhoraram o backtest. Todo pesquisador tende a pensar que candles de 1 hora são a opção conservadora: menos ruído, menos sobreajuste à microestrutura. Mas, com uma convenção intrabar que resolve a seu favor, um candle mais longo é só uma caixa maior dentro da qual você pode presumir que teve sorte. Em candles de 1h, 7 de cada 10 operações dependem puramente da convenção. Esse backtest não está testando uma estratégia; está testando a ordem de 2 instruções `if` 3,400 vezes.
Erro número 2: presumir que a execução do stop aconteceu no preço do stop
Digamos que você defina a ordem de resolução. O stop é resolvido primeiro, você contabiliza uma perda de exatamente 0.10% mais a taxa taker e se sente rigoroso. Ainda há 2 coisas erradas.
A primeira é que um stop é um gatilho, não uma execução. Na Binance USDⓈ-M, uma ordem STOP_MARKET se torna uma ordem a mercado assim que a condição de gatilho é atendida e, em seguida, consome o que houver no livro. Em um minuto tranquilo, isso significa 1 ou 2 ticks de slippage. No minuto que de fato acionou seu stop — aquele com um candle de 40 pontos e uma cascata de liquidações logo abaixo — o livro está raso exatamente no lado que você está cruzando. Na minha amostra, comparando os gatilhos de stop com os dados tick a tick, a execução mediana ficou 1.4 bps além do gatilho e o percentil 95 ficou em 11 bps. Em um stop de 10 bps, essa cauda custa mais um décimo do risco que você achava ter definido.
A segunda é mais sutil e específica dos contratos perp: qual preço aciona a ordem. A Binance define o mark price como padrão para ordens stop, e o mark price é calculado a partir do índice mais uma base suavizada, não a partir do último negócio naquela plataforma. Sua série OHLC é de último preço. São séries diferentes, e a divergência é maior justamente durante os eventos que acionam stops.
| Último preço (seus klines) | Mark price (gatilho padrão) | |
|---|---|---|
| Fonte | negócios nesta plataforma | índice de várias plataformas + base |
| Comportamento dos pavios | variação completa | fortemente amortecido |
| Divergência típica | 1–3 bps em condições calmas, 20–35 bps em um minuto de cascata | |
| Consequência para o backtest | stops acionados que não deveriam ter sido, e vice-versa | |
Assim, um pavio de 25 bps na série de último preço tira você da posição no backtest, enquanto o mark price ao vivo nem chega a ficar a 10 bps do seu gatilho. Ou acontece o contrário, no dia em que o índice se move e sua plataforma fica para trás. Se você definir workingType como CONTRACT_PRICE, pelo menos alinhará o comportamento ao vivo aos seus dados, e essa costuma ser a escolha certa para quem pesquisa, já que simular com precisão um gatilho baseado no mark price significa carregar uma segunda série por todo o mecanismo de execução.
Lembro melhor de um caso assim: alguém da nossa equipe “melhorou” uma estratégia ao mover o take-profit de 0.18% para 0.21%. O Sharpe passou de 2.3 para 3.1. Nenhuma vantagem nova. O alvo simplesmente tinha saído da parte mais concentrada da distribuição de pavios de 1 minuto, então menos operações caíam na categoria ambígua em que o código, sem alarde, lhes concedia a vitória. A pessoa tinha otimizado o critério de desempate.
Erro número 3: sempre presumir o pior e chamar isso de conservadorismo
A reação imediata é ser pessimista. Se os dois níveis forem atingidos, considere o stop executado. Pronto, sem mais otimismo, pode publicar.
Eu costumava fazer isso. É melhor que a alternativa, mas ainda está errado, por 2 motivos.
Isso descarta estratégias que estão boas. Resolver pessimisticamente 12.6% das operações custou 2.1 pontos de Sharpe a essa estratégia em relação ao 0.94 obtido com resolução tick a tick. Se o número real é 0.94 e sua convenção informa 0.18, você abandona a ideia e vai trabalhar em algo pior. Um conservadorismo que erra por 2 pontos de Sharpe não é conservadorismo; é ruído com pose de virtude.
Pior ainda: isso corrompe a otimização. Dê a uma busca de parâmetros um critério pessimista para desempate e o otimizador aprenderá a evitar a ambiguidade, porque ela agora representa uma penalidade pura. Ele tenderá a stops mais largos e alvos mais próximos, ou a candles mais longos, nos quais os dois níveis raramente coexistem, e apresentará parâmetros escolhidos com base na sua convenção de execução, não no mercado. É a mesma falha da versão generosa, com sinal oposto e igualmente invisível no tearsheet.
Regra prática que usamos antes de qualquer outra coisa: se stop_distance + target_distance for menor que a amplitude no percentil 75 do seu intervalo de candle, sua premissa intrabar pesa mais no P&L do que seu sinal. Calcule os dois números. São 4 linhas de código e essa verificação encerrou mais revisões de estratégias do que qualquer outra, isoladamente.
O que funciona de verdade
O caminho dentro do candle é um dado. Busque-o ou delimite o que não conseguir obter.
- Resolva com a série mais granular que tiver. Os aggTrades da Binance para os minutos relevantes somam algumas centenas de linhas e resolvem a questão de vez: qual nível foi atingido primeiro e a que preço a varredura foi executada. Você não precisa de dados tick a tick para o backtest inteiro, só para os candles ambíguos. Na minha amostra, foram 606 minutos em 129,600. É um download pequeno, não um projeto de infraestrutura.
- Se não houver dados tick disponíveis, desça 1 ou 2 níveis de timeframe apenas para resolver as saídas. Gere sinais em 15m e resolva as saídas em candles de 1s ou 1m. A ambiguidade cai de 49% para uma fração de um por cento, e o resíduo fica pequeno o bastante para ser ignorado com honestidade.
- Sempre informe a faixa. Rode cada backtest 2 vezes, com resolução otimista e pessimista, e mostre os dois Sharpes ao lado do resultado resolvido. Essa diferença é sua incerteza intrabar e deve aparecer no tearsheet junto do intervalo de confiança que você incluiria para o próprio Sharpe. Quando a faixa vai de 0.2 a 2.3, nenhuma conclusão dentro dela é real.
- Acompanhe a taxa de ambiguidade como métrica de primeira classe. A nossa aparece no topo de cada ficha de estratégia, ao lado da contagem de operações e do giro. Uma taxa acima de cerca de 5% significa que a lógica de saída, e não a de entrada, é o que está sendo testado.
- Modele o gatilho separadamente da execução. Acione a ordem com base na série de preços que a plataforma realmente usa; simule a execução no preço do gatilho mais um slippage sorteado, calibrado com os dados de mercado, e não no preço do próprio gatilho.
Ações têm o mesmo problema, só que com outra cara. Um stop em 62.00 num ativo que abre com gap em 58.40 durante a noite não é executado em 62.00; ele é executado abaixo do preço de abertura, e um backtest com candles diários que contabiliza slippage de −$0.00 nos gaps de abertura vai dizer, todo satisfeito, que uma camada de proteção com stop loss melhorou o drawdown. Não melhorou. Ela simplesmente nunca foi testada nos dias que importam. Os leilões também causam isso: é no leilão de reabertura que seu stop é de fato executado, a um preço que a mínima do candle não mostra.
Nada disso é exótico. É reconhecer que um candle é um resumo e que uma estratégia com stop e alvo aposta na ordem dos eventos que esse resumo descartou. Quando o mecanismo de paper trading finalmente roda a estratégia com dados de mercado ao vivo, esses dados têm uma resposta sobre a ordem dos eventos e nunca se importaram com qual ramo da instrução if você digitou primeiro.
← Todos os artigos


