blended-spread-position draft
When you hold positions across multiple strikes of the same spread or total market, your true exposure is an outcome-by-outcome payoff curve — not a sum of stakes
- Tags
- spread total position hooks key-numbers payoff exposure
- Vocabulary
- blended_position
- Combined exposure across two or more legs at different strikes of the same spread or total market. The economically correct view is the sum-by-outcome payoff vector, not the sum-of-stakes.
- strike
- The line at which a spread or total is priced — e.g. -3, -3.5, +7. Each strike is a different bet, even on the same market.
- spread
- A bet on margin of victory rather than who wins outright. -3.5 means 'this team must win by more than 3.5 points'; the line equalizes the matchup.
- total
- A bet on the combined score of both teams (e.g. 'over 47.5'). Independent of which team wins.
- hook
- A range of final-margin (or total) outcomes between two strikes of the same market where you collect on one leg and don't lose (or only partially lose) on another. Hooks are the structural 'free' value of holding a blend across strikes.
- key_number
- A final-margin value that occurs disproportionately often in a sport. NFL: 3 and 7 (most common margins of victory). NBA: smaller hooks. MLB run lines are mostly 1 or 2. The half-point either side of a key number is more valuable than the half-point elsewhere.
- push
- An outcome that lands exactly on a whole-number strike, refunding the bet. -3 has a non-zero push probability when the actual margin is 3; -3.5 has none.
- push_protection
- Holding a leg at a strike that returns your stake when the game lands on a key margin, while another leg in the same blend pays out. The integer-strike leg pushes (refund) instead of losing, turning what would be a dead-zone outcome into a positive one.
- half_point
- The .5 in a strike like -3.5 — distinguishes from the integer line -3. A half-point eliminates the push at the integer margin; the price difference between -3 and -3.5 is roughly the market's estimate of the push probability.
A blended spread position is what you have when you've taken bets on the same game (or total) across two or more different strike points. Every individual strike is a separate bet with its own break-even — and once you hold more than one, the right way to think about your exposure is not the sum of the individual stakes, but the dollar P&L as a function of the actual final margin (or total). That P&L is a step function with jumps at each strike, and the steps create regions where you can collect on one leg without giving back on another. Those favorable regions are called hooks, and they're the structural reason a blended position is not the same bet as a single big bet at one strike.
This is the entry the user has flagged as a personal source of confusion. The trick is to stop adding stakes and start thinking in terms of the payoff curve — for every plausible final margin, what does my portfolio pay?
For typical bet sizes (small relative to bankroll), the payoff curve is informational; for very large positions or tight risk tolerance, materializing the curve becomes important. This is essentially correlated-risk territory and may be worth revisiting in the future.
Why this isn't a sum-of-stakes
If you bet $100 on Patriots -3.5 at one price and $100 on Patriots -7 at another, your "total stake" is $200 and your "total maximum win" is whatever the two payouts add to. That number is correct as an answer to how much money is in play, but it is the wrong answer to what is my position. The reason: the two bets resolve on different events.
- Patriots -3.5 wins iff actual margin > 3.5 (i.e. ≥ 4 if margins are integer).
- Patriots -7 wins iff actual margin > 7 (i.e. ≥ 8 if margins are integer).
So if the Patriots win by exactly 5, leg 1 (-3.5) wins and leg 2 (-7) loses. If they win by exactly 9, both win. If they win by exactly 2, both lose. The position has three different P&L outcomes depending on which range the margin lands in. That's the blend.
Now suppose you flip leg 2: instead of Patriots -7, you bet $100 on Patriots +3 (the other side at a closer strike). Then:
- Margin ≥ 4 → leg 1 wins, leg 2 loses.
- Margin = 3 → leg 1 loses (margin not strictly greater than 3.5), leg 2 pushes (refunded, no profit).
- Margin ≤ 2 → leg 1 loses, leg 2 wins.
The two legs hedge each other across most of the outcome space, but in the narrow band between the strikes (margin of 3, in this example), you have the asymmetric outcome where you partially recover (the push). On a wider hook — say Patriots -7 vs. Patriots +3 — you'd have a middle: margin between 4 and 6 inclusive, both legs win. That's the structural value of the blend.
The math: outcome-by-outcome P&L
Take any blend of N legs on the same market. Define M as the realized final margin (or total). For each leg i, define a payoff function f_i(M) that returns the dollar P&L of that leg given M:
f_i(M) = + win_amount_i if leg i wins given M
= − stake_i if leg i loses given M
= 0 if leg i pushes given M
The blended P&L is F(M) = Σ f_i(M). To analyze the position, plot or tabulate F(M) over the plausible range of M. The interesting features of the plot are:
- Step locations: at each strike, exactly one leg flips state (win → loss, or loss → win). The total
F(M)jumps at that strike by the sum of the flipping leg's(win_amount + stake). - Hooks: any region of
MwhereF(M)is positive. Hooks can span between strikes (a "middle"), or extend past the most extreme strike (when a leg covers the tail). - Break-even points: values of
MwhereF(M) = 0. There can be more than one. The position is profitable on the union of intervals whereF(M) > 0. - Push values: integer
Mequal to a whole-number strike. At those points the corresponding leg returns its stake (net zero, not negative); the rest of the P&L still applies.
The discrete structure of margins (integers in most sports, half-integers in MLB run lines for some events) means F(M) is a step function over the integers, not a continuous curve. That makes it easy to enumerate.
Why key numbers and half-points matter
Final margins in real games are not uniform. NFL margins cluster at 3 and 7 (and to a lesser extent 10, 14, 6); NBA at smaller numbers but more diffusely; MLB run lines mostly land at 1 or 2. The probability mass on each integer margin is part of the blend's expected value:
EV(blend) = Σ_M P(margin = M) × F(M)
If most of the probability mass on positive-F(M) outcomes lands on key numbers, the blend is more valuable than its outcome-count would suggest. Conversely, a hook between two strikes that contains a key number is much more valuable than a hook of the same width elsewhere.
Half-points matter because they shift a strike past a key number. Patriots -3 has push probability when margin is exactly 3; Patriots -3.5 doesn't. The price difference between -3 and -3.5 reflects roughly the probability mass at margin = 3 (a sizable fraction in NFL, near zero elsewhere). When you blend a -3 with a -3.5, you're effectively buying the push protection on the -3 leg by adding the -3.5 leg.
Kairos's position_agg.py implementation
Kairos's blending happens in kairos/core/position_agg.py::aggregate_spread_total_sides. The function aggregates Kalshi positions and manual book lots for a single spread/total bucket (the bucket being one market, e.g. "Patriots -3.5 spread market"). It returns six totals: yes_qty, yes_stake, yes_win, no_qty, no_stake, no_win.
Important: this aggregator is per-bucket, not per-strike. Within a single bucket, all positions share the same strike. Kairos's data model represents different strikes as different buckets (spread_-3, spread_-3.5, etc.), so the cross-strike blend is computed at the consumer level by combining results across multiple aggregate_spread_total_sides calls — one per strike — into a payoff curve.
What aggregate_spread_total_sides itself does:
- Sums
qty,stake, andwinseparately for the YES side and the NO side of the bucket. - For Kalshi rows, it uses
parse_odds_any(f"p{int(px)}")(the "p" prefix = no-fee), since spread/total buckets are fee-exempt on Kalshi today (is_fee_exempt_bucketreturns True for them). - For manual book lots, it routes them to YES or NO based on
_source_sideand theyes_source_sideparameter, which says which side panel ("A" or "B") corresponds to Kalshi YES for this market. This is determined dynamically bydetect_yes_source_side, which inspects the panel'skpos_rowsand finds the side that holds a Kalshi YES position. - Returns the dollar-level totals callers use to compute cross-portfolio P&L:
pnl_if_yes = yes_win − no_stake
pnl_if_no = no_win − yes_stake
This is the binary payoff for a single strike: "if YES wins, my net P&L is yes_win − no_stake; if NO wins, my net P&L is no_win − yes_stake." For a blended cross-strike position, the consumer iterates this per strike and sums the P&Ls per outcome — but only the outcomes that the strike resolves on. The actual payoff function F(M) over margins is the consumer's responsibility; aggregate_spread_total_sides provides the per-strike binary inputs.
There is no single "blended-position" object in Kairos's core — the blend is implicit in the per-bucket aggregates and the operator's mental model. The header table displays per-bucket exposure (one row per strike); the operator looks across rows to reason about the blend.
Worked example: a three-strike NFL blend
Patriots favored over the Jets. Operator has three legs:
| Leg | Side | Strike | Stake | Decimal odds | Win on this leg |
|---|---|---|---|---|---|
| 1 | Patriots | -3.5 | $100 | 1.91 (-110) | $91 |
| 2 | Jets | +7 | $50 | 1.91 (-110) | $45.50 |
| 3 | Patriots | -10 | $50 | 2.10 (+110) | $55 |
(Simplified; assume no pushes since -3.5 and -10 don't push, and +7 pushes only at margin = -7 from Jets perspective i.e. Patriots win by 7. We'll handle the push.)
Build F(M) over the plausible range of Patriots' margin of victory:
| Margin M | Leg 1 (-3.5) | Leg 2 (Jets +7) | Leg 3 (-10) | F(M) |
|---|---|---|---|---|
| Pats win by ≥ 11 | +91 | -50 | +55 | +96 |
| Pats win by 10 (push leg 3) | +91 | -50 | 0 | +41 |
| Pats win by 8 to 9 | +91 | -50 | -50 | -9 |
| Pats win by 7 (push leg 2) | +91 | 0 | -50 | +41 |
| Pats win by 4 to 6 | +91 | +45.50 | -50 | +86.50 |
| Pats win by 3 | -100 | +45.50 | -50 | -104.50 |
| Pats win by 1 to 2 | -100 | +45.50 | -50 | -104.50 |
| Pats lose or tie | -100 | +45.50 | -50 | -104.50 |
(Leg 2 wins at margin ≤ 6 — Jets +7 covers any Patriots margin of victory < 7. Leg 2 pushes at margin = 7 exactly. Leg 2 loses at margin ≥ 8.)
Notice that the bottom three rows all collapse to the same −$104.50 dead zone — that's because legs 1 and 3 are losers in all of those outcomes, and only leg 2 (Jets +7) cashes. Distinct game outcomes can map to the same combined P&L when only one leg is contributing.
Notice the +$41 hook at margin 7 — that's the push-protection on the Jets +7 leg paying off. Without that push-protected strike, this margin would fall in the dead zone at −$50. This is the lesson at the top of the entry made concrete: half-points and integer strikes have different hook structures, and the difference matters for combined positions.
Reading the curve:
- The big hook is at margin 4 through 6. Patriots win by a touchdown or a field goal more than 3 — leg 1 cashes, leg 2 cashes (Jets covered the +7), leg 3 still loses. Net: +$86.50.
- A second hook at margin 7. Leg 1 cashes (Pats won by more than 3.5), leg 2 pushes (refund of $50 stake), leg 3 still loses. Net: +$41.
- A third hook at margin 10. Leg 1 cashes, leg 2 loses, leg 3 pushes. Net: +$41.
- A fourth hook at margin ≥ 11. All three legs settle: leg 1 wins, leg 2 loses, leg 3 wins. Net: +$96.
- The dead zones: margin 8-9 (leg 1 wins, but lost more on legs 2+3 than leg 1 paid); margin ≤ 3 (leg 1 loses, only the +7 cash on leg 2 partially recovers).
Notice what happens to the position when you mistakenly think about it as "three bets totaling $200 stake with $191.50 max single-leg win." That summary tells you nothing about the curve. The actual position is profitable on five distinct outcome bands (three of them sizable hooks) and unprofitable in two bands. To compute true EV, you need probability mass on each integer margin and you sum P(M) × F(M). NFL margin distributions concentrate mass on 3, 7, 10, 14 — and three of those four numbers fall in unprofitable bands of this particular blend. That is information the sum-of-stakes view hides entirely.
Gotchas
- Sum-of-stakes underestimates risk and overstates "diversification." Two legs at the same strike on the same side don't diversify anything — they just double the directional exposure. Different strikes diversify outcome bands, not money at risk. The naive sum hides which outcomes are catastrophic.
- Key numbers in NFL are 3 and 7; in NBA there are no strong key numbers; in MLB the run line is mostly 1 or 2. A hook centered on a key number is dramatically more valuable than a hook of equal width centered elsewhere. EV calculations on blends must use the empirical margin distribution, not a uniform-across-integers approximation.
- Half-points eliminate pushes; whole-number strikes don't. -3 and -3.5 are different bets, and the half-point cost (typically 5-10 cents in price) prices the push probability. When blending a -3 with another leg, model the push at integer 3 explicitly; the leg returns its stake (zero P&L on that leg), not zero in the position.
- Kairos's
aggregate_spread_total_sidesis per-bucket, not cross-strike. If you call it once and treat the result as your "total spread exposure," you'll be looking at one strike only. To see the blend, call it per strike and combine. - Kalshi spread/total buckets are fee-exempt today; game buckets are not. The aggregator handles this with
is_fee_exempt_bucket; the operator should not assume Kalshi fees are always zero, only that they're zero on these specific buckets. If Kalshi changes its fee schedule, the aggregator's "p"-prefix (no-fee) path becomes wrong. - The
pnl_if_yes/pnl_if_noformula collapses the bucket to a binary. It treats the bucket's outcomes as YES vs NO with no intermediate states. Pushes at the strike, half-point quirks, and three-way effects (win/push/lose at whole-number strikes) are not visible in this two-row summary. For a strike where pushes matter, the binary view is an approximation. - A "middle" between two strikes is structurally beautiful but rare in practice. The price gap between -3 and -7 is wide enough that getting both at favorable prices typically means catching line moves separately at different times. The middle exists; finding it priced into both legs is the hard part. Don't confuse "the math allows a middle" with "I can routinely capture a middle."
- Be careful about treating different strikes as equivalent. A -3 and a -7 are different bets even when both are "Patriots favored." The implied probabilities differ, the push-risk differs, and the EV differs per integer margin. The blend is a vector of separate bets, not a single fungible "Patriots position."
- Quantity is unitless across legs only when the venues quote the same payout structure. Kalshi pays $1 per contract; sportsbooks pay decimal-odds × stake. To compare leg sizes meaningfully, convert each to dollars-at-risk (stake) and dollars-of-payout (win), which is exactly what
aggregate_spread_total_sidesreturns. Don't compare contract counts across venues.
Open questions
- The push-handling in
aggregate_spread_total_sidesis implicit: pushes at integer strikes resolve to "NO win" or "YES win" depending on which side held the position, and the dollar amounts come from whatever the venue settled at. The aggregator doesn't have a separate "push" state. For markets where pushes are meaningful (NFL whole-number spreads, NBA totals at integers), this means the per-bucket binary view loses information; a more granular model would trackF(M)over the integer outcomes directly. - Half-point pricing (the price difference between -3 and -3.5, between 47.5 and 48 totals) is the market's revealed estimate of push probability at the integer. Whether Mimir should expose half-point-implied push probability as a derived quantity — useful for sizing the "buy push protection" decision — is a future-entry question. Kairos doesn't currently surface it.
- The worked example uses simplified per-leg payouts (-110 = 1.91, +110 = 2.10) and treats integer margins as the outcome space. Real markets quote prices that move and that include partial-game effects (overtime in NFL, extra innings in MLB) that may shift the empirical distribution slightly. Mimir's worked examples document the math, not the empirical distribution; consumers needing the latter should bring their own.
- Kalshi's spread/total buckets have a
_orig_sidefield and aside_lbl("YES"/"NO") which together withdetect_yes_source_sideresolve which team the YES contract represents. The mapping is per-market (Kalshi can have YES = home or YES = away depending on the market's setup). Whether the operator-side mental model aligns with Kalshi's YES designation in every blend is a real source of bugs; the aggregator is robust to it but human reasoning often isn't.