Skip to main content
Pro Feature. Requires a Pro or Ultra subscription. Get started at api.mathematicalcompany.com
What is this? Some markets move before others. Lead-lag detection finds these relationships - for example, a Polymarket contract that consistently reacts to news 30 seconds before a Kalshi contract on the same event. Use it to build cross-market signals or identify which feeds to watch for early information.

Lead-Lag Networks

Horizon provides Rust-native tools for detecting which prediction markets lead or lag others. These relationships are fundamental to cross-market arbitrage, informed order routing, and signal extraction from related markets.

Hayashi-Yoshida

Asynchronous tick-by-tick correlation that handles irregular timestamps without synchronization bias.

Cross-Correlation

Multi-lag cross-correlation analysis to identify optimal lead/lag offsets.

Granger Causality

Statistical test for whether past values of one market predict another.

Lead-Lag Network

Build a directed network of lead-lag relationships across many markets.

hz.hayashi_yoshida

Compute the Hayashi-Yoshida estimator for asynchronous correlation between two tick streams. Unlike standard Pearson correlation, this does not require synchronized timestamps and avoids the Epps effect (correlation attenuation from data aggregation).
import horizon as hz

result = hz.hayashi_yoshida(
    timestamps_x=[1.0, 2.5, 3.0, 5.0, 7.0],
    prices_x=[0.50, 0.52, 0.51, 0.55, 0.54],
    timestamps_y=[1.5, 2.0, 4.0, 6.0, 7.5],
    prices_y=[0.60, 0.62, 0.61, 0.65, 0.64],
)

print(f"Correlation: {result.correlation:.4f}")
print(f"Covariance: {result.covariance:.6f}")
print(f"N overlaps: {result.n_overlaps}")
ParameterTypeDescription
timestamps_xlist[float]Tick timestamps for market X
prices_xlist[float]Tick prices for market X
timestamps_ylist[float]Tick timestamps for market Y
prices_ylist[float]Tick prices for market Y

LeadLagResult Type

FieldTypeDescription
correlationfloatHayashi-Yoshida correlation estimate (-1 to 1)
covariancefloatHayashi-Yoshida covariance estimate
n_overlapsintNumber of overlapping return intervals used

hz.cross_correlation_lags

Compute cross-correlation between two synchronized series at multiple lags to identify the optimal lead-lag offset.
import horizon as hz

result = hz.cross_correlation_lags(
    series_x=returns_a,
    series_y=returns_b,
    max_lag=10,
)

print(f"Best lag: {result.best_lag}")
print(f"Best correlation: {result.best_correlation:.4f}")
for lag, corr in zip(result.lags, result.correlations):
    print(f"  lag={lag:+3d}: corr={corr:.4f}")
ParameterTypeDefaultDescription
series_xlist[float]requiredFirst time series (returns or prices)
series_ylist[float]requiredSecond time series (same length as series_x)
max_lagint10Maximum lag to compute (both positive and negative)

Cross-Correlation Result Type

FieldTypeDescription
lagslist[int]Lag values from -max_lag to +max_lag
correlationslist[float]Correlation at each lag
best_lagintLag with the highest absolute correlation
best_correlationfloatCorrelation value at the best lag
A positive best_lag means series_x leads series_y by that many observations. A negative best_lag means series_y leads series_x.

hz.granger_causality

Test whether past values of series X help predict series Y beyond what series Y’s own past values provide. Uses an F-test on a bivariate VAR model.
import horizon as hz

result = hz.granger_causality(
    series_x=returns_a,
    series_y=returns_b,
    max_lag=5,
)

print(f"F-statistic: {result.f_statistic:.4f}")
print(f"p-value: {result.p_value:.6f}")
print(f"Optimal lag: {result.optimal_lag}")
if result.p_value < 0.05:
    print("X Granger-causes Y at 5% significance")
ParameterTypeDefaultDescription
series_xlist[float]requiredPotential causal series
series_ylist[float]requiredPotential effect series
max_lagint5Maximum lag order for the VAR model

GrangerResult Type

FieldTypeDescription
f_statisticfloatF-test statistic for the null hypothesis that X does not Granger-cause Y
p_valuefloatp-value of the F-test (lower = stronger evidence of causality)
optimal_lagintLag order selected by the test
is_significantboolTrue if p_value < 0.05

hz.lead_lag_network

Build a directed network of lead-lag relationships across multiple markets. Computes pairwise Hayashi-Yoshida correlations at multiple lags and constructs a graph where edges point from leaders to followers.
import horizon as hz

network = hz.lead_lag_network(
    market_ids=["trump-win", "harris-win", "senate-gop", "house-gop"],
    timestamps=[ts_trump, ts_harris, ts_senate, ts_house],
    prices=[px_trump, px_harris, px_senate, px_house],
    max_lag=5,
    min_correlation=0.3,
)

print(f"Nodes: {len(network.nodes)}")
print(f"Edges: {len(network.edges)}")
for edge in network.edges:
    print(f"  {edge.source} -> {edge.target}: "
          f"lag={edge.lag}, corr={edge.correlation:.3f}")

# Identify market leaders (most outgoing edges)
for node in network.nodes:
    print(f"  {node.market_id}: out_degree={node.out_degree}, "
          f"in_degree={node.in_degree}")
ParameterTypeDefaultDescription
market_idslist[str]requiredMarket identifiers
timestampslist[list[float]]requiredTick timestamps for each market
priceslist[list[float]]requiredTick prices for each market
max_lagint5Maximum lag to test in cross-correlation
min_correlationfloat0.3Minimum absolute correlation to create an edge

LeadLagNetwork Type

FieldTypeDescription
nodeslist[GraphNode]Market nodes with degree statistics
edgeslist[GraphEdge]Directed edges from leaders to followers
adjacencylist[list[float]]Weighted adjacency matrix (correlation values)

GraphNode Type

FieldTypeDescription
market_idstrMarket identifier
out_degreeintNumber of markets this one leads
in_degreeintNumber of markets that lead this one
leadership_scorefloatout_degree / (in_degree + out_degree)

GraphEdge Type

FieldTypeDescription
sourcestrLeader market ID
targetstrFollower market ID
lagintOptimal lag (in ticks) from source to target
correlationfloatCorrelation strength at the optimal lag

Pipeline Integration

hz.lead_lag_detector

Creates a pipeline function that tracks lead-lag relationships between feeds and injects leadership scores into ctx.params.
import horizon as hz

def follow_leader(ctx):
    ll = ctx.params.get("lead_lag")
    if ll is None:
        return []

    # If our market is a follower, use the leader's signal
    leader_price = ll.get("leader_price")
    if leader_price is not None:
        lag = ll["lag"]
        # Leader moved up -- anticipate follower moving up
        if leader_price > ctx.feed.price + 0.02:
            return hz.order(side="buy", price=ctx.feed.price, size=10)
    return []

hz.run(
    name="lead_lag_trader",
    markets=["senate-gop"],
    feeds={
        "target": hz.PolymarketBook("senate-gop-token"),
        "leader": hz.PolymarketBook("trump-win-token"),
    },
    pipeline=[
        hz.lead_lag_detector(
            feed_x="leader",
            feed_y="target",
            lookback=500,
            max_lag=10,
        ),
        follow_leader,
    ],
)
ParameterTypeDefaultDescription
feed_xstrrequiredPotential leader feed
feed_ystrrequiredPotential follower feed
lookbackint500Number of observations to retain for correlation
max_lagint10Maximum lag to scan
min_correlationfloat0.3Minimum correlation to flag a lead-lag relationship
param_namestr"lead_lag"Key in ctx.params

Mathematical Background

The HY estimator computes the realized covariance between two asynchronously observed processes. For each pair of return intervals that overlap in time, it accumulates the product of the returns. This avoids synchronization (e.g., previous-tick interpolation) which attenuates correlation at high frequencies (the Epps effect).HY_cov = sum over overlapping intervals (delta_X_i * delta_Y_j)
X Granger-causes Y if past values of X improve the prediction of Y beyond what past values of Y alone provide. The test compares two VAR regressions:
  • Restricted: Y_t = c + sum(a_i * Y_{t-i})
  • Unrestricted: Y_t = c + sum(a_i * Y_{t-i}) + sum(b_i * X_{t-i})
An F-test on the incremental explanatory power of X determines significance.
In prediction markets, lead-lag relationships arise because:
  1. Correlated events: Related markets (e.g., presidency and Senate) share underlying information
  2. Liquidity differences: More liquid markets incorporate information faster
  3. Attention asymmetry: High-profile markets attract faster traders
Detecting these relationships enables cross-market signal extraction and early positioning.