Settlement
The resolution-aware risk heuristics for a binary market — a linear force-flatten schedule that shifts the reservation toward flat as time runs out, and a time-accelerated pull that trips the fill-rate breaker sooner.
crates/vike-mm/src/settlement.rs holds the pure τ-driven risk heuristics of the
spread maker on a binary [0,1] prediction market. The
header states the hazard: holding inventory to resolution on the wrong side is a total loss, so
as τ = T − t runs to zero the maker must (1) actively unwind toward flat and (2) tighten its
circuit breaker on the toxic side faster than symmetric time decay would. Everything here keys off
one anchor, AsParams::resolution_ts — the resolution timestamp T in epoch milliseconds, which
for Polymarket is GammaMarket.end_date parsed at mount. SpreadMaker::from_params's reader says
every settlement feature reads it: the force-flatten τ, the blackout, the terminal penalty, the
underlying anchor and the pull acceleration.
What the pricing layer already does
The header separates this module from its neighbour. The
Avellaneda–Stoikov layer already skews harder and
widens through a settlement-variance term — AsParams::terminal_penalty_gamma, folded in over the
last terminal_ramp_ms, so the reservation skew strengthens and the half-spread widens at
p ≈ 0.5 as t → T — and blacks out near resolution: within resolution_blackout_ms of T it
quotes maximally wide, pushing fills out to the [tick, 1 − tick] walls, to dodge the
resolution-news vol spike the Bernoulli cap does not catch. That doc also draws a line the operator
should know: the blackout is the pricing layer's stance, while an active mass-cancel pull is the
mount's on_feed_status job. What this module adds is the thing the pricing layer lacks — an active
force-flatten schedule that ramps the target inventory toward zero.
These are practitioner heuristics
The header says so in as many words. There is no canonical closed-form f(τ) for a force-flatten
schedule: the literature — Avellaneda–Stoikov and its settlement variants — prices the reservation
and the spread but does not prescribe an active-liquidation ramp. The shapes here are deliberately
the simplest defensible ones, linear in τ; a caller wanting a convex late-panic curve composes
these, and the linear primitives are the pinned, testable baseline.
The force-flatten schedule
flatten_weight(tau_ms, flatten_by_ms) answers how much of the current position should already be
unwound, in [0, 1]. flatten_by_ms is the τ at which the maker wants to be fully flat, measured
as time-to-resolution — 30_000 means "flat by 30 s before resolution". A non-positive window is
disabled and returns 0.0; a τ at or beyond the window is 0.0, nothing to unwind yet; inside the
window the weight ramps linearly, 1 − tau_ms / flatten_by_ms; and a τ at or below zero is 1.0,
the whole position wanted gone. It is monotone non-increasing in τ and always clamped.
force_flatten_skew(q_norm, flatten_w, strength) turns that weight into an additive reservation
shift: −strength · flatten_w · q_norm. A long book yields a negative shift — a lower reservation
price, so the maker leans to sell and flatten — and a short book mirrors. The lean grows with the
weight, so the pressure to be flat rises as τ runs out, and it is exactly 0.0 when the weight is
zero or strength is zero, the opt-out. The call site is the A-S reservation in
crates/vike-mm/src/avellaneda.rs's AsState: τ is resolution_ts − ts, or i64::MAX without a
known resolution — outside any window, weight zero, inert — and the shift is added to the
reservation after the Cartea–Jaimungal short-alpha term. The keys are flatten_by_ms and
flatten_strength, both defaulting to off; AsParams::flatten_strength's doc notes that 0.0 is
byte-identical even with a window set.
The accelerated pull
accelerated_pull(tau_ms, ramp_ms, max_accel) is the factor the per-side
fill-rate breaker divides its effective net-fill
threshold by, so it trips sooner as τ runs out. A non-positive ramp, or a τ outside it, returns
1.0; inside, the multiplier ramps linearly from 1.0 to 1 + max_accel; at or after resolution
it is 1 + max_accel. It is always at least 1.0 — floored, so a negative max_accel can never
weaken the breaker — and monotone non-decreasing toward resolution. Because it is 1.0 bit-for-bit
when off, dividing the threshold by it is byte-identical on the default path: x / 1.0 == x.
The wiring is in two places. SpreadMaker::pull_accel_mult reads 1.0 unless the A-S layer is on
with a known resolution_ts and pull_accel_ramp_ms > 0 — the two knobs live on AsParams,
so a breaker-only maker with no A-S layer reads 1.0 and is byte-identical. Strategy::on_fill in
crates/vike-mm/src/strategy_impl.rs then compares the netted fills against
net_fill_threshold / pull_accel_mult(fill.ts); the pure net_signed_fills is untouched, and the
multiplier is applied only at that comparison. The keys are pull_accel_ramp_ms and
pull_accel_max, defaulting to off.
The inert-when-off invariant
Every function here returns its neutral value — a 0.0 shift, a 1.0 multiplier — when its window
parameter is zero or non-positive, so a maker that has not opted in is byte-identical to the
pre-settlement path. The in-file tests pin the disabled, far, at-resolution and mid-ramp values for
all three functions, including accelerated_pull's 1.0 being bit-exact so the divided threshold
is unchanged.
Two things this page does not cover. The active unwind that crosses the book is a different leg — taker flatten — and both the schedule here and the skew are passive. And the venue side of resolution — what the Polymarket bridge does when a market resolves, auto-redeem included — is on Polymarket operations; the reward-band fold's own blackout rule is on liquidity rewards.
Liquidity rewards
Reward-aware quoting for a venue that pays for resting liquidity near the midpoint — the pure scoring model, the band clamp and min-order-age hold that fold it into the quote, and the two safety authorities that outrank it.
Cross-exchange market making
The xEMM — rest passively on one venue at prices derived from another's touch and hedge every fill there — its pure pricing core, its four safety layers, and the fact that no binary mounts it.