August 26, 2026 · dados de mercado

Anatomia de uma barra de 1 minuto: tudo o que o OHLCV não diz ao seu backtest

Anatomia de uma barra de 1 minuto: tudo o que o OHLCV não diz ao seu backtest

Aqui está uma barra. Perpétuo USDⓈ-M da Binance, BTCUSDT, o minuto que começa às 13:46:00 UTC. Chegou do endpoint de klines como um array simples de doze valores, e é a unidade de input mais comum em toda a investigação quantitativa retail e adjacente. Quase todas as estratégias que geramos tocam em algo com esta forma.

Portanto vamos desmontá-la, índice a índice, e ver quanto disto um backtest interpreta mal.

ÍndiceValorNome
01773495960000hora de abertura (ms)
1"84120.50"abertura
2"84177.90"máximo
3"84098.10"mínimo
4"84163.40"fecho
5"38.417"volume base (BTC)
61773496019999hora de fecho (ms)
7"3232108.94"volume em quote (USDT)
81204número de negócios
9"21.883"volume de compra taker (base)
10"1841203.55"volume de compra taker (quote)
11"0"ignorar

[0] e [6]: que minuto é este, e o relógio de quem o diz

Hora de abertura 1773495960000, hora de fecho 1773496019999. Repare na segunda: termina em 19999, um milissegundo antes da abertura da barra seguinte. A janela é semiaberta, e a exchange diz-nos isso explicitamente. Metade dos bugs de dados que já persegui começaram com alguém a tratar ambos os extremos como inclusivos, contando um negócio em duplicado na fronteira, ou a alinhar um resample de forma que cada barra de 5 minutos roubasse um milissegundo à barra vizinha.

Os timestamps são a hora do motor de matching da exchange. Não é o seu relógio, não é o relógio de ingestão do seu fornecedor, e não é o mesmo relógio que o snapshot de funding usa. Quando junta uma série de klines com uma série de funding rate ou de open interest obtida de um endpoint diferente, está a juntar fontes que concordam dentro de aproximadamente um segundo, na maior parte do tempo. Para uma estratégia de 1 minuto, isso é um erro de timing de 1,7% em cada linha unida. Para qualquer coisa abaixo de um minuto, é fatal.

E esta barra está datada na sua abertura. A informação nela contida não é conhecível até às 13:46:59.999. Toda a questão de convenção de indexação — desloco um índice, uso o timestamp de fecho ou de abertura da barra como o meu tempo de evento — é, na verdade, a mesma questão de lookahead disfarçada.

[1] "84120.50": a abertura que não pode negociar

A abertura é o preço do primeiro negócio registado dentro da janela. É uma transação já concluída entre outras duas partes. Quando a barra existe como uma linha no seu dataframe, esse preço já tem sessenta segundos.

A barra anterior fechou em 84109.80, portanto há $10.70 de movimento de preço escondido no intervalo entre duas barras de 1 minuto adjacentes. Isso são 1,3 pontos base, invisíveis num gráfico, e aproximadamente um quarto da taxa taker. Está bem. Mas vá ver a mesma série num alt perp durante a divulgação do CPI dos EUA e esses gaps entre barras andam entre 15 e 40 pontos base. Um backtest que gera sinal no fecho da barra e executa na abertura da barra seguinte está silenciosamente a assumir que o gap é zero, e ele é zero exatamente quando isso não importa.

[2] e [3]: "84177.90" máximo, "84098.10" mínimo

É aqui que a maioria dos motores de execução vai morrer, por isso esta secção é a mais longa.

O máximo e o mínimo são preços extremos tocados. Não têm tamanho nem duração associados. A partir da fita de negócios crua desse mesmo minuto, tudo o que ficou em ou abaixo de 84100.00 foram 0,62 BTC distribuídos por nove negócios numa janela de 1,4 segundos. Portanto um stop de backtest em 84100 "executa" — e se a sua posição é de 3 BTC, comeu todo o livro visível na descida e o resto do seu tamanho foi executado algures no recuo entre 84105 e 84130. A barra diz que o mínimo foi 84098.10. A barra não diz que apenas $52.000 de nocional foi negociado ali.

1204negócios no minuto
0.032 BTCtamanho médio do negócio (~$2,7k)
0.62 BTCtotal negociado em ou abaixo do tick mínimo
57%do volume foi iniciado por compradores

A direção contrária é ainda pior, porque o favorece. Suponha que tinha uma ordem limite de venda em espera a 84175. O máximo da barra é 84177.90, portanto um motor ingénuo executa-o a 84175 e regista um rebate de maker em vez de um custo de taker. Se essa execução realmente aconteceu depende da posição na fila nesse nível de preço, o que a barra não pode saber e que provavelmente nunca registou. Tocado não é o mesmo que executado.

A regra em que assentámos no nosso motor de execução: uma ordem limite em espera só é executada se a barra negociar através do nível, não apenas até ele. Execuções exatamente no tick extremo exigem evidência de volume-por-preço a partir da fita de negócios; caso contrário são rejeitadas. Isto removeu cerca de 6% dos negócios de um livro típico de mean-reversion e cortou o Sharpe backtestado de um candidato de 1,9 para 1,1. Esse candidato nunca foi real; a regra de execução foi apenas a primeira coisa honesta o suficiente para o dizer.

Armadilha relacionada: o percurso intrabarra. Se o intervalo de uma barra abrange tanto o seu stop como o seu take-profit, o OHLCV não consegue dizer-lhe qual chegou primeiro. Todo o motor tem de escolher uma convenção. O nosso assume sempre stop-primeiro, o que é pessimista e ocasionalmente errado, mas nunca gera um ganho falso. Se o seu motor assume take-profit-primeiro, as barras de intervalo largo vão fabricar lucro a partir da ambiguidade, e as barras de intervalo largo são exatamente as que dominam a sua distribuição de PnL.

[4] "84163.40": o número menos robusto da barra

O fecho é o último negócio registado na janela. É só isso. Pode ser um lote fracionário de 0,002 BTC de um bot a arredondar uma posição às 13:46:59.8. Este único tick, estruturalmente arbitrário, é o que a maioria dos pipelines de investigação usa para calcular todos os sinais, marcar todas as posições e avaliar todas as saídas.

Nos perps de BTC quase não importa; num perp de altcoin pouco líquido às 04:00 UTC importa enormemente, e a diferença entre os fechos de duas venues para o mesmo minuto pode superar todo o seu edge por negócio. Quando o PnL de uma estratégia depende especificamente do fecho, voltamos a correr o backtest marcando pelo mark price da exchange, que é derivado de um índice e muito mais difícil de manipular. Se os resultados divergem, a estratégia estava a negociar o artefacto.

[5] e [7]: "38.417" e "3232108.94", volume em unidades de quê

O volume base é em BTC; o volume em quote é em USDT. Ambos aqui, o que é uma cortesia que nem todas as venues oferecem. A razão para nos preocuparmos é a agregação entre venues. Contratos com margem em coin são cotados em contratos de $100 de nocional. Alguns feeds de ações reportam lotes redondos. As venues de prediction markets reportam contagens de shares em que uma share é uma reivindicação binária denominada em dólares. Somar "volume" através de um universo misto sem normalizar para uma única unidade nocional produz um ranking de liquidez que é puro absurdo, e será um absurdo de forma estável o suficiente para sobreviver a uma revisão.

Normalize tudo para nocional em quote no momento da ingestão. Guarde também o campo bruto, mas nunca deixe uma estratégia vê-lo.

[8] 1204: o campo que devia definir o seu modelo de impacto

Dividindo o volume base pelo número de negócios obtém-se um negócio médio de 0,032 BTC, cerca de $2.700. Se a sua estratégia candidata quer entrar com $250.000 de uma só vez, está a pedir para ser aproximadamente 92 vezes o tamanho da transação típica nesse minuto. Esse número, e não uma constante genérica de slippage de 5 pontos base, é o que devia guiar o seu termo de impacto de raiz quadrada. Calculamos a taxa de participação por barra como uma coluna de primeira classe e rejeitamos estratégias cuja entrada mediana excede alguns por cento do nocional da barra, porque tudo o que vem depois disso é ficção.

O número de negócios também sinaliza minutos estranhos a baixo custo. Volume normal, número de negócios reduzido a 11? Alguém fez um bloco. Volume normal, número de negócios em 9.000? Isso é uma cascata de liquidações a ser mastigada em pedacinhos.

[9] e [10]: "21.883", o campo que todos deitam fora

Volume base de compra taker. 21,883 dos 38,417 BTC nesta barra foram iniciados por compradores, portanto o delta de volume assinado é +5,349 BTC e a divisão do agressor é 57/43 a favor dos compradores. A exchange está a entregar-lhe order flow imbalance, de graça, num campo que a maioria das pessoas nunca lê porque o pandas não nomeou a coluna por elas.

Não estou a afirmar que preveja retornos por si só; estratégias de delta ingénuas estão entre as formas mais fiáveis de doar dinheiro ao livro de taxas. Mas é uma medida genuinamente diferente do preço, está disponível no mesmo pedido que já estava a fazer, e permite distinguir um rally que foi comprado de um rally que aconteceu porque os vendedores se afastaram. Os dois parecem idênticos em OHLC e comportam-se de forma diferente dez minutos depois. O nosso agente de investigação trata uma hipótese que ignora a divisão taker numa venue que a publica como deixar evidência em cima da mesa.

[11] "0": ignorar — e tudo o resto que não está aqui

O índice 11 é um campo obsoleto, permanentemente zero. Mais interessante é a lista de coisas que esta barra não contém: sem bid, sem ask, sem spread, sem profundidade de livro, sem funding rate, sem open interest, sem liquidações, sem mark price, sem index price. E, de forma crucial, sem maneira de saber se a sua ordem teria sido maker ou taker, o que é a diferença entre pagar 0,045% e ganhar 0,01% nesta venue.

Portanto, qualquer modelo de taxas construído apenas com klines é uma suposição vestida de número. Resolvemos isto forçando cada estratégia a declarar o seu estilo de execução antecipadamente, e depois cobrando taker em tudo o que não conseguir provar o contrário.

A barra que nunca apareceu

Última peça, e a que morde mais forte fora dos majors. Um minuto com zero negócios não produz kline nenhuma. Fornecedores e bibliotecas costumam fazer forward-fill: abertura = máximo = mínimo = fecho = fecho anterior, volume 0. O seu indicador calcula alegremente. A sua estratégia vê uma linha válida e pode gerar um sinal num minuto em que ninguém no mundo negociou esse instrumento.

Num perp mid-cap que ingerimos, 4,1% das barras de 1 minuto numa janela de doze meses tinham zero negócios. Um candidato de mean-reversion nesse símbolo estava a colocar 38% das suas entradas em barras sintéticas, porque preços sintéticos planos são um íman para qualquer coisa que meça o desvio em relação a uma média móvel. Fez um backtest belíssimo. Estava a negociar os buracos nos dados.

É por isso que a camada de ingestão agora carrega um booleano synthetic por barra, propagado através de cada resample, e o crivo de verificação reprova qualquer estratégia cujos negócios se concentrem nele. Coluna barata. Já matou mais candidatos do que qualquer indicador que já tenhamos escrito.

Doze valores. Quatro deles interpretados incorretamente com regularidade, dois deles descartados com regularidade, e uma categoria inteira deles ausente da linha e imaginada pelo motor. Antes do seu próximo backtest, vá buscar uma barra crua ao seu próprio armazenamento e leia cada campo em voz alta comparando com o que a sua lógica de execução assume sobre ele. É um exercício de vinte minutos e nunca vi ninguém fazê-lo e não encontrar nada.

ohlcvmicroestrutura de mercadoengenharia de dadosbacktestingfuturos cripto
← All posts