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
- Question: does causal information flow exist in the microstructure of recurring S&P 500 prediction markets?
- Approach: stitch one year of Kalshi
KXINXUcontracts 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.
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?
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.
Given a target series
Where every symbol is:
| Symbol | Meaning |
|---|---|
| the target series (e.g. log-return of yes price) | |
| the candidate driver series (e.g. lagged log-volume) | |
| finite difference of |
|
| rolling window length (we use |
|
| variance of |
|
| variance of |
|
| covariance of |
|
| covariance of |
|
| covariance of |
Normalize to a dimensionless coefficient:
Critically,
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.
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.
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.
Each individual KXINXU contract trades for only a few hours before resolving. To produce a continuous series suitable for LKIF, we use this procedure:
- Group all
KXINXUmarkets byevent_ticker. Each event = one (date, hour) cohort with ~13 strike prices. - For each event, identify the most-traded strike — this is the contract participants treat as at-the-money (ATM).
- Pull all trades for that ATM contract (149,394 trades total).
- Aggregate trades to 15-minute bars (and an hourly resampled version) with:
close= volume-weighted average yes price (cents)volume= total contracts tradedyes_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)
- Stitch all events together along the time axis. Hours with no active ATM contract are forward-filled (price) and zero-filled (volume).
- 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.
| 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 |
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
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.
| 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.
The model:
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 |
|
HAC SE | t-stat | p-value | Significance |
|---|---|---|---|---|---|
|
|
−0.009 | 0.012 | −0.77 | 0.44 | |
|
|
+0.078 | 0.054 | +1.44 | 0.15 | |
|
|
+1.068 | 0.553 | +1.93 | 0.05 | * |
|
|
−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.
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.
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 ( |
The cumulative alpha line grows steadily across the year — the strategy isn't just front-loaded; it adds value across most test windows.
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.
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.
-
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.
-
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.
-
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.
-
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.
-
Single underlying tested. Only S&P 500 markets analyzed. Generalization to Bitcoin daily, Fed rate decisions, or CPI markets is open.
-
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.
Three natural extensions for a real deployment:
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.
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.
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.
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.
- Liang, X. S. (2014). Unraveling the cause–effect relation between time series. Physical Review E, 90(5), 052150.
- Liang, X. S., & Kleeman, R. (2005). Information transfer between dynamical system components. Physical Review Letters, 95(24), 244101.
- Liang, X. S. (2021). Normalized multivariate time series causality analysis and causal graph reconstruction. Entropy, 23(6), 679.
- Becker, J. (2025). Prediction Market Analysis dataset.
- Wolfers, J., & Zitzewitz, E. (2004). Prediction markets. Journal of Economic Perspectives, 18(2), 107–126.
- Manski, C. F. (2006). Interpreting the predictions of prediction markets. Economics Letters, 91(3), 425–429.
# 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# 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| 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 |
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



