← writing

Apr 2026

From negative edge to $30.18: building a market maker for Paradigm's Prediction Market Challenge

How I went from a negative-edge baseline to $30.18 mean edge on Paradigm's Prediction Market Challenge — regime segmentation, a closed-form volatility gate, and reclassifying a consumed order book as an opportunity.

Leaderboard

Paradigm's Prediction Market Challenge is deceptively simple: write a Python bot that posts passive limit orders on a binary prediction market. You share a FIFO order book with a static competitor, an omniscient arbitrageur, and uninformed retail flow. The arbitrageur knows the true probability and sweeps every mispriced quote. Retail is your profit source. Your score is mean edge — averaged across 200 randomized simulations with hidden parameters.

Starting point: a penny-stepper with negative edge. Final result: $30.18 mean edge. Nearly every jump along the way came from catching a structural assumption that was quietly destroying value.

The 54% problem

The first working strategy was a standard penny-stepper: quote one tick inside the competitor's best bid and ask, gated by momentum and toxicity EMAs. It scored marginally positive on wide-spread simulations but was negative overall.

Step-level debugging revealed the root cause. The simulator randomizes competitor_spread_ticks (1 to 4) per simulation. The strategy only quoted when the spread was 3 or wider — and returned CancelAll() for everything else.

The result:

| Competitor Spread | Sims | Dead (0 fills) | |---|---|---| | 1 | 52 | 52 (100%) | | 2 | 57 | 57 (100%) | | 3 | 51 | 0 | | 4 | 40 | 0 |

109 of 200 simulations — 54% — produced zero fills. The mean was being computed over 200 sims but earned in only 91 of them. The entire edge was diluted by a denominator twice as large as the numerator.

The fix was regime segmentation. Spread-4+ sims were nearly unconstrained (wide enough to absorb arb moves). Spread-3 sims needed selective gating. Spread-2 required a different approach entirely. Spread-1 was geometrically impossible to inside-quote — accepted as structural zeros.

Edge: negative → ~4.

Deriving the volatility gate

The challenge spec describes a jump-diffusion score process where the true probability follows p_t = Pr(Z_T > 0 | Z_t). A key property — noted by the challenge creator himself — is that per-step volatility in probability space is deterministic given the current probability and time remaining, regardless of the unknown jump parameters:

vol_step = φ(Φ⁻¹(p)) / √T

This gives a closed-form expression for the arbitrage probability at any inside quote:

arb_prob ≈ 2 · Φ(-half_spread / (vol_step × 100 × 1.4))

The 1.4 multiplier accounts empirically for jump effects beyond diffusion.

For spread-2 at p = 0.50 mid-simulation, arb_prob ≈ 69%. Terrible. But at extreme prices (p near 0.01 or 0.99), vol collapses and the same geometric spread becomes safe.

The first instinct was to build an entirely new strategy that quotes around a volatility-derived fair value. This failed badly — the EMA-based fair value lagged the live BBO, introducing systematic mispricing. What worked instead was using the vol model as a gate and sizing function on the existing BBO-anchored penny-stepper:

  • Only quote in spread-2 regimes when arb_prob < 0.40
  • For spread-3, use the vol model as an override when EMA gates reject but arb probability is low
  • Scale order sizes continuously: vol_safety = max(0.2, min(2.0, 2.0 - 4.0 × arb_prob))

The continuous sizing was the real lever. When conditions are safe, the strategy doubles its order size. When risky, it drops to 20% of base. This alone accounted for roughly +2.5 edge across all regimes.

Edge: ~4 → ~7.

Avellaneda-Stoikov in discrete markets

The natural next step was inventory management. The Avellaneda-Stoikov framework prescribes skewing quote prices based on inventory position — when long, shift quotes down to attract sellers. The textbook implementation shifts the reservation price by γ × inventory × σ².

In a continuous market, this works beautifully. In a discrete FIFO order book with integer ticks, it's destructive. Moving a bid from comp_bid + 1 to comp_bid means losing fill priority to the competitor entirely. There is no partial-tick adjustment. You're either at the best price capturing all flow, or you're behind the competitor capturing nothing.

The adaptation: keep prices fixed at the penny-step levels, but apply A-S asymmetric sizing. When net inventory is positive, shrink bid size and grow ask size. The positions unwind naturally without sacrificing queue priority.

bid_size = base_size × max(0.2, 1.0 - γ × net_inventory)
ask_size = base_size × max(0.2, 1.0 + γ × net_inventory)

With γ = 0.06 and a hard cutoff at ±15 contracts (suppress one side entirely), this added consistent edge across all regimes.

A further refinement: directional alpha sizing. When the fast EMA momentum signal indicates an uptrend, bids are safer and asks are riskier. Scaling bid size up and ask size down by α_fast × 0.35 captured a small additional edge from informed flow.

Edge: ~7 → ~10.

The size cap discovery

At this point the strategy was submitted and scored $10.04 on the remote leaderboard. The distribution showed 103 of 200 sims in the -3 to +2 bucket — still structural dead weight from spread-1 and spread-2 at p ≈ 0.50.

The question shifted: if the dead sims are structural, can the live sims work harder?

A parameter sweep on the size cap revealed the answer. The vol-safety multiplier (up to 2.0×), A-S sizing (up to 1.9×), and alpha skew (up to 1.35×) could theoretically push orders to ~100 contracts. But an early size cap of 28 was binding on every profitable trade.

| Size Cap | Mean Edge | |---|---| | 28 | 8.52 | | 50 | 9.94 | | 100 | 11.07 | | 150 | 11.47 |

The strategy had been throttling itself on its best opportunities. Raising the cap to 100 added +2.5 edge — not by being smarter, but by removing a constraint that was silently capping upside on trades the vol model had already identified as safe.

Edge: ~10 → ~11.5.

Monopoly quoting: the biggest single improvement

This was the inflection point, and it came from questioning the most unremarkable piece of code in the entire strategy.

When competitor_best_bid or competitor_best_ask is None — meaning the competitor's order book on that side is fully consumed — the strategy returned CancelAll(). This had been the behavior since the first version. It seemed conservative and correct: the book is broken, sit out until it recovers.

It was the single most expensive assumption in the strategy.

When the competitor's book is consumed on one side, two things are simultaneously true:

1. The surviving side is structurally safe. If all asks were consumed, the arb was buying — it swept upward through every ask level. It will not reverse and sell at your bid. A penny-step bid at comp_bid + 1 faces zero arb risk; only retail flow can fill it.

2. You have monopoly pricing power on the consumed side. You are the only market maker. All retail flow on that side must trade with you at whatever price you set.

Instrumentation across 200 sims revealed the scale: 54,118 monopoly steps — 13.5% of all trading steps. They occurred almost exclusively at extreme probabilities (p < 0.05 or p > 0.95) where per-step volatility was just 0.2–0.3 ticks. At these extremes, the fair value lagged the surviving BBO by only ~1.1 ticks on average.

The implementation quotes both sides: an aggressive penny-step on the surviving side (arb-safe, full sizing), and a vol-based wide catch order on the consumed side (monopoly-priced, with a 10-tick minimum half-spread for jump protection). FV snaps aggressively toward the surviving side. The predictor stays alive. Sizing splits between conservative on the consumed side (40 contracts — stale FV risk) and aggressive on the surviving side (50 contracts — zero arb risk).

Edge: ~11.5 → ~23. A 100% improvement from reclassifying a None value as an opportunity instead of an error.

Most of the gain came from the penny-step on the surviving side — safe retail fills the strategy had been discarding for the entire development cycle.

Final tuning

With the architecture locked, two parameter changes squeezed out the remaining edge:

Stop-steps: 40 → 0. The strategy had been canceling all orders 40 steps before settlement as a safety measure. But monopoly events cluster at extreme probabilities where the outcome is effectively determined — the contract is worth either ~$0.99 or ~$0.01. Continuing to quote in the endgame is nearly free edge. (+1.3)

Spread-2 base size: 22 → 50. The vol gate was already filtering for safe conditions. Once past the gate, timidity was unjustified. (+0.5)

Local score: ~25. Remote: $30.18.

The local-to-remote gap reflects seed variance. The remote seed distribution happened to include more extreme-probability simulations where monopoly quoting fires more frequently. This is inherent to the challenge structure — 200 random sims produce meaningful variance, and the leaderboard stores your best submission.

Architecture summary

The final strategy operates in three modes:

Normal regime (both sides of competitor book present): Penny-step at comp_bid+1 / comp_ask-1. Spread-4+ is nearly unconstrained. Spread-3 uses EMA toxicity gates with a vol-model override. Spread-2 uses pure vol-gating. Spread-1 sits out. Sizing flows through four layers: base → vol-safety scaling → A-S inventory skew → alpha directional skew, capped at 100.

Monopoly regime (one side consumed): Penny-step the surviving side aggressively. Place a wide vol-based catch order on the consumed side. Snap FV to the surviving-side boundary. Keep the momentum predictor alive.

Degenerate regime (both sides consumed): Extremely conservative. Huge spread, minimal size. Empirically, this never fires.

~480 lines of Python. No external dependencies beyond the challenge SDK.

Reflections

The largest gains came from identifying structural assumptions, not from parameter optimization. Regime segmentation, the vol gate, A-S sizing adaptation, and monopoly quoting were each architectural decisions that unlocked new categories of edge. Parameter sweeps within a fixed architecture rarely moved the needle more than 10–15%. The monopoly-quoting breakthrough — worth more than every other optimization combined — was a one-line return [CancelAll()] that had sat unexamined since the first version.

Closed-form models beat heuristics when the dynamics are known. The vol model derivation took an hour and replaced weeks of EMA tuning with three lines of math. In a market with specified dynamics, analytical solutions dominate.

Discrete microstructure breaks continuous intuition. The A-S mid-shift failure is a clean example. Inventory management theory assumes continuous price adjustment. FIFO integer-tick markets have a discontinuity at every price level — you're either at the top of the queue or invisible. Strategies must be designed for the actual market mechanism, not the textbook abstraction.

| Stage | Mean Edge | Key Decision | |---|---|---| | Baseline | negative | Penny-stepping without regime awareness | | Regime segmentation | ~4 | Handle each competitor spread as a different game | | Volatility gate + continuous sizing | ~7 | Derive arb probability from the spec; use it for gating and sizing | | A-S asymmetric sizing | ~10 | Skew quantities not prices in discrete FIFO markets | | Size cap removal | ~11.5 | Stop throttling upside on safe trades | | Monopoly quoting | ~23 | A consumed book is an opportunity, not an error | | Final tuning | ~25 local / ~30 remote | Don't quit early; don't be timid past the vol gate |

Built for the Paradigm Prediction Market Challenge.