Skip to main content

Exposure Netting & AC Calculation — Test Cases

Date: 2026-04-08 Base: dev at PR #344 merge Status: Pending team review


Terminology

SymbolMeaning
PmaxVector of max possible PnL per outcome in a market
Emaxmin(Pmax) — worst-case PnL across all outcomes
ACAvailable Credit — tracks how much the user can bet
ACrRequired exposure for a new bet: Emax - min(Pmax*)
RCReleased capital on match/cancel: min(Pmax) - Emax
PoPnL vector for a bet: how much user gains/loses per outcome

PnL Vector Reference

Back bet on outcome k, stake S, odds O:
P[k] = S × (O - 1) ← profit if k wins
P[j] = -S (j ≠ k) ← loss if any other outcome wins

Lay bet on outcome k, stake S, odds O:
P[k] = -S × (O - 1) ← loss (liability) if k wins
P[j] = S (j ≠ k) ← profit if any other outcome wins

N. Basic Netting (2-Outcome Market)

N1: Single back bet — full lock (no netting possible)

  • Setup: AC=1000, empty market {o₁, o₂}
  • Action: Back o₁, stake=500, odds=2.0. PnL: (+500, -500)
  • Compute:
    • Pmax* = (0, 0) + {min(500,0), min(-500,0)} = (0, -500)
    • Emax* = min(0, -500) = -500
    • ACr = 0 - (-500) = 500
  • Assert:
    • Bet accepted. AC = 1000 - 500 = 500.
    • Pmax = (0, -500). Emax = -500.

N2: Opposing bet — zero additional exposure

  • Setup: From N1. AC=500, Pmax=(0,-500), Emax=-500
  • Action: Back o₂, stake=300, odds=3.0. PnL: (-300, +600)
  • Compute:
    • Pmax* = (0+(-300), -500+0) = (-300, -500)
    • Emax* = min(-300, -500) = -500
    • ACr = -500 - (-500) = 0
  • Assert:
    • Bet accepted with zero additional lock. AC stays 500.
    • Pmax = (-300, -500). Emax = -500.

N3: Match releases capital

  • Setup: From N2. N2's bet matches.
  • Action: Match event for N2 bet.
  • Compute:
    • Pmax += {max(-300,0), max(600,0)} = {0, 600}
    • Pmax = (-300+0, -500+600) = (-300, 100)
    • RC = min(-300, 100) - (-500) = -300 + 500 = 200
  • Assert:
    • AC = 500 + 200 = 700. Emax = -300.

N4: Notion CASE 2 — hedge costs nothing unmatched

  • Setup: AC=100, Pmax=(-100, 1000), Emax=-100
  • Action: New bet with PnL (+70, -100)
  • Compute:
    • Pmax* = (-100+0, 1000+(-100)) = (-100, 900)
    • Emax* = -100. ACr = -100-(-100) = 0
  • Assert: AC stays 100.

N5: From N4 — match releases 70

  • Setup: From N4, bet matches.
  • Compute:
    • Pmax += {70, 0} → (-30, 900)
    • RC = -30 - (-100) = 70
  • Assert: AC = 100 + 70 = 170. Emax = -30.

N6: Notion CASE 1 — same direction costs full stake

  • Setup: AC=100, Pmax=(-100, 1000), Emax=-100
  • Action: New bet with PnL (-70, +100)
  • Compute:
    • Pmax* = (-100+(-70), 1000+0) = (-170, 1000)
    • Emax* = -170. ACr = -100-(-170) = 70
  • Assert: AC = 100 - 70 = 30.

N7: From N6 — match releases nothing

  • Setup: From N6, bet matches.
  • Compute:
    • Pmax += {0, 100} → (-170, 1100)
    • RC = -170 - (-170) = 0
  • Assert: AC stays 30.

C. Cancel Tests

C1: Cancel only unmatched bet — full release

  • Setup: AC=500, Pmax=(0,-500), Emax=-500. One unmatched bet: back o₁, PnL (+500,-500).
  • Action: Cancel the bet.
  • Compute:
    • Pmax -= {min(500,0), min(-500,0)} = {0, -500}
    • Pmax = (0-0, -500-(-500)) = (0, 0)
    • RC = min(0,0) - (-500) = 500
  • Assert: AC = 500 + 500 = 1000. Back to initial state.

C2: Cancel one of two bets

  • Setup: From N2 state. AC=500, Pmax=(-300,-500). Two unmatched bets.
  • Action: Cancel N2 bet (PnL: -300, +600).
  • Compute:
    • Pmax -= {min(-300,0), min(600,0)} = {-300, 0}
    • Pmax = (-300-(-300), -500-0) = (0, -500)
    • RC = min(0,-500) - (-500) = 0
  • Assert: AC stays 500. State = N1 state.

M. 3-Outcome Market Tests

M1: First bet on 3-outcome market

  • Setup: AC=1000, market {Home, Draw, Away}
  • Action: Back Home, stake=100, odds=3.0. PnL: (+200, -100, -100)
  • Compute:
    • Pmax* = (0, -100, -100). Emax* = -100. ACr = 100.
  • Assert: AC = 900.

M2: Second bet — different outcome

  • Setup: From M1. AC=900.
  • Action: Back Draw, stake=100, odds=4.0. PnL: (-100, +300, -100)
  • Compute:
    • Pmax* = (0+(-100), -100+0, -100+(-100)) = (-100, -100, -200)
    • Emax* = -200. ACr = -100-(-200) = 100.
  • Assert: AC = 800.

M3: Third bet — covers last outcome (free!)

  • Setup: From M2. AC=800.
  • Action: Back Away, stake=100, odds=5.0. PnL: (-100, -100, +400)
  • Compute:
    • Pmax* = (-100+(-100), -100+(-100), -200+0) = (-200, -200, -200)
    • Emax* = -200. ACr = -200-(-200) = 0
  • Assert: AC stays 800. Third bet is FREE (worst case unchanged).

M4: All 3 bets match — capital released

  • Setup: From M3. All bets match.
  • Compute: Each match adds best-case:
    • Match bet 1: +{200,0,0} → (-200+200, -200, -200) = (0,-200,-200). RC = -200-(-200) = 0.
    • Match bet 2: +{0,300,0} → (0, 100, -200). RC = -200-(-200) = 0. Wait, Emax was -200 before.
      • Actually: Emax before this match = min(0,-200,-200) = -200. After: min(0,100,-200) = -200. RC = -200-(-200) = 0.
    • Match bet 3: +{0,0,400} → (0, 100, 200). RC = min(0,100,200) - (-200) = 0 + 200 = 200
  • Assert: AC = 800 + 200 = 1000. Only lost 0 net (guaranteed profit in 2 of 3 outcomes, worst case 0).

L. Lay Bet Netting Tests

L1: Lay bet — locks liability

  • Setup: AC=1000, 2-outcome market.
  • Action: Lay o₁, stake=100, odds=3.0. PnL: (-200, +100)
  • Compute:
    • Pmax* = (-200, 0). Emax* = -200. ACr = 200.
  • Assert: AC = 800.

L2: Hedge with back — zero additional cost

  • Setup: From L1. AC=800.
  • Action: Back o₁, stake=200, odds=3.0. PnL: (+400, -200)
  • Compute:
    • Pmax* = (-200+0, 0+(-200)) = (-200, -200)
    • Emax* = -200. ACr = -200-(-200) = 0
  • Assert: AC stays 800. Hedging is free.

L3: Both match — capital released

  • Setup: From L2. Both match.
  • Compute: Add best-cases:
    • Match L1: +{0,100} → (-200, -100). RC = min(-200,-100)-(-200) = -200+200 = 0.
    • Match L2: +{400,0} → (200, -100). RC = min(200,-100)-(-200) = -100+200 = 100
  • Assert: AC = 800 + 100 = 900. Net position: (+200,-100), guaranteed profit on o₁, small loss on o₂.

R. Rejection Tests

R1: Insufficient AC for basic bet

  • Setup: AC=50, no existing bets.
  • Action: Back bet, stake=100, odds=2.0. ACr=100.
  • Assert: REJECT. AC=50 < ACr=100.

R2: Insufficient AC even with partial netting

  • Setup: AC=50, Pmax=(-100, 500), Emax=-100.
  • Action: New bet PnL (-70, +100). ACr = -100-(-170) = 70.
  • Assert: REJECT. AC=50 < ACr=70.

R3: Zero-balance rejection

  • Setup: AC=0.
  • Action: Any bet.
  • Assert: REJECT (unless bet fully nets and ACr=0... edge case to decide).

S. Settlement Integration

S1: Market settles — Pmax cleared

  • Setup: Market with 2 matched bets, Pmax=(-300,100), AC=700.
  • Action: Market o₂ wins. Player's PnL: settle at +100.
  • Assert:
    • Pmax for this market → zeroed/removed
    • Take delta: +100 (or whatever actual PnL is)
    • AC now reflects full balance (no locked exposure for this market)

S2: Partial settlement — Pmax adjusted

  • Setup: 2 bets on same market. Bet 1 settles (win), bet 2 still open.
  • Assert:
    • Pmax adjusted to reflect only bet 2's PnL vector
    • Take delta from bet 1's PnL applied
    • AC reflects released capital from settled bet

S3: Void — treated as cancel

  • Setup: Market with netted bets. One bet voided.
  • Assert: Same as cancel — Pmax adjusted, RC released to AC.

E. Edge Cases

E1: Bet on market with unknown number of outcomes

  • Scenario: Player props — many possible outcomes.
  • Question: How do we handle growing Pmax vectors?

E2: Concurrent bets on same market

  • Scenario: Two bet requests arrive simultaneously for same user+market.
  • Assert: Only one processes at a time (per-market per-user lock). No double-spend.

E3: Partially matched bet

  • Scenario: Bet stake=100, only 60 matched.
  • Assert: 60 contributes full PnL to Pmax. 40 contributes only worst-case.

E4: AC goes negative due to settlement (edge)

  • Scenario: AC was locked by netting. Settlement results in loss.
  • Assert: AC reflects actual balance. Cannot go below 0 for new bets.

Comparison: Current vs Proposed Lock Amounts

ScenarioCurrent LockProposed LockDiff
1 back bet (S=500, O=2.0)5005000%
2 same-dir back bets (500+300)8008000%
2 opposing back bets (500+300) on 2-way800500 (at accept) → 200 (at match)37-75%
Lay 100 @ 3.0, then back 200 @ 3.040020050%
3-way: 100+100+100 covering all outcomes300200 (at accept) → 0 (all matched)33-100%
Hedge after existing betFull new stake0100%