4 de setembro de 2026 · backtesting

Tanto o stop como o objetivo estavam dentro da mesma vela. Qual deles escolheu o teu backtest?

Tanto o stop como o objetivo estavam dentro da mesma vela. Qual deles escolheu o teu backtest?

Compra a 84,120. Stop em 84,036, objetivo em 84,271. Chega a vela do minuto seguinte: abertura 84,118, máximo 84,290, mínimo 84,010, fecho 84,240.

Os dois níveis estão dentro dessa vela. O stop foi tocado e o objetivo foi tocado, mas os quatro valores OHLC não contêm absolutamente nenhuma informação sobre qual foi atingido primeiro. Ainda assim, o teu backtest devolveu um número. Algures no ciclo, uma linha decidiu — provavelmente uma linha que não consideraste uma suposição de modelação quando a escreveste.

Esta é a maior fonte isolada de desempenho fictício que vejo em estratégias de horizonte curto, à frente das comissões e do slippage, porque não parece uma suposição. Parece apenas código de suporte.

Primeira forma de errar: deixar a cadeia de if decidir

A estrutura habitual:

if bar.high >= target:
    exit(target, "tp")
elif bar.low <= stop:
    exit(stop, "sl")

Ninguém escolheu «resolver primeiro os objetivos». A verificação do objetivo foi escrita primeiro porque esse é o caminho feliz e era nisso que estavas a pensar. Troca a ordem dos dois ramos e a curva de capital muda; isso, por si só, devia dizer-te que o P&L da estratégia depende em parte do teu editor.

Testei um scalper de reversão à média deliberadamente banal no perp BTCUSDT, com três meses de velas de 1 minuto, 4,812 operações, stop a 0.10% e objetivo a 0.18% da entrada. Os mesmos sinais, as mesmas comissões; só mudou a convenção para desempatar.

12.6%das operações tocaram nos dois níveis numa só vela
2.31Sharpe, objetivos resolvidos primeiro
0.18Sharpe, stops resolvidos primeiro
0.94Sharpe, resolvido com velas de 1 segundo

Um oitavo das operações sustenta o resultado inteiro. É essa a matemática de um par apertado de stop e objetivo: as operações ambíguas são aquelas em que o preço foi nos dois sentidos, ou seja, a maioria das interessantes, e cada uma vale a distância total entre o stop e o objetivo, dependendo do lançamento da moeda. 12.6% das operações × 0.28% de amplitude correspondem a 3.5% do volume nominal bruto por unidade da amostra, muito mais do que a verdadeira vantagem da estratégia.

A taxa depende do tamanho da vela em relação ao espaçamento entre os níveis, e piora rapidamente à medida que aumentas a duração das velas. Mesmo stop e objetivo, mesmos sinais, reamostrados:

Intervalo da velaOperações com os dois níveis dentro de uma velaSharpe (objetivo primeiro)
1s0.3%0.91
1m12.6%2.31
5m34%3.60
15m49%4.42
1h71%5.88

Repara no que essa tabela está realmente a dizer. Velas mais grosseiras melhoraram o backtest. O instinto de qualquer investigador é achar que as velas horárias são a opção conservadora: menos ruído, menos sobreajuste à microestrutura. Mas, com uma convenção intrabar que resolve a teu favor, uma vela mais grosseira é apenas uma caixa maior dentro da qual podes presumir que tiveste sorte. Com velas de 1h, sete em cada dez operações dependem inteiramente da convenção. Esse backtest não está a testar uma estratégia; está a testar a ordem de duas instruções `if` 3,400 vezes.

Segunda forma de errar: presumir que o stop foi executado ao preço do stop

Digamos que corriges a ordem. O stop é resolvido primeiro, registas uma perda de exatamente 0.10% mais a comissão taker e sentes que fizeste tudo com rigor. Ainda há duas coisas erradas.

A primeira é que um stop é um gatilho, não uma execução. Na Binance USDⓈ-M, uma ordem STOP_MARKET transforma-se numa ordem de mercado assim que a condição de ativação é satisfeita, e depois consome o que houver no livro de ordens. Num minuto calmo, isso significa um ou dois ticks de slippage. No minuto que efetivamente ativou o teu stop — aquele com uma vela de 40 pontos e uma cascata de liquidações por baixo — o livro está escasso precisamente do lado em que estás a executar. Na minha amostra, ao comparar as ativações dos stops com os dados tick a tick, a execução mediana ficou 1.4 bps além do gatilho e o percentil 95 ficou a 11 bps. Num stop de 10 bps, a cauda custa-te mais um décimo do risco que julgavas ter definido.

A segunda é mais subtil e específica dos perps: qual é o preço que ativa a ordem. Por predefinição, a Binance usa o mark price para as ordens stop, e o mark price é calculado a partir do índice mais uma base suavizada, não a partir da última transação nessa plataforma. A tua série OHLCV usa o último preço. São séries diferentes e divergem mais precisamente durante os eventos que ativam os stops.

Último preço (as tuas klines)Mark price (gatilho predefinido)
Origemtransações nesta plataformaíndice de várias plataformas + base
Comportamento das mechasexcursão completafortemente atenuado
Divergência típica1–3 bps em períodos calmos, 20–35 bps num minuto de cascata
Consequência para o backteststops ativados que não deviam ter sido, e vice-versa

Assim, uma mecha de 25 bps na série do último preço faz o backtest fechar a posição pelo stop, embora o mark price em tempo real nunca se tenha aproximado a menos de 10 bps do teu gatilho. Ou acontece o inverso, no dia em que o índice se move e a tua plataforma fica para trás. Se definires workingType como CONTRACT_PRICE, pelo menos alinhas o comportamento em tempo real com os teus dados, o que costuma ser a escolha certa para quem faz investigação, já que simular corretamente um gatilho baseado no mark price implica manter uma segunda série em todo o teu motor de execução.

Lembro-me melhor deste caso: alguém da nossa equipa «melhorou» uma estratégia ao passar o take-profit de 0.18% para 0.21%. O Sharpe subiu de 2.3 para 3.1. Não surgiu nenhuma vantagem nova. O objetivo limitou-se a sair da parte mais densa da distribuição das mechas de 1 minuto, pelo que menos operações caíram na categoria ambígua em que o código lhes atribuía discretamente a vitória. Tinham otimizado o critério de desempate.

Terceira forma de errar: presumir sempre o pior e chamar-lhe conservadorismo

A reação instintiva é optar pelo pessimismo. Se os dois níveis forem tocados, assume o stop. Feito, já não há otimismo; siga.

Eu costumava fazer isto. É melhor do que a alternativa e continua errado, por duas razões.

Elimina estratégias que estão bem. Resolver pessimisticamente 12.6% das operações custou a esta estratégia 2.1 pontos de Sharpe em comparação com os 0.94 resolvidos com dados tick a tick. Se o valor verdadeiro for 0.94 e a tua convenção indicar 0.18, deitas fora a ideia e vais trabalhar em algo pior. Um conservadorismo que falha por dois pontos de Sharpe não é conservadorismo; é ruído com pose moral.

Pior ainda, distorce a otimização. Dá a um varrimento de parâmetros um critério de desempate pessimista e o otimizador aprende a evitar a ambiguidade, porque agora a ambiguidade é apenas uma penalização. Vai procurar stops largos e objetivos próximos, ou velas mais lentas em que os dois níveis raramente coincidem, e apresenta-te parâmetros escolhidos em função da tua convenção de execução, em vez do mercado. É a mesma falha da versão generosa, com sinal oposto, e igualmente invisível no relatório de desempenho.

Regra prática que usamos antes de tudo: se stop_distance + target_distance for inferior à amplitude do percentil 75 do teu intervalo de velas, a tua suposição intrabar pesa mais no P&L do que o teu sinal. Calcula os dois valores. Leva quatro linhas de código e já encerrou mais revisões de estratégias do que qualquer outra verificação isolada.

O que funciona na prática

O percurso dentro da vela é um dado. Vai buscá-lo ou delimita o que não consegues obter.

  1. Resolve com a série mais granular de que dispões. Os aggTrades da Binance para os minutos relevantes são algumas centenas de linhas e resolvem a questão de vez: qual foi o primeiro nível tocado e a que preço foi executada a varrida. Não precisas de dados tick a tick para todo o backtest, apenas para as velas ambíguas. Na minha amostra, isso correspondeu a 606 minutos em 129,600. É um pequeno volume de dados para descarregar, não um projeto de infraestrutura.
  2. Se não tiveres dados tick a tick, desce um ou dois níveis de periodicidade apenas para resolver as saídas. Sinais em 15m, saídas resolvidas com velas de 1s ou 1m. A ambiguidade cai de 49% para uma fração de um por cento, e o que resta é suficientemente pouco para ser ignorado com honestidade.
  3. Apresenta sempre o intervalo de resultados. Executa cada backtest duas vezes, com resolução otimista e pessimista, e mostra ambos os valores de Sharpe ao lado do valor resolvido. Essa amplitude representa a tua incerteza intrabar e deve constar do relatório de desempenho, junto do intervalo de confiança que apresentarias para o próprio Sharpe. Quando o intervalo vai de 0.2 a 2.3, nenhuma conclusão dentro dele é real.
  4. Acompanha a taxa de ambiguidade como métrica de primeira linha. A nossa aparece no topo de cada ficha de estratégia, junto do número de operações e do volume transacionado. Uma taxa superior a cerca de 5% significa que o que está a ser testado é a lógica de saída, não a lógica de entrada.
  5. Modela o gatilho separadamente da execução. Usa como gatilho a série de preços que a plataforma realmente utiliza; executa ao preço do gatilho mais um valor de slippage sorteado e calibrado com os dados de mercado, não ao preço do gatilho.

Nas ações, o mesmo problema aparece com outra roupagem. Um stop a 62.00 numa ação que abre em gap a 58.40 durante a noite não é executado a 62.00; é executado algures abaixo do preço de abertura. Um backtest com velas diárias que regista −$0.00 de slippage nos gaps vai dizer-te, sem hesitar, que uma camada de stop-loss melhorou a tua redução máxima. Não melhorou. Simplesmente nunca foi testada nos dias que contam. As suspensões de negociação também causam isto: é na leilão de reabertura que o stop é efetivamente executado, a um preço que o mínimo da vela nunca mostra.

Nada disto é exótico. É reconhecer que uma vela é um resumo, e que uma estratégia de stop e objetivo aposta na ordem dos acontecimentos que o resumo descartou. Quando o motor de paper trading finalmente executa a estratégia com dados de mercado em tempo real, esses dados têm uma opinião sobre a ordem dos acontecimentos e nunca se importaram com o ramo da instrução if que escreveste primeiro.

execuções intrabarordens stopbacktestingfuturos de criptomicroestrutura de mercado
← Todos os artigos