Skip to content

Latest commit

 

History

History
434 lines (304 loc) · 22.1 KB

File metadata and controls

434 lines (304 loc) · 22.1 KB

Causal Information Flow as a Trading Signal in S&P 500 Prediction Markets

Independent quantitative research · 2026 Author: Chip Dullanty Dataset: Kalshi subset of the Prediction Market Analysis repository (Becker, 2025) Method: Liang–Kleeman Information Flow (LKIF) applied to a continuous, year-long stitched series of S&P 500 binary contracts


TL;DR

  • Question: does causal information flow exist in the microstructure of recurring S&P 500 prediction markets?
  • Approach: stitch one year of Kalshi KXINXU contracts into a continuous hourly series, compute a multi-channel LKIF signal, and run a long/short trend strategy gated by that signal.
  • Headline result: the strategy preserves 633× more capital than passive holding of ATM contracts. Walk-forward: 25 out-of-sample folds, +0.24 mean alpha (log) per fold, alpha-positive in 56%. Monte Carlo permutation test: real strategy beats 99.3% of n=300 noise replacements (p = 0.007).
  • Honest reading: the underlying market structurally bleeds capital (always-long loses 99.99997% across the year), so this is a signal-detection result, not a profitable trading system. The natural commercial extension is to use the Kalshi-derived LKIF as a leading indicator for SPX/ES futures, where capacity is unbounded.

1. The Problem

A hedge fund building out its systematic strategy book has access to the Prediction Market Analysis dataset and wants to know whether prediction markets contain tradeable causal information beyond the implied probability itself.

Most quant signals rely on correlation, which is symmetric and noisy. This project applies a less common technique — Liang–Kleeman Information Flow (LKIF) — that measures directional causation between time series.

The headline question:

Does causal information flow exist in the microstructure of recurring S&P 500 prediction markets, and can it be used as a trading signal?


2. Why S&P 500 Prediction Markets

Many prediction markets are one-off events (an election, a championship). Those don't generalize: a strategy that worked on a single event tells you nothing about whether the technique is robust.

Recurring markets are different. Kalshi creates a fresh slate of S&P 500 contracts every trading hour, every trading day. The dataset contains:

Trading days 270 (Oct 24 2024 → Nov 21 2025)
Distinct events 1,455 (each event = one date+hour resolution cohort)
Individual contracts 435,308 (~13 strike prices per event)
Trades extracted 511,097
Cumulative volume $94 million

Each event resolves cleanly at a specific hour — e.g. "Will the S&P 500 be above 7449.99 on Nov 17, 2025 at 4 pm EST?"

This recurring structure lets us build a continuous time series spanning more than a year, run walk-forward validation across many serial folds, and avoid the survivorship bias of cherry-picking a single event.


3. Theoretical Framework

3.1 LKIF in one equation

Given a target series $x_1$ (e.g. log-returns) and a candidate driver $x_2$ (e.g. lagged volume), the Liang information flow from $x_2 \to x_1$ over a rolling window is:

$$T_{2 \to 1} = \frac{C_{11} , C_{2,dx} - C_{12} , C_{1,dx}}{C_{11} , C_{22} - C_{12}^2}$$

Where every symbol is:

Symbol Meaning
$x_1$ the target series (e.g. log-return of yes price)
$x_2$ the candidate driver series (e.g. lagged log-volume)
$dx_1$ finite difference of $x_1$: $dx_1[t] = x_1[t+1] - x_1[t]$
$w$ rolling window length (we use $w \in {20, 40, 80}$ hours)
$C_{11}$ variance of $x_1 = \langle (x_1 - \bar{x}_1)^2 \rangle$ over the window
$C_{22}$ variance of $x_2$
$C_{12}$ covariance of $x_1$ and $x_2$
$C_{1,dx}$ covariance of $x_1$ with $dx_1$
$C_{2,dx}$ covariance of $x_2$ with $dx_1$

Normalize to a dimensionless coefficient:

$$\tau = \frac{T_{2 \to 1}}{|T_{2 \to 1}| + |T_{1 \to 2}|} \in [-1, +1]$$

Critically, $\tau$ is asymmetric$\tau(x_2 \to x_1) \neq \tau(x_1 \to x_2)$. That's what separates it from correlation, which is by construction symmetric.

3.2 Multi-channel composite

A single LKIF measurement is fragile. The strategy combines three channels (drivers) and three windows:

Channel Driver Weight What it captures
Momentum Lagged log-returns of yes price 0.50 Self-driving information in price
Volume Lagged log-volume 0.35 Informed flow proxy
Order imbalance Lagged yes-vs-no taker ratio 0.15 Aggressor side / one-sided flow

The composite is then aggregated across three rolling windows (20, 40, 80 hours) with weights 0.25 / 0.50 / 0.25, smoothing noise and capturing dynamics at different frequencies.

3.3 Strategy architecture

       Trend signal (EMA-12 / EMA-26 crossover on yes price)
                       │
                       ▼
         LKIF composite as confirmation gate
            τ > +threshold AND trend up   →   LONG  YES
            τ < −threshold AND trend down →   SHORT YES (= LONG NO)
            otherwise                     →   FLAT
                       │
                       ▼
           Volatility storm gate (force flat in high-vol regime)
                       │
                       ▼
        Trailing stop (4% from peak yes-price since entry)
                       │
                       ▼
                  POSITION ∈ { −1, 0, +1 }

LKIF is not used alone — it's a filter on top of the trend signal. The trend tells direction; LKIF says whether the trend is being driven by real information flow rather than noise.

In prediction markets you can short YES by buying NO contracts, so the strategy treats both directions equivalently. Maximum loss per position = stake size (no margin, no leverage, no infinite downside) — fundamentally different from shorting equities.


4. Data Construction

4.1 Source

Kalshi subset of the Prediction Market Analysis repository. Kalshi-wide trade data spans 2021-06-30 to 2025-11-25; the KXINXU (S&P 500) series specifically covers Oct 2024 → Nov 2025.

4.2 Stitching continuous bars from discrete contracts

Each individual KXINXU contract trades for only a few hours before resolving. To produce a continuous series suitable for LKIF, we use this procedure:

  1. Group all KXINXU markets by event_ticker. Each event = one (date, hour) cohort with ~13 strike prices.
  2. For each event, identify the most-traded strike — this is the contract participants treat as at-the-money (ATM).
  3. Pull all trades for that ATM contract (149,394 trades total).
  4. Aggregate trades to 15-minute bars (and an hourly resampled version) with:
    • close = volume-weighted average yes price (cents)
    • volume = total contracts traded
    • yes_ratio = fraction of taker volume on the yes side (order imbalance proxy)
    • log_return = log change of close from prior bar — scoped within a single contract (NaN at contract boundaries to avoid spurious 30+ cent jumps when one ATM contract rolls into the next)
    • log_volume = log(1 + volume)
  5. Stitch all events together along the time axis. Hours with no active ATM contract are forward-filled (price) and zero-filled (volume).
  6. Winsorize log returns to ±0.5 to handle resolution-time spikes (when prices snap to 0¢ or 100¢ at expiration).

This produces a single continuous series spanning 9,441 hourly bars (2,542 active) over 13 months.

4.3 Trade schema

Field Type Notes
trade_id string Unique
ticker string Maps to one strike of one event
count int Contracts traded
yes_price int Cents 1–99 = implied probability
taker_side string "yes" or "no" — aggressor side
created_time datetime UTC Microsecond precision

5. Methodology Pipeline

Raw Kalshi trades (Parquet, 7,214 files across 4.4 years)
       │
       ▼ filter to KXINXU tickers (parallel scan, 6 workers)
S&P 500 prediction-market trade panel (511,097 trades)
       │
       ▼ pick most-traded strike per event
ATM contract trades only (149,394 trades)
       │
       ▼ aggregate to 15-min and hourly VWAP bars
Continuous bars
   close, volume, yes_ratio, log_return, log_volume
       │
       ▼ feature engineering (lag by 1 — no lookahead)
       ▼ Numba-JIT rolling LKIF
Per-channel τ across windows {20, 40, 80}
       │
       ▼ composite signal
LKIF_multi time series
       │
       ▼ long/short strategy logic
Position {-1, 0, +1} — entries / exits / vol gates
       │
       ▼ walk-forward backtest + Monte Carlo
Per-fold returns, alpha, p-value

6. Results

6.1 Heads-up on units: log returns vs simple returns

All headline numbers below are LOG returns, which are additive across bars. Simple returns are multiplicative — convert with simple = exp(log) − 1. Quick reference:

Log return Simple return
−0.69 ≈ −50%
−2.30 ≈ −90%
−7.0 ≈ −99.9%
−12.76 ≈ −99.99997%

When you see "log return = −12.76" later in this report, that means lost 99.99997% of capital, not "−1276%". Cumulative log returns can sum below −1 because they're a sum, not a percentage of the original stake.

6.2 Headline strategy backtest

Metric LKIF L/S Trend Only Always-Long
Total log return (strategy) −6.31 −9.77 −12.76
Alpha vs always-long (log) +6.45 +2.99
Capital preservation vs passive 633× 20×
Sharpe (annualized) −1.36 −1.51
Win rate 43.5% 44.1%
% time long 4.4% 13.8% 100%
% time short 19.2% 33.2% 0%
% time in market 23.6% 46.9% 100%
Number of entries 456 952

Reading this table. Holding ATM Kalshi contracts continuously is structurally a losing proposition because each contract resolves at exactly 0¢ or 100¢ — there's no equity-style drift. The "always-long" baseline loses 99.99997% of capital across the year. The LKIF L/S strategy preserves 633× more capital than passive (alpha of +6.45 log = $e^{6.45}$). Whether that's tradeable as-is is debatable; what matters is that the LKIF signal extracts real information from a market that otherwise destroys all capital.

6.3 OLS regression — does LKIF predict next-bar returns?

The model:

$$y_{t+1} = \beta_0 + \beta_1 , \tau^{\text{mom}}_t + \beta_2 , \tau^{\text{vol}}_t + \beta_3 , \tau^{\text{oi}}_t + \varepsilon_t$$

Where:

  • $y_{t+1}$ = log return of ATM yes_price at the next bar
  • $\tau^{\text{mom}}_t,\ \tau^{\text{vol}}_t,\ \tau^{\text{oi}}_t$ = the three LKIF channels (momentum, volume, order imbalance) at time $t$
  • $\beta_0..\beta_3$ = OLS coefficients (estimated by minimizing $\sum \varepsilon^2$)
  • $\varepsilon_t$ = residual

Estimated coefficients (n = 858 active bars, Newey-West HAC SE with 24-lag):

Variable $\beta$ coef HAC SE t-stat p-value Significance
$\beta_0$ (constant) −0.009 0.012 −0.77 0.44
$\beta_1$ $\tau^{\text{mom}}$ +0.078 0.054 +1.44 0.15
$\beta_2$ $\tau^{\text{vol}}$ +1.068 0.553 +1.93 0.05 *
$\beta_3$ $\tau^{\text{oi}}$ −0.081 0.048 −1.69 0.09 .

Goodness of fit:

Metric Value
OLS R² 0.006
F-stat (p) 1.96 (0.12)
Logit McFadden pseudo-R² 0.00003
Logit AUC 0.502 (≈ coin flip)
Information Coefficient (Pearson) +0.004
Information Coefficient (Spearman) +0.013

Linear verdict. At the per-bar level, LKIF has essentially zero linear predictive content. If you used τ as a direct return predictor and sized positions linearly, it would not work.

6.4 Where the edge actually hides — nonlinear and at the extremes

Statistical evidence of predictive content

Figure: Top-left — OLS multivariate coefficients with HAC 95% CIs. Top-right — R² and pseudo-R² across model types (all small but non-zero). Bottom-left — scatter of τ vs next-bar return; the OLS slope is essentially flat. Bottom-right — direction accuracy by τ decile. The top decile shows a 62% up-rate vs the 53% base rate — a real but nonlinear ~9pp edge that linear models miss.

Top decile of τ — fraction of next bars with positive return 62%
Base rate — fraction over all 858 active bars 53%
Edge at extremes +9 pp

This is why the thresholded strategy works. It only trades when |τ| > 0.04 — exactly exploiting this nonlinearity. Linear models miss the signal because the edge is concentrated in the τ tails.

6.5 Walk-forward validation

Walk-forward validation

Figure: 25 out-of-sample 14-day folds. Top — per-fold strategy return (blue) vs always-long baseline (gray) in log units. Bottom — cumulative alpha across folds.

Metric Value
Folds tested 25 (14-day non-overlapping)
Initial training window 30 days (expanding thereafter)
Re-optimization within folds None
Mean alpha per fold (log) +0.24 (≈ +27% simple)
Alpha-positive fold rate 56%
Cumulative alpha across folds (log) +6.02 ($e^{6.02}$ = 412× capital ratio)

The cumulative alpha line grows steadily across the year — the strategy isn't just front-loaded; it adds value across most test windows.

6.6 Monte Carlo permutation test

Monte Carlo permutation test

Figure: distribution of total log returns across 300 random-LKIF replacements (gray) vs the real strategy (blue dashed line). The real strategy sits at the 99.3rd percentile.

The permutation test replaces only the LKIF signal with random noise of matched moments (same mean, same standard deviation), keeping the trend filter, vol gate, trailing stop, and threshold identical. This controls for time-in-market — random LKIF strategies have approximately the same flatness as the real one.

Metric Value
Simulations 300
Real strategy log return −6.31
Random-LKIF mean log return −13.72
Real percentile of distribution 99.3rd
p-value 0.007

The real strategy outperforms 99.3% of random-LKIF replacements with p < 0.01. This rules out the possibility that the alpha is luck or an artifact of the position-sizing logic — the LKIF signal itself carries information.

6.7 Channel decomposition — where does the signal come from?

Per-channel LKIF decomposition

Figure: per-channel τ over time, with rolling window w = 40 hours. Top — momentum. Middle — volume. Bottom — order imbalance.

Channel Activity Verdict
Momentum τ ranges across full ±1; sustained activity ACTIVE
Volume τ stays near 0; only occasional spikes MOSTLY SILENT
Order imbalance τ alternates strongly; comparable to momentum ACTIVE

In prediction markets, volume bursts mostly follow price moves rather than lead them, so the volume channel contributes little signal. Order imbalance carries comparable weight to momentum — suggesting the 0.50 / 0.35 / 0.15 channel weights inherited from the original LKIF implementation are likely suboptimal for this asset class.


7. Limitations and Honest Caveats

  1. Cumulative log returns are not literal P&L. The −12.76 baseline is the math sum across stitched contracts, not what a real trader would experience (a trader would re-enter contracts continuously, not hold a single position to −99.99997%). The LKIF–vs–passive relative comparison is valid; absolute cumulative returns shouldn't be taken at face value.

  2. Stitching introduces structural breaks. Volume and yes_ratio have discontinuities at contract boundaries. Returns are scoped to a single contract (NaN at edges) but other features are not.

  3. In-sample channel weights. 0.50 / 0.35 / 0.15 weights borrowed from the original LKIF implementation; not optimized for prediction markets. The channel-activity figure suggests volume should be down-weighted.

  4. No transaction-cost stress test. Slippage modeled at 0¢ per round trip. Real Kalshi spreads of 1–3¢ would meaningfully erode the strategy's edge.

  5. Single underlying tested. Only S&P 500 markets analyzed. Generalization to Bitcoin daily, Fed rate decisions, or CPI markets is open.

  6. No live cross-asset implementation. The most commercially interesting application — using Kalshi probabilities to predict actual SPY/SPX moves — is identified below as the natural next step but not implemented in this project.


8. Discussion — What This Would Look Like at the Hedge Fund

Three natural extensions for a real deployment:

8.1 Cross-asset signal extraction (highest priority)

Use LKIF from Kalshi S&P probabilities into actual SPY / ES futures returns. This is the only application with capacity for hedge-fund-scale capital — direct trading of Kalshi contracts is capped at low millions per signal before market impact, while SPX futures absorb essentially arbitrary size.

The hypothesis: the p = 0.007 we found means LKIF is detecting something real. If that something is informed flow (e.g. a fund hedging into Kalshi for tax efficiency), it should leak into actual SPX prices with a measurable lag.

Test protocol: compute LKIF(Kalshi → SPX) on 3-min bars during 4-hour pre-FOMC windows. n ≈ 14 FOMC meetings 2022–2025 = enough for OOS validation.

8.2 Cross-platform lead-lag with Polymarket

The same dataset includes Polymarket S&P-equivalent contracts. LKIF asymmetry would tell us which platform's price discovery leads — a regulated US retail market (Kalshi) versus a global crypto-funded market (Polymarket). If one consistently leads, that's a stat-arb opportunity between participant bases.

8.3 Per-event LKIF

Rather than stitching one continuous series, run LKIF within each event's trade history. Each of 1,442 events becomes its own backtest. This avoids the cumulative-returns artifact entirely and gives 1,442 truly independent trials — likely a stronger statistical case than the stitched approach used here.


9. Conclusion

LKIF — a measure of directional causation between time series — extracts statistically significant information (p = 0.007) from S&P 500 prediction-market microstructure. Across 25 out-of-sample 14-day walk-forward folds, an LKIF-filtered long/short strategy beats a passive baseline in 56% of folds with mean alpha of +0.24 log per fold.

At the per-bar level, linear models barely see the signal (R² ≈ 0, IC ≈ 0). The edge is concentrated in the τ tails (top decile = 62% up-rate vs 53% base) — exactly what the thresholded strategy is designed to exploit. LKIF is a regime/filter signal, not a direct return predictor.

The most promising commercial application is not trading Kalshi contracts directly (capacity-constrained, structurally bleeds) but using the Kalshi-derived signal to drive trading in liquid underlying instruments like SPX futures.


10. References


Appendix A: Reproducing the Analysis

Environment

# Conda env
conda create -n predmarket python=3.11
conda activate predmarket
pip install numpy pandas numba pyarrow matplotlib statsmodels scikit-learn pymupdf python-pptx

Pipeline

# 1. Clone the dataset
git clone https://github.com/Jon-Becker/prediction-market-analysis
# (download the compressed dataset and decompress; see repo README)

# 2. Extract KXINXU trades (parallel, ~50 min on 6 workers)
python extract_spx_parallel.py

# 3. Build the continuous stitched bars
python build_continuous_spx.py

# 4. Run the full LKIF analysis (strategy + walk-forward + Monte Carlo + plots)
python spx_analysis.py

# 5. Run the OLS / Logit / IC regression diagnostics
python regression_analysis.py

# 6. Optional: build the presentation deck
python build_pptx.py

Source files

File Purpose LOC
extract_spx_parallel.py Parallel scan of 7,214 trade files ~90
build_continuous_spx.py ATM-stitched 15-min and hourly bars ~110
lkif_engine.py Numba-JIT LKIF, multi-channel composite, vol gate ~280
spx_analysis.py Long/short strategy, walk-forward, Monte Carlo, plots ~360
regression_analysis.py OLS / Logit / IC diagnostics ~250
build_pptx.py PowerPoint deck generator ~700

Output files

All figures and tables generated by the pipeline land in output/:

output/
├── spx_main_analysis.png            # 4-panel strategy overview
├── spx_walk_forward.png             # 25-fold OOS results
├── spx_monte_carlo.png              # n=300 permutation distribution
├── spx_channel_decomposition.png    # per-channel τ over time
├── regression_table.png             # OLS / Logit / IC diagnostics
├── spx_summary.json                 # headline metrics
├── spx_walk_forward_folds.csv       # per-fold table
└── regression_results.json          # raw coefficient outputs