Lead-Lag Relationships and Cross-Asset Signal Transmission
Lead-lag relationships describe which asset moves first and which follows — ES futures leading SPY, BTC leading altcoins, credit spreads leading equities. Cross-asset signals exploit information transmission: a price move in market A that has not yet propagated to market B is a short-horizon alpha source. This is not correlation trading (betting two assets stay correlated); it is timing arbitrage on the propagation delay. This article covers detection methods, signal construction, and the pitfalls that turn lead-lag backtests into spurious noise.
Lead-lag vs correlation
Correlation measures co-movement over a window. Lead-lag measures directional precedence — does A's return at t predict B's return at t+k? Two assets can be uncorrelated but exhibit strong lead-lag at short horizons (A moves, B catches up, then mean-reverts together).
| Concept | Question | Horizon |
|---|---|---|
| Correlation | Do A and B move together? | Symmetric window |
| Lead-lag | Does A predict B (not vice versa)? | Short, asymmetric |
| Cointegration | Do A and B share a long-run equilibrium? | Long (cointegration) |
| Beta | How much of B is explained by A? | Contemporaneous or lagged |
Lead-lag alpha decays as markets become more efficient and transmission speeds up. What worked at 500ms in 2015 may be gone at 50ms in 2026 — measure signal decay explicitly.
Detection methods
Cross-correlation at multiple lags
Compute corr(rA(t), rB(t+k)) for k = -K..+K. Peak at k > 0 means A leads B by k bars.
import numpy as np
import pandas as pd
def lead_lag_ccf(ret_a: pd.Series, ret_b: pd.Series,
max_lag: int = 20) -> pd.Series:
"""Cross-correlation: positive lag => A leads B."""
ccf = {}
for k in range(-max_lag, max_lag + 1):
if k >= 0:
ccf[k] = ret_a.corr(ret_b.shift(-k))
else:
ccf[k] = ret_a.shift(k).corr(ret_b)
return pd.Series(ccf)
# Peak at lag=2 → A leads B by 2 bars
Use returns, not prices (spurious correlation on non-stationary levels). Apply on high-frequency or daily data depending on the hypothesized transmission channel.
Granger causality
Regress rB(t) on lags of rA and lags of r_B. If A's lags are jointly significant (F-test), A Granger-causes B. This is predictive causality, not structural — but it is a useful screen before building signals.
from statsmodels.tsa.stattools import grangercausalitytests
def granger_lead(data: pd.DataFrame, cause: str, effect: str,
max_lag: int = 10) -> int:
"""Return lag with lowest p-value for Granger causality."""
df = data[[effect, cause]].dropna()
results = grangercausalitytests(df, maxlag=max_lag, verbose=False)
best_lag = min(results, key=lambda k: results[k][0]['ssr_ftest'][1])
return best_lag
Run on rolling windows — lead-lag is regime-dependent. BTC→ETH lead may exist in bull markets and invert in crashes.
Hayashi-Yoshida estimator (async timestamps)
When assets trade on different clocks (crypto 24/7 vs equity RTH), standard correlation assumes synchronous sampling. The Hayashi-Yoshida estimator handles asynchronous tick data without artificial alignment — essential for cross-asset HFT signals. See tick data architecture for infrastructure.
Building trading signals
Template for a lead-lag signal:
signal_B(t) = zscore( r_A(t-l*) ) where l* = estimated optimal lag
position_B(t+1) = sign(signal_B(t)) × size
Refinements:
- Threshold — trade only when |signal| > kσ to avoid noise trades
- Half-life exit — close when B catches up (signal reverts to 0), not fixed horizon
- Vol scaling — size inversely to B's volatility
- Regime filter — trade only when Granger p-value < 0.05 on rolling 60-day window
Cross-asset pairs in crypto
Common lead-lag structures:
- BTC → altcoins — BTC moves, alts follow with 1-30 minute lag (varies by cap)
- Perp → spot — perp leads spot in high funding regimes (informed flow on leverage)
- CEX → DEX — price discovery on centralized venues leads decentralized pools
- US session → Asia session — equity/crypto macro leads regional open
Each has different lag, decay, and transaction costs. Altcoin follow trades pay wide spreads — the lag must be large enough to cover them.
Macro cross-asset
Rates → FX → equities → credit is a classic transmission chain. A quant signal might use:
- 10Y yield change → ES futures direction (lag 1-5 minutes intraday)
- Credit spread change → equity vol (lag hours to days)
- DXY move → commodity currencies (contemporaneous to 1-day lag)
These are crowded and well-arbitraged at slow horizons. Edge, if any, is in second-order effects: how the lag changes around regime shifts.
Spurious lead-lag: what to avoid
Multiple testing — scanning 50 assets × 50 lags yields "significant" lead-lag by chance. Apply deflated Sharpe or Bonferroni correction on the number of (asset pair, lag) combinations tested.
Non-stationarity — two trending assets show fake lead-lag on prices. Always use returns or fractionally differentiated series.
Microstructure noise — bid-ask bounce in illiquid assets creates fake reversal at lag=1 that looks like lead-lag at lag=2. Use mid-price or trade-price series.
Lookahead in alignment — joining async data with forward-fill from the faster asset introduces lookahead. Use point-in-time joins with last-known-value as of each timestamp.
Survivorship in pairs — testing lead-lag on today's top 100 coins ignores delisted zeros. Crypto lead-lag backtests on current universe are optimistically biased.
Combining with other signals
Lead-lag signals are naturally short-horizon overlays on slower strategies:
- Use BTC lead signal to time entry into altcoin mean-reversion
- Use futures lead to improve execution on ETF basket trades
- Combine with order flow imbalance when
both agree on direction
Alpha combination should treat lead-lag as a separate sleeve with its own turnover budget and decay profile — do not blend weights statically with monthly factors.
Key takeaways
- Lead-lag is asymmetric predictability, not correlation — measure with CCF and Granger tests
- Signals decay as transmission speeds up; monitor on rolling windows
- Crypto and macro chains have distinct lag structures and cost profiles
- Guard against multiple testing, lookahead, and microstructure noise
- Use lead-lag as a timing overlay, not a standalone slow factor
