3 de setembro de 2026 · engenharia de dados

Os minutos que não existem: lacunas, interrupções e barras sem volume no histórico OHLCV

Os minutos que não existem: lacunas, interrupções e barras sem volume no histórico OHLCV

Um ano civil de barras de 1 minuto devia ter 525,600 linhas. A nossa recolha de 2025 dos contratos perpétuos BTCUSDT chegou com 525,557 — menos 43 minutos, uma taxa de completude de 99.992%. Um contrato perpétuo de um alt de média capitalização na mesma plataforma, com a mesma recolha e o mesmo código, chegou com menos 1,247: 99.76%. E uma série de minutos de ações dos EUA para o mesmo ano tinha cerca de 427,000 linhas a menos do que a de cripto, o que não é uma lacuna, é apenas um mercado que fecha.

Três destes números são banais. O que vale por mil palavras é o 43.

525,600barras de 1 minuto num ano não bissexto
0.008%em falta em BTCUSDT, em 2025
61%desses minutos ocorreram em horas de volatilidade do decil superior

Quatro coisas diferentes que parecem todas um buraco no seu dataframe

Antes de voltarmos ao 43, vejamos a taxonomia, porque a maioria do código para lidar com lacunas já está errada a este nível. Quando falta uma linha no seu parquet local, não sabe qual destas situações a originou, e a resposta correta varia em cada caso.

TipoO que aconteceu realmenteComo apareceResposta adequada
Sem transaçõesO mercado estava aberto; ninguém cruzou o spread nesse minutoBarra de volume zero (OHLC todos iguais, trades=0) em alguns endpoints; linha ausente noutrosMantenha-a. É informação real: ninguém quis negociar.
Plataforma indisponívelMotor de correspondência offline, com ou sem aviso prévioLinha ausente, por vezes em sequênciaMarque como desatualizada. Não negoceie durante esse período.
Interrupção do instrumentoViolação do limite LULD, notícias pendentes, aviso de exclusão de cotaçãoLinha ausente, seguida de uma transação no leilão de reaberturaMarque como desatualizada e trate a reabertura como uma descontinuidade.
Erro seuPaginação limitada por taxa, uma nova tentativa que ignorou uma página, um erro de limite de fuso horário no cicloLinha ausente, indistinguível dos casos acimaDetete e volte a recolher. Este é o único caso que pode corrigir.

As plataformas divergem quanto à primeira linha da tabela, e é aí que está o problema. O endpoint de candles da Coinbase omite por completo os intervalos vazios, pelo que o histórico de um par pouco líquido está cheio de buracos literais. As klines da Binance costumam fornecer uma barra sintética com volume 0 e open=high=low=close fixados na transação anterior. O mesmo facto subjacente, duas formas diferentes; e um carregador que reindexa para uma grelha de minutos completa transforma uma na outra sem o avisar. Verifique o comportamento da sua plataforma antes de escrever o preenchimento de lacunas, não depois.

Os 1,247 minutos em falta no alt pertenciam quase todos à categoria um: um livro de ofertas pouco espesso às 04:00 UTC de domingo, sem ninguém a negociar. Irritante, fácil de compreender e praticamente inofensivo, porque a estratégia também não teria negociado nessa altura. Foi precisamente por isso que deixei de olhar para esses dados e voltei aos 43.

Os 43 minutos não estavam dispersos

Se as ausências fossem uniformes, 43 minutos ao longo de um ano surgiriam como 43 ocorrências isoladas, uma a cada oito dias e meio, cada uma um erro de arredondamento. Não foi isso que obtivemos. Surgiram em seis sequências: uma de 19 minutos consecutivos, uma de 11, duas de 4 e mais alguns pares. Seis eventos, não 43 acidentes.

E esses eventos dependem daquilo que lhe interessa. As plataformas ficam indisponíveis quando estão sob carga, e estão sob carga quando o preço se move. Agrupei todas as horas do ano por volatilidade realizada e verifiquei onde se encontravam os minutos em falta: 61% caíam no decil superior. A probabilidade incondicional de faltar um determinado minuto é 0.008%. Condicionada a estar numa hora de volatilidade do decil superior, é cerca de 0.05% — seis vezes mais alta, além de surgir em grupos.

Assim, a métrica de completude no painel de qualidade dos seus dados está a medir a coisa errada. 99.992% parece descrever um conjunto de dados em que já não precisa de pensar. Na realidade, descreve uma série completa nas horas em que a sua estratégia não faz nada e cheia de buracos nas horas em que faz tudo. Um sistema de momentum que dispara com a expansão da volatilidade tem uma probabilidade consideravelmente maior de encontrar uma lacuna do que o valor principal sugere, e encontra-a a meio de uma operação.

O que o preenchimento para a frente faz três linhas depois

Foi a falha que me levou a escrever isto. Consideremos a sequência de 19 minutos. Procedimento habitual: reindexar para a grelha completa de minutos, preencher OHLC para a frente com o último fecho e definir o volume como zero. A série fica contínua e os indicadores executam sem um único NaN à vista.

Essas 19 barras têm high == low == close. A amplitude verdadeira é zero em todas elas. Um ATR(14) calculado nessa janela, partindo de uma leitura antes da interrupção de cerca de 240 USDT, desce para aproximadamente 34 quando a plataforma regressa — as cinco barras reais sobreviventes na janela sustentam toda a média. Agora introduza isso num dimensionador de posições ajustado à volatilidade, do tipo comum size = risk_budget / ATR. O tamanho aumenta sete vezes.

A barra real seguinte é o registo da reabertura, e não é uma barra tranquila. No nosso caso, abriu 1.8% afastada do último fecho antes da interrupção. O backtest abriu alegremente uma posição 7x perante uma lacuna de 1.8%, com uma execução que não podia ter existido, a um preço que ninguém estava a cotar. Essa única operação sintética valia mais do que um mês de P&L legítimo na curva de capital, no sentido errado — e nasceu inteiramente de uma linha de limpeza de dados escrita para arrumar o dataframe.

Eliminar as linhas em vez de as preencher também não resolve; é o mesmo erro com outra aparência. Elimine-as e os seus lookbacks indexados por inteiros mentem: uma «EMA de 20 barras» passa a abranger 39 minutos de relógio durante a interrupção, o retorno entre barras no limite é o salto total de 1.8% tratado como movimento de um minuto, e qualquer estimativa de volatilidade por barra lê-o como um evento de 60-sigma. Nada o avisa. O índice continua monotónico.

A reamostragem é onde o problema fica invisível

A maior parte da investigação não usa barras de 1 minuto; usa dados agregados, e a agregação disfarça o problema. Reamostre para 5 minutos e um buraco de 19 minutos transforma-se em quatro barras, das quais a primeira e a última são parciais. O Pandas calcula um OHLC com aspeto perfeitamente razoável a partir de dois minutos sobreviventes e atribui-lhe o mesmo rótulo de uma barra construída com cinco. Nada no resultado permite distingui-las.

A solução mais simples que conheço: mantenha uma coluna bars_in_window em todas as reamostragens e nunca a elimine. Um inteiro por linha, e todas as perguntas posteriores sobre a fiabilidade de uma barra passam a ter resposta. Também mantemos seconds_since_last_real_print, que contém a mesma informação num formato que a camada de execução pode usar.

A política, na medida em que existe

O que os nossos agentes aplicam agora, por ordem:

  1. Nunca reindexe sem o indicar. O carregador produz um manifesto de lacunas — início, fim, duração e qual das quatro categorias lhe parece corresponder. Se uma sequência de minutos em falta tiver menos de 3 barras e o volume nas barras adjacentes for reduzido, trata-se de um minuto sem transações. Qualquer sequência mais longa durante horas de atividade é tratada como interrupção até prova em contrário.
  2. Volte a recolher os dados antes de os interpretar. Metade das nossas primeiras lacunas resultava de erros de paginação. Uma segunda recolha a partir de outro endpoint ou fornecedor resolve a categoria quatro e reduz o problema antes de ser necessário qualquer juízo.
  3. Bloqueio por dados desatualizados, não preenchimento de lacunas. A estratégia recebe um valor data_age e uma regra estrita: não abrir novas posições quando o último registo real tiver mais de N barras, e fechar posições abertas na reabertura apenas através de uma ordem de mercado, com o preço ajustado por um desconto explícito para o risco de lacuna. Execuções que não poderiam ter acontecido são piores do que operações que não aconteceram.
  4. Os indicadores recebem NaN, não ficção. Os preços preenchidos para a frente nunca chegam à camada de atributos. Se não for possível calcular o ATR, o valor fica indefinido, e indefinido significa posição neutra. Uma falha explícita é melhor do que um 7x silencioso.
  5. Apresente o desempenho condicionado às lacunas. Todos os backtests que publicamos mostram o P&L com as operações junto a interrupções excluídas, além do valor principal. Se essas operações sustentam o resultado, então o resultado é um artefacto dos dados.

Uma auditoria rápida que pode fazer hoje: agrupe os minutos em falta em sequências consecutivas e verifique que fração das operações do seu backtest abre ou fecha nos 30 minutos anteriores ou posteriores ao limite de uma sequência. Se for inferior a 1%, provavelmente as lacunas não estão a influenciar nada. Se for 5% ou mais, a curva de capital conta em parte uma história de indisponibilidade das plataformas.

O sinal em que aprendi a confiar é a forma como as ausências se distribuem, não a sua quantidade. Um conjunto de dados com milhares de buracos dispersos em horas mortas costuma estar bem. Um conjunto de dados com alguns grupos compactos está a dizer-lhe que algo falha sob carga, e é precisamente nessas condições que a sua estratégia atua.

lacunas OHLCVengenharia de dadosbacktestingreamostragemfuturos de cripto
← Todos os artigos