← Voltar aos artigos
August 13, 2026
5 min read

Updating the Volume Curve Intraday: Does Adaptive Forecasting Actually Help?

Updating the Volume Curve Intraday: Does Adaptive Forecasting Actually Help?
#microstructure
#liquidity
#execution
#prediction
#order-book

Em TWAP, VWAP e POV, executamos todos os três agendadores frente a frente em 90 dias de repetição BTCUSDT L2 e chegamos a uma conclusão que foi deliberadamente deixada como uma ferida aberta: na criptografia, a curva de volume é o elo mais fraco do pipeline, não o fatiamento. Esse artigo enviou a versão simples de propósito - um com base na mediana, curva do dia da semana x hora do dia, reajuste semanalmente, mantida estática durante o dia de negociação. E descobriu que toda a vantagem do VWAP sobre o TWAP é a qualidade da previsão: condicionada ao erro da curva de volume realizado, o VWAP supera o TWAP em cerca de 4 bps no tercil de dias com melhor previsão, e a diferença se transforma em ruído no pior tercil.

Esse resultado implica um acompanhamento específico e falsificável. Se o VWAP só vencer nos dias em que a curva estiver correta, então um previsor que se corrija à medida que o dia se desenrola deverá converter alguns dos dias do tercil ruim em bons. Ou não deveria - e a versão interessante deste artigo é aquela em que isso não acontece, porque o tercil ruim na criptografia são os dias em cascata e os dias de notícias, e esses são exatamente os dias em que as primeiras duas horas de volume observado são menos informativas sobre as próximas duas.

Este artigo implementa o atualizador intradiário, executa-o em um equipamento idêntico e relata o delta IS versus a curva estática – com a distribuição e o número do quartil final, não apenas a média.

O que está sendo testado

Atualização prévia da curva de volume adaptativa da atividade de negociação observada

A curva estática é uma anterior: um vetor fixo u={u1,,uB}u = \{u_1, \ldots, u_B\} de frações de volume esperadas por balde, estimadas off-line. A versão adaptativa trata isso como um pré-atualização. Depois cc os períodos decorridos e você observou os volumes reais V1,,VcV_1, \ldots, V_c, você tem duas informações novas que a curva estática descarta:

  1. Uma estimativa de nível. Se o anterior disser baldes 1..c1..c deveria ter carregado fração icui\sum_{i \le c} u_i do dia e eles carregaram icVi\sum_{i\le c} V_i unidades, o total implícito do dia inteiro é V^=icVi/icui\hat{V} = \sum_{i \le c} V_i \big/ \sum_{i \le c} u_i. Este é um sinal de hoje é um dia pesado / hoje é um dia morto e está disponível no primeiro balde.
  2. Uma correção de forma. Se a forma realizada dos intervalos decorridos se desviar sistematicamente da forma anterior, os intervalos restantes também poderão desviar-se - mas apenas se os erros da curva de volume intradiária forem autocorrelacionados dentro do dia, o que é em si uma questão empírica e não uma suposição que podemos fazer gratuitamente.

A implementação abaixo usa (1) totalmente e (2) nada: ela renormaliza a cauda anterior intocada no total atualizado. Essa é a versão conservadora e é o primeiro experimento correto, porque se a atualização de nível puro já captura a maior parte do delta disponível, então a maquinaria de forma é uma complexidade injustificada.

import numpy as np
from typing import Optional


class AdaptiveVolumePredictor:
    """
    Bayesian-style adaptive volume profile predictor.

    Combines a prior (the offline day-of-week x time-of-day curve)
    with volume observed so far today to produce an updated forecast
    for the remaining buckets.
    """

    def __init__(self, historical_profiles: np.ndarray):
        """
        Args:
            historical_profiles: shape (n_days, n_buckets), each row sums to 1.0.
                Use the median-based, day-of-week-conditioned curve from the
                TWAP/VWAP/POV article -- a pooled mean curve is misspecified
                and will make the adaptive version look better than it is by
                giving it a weaker baseline to beat.
        """
        self.prior_profile = np.median(historical_profiles, axis=0)
        self.prior_profile /= self.prior_profile.sum()
        self.prior_iqr = np.subtract(*np.percentile(historical_profiles, [75, 25], axis=0))
        self.n_buckets = len(self.prior_profile)

    def predict(
        self,
        observed_volumes: np.ndarray,
        current_bucket: int,
        total_volume_estimate: Optional[float] = None,
    ) -> np.ndarray:
        """
        Predict absolute volume for every bucket: realized values for elapsed
        buckets, forecasts for the remainder.

        Args:
            observed_volumes: actual volumes in buckets 0..current_bucket-1
            current_bucket: index of the current bucket (0-based)
            total_volume_estimate: external ADV estimate (optional prior on level)
        """
        profile = np.zeros(self.n_buckets)

        if current_bucket == 0:
            base = total_volume_estimate if total_volume_estimate else 1.0
            return self.prior_profile * base

        profile[:current_bucket] = observed_volumes[:current_bucket]
        observed_total = observed_volumes[:current_bucket].sum()

        expected_fraction_so_far = self.prior_profile[:current_bucket].sum()
        if expected_fraction_so_far > 0.01:
            implied_total = observed_total / expected_fraction_so_far
        else:
            implied_total = observed_total * self.n_buckets

        if total_volume_estimate:
            w_obs = expected_fraction_so_far
            implied_total = w_obs * implied_total + (1 - w_obs) * total_volume_estimate

        remaining_prior = self.prior_profile[current_bucket:]
        remaining_sum = remaining_prior.sum()
        if remaining_sum > 0:
            remaining_volume = max(0.0, implied_total - observed_total)
            profile[current_bucket:] = remaining_prior / remaining_sum * remaining_volume

        return profile

Dois detalhes nesse código conduzem o experimento. O anterior é a curva mediana condicionada ao dia da semana, não uma média agrupada - para saber por que isso é importante em um mercado onde uma cascata de liquidação pode ser 15% do volume de um dia em dez minutos, consulte a seção de curva de volume do artigo VWAP. E a atualização do nível é reduzida em direção a uma estimativa ADV externa com peso igual à fração anterior decorrida, porque no primeiro intervalo implied_total é uma observação dividida por um número próximo de zero. Um atualizador não reduzido faz seu pior trabalho exatamente quando ainda tem mais dias para arruinar.

Arnês

Caminhos de execução paralelos em um equipamento de pesquisa de previsão adaptativa

Idêntico à execução TWAP/VWAP/POV, portanto os números são comparáveis linha por linha:

  • Instrumento/dados: BTCUSDT perpétuo, 90 dias de replay L2 (20 níveis principais, 100 ms) mais a fita de negociações. UTC por toda parte; carimbos de data/hora de financiamento sinalizados.
  • Pedidos principais: 500, horizonte T=4T = 4h, tamanho 0,75% do ADV final de 30 dias, lado da compra, preço de decisão = médio no início. Os mesmos horários de início aleatórios da execução publicada.
  • Braços: (A) VWAP na curva estática de reajuste semanal — a linha de base publicada; (B) VWAP no mesmo anterior com atualização intradiária; (C) TWAP como piso.
  • Métricas: IS em bps de preço de decisão, taxas incluídas, versus chegada — relatado como média, mediana, desvio padrão, percentil 95 e IS do quartil final de cada pai. A derrapagem do VWAP é registrada apenas como um diagnóstico, nunca como um placar; a seção de benchmark do artigo VWAP mostra um caso resolvido em que o deslizamento do VWAP e o IS de chegada classificam dois algoritmos em ordens opostas. A decomposição completa do SI, incluindo o termo custo de oportunidade, segue déficit de implementação e TCA.

Resultados

Ajuda onde é necessário?

A média do título é o número menos interessante aqui. O resultado publicado estabeleceu que a lacuna VWAP-TWAP é monótona no erro da curva de volume realizada, portanto o braço adaptativo deve ser pontuado da mesma maneira: agrupar os 90 dias em tercis por L1L_1 distância entre a previsão estática e os volumes de bucket realizados e, em seguida, relate o delta IS adaptativo menos estático dentro de cada tercil.

Os dois resultados podem ser publicados e significam coisas opostas:

  • Delta concentrada no tercil bom. O atualizador está refinando dias que já eram fáceis. O efeito líquido na distribuição de custos é cosmético; a cauda – que é o que um pai orientado por alfa no horizonte rígido realmente paga – permanece inalterada. Isso diria que o atualizador intradiário não é a solução para o elo mais fraco, e a correção de forma e as rotas de ativos cruzados são o próximo passo.
  • Delta concentrado no tercil ruim. O atualizador está fazendo o trabalho para o qual foi construído: capturando a cascata e os dias de notícias onde a curva estática está mais errada. Este é o resultado que justificaria o estado adicionado no loop de execução.

Um resultado negativo é o esperado e vale a pena reportá-lo claramente. O mecanismo que dificulta as curvas de volume criptográfico – uma cascata é uma quebra de regime, não uma mudança de nível – é precisamente o mecanismo que derrota um previsor de atualização de nível: no momento em que os intervalos decorridos lhe disserem que hoje é pesado, a parte pesada pode ter acabado. Veja o negativo honesto para saber por que este blog os publica.

Inclinação do livro: acrescenta alguma coisa?

Paisagem abstrata de profundidade do livro de pedidos com limite e inclinação de liquidez

Um recurso do conjunto de recursos de rascunho não é abordado em nenhum outro lugar deste blog: inclinação do livro, a rapidez com que a profundidade cumulativa se acumula à medida que você se afasta do toque. Ajustar CumVol(d)=α+βd\text{CumVol}(d) = \alpha + \beta d por cima KK níveis; grande β\beta significa que a profundidade está concentrada no toque, pequeno β\beta significa que está espalhado pelo livro.

def book_slope(prices: np.ndarray, volumes: np.ndarray, mid: float) -> float:
    """Slope of cumulative depth vs. distance from mid. One side only."""
    distances = np.abs(prices - mid)
    return float(np.polyfit(distances, np.cumsum(volumes), 1)[0])

A única versão desta afirmação que vale a pena fazer é a medida: será que adicionar book_slope ao conjunto de recursos existente mover a previsão de volume, ou o IS resultante, além de uma linha de base AR/EWMA? O artigo sobre modelagem de spread define o padrão - habilidade de relatório acima de uma linha de base trivial com o horizonte declarado, porque essas séries são dominadas pela persistência e um título R² mede principalmente a autocorrelação.

O que este artigo deliberadamente não deriva

Módulo de pesquisa focado dentro de um pipeline quantitativo limitado

Tudo abaixo já está no blog com mais detalhes, e reexplicá-lo aqui apenas criaria uma segunda versão mais fraca:

  • Medição de liquidez. Estimador de Roll (com a correção de raiz assinada e a armadilha preço-unidades-vs-retornos) e lambda de Kyle: modelagem de propagação com aprendizado de máquina. O lambda de Kyle também é calibrado em Binance aggTrades reais como o coeficiente de impacto permanente em Almgren-Chriss. Custo de varredura padrão: modelos de derrapagem e custo.
  • Recursos do livro de pedidos. O meio ponderado (que não é o micropreço de Stoikov — aquele é ajustado por martingale), desequilíbrio do livro de vários níveis, OFI e a taxonomia completa do recurso LOB: DeepLOB e modelagem de spread.
  • Padrões intradiários. O formato em U do patrimônio é o modelo errado para um mercado sem fechamento; a estrutura criptográfica de sessão/financiamento/semanal que a substitui, mais a grade de dia da semana x hora do dia, está no artigo VWAP. A codificação cíclica da hora do dia está na tabela de recursos do artigo de divulgação.
  • Eventos programados. Deribit 08:00 UTC expira, macro dos EUA às 12:30/14:00 UTC, liquidações CME - trate-os como manequins em vez de deixá-los poluir a curva de linha de base; o calendário criptográfico concreto está no artigo VWAP. O atualizador adaptativo acima não é um manipulador de eventos e não deve ser solicitado que o seja.
  • Modelos. Aumento de gradiente com CV purgado e embargado (um simples TimeSeriesSplit vazamentos no alvo sobreposto da janela direta) está em modelagem de propagação; a família CNN-LSTM, o tensor de entrada de 40 recursos/10 níveis, LOBFrame e a discussão sobre crise de replicação estão em DeepLOB. Observe em particular que o eixo do nível não deve ser recolhido antes da camada recorrente.
  • A decisão da ordem infantil. Passivo versus agressivo é um ponto de equilíbrio calculado p=δ/(Π+δ)p^* = \delta/(\Pi + \delta), não um limite de propagação codificado: táticas de execução de ordem infantil. A segmentação pela taxa de participação é endógena – seus próprios preenchimentos estão impressos na fita – e a correção está no artigo VWAP.
  • Fragmentação do local. Roteamento inteligente de pedidos em criptografia, com o scorecard TCA por local em déficit de implementação.
  • Latência de produção. seção de produção do DeepLOB; observe que o artigo divulgado já qualifica declarações de tempo de inferência - um LightGBM de 2.000 rodadas prevê em dezenas de microssegundos do Python, um dígito apenas com um preditor compilado.

A dinâmica adversária merece um parágrafo em vez de uma seção. A profundidade exibida é parcialmente performática, e a mecânica de detecção – taxa de cancelamento, velocidade de construção da parede, comportamento conforme o preço se aproxima, camadas – é coberta com um método concreto baseado em PIQ em posição da fila e análise da parede. O único delta que este artigo poderia adicionar é tratá-los como entradas do previsor: proporção de cancelamento para negociação, tempo médio de descanso no toque e a fração de profundidade que é cancelada dentro de 100 ms após o aparecimento, alimentada no modelo de volume junto com o sinal de intervalo decorrido. Se eles acrescentam habilidade ao conjunto de recursos existente é uma medida aberta, não uma afirmação.

Conclusão

Caminho de evidência resolvendo da incerteza até um resultado de execução medido

A afirmação em teste é restrita e o blog já estabeleceu as condições para falsificá-la: o artigo VWAP estabeleceu que a vantagem do VWAP é inteiramente de qualidade de previsão e que a previsão falha nos dias que custam mais. Um atualizador intradiário corrige isso ou não, e a resposta é um número no tercil ruim de um replay de 500 pais.

O que este artigo não fará é anexar um número de bps a um sistema de produção sem um replay por trás dele. Alegações do tipo "previsão de liquidez bem ajustada economiza 2–5 bps, a diferença entre um Sharpe de 1,5 e 2,0" são exatamente o gênero que Sharpe deflacionado e testes múltiplos existe para desmantelar: sem fontes, sem condicionamento e nunca acompanhado pela dispersão. O número que importa aqui é o IS do quartil final no pior tercil de dias, e é medido ou não é relatado.

blog.disclaimer

Authors

Eugen Soloviov
Eugen Soloviov

Trading-systems engineer

Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.

Newsletter

Fique à frente do mercado

Assine nossa newsletter para insights exclusivos sobre trading com IA, análises de mercado e atualizações da plataforma.

Respeitamos sua privacidade. Cancele a inscrição a qualquer momento.