← Voltar aos artigos
August 7, 2026
5 min read

Toda-Yamamoto vs Differenced Granger: Does the BTC Lead-Lag Survive?

Toda-Yamamoto vs Differenced Granger: Does the BTC Lead-Lag Survive?
#causal-inference
#Granger-causality
#lead-lag
#crypto
#VAR

Quase todos os resultados publicados de causalidade de Granger em criptografia são calculados em retornos de log. Essa escolha não é gratuita. A diferenciação torna a série estacionária, o que o teste F padrão exige, mas também descarta a relação de nível – e se duas moedas são cointegradas, o VAR diferenciado é especificado incorretamente e o teste responde a uma pergunta ligeiramente diferente daquela que você fez.

Toda e Yamamoto (1995) oferecem uma saída: ajustar o VAR em níveis com defasagens extras, testar apenas os originais e obter um valor válido. χ2\chi^2 independentemente de raízes unitárias ou cointegração. É bem conhecido em econometria e quase nunca aplicado ao cripto lead-lag.

Portanto, este artigo faz uma coisa: executar ambos os testes nos mesmos pares, na mesma janela, e relatar se eles discordam. Em seguida, aplique o acompanhamento honesto - uma correção de múltiplos testes de família correlacionada na matriz resultante e um valor p contínuo para ver se o atraso "significativo" é estável o suficiente para negociar ou apenas oscila além do limite.

A teoria abaixo é apenas a necessária para tornar a comparação legível. O blog já cobre raízes unitárias, cointegração, carregamento de dados, dimensionamento e custos; esses são links, não seções.

O que a causalidade de Granger realmente afirma

Caminhos direcionais de lead-lag em eventos de mercado

A causalidade de Granger é precedência preditiva, não mecanismo: XX Causas Granger YY se passado XX reduz a variação do erro de previsão de YY além do que passado YY já explica.

XX Causas Granger YY se σ2(YtYt1,,Ytp)>σ2(YtYt1,,Ytp,Xt1,,Xtp)\sigma^2(Y_t \mid Y_{t-1}, \ldots, Y_{t-p}) > \sigma^2(Y_t \mid Y_{t-1}, \ldots, Y_{t-p}, X_{t-1}, \ldots, X_{t-p})

Duas séries impulsionadas por um fator oculto comum podem mostrar causalidade de Granger sem ligação direta entre elas. Na criptografia, essa advertência não é acadêmica - BTC é um único fator dominante em todo o complexo altcoin, então quase qualquer "causalidade" alt-to-alt é um artefato candidato de diferentes velocidades de resposta ao mesmo choque BTC.

A estrutura VAR

O teste reside dentro de uma autoregressão vetorial. Para duas variáveis ​​com pp atrasos:

Yt=α0+i=1pαiYti+i=1pβiXti+ϵ1tY_t = \alpha_0 + \sum_{i=1}^{p} \alpha_i Y_{t-i} + \sum_{i=1}^{p} \beta_i X_{t-i} + \epsilon_{1t} Xt=γ0+i=1pγiXti+i=1pδiYti+ϵ2tX_t = \gamma_0 + \sum_{i=1}^{p} \gamma_i X_{t-i} + \sum_{i=1}^{p} \delta_i Y_{t-i} + \epsilon_{2t}

Na forma de matriz, um VAR(p) com kk variáveis:

yt=c+A1yt1++Apytp+ut\mathbf{y}_t = \mathbf{c} + \mathbf{A}_1 \mathbf{y}_{t-1} + \cdots + \mathbf{A}_p \mathbf{y}_{t-p} + \mathbf{u}_t

Testando se XX Causas Granger YY está testando a restrição conjunta H0:β1==βp=0H_0: \beta_1 = \cdots = \beta_p = 0 na primeira equação. A rota padrão é um teste F em somas de quadrados residuais restritas versus irrestritas,

F=(RSSrRSSu)/pRSSu/(T2p1)F = \frac{(RSS_r - RSS_u) / p}{RSS_u / (T - 2p - 1)}

com a forma de Wald assintoticamente equivalente W=T(RSSrRSSu)/RSSudχ2(p)W = T \cdot (RSS_r - RSS_u)/RSS_u \xrightarrow{d} \chi^2(p). Tudo o que se segue é uma questão sobre a quais coeficientes essa restrição é aplicada e em quais dados o VAR foi ajustado.

Seleção de atraso

pp é mais importante do que a maioria dos artigos admite: poucos atrasos perdem a dinâmica, muitos queimam graus de liberdade e destroem o poder.

AIC(p)=lnΣ^u(p)+2pk2T,BIC(p)=lnΣ^u(p)+pk2lnTT\text{AIC}(p) = \ln|\hat{\Sigma}_u(p)| + \frac{2pk^2}{T}, \qquad \text{BIC}(p) = \ln|\hat{\Sigma}_u(p)| + \frac{pk^2 \ln T}{T} HQIC(p)=lnΣ^u(p)+2pk2ln(lnT)T\text{HQIC}(p) = \ln|\hat{\Sigma}_u(p)| + \frac{2pk^2 \ln(\ln T)}{T}

Intervalos de pesquisa razoáveis: 1-60 em dados de segundos, 1-30 em dados de minutos, 1-48 em dados de hora em hora. O BIC é o padrão certo aqui – é o critério mais parcimonioso, e o lead-lag em pares criptográficos líquidos é um fenômeno de memória curta. Cada resultado abaixo reporta o selecionado pelo BIC pp por par, em vez de fixar um atraso em todo o universo, porque um atraso fixo converte silenciosamente uma escolha de seleção de atraso em uma afirmação de significância.

Por que o teste diferenciado é o padrão errado

A diferenciação pode fragmentar uma relação lead-lag

As séries de preços criptográficos são I(1). A solução usual – obter retornos logarítmicos – compra estacionariedade ao custo do nível de cointegração, e se o par compartilha um equilíbrio de longo prazo, o VAR diferenciado é mal especificado. A máquina de raiz unitária e de cointegração por trás dessa sentença (ADF, Engle-Granger com seus valores críticos de Monte-Carlo, Johansen em um sistema VAR) já é abordada em arbitragem estatística e negociação de pares; assuma isso aqui. A questão é apenas que o remédio habitual – diferença e depois teste – é uma decisão modelar com consequências, e Toda-Yamamoto é a maneira de evitar tomá-lo.

O procedimento Toda-Yamamoto

Coloque um VAR com p+dmaxp + d_{max} atrasos, onde pp é a ordem de atraso ideal e dmaxd_{max} é a ordem máxima de integração da série, então teste as restrições somente no primeiro pp atrasos. O extra dmaxd_{max} as defasagens absorvem a não estacionariedade; a estatística Wald no primeiro pp coeficientes seguem um padrão χ2(p)\chi^2(p) independentemente de as séries serem I(0), I(1) ou cointegradas.

  1. Determinar dmaxd_{max} — ADF e KPSS em cada série. Para preços criptográficos dmax=1d_{max} = 1 quase sempre.
  2. Selecione pp — ajuste de VAR por níveis, escolha por BIC.
  3. Estimar o VAR aumentado(p+dmaxp + d_{max}) em níveis, sem diferenciação.
  4. ** Teste Wald o primeiro pp apenas atrasos **, ignorando o dmaxd_{max} extras. O resultado é χ2(p)\chi^2(p).

A recompensa: nenhum pré-teste de cointegração, nenhuma diferenciação e tamanho de teste correto – a taxa de rejeição sob o valor nulo permanece próxima do nominal, quaisquer que sejam as propriedades de integração. O custo é que a etapa 4 não é o que test_causality faz por padrão, que é onde a maioria das implementações erram silenciosamente.

Implementação

Fluxo de implementação da matriz Lag

Suponha que você já tenha barras de minutos alinhadas em um DataFrame - o padrão de busca e índice ccxt está em detecção de regime com HMM, e o wrapper ADF que você usaria para a etapa 1 está em arbitragem estatística e negociação de pares.

Uma nota de versão antes de qualquer coisa ser executada: grangercausalitytests(..., verbose=False) foi descontinuado e removido no statsmodels 0.15. Elimine o argumento (a função não é mais impressa por padrão) ou fixe statsmodels<0.15. O código abaixo assume a assinatura moderna.

Toda-Yamamoto, feito corretamente

A restrição deve ser construída manualmente. VARResults.test_causality testa todos os atrasos da variável causadora no modelo ajustado - em um VAR aumentado (p+1p+1) que inclui o dmaxd_{max} atraso na restrição, que é exatamente o que Toda-Yamamoto diz para não fazer. A correção é explícita RR matriz contra a covariância do coeficiente completo:

import numpy as np
from scipy.stats import chi2
from statsmodels.tsa.api import VAR


def toda_yamamoto_test(data, target, predictor, max_lag=15, d_max=1,
                       significance=0.05):
    """
    Toda-Yamamoto Granger causality on levels (no differencing).

    Fits VAR(p + d_max) and applies a Wald test to the predictor's
    coefficients at lags 1..p ONLY, leaving the d_max augmenting lags
    unrestricted. Statistic is chi2(p).
    """
    pair = data[[target, predictor]].dropna()
    n_vars = pair.shape[1]
    target_idx = list(pair.columns).index(target)
    pred_idx = list(pair.columns).index(predictor)

    model = VAR(pair)
    p = model.select_order(maxlags=max_lag).bic or 1
    res = model.fit(p + d_max)

    beta = res.params.values.ravel(order="F")
    cov = res.cov_params()
    cov = cov.values if hasattr(cov, "values") else np.asarray(cov)

    n_per_eq = res.params.shape[0]
    idx = [target_idx * n_per_eq + 1 + lag * n_vars + pred_idx
           for lag in range(p)]                      # first p lags only

    R = np.zeros((p, beta.size))
    R[np.arange(p), idx] = 1.0

    Rb = R @ beta
    V = R @ cov @ R.T
    wald = float(Rb @ np.linalg.solve(V, Rb))        # no pinv: V must be PD
    p_value = float(chi2.sf(wald, df=p))

    return {
        "target": target, "predictor": predictor,
        "p": p, "augmented_lag": p + d_max, "d_max": d_max,
        "wald_stat": wald, "p_value": p_value,
        "significant": p_value < significance,
    }

Dois detalhes que são fáceis de errar e que invalidam o teste se você fizer isso. Primeiro, a aritmética do índice deve corresponder à forma como os modelos estatísticos se nivelam params — verificar em um modelo ajustado que beta[target_idx * n_per_eq + 1] é igual res.params.iloc[1, target_idx] antes de confiar em qualquer valor p. Em segundo lugar, use np.linalg.solve, não pinv: se VV é singular, a restrição é degenerada e a execução deve falhar ruidosamente em vez de retornar um número de aparência plausível. Um pseudo-inverso aqui é como os testes Wald quebrados sobrevivem à revisão de código.

Granger diferenciado padrão, para comparação

A linha de base contra a qual a comparação se baseia - retornos logarítmicos, mesmos pares, mesmo atraso de BIC:

from statsmodels.tsa.stattools import grangercausalitytests


def differenced_granger(data, target, predictor, p):
    """Standard Granger test on log-returns at a fixed lag p."""
    pair = data[[target, predictor]].dropna()   # returns, not levels
    out = grangercausalitytests(pair, maxlag=[p])   # statsmodels >= 0.15
    f_stat, p_value = out[p][0]["ssr_ftest"][:2]
    return {"target": target, "predictor": predictor, "lag": p,
            "f_stat": f_stat, "p_value": p_value}

Observação maxlag=[p] em vez de maxlag=p: passar um int executa cada atraso de 1 a pp e tenta você a relatar o melhor, que é um teste múltiplo não registrado além do teste múltiplo que você já está executando.

A comparação

Para cada par, execute o teste diferenciado nos retornos e Toda-Yamamoto nos níveis, no mesmo nível selecionado pelo BIC. ppe tabule onde os dois discordam. A discordância é a célula interessante: um par significativo nas diferenças, mas não nos níveis, é um artefato candidato à diferenciação de um relacionamento de cointegração; o inverso sugere que o relacionamento de nível carrega informações que o teste de retorno não pode ver.

Múltiplas comparações em uma matriz de causalidade

Filtrando uma grande matriz de causalidade

O completo n×nn \times n Matrix é onde isso se torna perigoso. Uma varredura de 20 ativos e 10 atrasos equivale a 3.800 testes de hipóteses, e Bonferroni é a correção errada para uma família tão correlacionada – os testes compartilham dados, compartilham o fator BTC e estão longe de ser independentes, então Bonferroni é simultaneamente muito conservador no agregado e enganoso sobre quais células sobrevivem. Use o maquinário N efetivo do artigo desinflado de Sharpe, que é construído exatamente para esta situação: agrupar a família de testes correlacionados, contar ensaios independentes em vez de brutos, e estabelecer limites contra isso.

def granger_matrix(data, max_lag=15, d_max=1):
    """Toda-Yamamoto p-value matrix. Entry (i, j): does col_j cause col_i?"""
    cols = list(data.columns)
    out = pd.DataFrame(np.nan, index=cols, columns=cols, dtype=float)
    for target in cols:
        for predictor in cols:
            if target == predictor:
                continue
            r = toda_yamamoto_test(data, target, predictor,
                                   max_lag=max_lag, d_max=d_max)
            out.loc[target, predictor] = r["p_value"]
    return out

O número que importa não é quantas células estão abaixo de 0,05 – é quantas sobrevivem ao limite corrigido do N efetivo.

Leia a matriz sobrevivente estruturalmente, não célula por célula: linhas onde um ativo causa muitos outros confirmam uma hierarquia de informações; uma coluna causada por nada sugere uma dinâmica idiossincrática e específica do token, em vez de uma descoberta.

O teste honesto: estabilidade de rolamento

Sinal estável através de janelas rolantes

Um único valor p na amostra é quase inútil para negociação. A questão relevante é se a significância persiste. As relações entre ativos em criptomoedas são dependentes do regime — a estrutura de correlação difere acentuadamente entre calma e pânico, e o regime em si é estimável (detecção de regime HMM) — portanto, um lead-lag estimado em uma janela é uma declaração sobre essa janela.

def rolling_granger(data, target, predictor, window=500, lag=5, step=50):
    """Rolling-window p-value: when is the lead-lag active vs dormant?"""
    rows = []
    for start in range(0, len(data) - window, step):
        chunk = data.iloc[start:start + window][[target, predictor]].dropna()
        if len(chunk) < window * 0.8:
            continue
        try:
            out = grangercausalitytests(chunk, maxlag=[lag])
            f_stat, p_value = out[lag][0]["ssr_ftest"][:2]
        except Exception:
            f_stat, p_value = np.nan, np.nan
        rows.append({"timestamp": data.index[start + window - 1],
                     "f_stat": f_stat, "p_value": p_value})
    return pd.DataFrame(rows).set_index("timestamp")

Relate duas coisas desta série: a fração de janelas abaixo de 0,05 e o número de cruzamentos de limiares. Uma relação significativa em 80% das janelas com três cruzamentos é um objeto diferente de uma relação significativa em 55% das janelas com quarenta cruzamentos, mesmo que o valor p agrupado seja idêntico. O segundo não é negociável – você não pode dimensionar uma posição em um sinal cuja existência muda a cada poucas centenas de barras, e o atraso na reestimativa garante que você estará sempre negociando no regime anterior.

A armadilha de dados específica de Granger

Os ticks de criptografia irregulares interrompem os sinais causais

A higiene genérica de dados criptográficos é abordada em outro lugar - lacunas e velas ausentes em paridade backtest-live, disciplina de carimbo de data/hora na taxonomia de viés de antecipação e provando que não há previsão em intervalos de tempo. Um modo de falha, porém, é específico de Granger e grave o suficiente para ser nomeado:

O preenchimento direto produz o resultado. Uma vela preenchida para frente repete o fechamento anterior, o que injeta autocorrelação pura na série - e autocorrelação lag-1 em YY que está mecanicamente alinhado com XX é indistinguível, para o teste F, de uma relação causal de uma defasagem. O mesmo se aplica a todos os locais: um deslocamento de relógio de 1 segundo entre duas trocas nas barras de minutos pode criar ou destruir a importância do lag-1 de uma vez, porque muda o lado do limite da barra em que um movimento ocorre. Antes de executar qualquer uma dessas etapas, verifique se as lacunas foram eliminadas em vez de preenchidas e se ambas as séries foram registradas no mesmo relógio.

Descobertas de lead-lag relatadas na literatura

Constelação de evidências para pesquisa lead-lag

Estas são citações, não medições deste artigo. Eles estão aqui para dizer o que o registro publicado afirma, para que a comparação acima tenha algo com que concordar ou discordar.

  • BTC para altcoins. Um estudo de 2026 nos Mercados Financeiros da Ásia-Pacífico (Springer) relata transmissão de preços de alta frequência do Bitcoin para altcoins, com moedas de pequena capitalização mostrando respostas significativamente atrasadas e menor liquidez associada a reações mais lentas. As magnitudes de atraso comumente citadas nesta linha de trabalho – cerca de 1-3 minutos de BTC para ETH, mais longas para empresas de média e pequena capitalização, cada vez mais unidirecionais à medida que a capitalização de mercado cai – são descobertas citadas, não re-derivadas aqui. O nível de capitalização de mercado em que eles se baseiam é a mesma escada usada na validação multisímbolo.
  • CEX lidera DEX. Pesquisa sobre microestrutura criptográfica (MDPI) relata locais centralizados que dominam a descoberta de preços, com fluxo de informações executando CEX para DEX e nenhuma causalidade reversa significativa – consistente com mecanismos de correspondência abaixo de milissegundos versus liquidação em tempo de bloco.
  • ETH como moeda independente. Trabalho VAR-SVAR (MDPI) encontra regimes nos quais o Ethereum atua como fonte e o Bitcoin como receptor de repercussão, principalmente durante as fases orientadas por DeFi e NFT.

Intercâmbio, brevemente

O atraso da mesma moeda e dois locais é uma configuração real, mas a economia disso - limites de taxas, a escada de latência, por que o atraso existe - já é trabalhada em arbitragem estatística e negociação de pares e o kimchi premium. A única coisa que Granger acrescenta é uma questão de estabilidade: a causalidade Binance-Coinbase é persistentemente significativa ou ela oscila como todo o resto? Execute o teste contínuo no par de locais; a resposta é uma série temporal de valor p, não um sim.

Transformando um atraso de sobrevivência em uma posição

Limite de sinal se tornando uma posição controlada

Se um par ultrapassar o limite corrigido e o teste contínuo, o sinal em si é trivial: retorno cumulativo do preditor ao longo da janela de atraso, com limite.

def lead_lag_signal(btc_returns, alt_returns, lag=5, threshold=0.001):
    """+1 / -1 / 0 on ALT from BTC's cumulative return over `lag` bars."""
    btc_cum = btc_returns.rolling(lag).sum()
    signal = pd.Series(0, index=alt_returns.index)
    signal[btc_cum > threshold] = 1
    signal[btc_cum < -threshold] = -1
    return signal

Três coisas que não fazem parte deste artigo porque já estão resolvidas aqui:

Limitações

Limites de incerteza causal

Causalidade de Granger não é causalidade. Confundidores (um evento macro movendo ambas as pernas com ligeiro desalinhamento de carimbo de data/hora), motivadores comuns (dois alts seguindo BTC em velocidades diferentes) e variáveis omitidas (testando BTC para DOGE sem ETH no sistema) produzem estatísticas significativas sem fluxo direto de informações. Restringir os testes bivariados, como faz este artigo, torna o problema da variável omitida pior, e não melhor.

Linearidade. O teste vê apenas estrutura preditiva linear na média condicional. A criptografia tem agrupamento de volatilidade, efeitos de alavancagem e mudanças de regime - consulte previsão de volatilidade GARCH e correlação dinâmica DCC-GARCH para a história do segundo momento. Alternativas não lineares que vale a pena conhecer: o teste de kernel de Diks-Panchenko (2006), a entropia de transferência como o análogo da teoria da informação e a causalidade de Granger da cópula em toda a distribuição conjunta completa.

Quebras estruturais. Qualquer relação estimada em uma janela está condicionada ao regime dessa janela; o teste contínuo acima é o diagnóstico mínimo e é um diagnóstico, não uma solução.

Resumo

Sinal robusto de lead-lag destilado do ruído

  1. Ajuste o VAR em níveis, selecione pp por BIC por par, aumentar por dmaxd_{max}, e teste de Wald o primeiro pp apenas atrasos. Construa a matriz de restrição manualmente - a biblioteca test_causality restringe também o atraso de aumento, o que não é Toda-Yamamoto.
  2. Execute o teste de retorno diferenciado como linha de base nos mesmos pares e defasagem e relate as divergências. Essa tabela é a descoberta; uma matriz de asteriscos não é.
  3. Corrija a matriz para uma família de testes correlacionada via N efetivo, não Bonferroni, e relate quantas células sobrevivem.
  4. Role. Relate a fração de janelas significativas e a contagem de cruzamentos. A significância de que as oscilações não são um atraso negociável, não importa quão pequeno seja o valor p agrupado.
  5. Elimine lacunas em vez de preencher antecipadamente e verifique o alinhamento do relógio entre os locais, antes de acreditar em qualquer resultado de lag-1.

Um resultado perfeitamente bom deste procedimento é negativo – uma relação BTC-alt que seja significativa na amostra, sobreviva à correção e ainda cruze a linha 0,05 em ambas as direções a cada poucas centenas de barras é um resultado real sobre a negociabilidade e vale a pena publicar como tal.

Referências

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.