Event contracts look simple from the outside. A price between a fraction of a cent and a dollar. A YES or a NO. A number you can size and forget until resolution.

The book is not simple. If you B-book a portfolio of those contracts (you are the counterparty, the trader never hits the real CLOB, prices still mark to a public market), a flat "25% max position" rule will not save you. Thin names can be pushed. Near-certain prices can be farmed. Three House seats in the same state are not independent, and they are not the same market.

Fig 01Three failure modes
01Pushed
A thin external mid moves on a small push, and the B-book marks to it.
02Farmed
Near-certain prices pay small and lose large. At 90¢ one loss erases nine wins.
03Stacked
Three seats each pass a per‑market limit, yet all load on one swing.
A flat “25% max position” rule passes all three.

That is the world of funded trading platforms and other B-book event books. The risk is not "do we understand YES and NO." The risk is that the desk is short a correlated stack of outcomes while someone can still move a thin external mid.

I needed a risk layer that treated those as first-class problems. Not a notebook dump. Not a boolean "is this correlated?" flag. Something you can put in front of an order gate and defend with numbers a quant would actually check.

So I built one. Continuous correlation (rho or factor loadings). Liquidity-scaled position caps derived from a dual-book attack model. High-accuracy size cliffs. Multi-outcome group exposure. One trade gate that returns allow, reason, and details. Pure Python. No framework.

Then I stress-tested it the way you should stress-test risk code: a full tradable price band from 0.1¢ toward the top of the book, liquidity from $1k to $1M, multi-seed attack simulations, and a threshold grid that is not allowed to leak.

This is that story.

Three failure modes

Most retail risk UIs stop at max notional. That is necessary and not sufficient.

Manipulation. A B-book platform that marks to an external book creates a dual-book surface. Open large size on the B-book at mid. Push the real mid with personal capital. The B-book marks to the new mid and captures the full impact. Personal pays VWAP and reversion. If B-book size is not capped against liquidity, the attack is positive EV.

High-accuracy farming. At 50¢ an efficient market is roughly fair before fees. At 90¢ the same statement is only true if true probability equals price. If the market is soft, the book is short a yield farm. Even when fair, path risk into a tight drawdown is brutal. A few 80¢ losers erase a month of small wins. In practice we tighten size hard by 80¢ and especially at 90¢, even when the venue can quote finer tops like 99.9¢.

Correlation. Binary "correlated / not" fails on real politics and sports. Neighboring districts share a swing factor without being identical. A nominee YES and a party YES are linked hard but not one-to-one. You need a score, or better, factor loadings.

Everything below is designed around those three.

The dual-book attack

Assume a B-book that marks to an external order book. The platform is the counterparty. It does not need to place the trader's order on the real CLOB. That is the whole point of B-booking: internalize the trade, still use public prices.

Fig 02The dual-book attack
PUBLIC CLOBB-BOOK46¢44¢42¢40¢38¢36¢VWAPEXITpp+ΔSS = c·p·L0+ (S/p)·Δ− IMPACT− FEES= NETUNCAPPED
  1. Open on the B-book. Buy YES against the platform at mid p. No real CLOB fill. The desk is the counterparty.
  2. Push the external book. Buy the same side on the public market with personal capital. Mid walks by impact Δ.
  3. B-book marks. Internal mark jumps to p+Δ. B-book profit is about (S / p) × Δ while personal paid VWAP.
  4. Unwind personal. Sell after partial reversion. Fees and reversion usually dominate unless S is too large for L.
  5. Cap S. Size S at c × p × L. The B-book leg can no longer pay for the push, so the same attack nets below zero.
Dual-book attack when a B-book marks to an external market. The defense is not hoping reversion is high. It is capping S against price × liquidity.

The naive simulator models the personal leg as entering at the old mid and exiting above it. Then the attacker always "wins." That is wrong. Personal entry walks the book. Exit meets reversion. Fees are real.

With linear impact Δ ≈ Q / L, half-spread, round-trip costs around 3–4%, and reversion near 50%, attacker net is approximately:

net ≈ (S / p) × Δ − personal impact costs − fees

When reversion is about half, personal mid PnL collapses toward zero and costs dominate. For the B-book leg, substitute the liquidity cap S = c · p · L into the mark-to-market profit term:

S_max = min(account_cap, c × p × L)
c ≈ 0.03 × 0.85 = 0.0255

Fig 03Where 0.0255 comes fromScroll adds size
PUSH SIZE Q →01%2%4%3% COSTc = 2.55%S / (p·L), GATED—B-BOOK GAIN PER $1—ROUND TRIP PER $1—NET PER $1 PUSHED—UNCAPPED +0.00¢
Substitute S = c·p·L and the B-book earns c = 2.55¢ for every dollar pushed, against roughly 3¢ of round-trip cost. Under the cap, pushing harder only loses more.

That coefficient is the production default. It scales with price and liquidity. A 10¢ name on $25k depth is not allowed the same notional as a 60¢ name on $500k depth. Above 80¢, high-accuracy cliffs cut size further.

Impact itself is room-limited. You cannot push a 99¢ contract through a 50-point linear move. The model clamps Δ to the remaining distance to the top of the book. Without that clamp, extreme prices produce fake LEAKs. With it, the physics stay inside the probability simplex.

Fig 04Impact is room-limited
OUTSIDE THE SIMPLEX$1 · TOP0¢25¢50¢75¢40¢+-10¢ FAKEΔ 50¢80¢+30¢ FAKEΔ 20¢99¢+49¢ FAKEΔ 1¢Δ = min(Q / L, 1 − p)
The same 50-point push at three prices. Without the clamp, a 99¢ name gains room that does not exist and the grid reports a fake LEAK.

Public venues can quote very fine ticks (down to 0.01¢ on some books, and tops near 99.9¢). Our risk defaults still treat 80¢ / 85¢ / 90¢ as the practical high-accuracy cliffs for size, because that is where yield-farm path risk and soft pricing start to matter for a B-book.

Evidence across the full price band

I do not want a grid that only covers "comfortable" mids. Event contracts trade from a tenth of a cent up through the nineties. The suite covers 0.001–0.99 and liquidities from $1k to $1M.

Threshold grid at reversion 0.5: 171 cells, 171 OK, 0 LEAK.

Attack simulations under the cap: 32 seeds × 200 attempts, prices log-uniform on 0.1¢–99¢, liquidity log-uniform on $1k–$1M, random reversion 0.3–0.7.

Attacker PnL per attack32 seeds × 200
−$3k−$2k−$1k0+$1k+$2k+$3kUNCAPPED MEAN +$2,654MEAN −$1,07713.5% WINP10 −$3,352MEDIAN −$752P90 +$497
StatisticValue
Seeds × attacks32 × 200
Price range0.1¢ – 99¢
Liquidity range$1k – $1M
Mean attacker PnL (capped)-$1,077
Median attacker PnL-$752
P10 / P90-$3,352 / +$497
Attacker win rate13.5%
Uncapped mean (baseline)+$2,654
Threshold grid171 / 171 OK · 0 LEAK

Uncapped baseline (same family of attacks, no size formula): mean attacker PnL is positive (about +$2.7k per seed mean). With the cap: mean is -$1,077, median -$752, seed-median-of-means firmly negative. Lucky wins still happen (about 14% of attacks). EV is what matters.

The cap curve is the other half of the story. Below the high-accuracy region, max B-book size rises with price × liquidity. At 80¢ / 85¢ / 90¢ the cliffs flatten the curve on a $5k account.

Fig 05Max B-book size, $5k account99¢
HIGH-ACCURACY CLIFFS$1$10$100$1k0.1¢1¢10¢50¢80¢90¢99¢PRICE, LOG-ODDS →25% OF ACCOUNT · $1,2505%3%2%$10k$50k$100k$500k
Max allowed B-book size on a $5k account across 0.1¢–99¢. Below about 80¢ the curve is liquidity × price. Above 80¢ the high-accuracy caps flatten it. Price on log-odds, size on a log scale.

And the attack surface as a heatmap: best attacker net under the cap. Every cell is negative.

Fig 06Attack surface under the cap171 / 171 OK · 0 LEAK
$1k$5k$10k$25k$50k$100k$250k$500k$1MPRICE0.1¢−3.4−10.2−16.8−30.0−41.5−51.5−42.6−39.0−36.90.5¢−5.9−16.8−23.7−31.8−35.9−38.5−40.2−40.8−41.11¢−9.4−23.7−30.0−35.9−38.5−39.9−40.8−41.1−52.62¢−14.8−30.0−34.8−38.5−39.9−40.7−41.1−52.6−60.05¢−23.7−35.9−38.5−33.7−34.2−34.5−31.1−62.2−68.510¢−30.0−38.5−39.9−19.3−19.5−24.9−34.8−68.5−47.615¢−33.0−28.5−29.2−13.8−13.9−26.5−37.6−54.7−40.220¢−34.8−22.7−23.2−10.9−13.9−27.8−39.7−47.6−36.530¢−26.4−16.4−16.6−7.9−15.0−30.1−35.0−40.2−32.740¢−21.4−12.9−13.1−8.0−16.1−32.1−32.6−36.5−30.850¢−18.1−10.8−10.9−8.5−17.0−33.7−31.1−34.2−29.660¢−15.7−9.3−9.4−9.0−18.0−32.4−30.1−32.7−28.970¢−13.9−8.3−8.3−9.5−19.0−31.4−29.4−31.6−28.380¢−12.6−7.4−7.6−6.5−7.3−7.7−6.4−6.5−5.785¢−12.0−7.1−7.9−5.6−6.0−6.2−5.1−5.1−4.690¢−11.4−7.1−8.3−5.8−6.0−6.1−5.1−5.1−4.595¢−10.2−7.0−8.1−5.7−5.9−6.0−5.0−5.0−4.598¢−9.5−5.5−6.9−5.6−5.8−5.9−5.0−5.0−4.599¢−9.2−6.0−6.0−5.3−5.6−5.8−5.0−5.0−4.5
Best attacker net, $ · reversion 0.5Dashed: 80¢ cliffs begin
Best attacker net ($) under the cap, by price and liquidity
Price$1k$5k$10k$25k$50k$100k$250k$500k$1M
0.1¢−3.4−10.2−16.8−30.0−41.5−51.5−42.6−39.0−36.9
0.5¢−5.9−16.8−23.7−31.8−35.9−38.5−40.2−40.8−41.1
1¢−9.4−23.7−30.0−35.9−38.5−39.9−40.8−41.1−52.6
2¢−14.8−30.0−34.8−38.5−39.9−40.7−41.1−52.6−60.0
5¢−23.7−35.9−38.5−33.7−34.2−34.5−31.1−62.2−68.5
10¢−30.0−38.5−39.9−19.3−19.5−24.9−34.8−68.5−47.6
15¢−33.0−28.5−29.2−13.8−13.9−26.5−37.6−54.7−40.2
20¢−34.8−22.7−23.2−10.9−13.9−27.8−39.7−47.6−36.5
30¢−26.4−16.4−16.6−7.9−15.0−30.1−35.0−40.2−32.7
40¢−21.4−12.9−13.1−8.0−16.1−32.1−32.6−36.5−30.8
50¢−18.1−10.8−10.9−8.5−17.0−33.7−31.1−34.2−29.6
60¢−15.7−9.3−9.4−9.0−18.0−32.4−30.1−32.7−28.9
70¢−13.9−8.3−8.3−9.5−19.0−31.4−29.4−31.6−28.3
80¢−12.6−7.4−7.6−6.5−7.3−7.7−6.4−6.5−5.7
85¢−12.0−7.1−7.9−5.6−6.0−6.2−5.1−5.1−4.6
90¢−11.4−7.1−8.3−5.8−6.0−6.1−5.1−5.1−4.5
95¢−10.2−7.0−8.1−5.7−5.9−6.0−5.0−5.0−4.5
98¢−9.5−5.5−6.9−5.6−5.8−5.9−5.0−5.0−4.5
99¢−9.2−6.0−6.0−5.3−5.6−5.8−5.0−5.0−4.5
Best attacker net under the B-book size cap across 0.1¢–99¢ and $1k–$1M liquidity, at reversion 0.5. Every one of the 171 cells is negative.

That is the bar I care about: not "we thought about manipulation," but here is the grid, here are the seeds, here is the uncapped control.

High-accuracy cliffs

Even without a dual-book attack, near-certain contracts are awkward inventory for a B-book. Path risk and soft pricing both matter. Defaults:

PriceMax size
Under 80¢Normal book limits
80¢+5% of account
85¢+3% of account
90¢+2% of account
Fig 07One loss, many wins
80¢1 LOSS = 4 WINSSTAKE ≤ 5%85¢1 LOSS ≈ 5.7 WINSSTAKE ≤ 3%90¢1 LOSS = 9 WINSSTAKE ≤ 2%99¢1 LOSS = 99 WINSSTAKE ≤ 2%100 TRADES →
A hundred trades of a YES position at fair odds, PnL per contract. Every row climbs about as high and falls as far; near certainty the climb just takes more wins. The cliffs cap the stake, so they cap the fall.

Policy choice on top of sizing: treat 80¢+ as hedge-required if you can transfer risk (A-book that slice). The size cliffs are the floor either way.

Correlation is continuous

Take two patterns that show up constantly on public books.

House seats, same state, same cycle. Dem YES on neighboring districts loads on a midterm swing. Not independent. Not identical. Something like ρ ≈ 0.35–0.50 is honest. Binary "yes" over-blocks multi-district books. Binary "no" lets a trader stack the same factor three times.

Person winner vs party winner. Nominee YES and same-party YES: high but not 1.0. Opposing party: offsetting. Newsom does not load on the Republican factor the way Vance does. He loads on the Democratic side.

Factor form (independent factors for the default engine):

E_f = Σ size_i × side_i × loading_i,f
effective = √(Σ E_f²)

Pairwise form when you only have a rho table:

Var = wᵀ Σ w
effective = √(max(Var, 0))

Cap effective exposure (default 20% of account). Same-direction stacks burn budget. Offsets shrink it.

Fig 08Correlation is continuous
−0.850.901.001.000.80pres_2028_Rpres_2028_DVance YESPERSONParty R YESPARTYParty D YESPARTYNewsom YESPERSONBINARY NOBINARY YESCONTINUOUSHouse seats3 × $400 · ρ 0.40$930ALLOWNominee + same party2 × $500$1,074BLOCKNominee + opposing party2 × $500$50ALLOW$0$500$1,000 · 20% CAPEFFECTIVE EXPOSURE
Newsom is not wired into the Republican factor; he loads on Dem with Party D, while Vance and Party R stack on R. Against the 20% cap on a $5k account, a binary flag lands on the wrong side in both directions.

Discovery of rho is separate from enforcement. Hand labels, rules, later a model. The engine only needs loadings or a matrix. Do not block shipping risk on perfect ontology.

The gate

One function sits in front of every order. It runs a fixed sequence of checks and returns a reason you can log or show in a UI.

First it rejects if the account already has too many open positions, or if the market fails min liquidity / min 24h volume. Then it computes the liquidity-scaled max size (price × depth × 0.0255, floored by account and high-accuracy cliffs) and rejects if the order is larger. Then per-trade size, per-market concentration, and multi-outcome group exposure. Finally it recomputes effective correlated exposure with the candidate added and rejects if that would breach the portfolio cap.

Fig 09The gate, in order
ORDERYES 42¢ · $300 · $120k deep01 Open positionsmax open02 Min liquidity$10,00003 Min 24h volume$5,00004 Liquidity-scaled size0.0255·p·L05 Per trade25%06 Per market10%07 Multi-outcome group15%08 Correlated exposure20% eff.ALLOWcheck_trade(order) →allow—reason—details—log42¢ $300allow12¢ $400reject · above liquidity cap91¢ $250reject · 90¢ cliff55¢ $300reject · correlated exposure30¢ $200reject · market liquidity
Every order runs the same fixed sequence and stops at the first check it fails. What comes back is allow, a reason you can log or show, and the details. Orders here are illustrations; their limits come from the defaults on a $5k account.

That is the whole production surface for the open-source core: pure functions, no I/O, no product branding. You wire check_trade into whatever stack takes orders for a B-book event platform.

Defaults

Opinionated starting points. Override them. Calibrate on your own attack sims and loss data.

KnobDefault
Max position25% of account
Per market10%
Per multi-outcome group15%
Effective correlated exposure20%
Min liquidity$10,000
Min 24h volume$5,000
Manip coefficient0.0255 × price × liquidity
High-accuracy 80¢ / 85¢ / 90¢5% / 3% / 2% of account

The primitives are the caps, the factor math, and the evidence that the caps hold on a full price grid. A firm-wide VaR dashboard, continuous futures margin, and automated correlation discovery are products you build around those primitives, not substitutes for them.

What I would check if I were reviewing this

If someone handed me a risk engine for event contracts, I would ask:

  • Do you model dual-book manipulation with VWAP and reversion, or only B-book mark-to-market?
  • Does your price grid include the thin tails (0.1¢ and the high nineties), or only comfortable mids?
  • Is impact room-limited near the top of the book?
  • Is correlation continuous?
  • Can I see multi-seed EV under the cap and an uncapped control?
  • Are the constants justified?

This package is my answer to those questions. The simulations and threshold grid are in-repo. The charts above are the same numbers.

If you B-book public event markets, start with liquidity × price caps and continuous correlation. Binary flags will either over-trade or over-block. Thin books without a manip formula will get farmed. Extreme prices without room-limited impact will lie to you.

That is the work.