FA-61766 / Options payoff and settlement / Open access
Delta hedge order sized in lots: the hedge buys the exposure instead of offsetting it · case 01
Hedging doubles the delta instead of neutralizing it.
ROOT CAUSE
The target is +exposure.
VERIFIED REPAIR
Target the negative of the option delta exposure.
Unsuccessful approach: Forcing the target negative only works for net long delta books.
Case contract
Inputs positions [kind, signed contracts, absolute delta], multiplier, lot size and existing shares. Put delta is negative. Exposure = sum(sign*delta*contracts*multiplier); target shares = -exposure; the order is target - existing, rounded to whole lots with halves away from zero. Return the signed share quantity.
Why this case matters
Option expiry, exercise and settlement engines move cash and shares; a wrong branch misstates obligations.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(positions, multiplier, lot, existing):
exposure = Fraction(0)
for kind, c, d in positions:
sgn = -1 if kind == 'P' else 1
exposure += sgn * Fraction(str(d)) * c * multiplier
target = exposure
trade = target - existing
lots = trade / lot
n = math.floor(abs(lots) + Fraction(1, 2))
return int((n if lots >= 0 else -n) * lot)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression hedge direction 1', [[['C', 2, 0.5], ['P', 1, 0.25], ['P', 1, 0.35], ['C', 2, 0.6]], 10, 25, 100], -125], ['regression hedge direction 2', [[['P', 1, 0.25], ['C', 2, 0.6], ['P', -1, 0.5]], 100, 25, -250], 100], ['partial repair probe 1', [[['C', 2, 0.35], ['C', -3, 0.75], ['P', 10, 0.35]], 100, 100, -250], 800], ['partial repair probe 2', [[['P', 5, 0.35], ['P', 2, 0.5], ['C', 5, 0.25], ['P', 1, 0.75]], 100, 25, 0], 225], ['normal control 1', [[['P', 5, 0.75]], 10, 100, 100], -100], ['normal control 2', [[['C', 1, 0.25], ['C', 1, 0.6]], 10, 25, 0], 0], ['normal control 3', [[['P', 1, 0.5], ['P', -1, 0.35], ['C', 1, 0.35]], 10, 100, 100], -100], ['normal control 4', [[['P', -3, 0.75], ['C', -1, 0.05], ['C', -3, 0.6]], 10, 50, 0], 0]], [['regression hedge direction 1', [[['P', 1, 0.35]], 100, 50, 100], -50], ['regression hedge direction 2', [[['C', -3, 0.6], ['C', 5, 0.5], ['P', 5, 0.5]], 100, 100, 100], 100], ['partial repair probe 1', [[['P', 10, 0.6], ['C', 5, 0.05]], 100, 50, 75], 500], ['partial repair probe 2', [[['P', 5, 0.5], ['P', -3, 0.6], ['P', -1, 0.05]], 100, 100, 0], 100], ['normal control 1', [[['C', 5, 0.75]], 10, 100, 100], -100], ['normal control 2', [[['C', 2, 0.25]], 10, 100, 75], -100], ['normal control 3', [[['P', 1, 0.05], ['C', 10, 0.05]], 10, 25, 75], -75], ['normal control 4', [[['C', 5, 0.25], ['C', 5, 0.35]], 10, 100, 0], 0]], [['regression hedge direction 1', [[['C', 1, 0.35], ['C', -1, 0.35], ['C', -1, 0.35]], 100, 25, -250], 275], ['regression hedge direction 2', [[['C', 1, 0.5]], 100, 100, 0], -100], ['partial repair probe 1', [[['C', 5, 0.75], ['P', 5, 0.05], ['P', 5, 0.75]], 100, 50, 100], -100], ['partial repair probe 2', [[['P', 2, 0.75], ['P', 10, 0.35], ['P', 1, 0.05]], 100, 50, 75], 450], ['normal control 1', [[['C', 2, 0.5]], 10, 25, 0], 0], ['normal control 2', [[['P', 5, 0.05], ['C', -1, 0.6], ['P', 1, 0.75]], 10, 100, 75], -100], ['normal control 3', [[['P', 5, 0.05], ['P', -3, 0.25]], 10, 25, 0], 0], ['normal control 4', [[['C', 2, 0.05], ['P', 1, 0.75], ['P', 2, 0.75]], 10, 50, 100], -100]], [['regression hedge direction 1', [[['C', 1, 0.5], ['P', -1, 0.5], ['P', 5, 0.75], ['C', -3, 0.25]], 100, 25, 75], 275], ['regression hedge direction 2', [[['C', 1, 0.6], ['C', 2, 0.25]], 100, 100, 75], -200], ['partial repair probe 1', [[['P', 2, 0.25], ['P', -3, 0.75], ['C', 1, 0.6], ['P', 10, 0.75]], 100, 100, 0], 500], ['partial repair probe 2', [[['P', 1, 0.6], ['P', 5, 0.75], ['P', 5, 0.75], ['P', 10, 0.35]], 100, 50, -250], 1400], ['normal control 1', [[['C', 5, 0.35]], 10, 100, 75], -100], ['normal control 2', [[['C', -3, 0.35], ['C', 1, 0.5], ['P', 1, 0.35]], 10, 50, 0], 0], ['normal control 3', [[['C', 10, 0.05]], 10, 100, 0], 0], ['normal control 4', [[['C', 2, 0.5]], 10, 100, 100], -100]], [['regression hedge direction 1', [[['P', -3, 0.05]], 100, 25, -250], 225], ['regression hedge direction 2', [[['C', 5, 0.6], ['P', -3, 0.35], ['P', 2, 0.6]], 10, 25, -250], 225], ['partial repair probe 1', [[['P', 5, 0.6], ['C', -1, 0.05], ['C', 1, 0.25]], 10, 50, 0], 50], ['partial repair probe 2', [[['C', 1, 0.25], ['C', 1, 0.5], ['C', -1, 0.75], ['P', 10, 0.6]], 100, 100, 100], 500], ['normal control 1', [[['C', 5, 0.75], ['P', 10, 0.05]], 10, 100, 100], -100], ['normal control 2', [[['P', -3, 0.5]], 10, 100, 0], 0], ['normal control 3', [[['C', 1, 0.75], ['C', 1, 0.05], ['C', 2, 0.05], ['C', -1, 0.25]], 10, 100, 0], 0], ['normal control 4', [[['C', -3, 0.5], ['C', 2, 0.5], ['C', 2, 0.75]], 10, 25, 75], -75]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression hedge direction 1 | -75 | -125 | Failed |
| regression hedge direction 2 | 400 | 100 | Failed |
| partial repair probe 1 | -300 | 800 | Failed |
| partial repair probe 2 | -225 | 225 | Failed |
| normal control 1 | -100 | -100 | Passed |
| normal control 2 | 0 | 0 | Passed |
| normal control 3 | -100 | -100 | Passed |
| normal control 4 | 0 | 0 | Passed |
SHA-256 / 0d6daedfde58fff789b847d9ab7558ad3ba41797709aae38f4d87752c0c6c4f1
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(positions, multiplier, lot, existing):
exposure = Fraction(0)
for kind, c, d in positions:
sgn = -1 if kind == 'P' else 1
exposure += sgn * Fraction(str(d)) * c * multiplier
target = -abs(exposure)
trade = target - existing
lots = trade / lot
n = math.floor(abs(lots) + Fraction(1, 2))
return int((n if lots >= 0 else -n) * lot)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression hedge direction 1', [[['C', 2, 0.5], ['P', 1, 0.25], ['P', 1, 0.35], ['C', 2, 0.6]], 10, 25, 100], -125], ['regression hedge direction 2', [[['P', 1, 0.25], ['C', 2, 0.6], ['P', -1, 0.5]], 100, 25, -250], 100], ['partial repair probe 1', [[['C', 2, 0.35], ['C', -3, 0.75], ['P', 10, 0.35]], 100, 100, -250], 800], ['partial repair probe 2', [[['P', 5, 0.35], ['P', 2, 0.5], ['C', 5, 0.25], ['P', 1, 0.75]], 100, 25, 0], 225], ['normal control 1', [[['P', 5, 0.75]], 10, 100, 100], -100], ['normal control 2', [[['C', 1, 0.25], ['C', 1, 0.6]], 10, 25, 0], 0], ['normal control 3', [[['P', 1, 0.5], ['P', -1, 0.35], ['C', 1, 0.35]], 10, 100, 100], -100], ['normal control 4', [[['P', -3, 0.75], ['C', -1, 0.05], ['C', -3, 0.6]], 10, 50, 0], 0]], [['regression hedge direction 1', [[['P', 1, 0.35]], 100, 50, 100], -50], ['regression hedge direction 2', [[['C', -3, 0.6], ['C', 5, 0.5], ['P', 5, 0.5]], 100, 100, 100], 100], ['partial repair probe 1', [[['P', 10, 0.6], ['C', 5, 0.05]], 100, 50, 75], 500], ['partial repair probe 2', [[['P', 5, 0.5], ['P', -3, 0.6], ['P', -1, 0.05]], 100, 100, 0], 100], ['normal control 1', [[['C', 5, 0.75]], 10, 100, 100], -100], ['normal control 2', [[['C', 2, 0.25]], 10, 100, 75], -100], ['normal control 3', [[['P', 1, 0.05], ['C', 10, 0.05]], 10, 25, 75], -75], ['normal control 4', [[['C', 5, 0.25], ['C', 5, 0.35]], 10, 100, 0], 0]], [['regression hedge direction 1', [[['C', 1, 0.35], ['C', -1, 0.35], ['C', -1, 0.35]], 100, 25, -250], 275], ['regression hedge direction 2', [[['C', 1, 0.5]], 100, 100, 0], -100], ['partial repair probe 1', [[['C', 5, 0.75], ['P', 5, 0.05], ['P', 5, 0.75]], 100, 50, 100], -100], ['partial repair probe 2', [[['P', 2, 0.75], ['P', 10, 0.35], ['P', 1, 0.05]], 100, 50, 75], 450], ['normal control 1', [[['C', 2, 0.5]], 10, 25, 0], 0], ['normal control 2', [[['P', 5, 0.05], ['C', -1, 0.6], ['P', 1, 0.75]], 10, 100, 75], -100], ['normal control 3', [[['P', 5, 0.05], ['P', -3, 0.25]], 10, 25, 0], 0], ['normal control 4', [[['C', 2, 0.05], ['P', 1, 0.75], ['P', 2, 0.75]], 10, 50, 100], -100]], [['regression hedge direction 1', [[['C', 1, 0.5], ['P', -1, 0.5], ['P', 5, 0.75], ['C', -3, 0.25]], 100, 25, 75], 275], ['regression hedge direction 2', [[['C', 1, 0.6], ['C', 2, 0.25]], 100, 100, 75], -200], ['partial repair probe 1', [[['P', 2, 0.25], ['P', -3, 0.75], ['C', 1, 0.6], ['P', 10, 0.75]], 100, 100, 0], 500], ['partial repair probe 2', [[['P', 1, 0.6], ['P', 5, 0.75], ['P', 5, 0.75], ['P', 10, 0.35]], 100, 50, -250], 1400], ['normal control 1', [[['C', 5, 0.35]], 10, 100, 75], -100], ['normal control 2', [[['C', -3, 0.35], ['C', 1, 0.5], ['P', 1, 0.35]], 10, 50, 0], 0], ['normal control 3', [[['C', 10, 0.05]], 10, 100, 0], 0], ['normal control 4', [[['C', 2, 0.5]], 10, 100, 100], -100]], [['regression hedge direction 1', [[['P', -3, 0.05]], 100, 25, -250], 225], ['regression hedge direction 2', [[['C', 5, 0.6], ['P', -3, 0.35], ['P', 2, 0.6]], 10, 25, -250], 225], ['partial repair probe 1', [[['P', 5, 0.6], ['C', -1, 0.05], ['C', 1, 0.25]], 10, 50, 0], 50], ['partial repair probe 2', [[['C', 1, 0.25], ['C', 1, 0.5], ['C', -1, 0.75], ['P', 10, 0.6]], 100, 100, 100], 500], ['normal control 1', [[['C', 5, 0.75], ['P', 10, 0.05]], 10, 100, 100], -100], ['normal control 2', [[['P', -3, 0.5]], 10, 100, 0], 0], ['normal control 3', [[['C', 1, 0.75], ['C', 1, 0.05], ['C', 2, 0.05], ['C', -1, 0.25]], 10, 100, 0], 0], ['normal control 4', [[['C', -3, 0.5], ['C', 2, 0.5], ['C', 2, 0.75]], 10, 25, 75], -75]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression hedge direction 1 | -125 | -125 | Passed |
| regression hedge direction 2 | 100 | 100 | Passed |
| partial repair probe 1 | -300 | 800 | Failed |
| partial repair probe 2 | -225 | 225 | Failed |
| normal control 1 | -100 | -100 | Passed |
| normal control 2 | 0 | 0 | Passed |
| normal control 3 | -100 | -100 | Passed |
| normal control 4 | 0 | 0 | Passed |
SHA-256 / d68a6f6ad8478bf5e0f94683bfb165db146f22a3479b060f29029bbbbd1a06c8
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
import math
from fractions import Fraction
N = 1
observations = []
def solve(positions, multiplier, lot, existing):
exposure = Fraction(0)
for kind, c, d in positions:
sgn = -1 if kind == 'P' else 1
exposure += sgn * Fraction(str(d)) * c * multiplier
target = -exposure
trade = target - existing
lots = trade / lot
n = math.floor(abs(lots) + Fraction(1, 2))
return int((n if lots >= 0 else -n) * lot)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression hedge direction 1', [[['C', 2, 0.5], ['P', 1, 0.25], ['P', 1, 0.35], ['C', 2, 0.6]], 10, 25, 100], -125], ['regression hedge direction 2', [[['P', 1, 0.25], ['C', 2, 0.6], ['P', -1, 0.5]], 100, 25, -250], 100], ['partial repair probe 1', [[['C', 2, 0.35], ['C', -3, 0.75], ['P', 10, 0.35]], 100, 100, -250], 800], ['partial repair probe 2', [[['P', 5, 0.35], ['P', 2, 0.5], ['C', 5, 0.25], ['P', 1, 0.75]], 100, 25, 0], 225], ['normal control 1', [[['P', 5, 0.75]], 10, 100, 100], -100], ['normal control 2', [[['C', 1, 0.25], ['C', 1, 0.6]], 10, 25, 0], 0], ['normal control 3', [[['P', 1, 0.5], ['P', -1, 0.35], ['C', 1, 0.35]], 10, 100, 100], -100], ['normal control 4', [[['P', -3, 0.75], ['C', -1, 0.05], ['C', -3, 0.6]], 10, 50, 0], 0]], [['regression hedge direction 1', [[['P', 1, 0.35]], 100, 50, 100], -50], ['regression hedge direction 2', [[['C', -3, 0.6], ['C', 5, 0.5], ['P', 5, 0.5]], 100, 100, 100], 100], ['partial repair probe 1', [[['P', 10, 0.6], ['C', 5, 0.05]], 100, 50, 75], 500], ['partial repair probe 2', [[['P', 5, 0.5], ['P', -3, 0.6], ['P', -1, 0.05]], 100, 100, 0], 100], ['normal control 1', [[['C', 5, 0.75]], 10, 100, 100], -100], ['normal control 2', [[['C', 2, 0.25]], 10, 100, 75], -100], ['normal control 3', [[['P', 1, 0.05], ['C', 10, 0.05]], 10, 25, 75], -75], ['normal control 4', [[['C', 5, 0.25], ['C', 5, 0.35]], 10, 100, 0], 0]], [['regression hedge direction 1', [[['C', 1, 0.35], ['C', -1, 0.35], ['C', -1, 0.35]], 100, 25, -250], 275], ['regression hedge direction 2', [[['C', 1, 0.5]], 100, 100, 0], -100], ['partial repair probe 1', [[['C', 5, 0.75], ['P', 5, 0.05], ['P', 5, 0.75]], 100, 50, 100], -100], ['partial repair probe 2', [[['P', 2, 0.75], ['P', 10, 0.35], ['P', 1, 0.05]], 100, 50, 75], 450], ['normal control 1', [[['C', 2, 0.5]], 10, 25, 0], 0], ['normal control 2', [[['P', 5, 0.05], ['C', -1, 0.6], ['P', 1, 0.75]], 10, 100, 75], -100], ['normal control 3', [[['P', 5, 0.05], ['P', -3, 0.25]], 10, 25, 0], 0], ['normal control 4', [[['C', 2, 0.05], ['P', 1, 0.75], ['P', 2, 0.75]], 10, 50, 100], -100]], [['regression hedge direction 1', [[['C', 1, 0.5], ['P', -1, 0.5], ['P', 5, 0.75], ['C', -3, 0.25]], 100, 25, 75], 275], ['regression hedge direction 2', [[['C', 1, 0.6], ['C', 2, 0.25]], 100, 100, 75], -200], ['partial repair probe 1', [[['P', 2, 0.25], ['P', -3, 0.75], ['C', 1, 0.6], ['P', 10, 0.75]], 100, 100, 0], 500], ['partial repair probe 2', [[['P', 1, 0.6], ['P', 5, 0.75], ['P', 5, 0.75], ['P', 10, 0.35]], 100, 50, -250], 1400], ['normal control 1', [[['C', 5, 0.35]], 10, 100, 75], -100], ['normal control 2', [[['C', -3, 0.35], ['C', 1, 0.5], ['P', 1, 0.35]], 10, 50, 0], 0], ['normal control 3', [[['C', 10, 0.05]], 10, 100, 0], 0], ['normal control 4', [[['C', 2, 0.5]], 10, 100, 100], -100]], [['regression hedge direction 1', [[['P', -3, 0.05]], 100, 25, -250], 225], ['regression hedge direction 2', [[['C', 5, 0.6], ['P', -3, 0.35], ['P', 2, 0.6]], 10, 25, -250], 225], ['partial repair probe 1', [[['P', 5, 0.6], ['C', -1, 0.05], ['C', 1, 0.25]], 10, 50, 0], 50], ['partial repair probe 2', [[['C', 1, 0.25], ['C', 1, 0.5], ['C', -1, 0.75], ['P', 10, 0.6]], 100, 100, 100], 500], ['normal control 1', [[['C', 5, 0.75], ['P', 10, 0.05]], 10, 100, 100], -100], ['normal control 2', [[['P', -3, 0.5]], 10, 100, 0], 0], ['normal control 3', [[['C', 1, 0.75], ['C', 1, 0.05], ['C', 2, 0.05], ['C', -1, 0.25]], 10, 100, 0], 0], ['normal control 4', [[['C', -3, 0.5], ['C', 2, 0.5], ['C', 2, 0.75]], 10, 25, 75], -75]]]
for label, args, expected in fixtures[N-1]:
check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression hedge direction 1 | -125 | -125 | Passed |
| regression hedge direction 2 | 100 | 100 | Passed |
| partial repair probe 1 | 800 | 800 | Passed |
| partial repair probe 2 | 225 | 225 | Passed |
| normal control 1 | -100 | -100 | Passed |
| normal control 2 | 0 | 0 | Passed |
| normal control 3 | -100 | -100 | Passed |
| normal control 4 | 0 | 0 | Passed |
SHA-256 / e4c5b017e8fed535041fca13ed6d9b9b5c3dffc723c68a4b1c6e39426863d600
Verification & scope
A deterministic toy contract stated explicitly in the contract field; no claim of conformance to any exchange or clearing rulebook. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:46:58.363087+00:00.
Case digest / 760f18af54d9bd4cf8728e9da42d10ec2f50f70be1ff2d511880e7f561c7961c