FA-61426 / Options payoff and settlement / Open access
Arithmetic Asian payoff with disrupted fixings: a disruption before any valid fixing falls back to the strike · case 01
Schedules that start with a disrupted fixing average in the strike.
ROOT CAUSE
The no-predecessor case substitutes the strike instead of the next valid fixing.
VERIFIED REPAIR
Use the next valid fixing when no earlier value exists.
Unsuccessful approach: Taking the last valid fixing of the schedule looks too far ahead.
Case contract
Inputs kind (price-call, price-put, strike-call), fixings (None = disrupted), strike and final price. A disrupted fixing takes the last preceding filled value; if none precedes, the next valid fixing. avg = mean of the filled fixings. price-call pays max(avg-K,0), price-put max(K-avg,0), strike-call max(final-avg,0). Round to 6.
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
N = 1
observations = []
def solve(kind, fixings, strike, final):
vals = list(fixings)
filled = []
for i, v in enumerate(vals):
if v is None:
prev = [x for x in filled if x is not None]
later = [x for x in vals[i + 1:] if x is not None]
v = prev[-1] if prev else strike
filled.append(v)
avg = sum(filled) / len(filled)
if kind == 'price-call':
pay = max(avg - strike, 0.0)
elif kind == 'price-put':
pay = max(strike - avg, 0.0)
else:
pay = max(final - avg, 0.0)
return round(pay, 6)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression leading disruption 1', ['price-call', [None, 114.94, 81.32], 90, 117.66], 13.733333], ['regression leading disruption 2', ['strike-call', [None, 100.0], 110, 106.13], 6.13], ['partial repair probe 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['partial repair probe 2', ['price-put', [None, None, None, 116.32, None, 103.45], 110, 84.85], 0.0], ['boundary control 1', ['strike-call', [100.0, None, 90.0], 100, 120.0], 23.333333], ['normal control 1', ['strike-call', [108.09, 82.6, 92.83, None, 118.91, 91.14, None], 100, 85.45], 0.0], ['normal control 2', ['price-call', [85.01, 94.18, 82.1], 105, 116.61], 0.0], ['normal control 3', ['price-call', [100.84, 82.19, 118.59, 115.87, 90.57], 110, 107.03], 0.0]], [['regression leading disruption 1', ['price-call', [None, 91.48, 111.91, 116.49, 84.19, None, 82.85, 115.29], 90, 118.37], 7.235], ['regression leading disruption 2', ['price-put', [None, 108.91, 94.86], 105, 114.49], 0.773333], ['partial repair probe 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['partial repair probe 2', ['price-put', [None, None, 108.41, None, None, 89.67], 100, 105.65], 0.0], ['boundary control 1', ['strike-call', [100.0, None, 90.0], 100, 120.0], 23.333333], ['normal control 1', ['strike-call', [119.84, 86.93, 84.4, 83.0, 99.72], 100, 106.94], 12.162], ['normal control 2', ['price-put', [85.42, 115.2, None, 88.06], 110, 85.92], 9.03], ['normal control 3', ['price-call', [None, None, 98.45, 106.81], 105, 119.97], 0.0]], [['regression leading disruption 1', ['price-put', [None, None, 85.09], 100, 101.35], 14.91], ['regression leading disruption 2', ['price-call', [None, 95.74, 111.04, None, 108.46, 115.32, 119.28, 106.15], 105, 114.03], 2.84625], ['partial repair probe 1', ['strike-call', [None, 119.28, 84.0], 110, 100.97], 0.0], ['partial repair probe 2', ['strike-call', [None, 114.32, 85.85, 84.03, 87.82, 93.76, 99.3, 80.58], 110, 93.61], 0.0], ['boundary control 1', ['strike-call', [100.0, None, 90.0], 100, 120.0], 23.333333], ['normal control 1', ['price-call', [83.94, None, 90.41, None], 110, 81.54], 0.0], ['normal control 2', ['price-put', [99.0, None, 90.73, None, None, 112.43], 90, 80.77], 0.0], ['normal control 3', ['price-put', [115.63, None, None], 110, 88.42], 0.0]], [['regression leading disruption 1', ['strike-call', [None, None, 103.2, 95.9, 102.92], 90, 119.13], 17.446], ['regression leading disruption 2', ['strike-call', [None, 91.0, 85.59, None, 86.07, None, None], 110, 104.25], 16.908571], ['partial repair probe 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['partial repair probe 2', ['strike-call', [None, 110.67, 87.17, None, None, 85.45], 90, 91.16], 0.0], ['boundary control 1', ['strike-call', [100.0, None, 90.0], 100, 120.0], 23.333333], ['normal control 1', ['price-call', [108.9, None, 106.45], 100, 117.18], 8.083333], ['normal control 2', ['strike-call', [93.3, 112.85], 90, 109.53], 6.455], ['normal control 3', ['price-put', [80.04, 94.93, None, 116.64, 87.99, None], 100, 114.94], 6.246667]], [['regression leading disruption 1', ['price-call', [None, 81.2, None, 82.68, 83.48, 83.72, 115.81, 110.77], 90, 93.39], 0.0075], ['regression leading disruption 2', ['strike-call', [None, 81.52, None, None, 94.05], 110, 84.49], 0.464], ['partial repair probe 1', ['price-put', [None, None, 94.96, 105.69, 84.52, None], 90, 83.58], 0.0], ['partial repair probe 2', ['price-call', [None, None, 96.59, None, 80.55, 118.41], 100, 119.18], 0.0], ['boundary control 1', ['strike-call', [100.0, None, 90.0], 100, 120.0], 23.333333], ['normal control 1', ['strike-call', [None, None, 109.7, 99.56, 90.86, 92.96, 99.08, None], 110, 90.22], 0.0], ['normal control 2', ['strike-call', [92.29, 83.29, None, 108.83, None, None, 117.63, 114.49], 110, 88.43], 0.0], ['normal control 3', ['strike-call', [108.57, 82.78, 81.2, 97.9], 100, 107.11], 14.4975]]]
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 leading disruption 1 | 5.42 | 13.733333 | Failed |
| regression leading disruption 2 | 1.13 | 6.13 | Failed |
| partial repair probe 1 | 3.333333 | 3.333333 | Passed |
| partial repair probe 2 | 0.0 | 0.0 | Passed |
| boundary control 1 | 23.333333 | 23.333333 | Passed |
| normal control 1 | 0.0 | 0.0 | Passed |
| normal control 2 | 0.0 | 0.0 | Passed |
| normal control 3 | 0.0 | 0.0 | Passed |
SHA-256 / 26866b5b6b2666130940c2fa8e7620434539ebc937c530282158f495c221aef0
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(kind, fixings, strike, final):
vals = list(fixings)
filled = []
for i, v in enumerate(vals):
if v is None:
prev = [x for x in filled if x is not None]
later = [x for x in vals[i + 1:] if x is not None]
v = prev[-1] if prev else later[-1]
filled.append(v)
avg = sum(filled) / len(filled)
if kind == 'price-call':
pay = max(avg - strike, 0.0)
elif kind == 'price-put':
pay = max(strike - avg, 0.0)
else:
pay = max(final - avg, 0.0)
return round(pay, 6)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression leading disruption 1', ['price-call', [None, 114.94, 81.32], 90, 117.66], 13.733333], ['regression leading disruption 2', ['strike-call', [None, 100.0], 110, 106.13], 6.13], ['partial repair probe 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['partial repair probe 2', ['price-put', [None, None, None, 116.32, None, 103.45], 110, 84.85], 0.0], ['boundary control 1', ['strike-call', [100.0, None, 90.0], 100, 120.0], 23.333333], ['normal control 1', ['strike-call', [108.09, 82.6, 92.83, None, 118.91, 91.14, None], 100, 85.45], 0.0], ['normal control 2', ['price-call', [85.01, 94.18, 82.1], 105, 116.61], 0.0], ['normal control 3', ['price-call', [100.84, 82.19, 118.59, 115.87, 90.57], 110, 107.03], 0.0]], [['regression leading disruption 1', ['price-call', [None, 91.48, 111.91, 116.49, 84.19, None, 82.85, 115.29], 90, 118.37], 7.235], ['regression leading disruption 2', ['price-put', [None, 108.91, 94.86], 105, 114.49], 0.773333], ['partial repair probe 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['partial repair probe 2', ['price-put', [None, None, 108.41, None, None, 89.67], 100, 105.65], 0.0], ['boundary control 1', ['strike-call', [100.0, None, 90.0], 100, 120.0], 23.333333], ['normal control 1', ['strike-call', [119.84, 86.93, 84.4, 83.0, 99.72], 100, 106.94], 12.162], ['normal control 2', ['price-put', [85.42, 115.2, None, 88.06], 110, 85.92], 9.03], ['normal control 3', ['price-call', [None, None, 98.45, 106.81], 105, 119.97], 0.0]], [['regression leading disruption 1', ['price-put', [None, None, 85.09], 100, 101.35], 14.91], ['regression leading disruption 2', ['price-call', [None, 95.74, 111.04, None, 108.46, 115.32, 119.28, 106.15], 105, 114.03], 2.84625], ['partial repair probe 1', ['strike-call', [None, 119.28, 84.0], 110, 100.97], 0.0], ['partial repair probe 2', ['strike-call', [None, 114.32, 85.85, 84.03, 87.82, 93.76, 99.3, 80.58], 110, 93.61], 0.0], ['boundary control 1', ['strike-call', [100.0, None, 90.0], 100, 120.0], 23.333333], ['normal control 1', ['price-call', [83.94, None, 90.41, None], 110, 81.54], 0.0], ['normal control 2', ['price-put', [99.0, None, 90.73, None, None, 112.43], 90, 80.77], 0.0], ['normal control 3', ['price-put', [115.63, None, None], 110, 88.42], 0.0]], [['regression leading disruption 1', ['strike-call', [None, None, 103.2, 95.9, 102.92], 90, 119.13], 17.446], ['regression leading disruption 2', ['strike-call', [None, 91.0, 85.59, None, 86.07, None, None], 110, 104.25], 16.908571], ['partial repair probe 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['partial repair probe 2', ['strike-call', [None, 110.67, 87.17, None, None, 85.45], 90, 91.16], 0.0], ['boundary control 1', ['strike-call', [100.0, None, 90.0], 100, 120.0], 23.333333], ['normal control 1', ['price-call', [108.9, None, 106.45], 100, 117.18], 8.083333], ['normal control 2', ['strike-call', [93.3, 112.85], 90, 109.53], 6.455], ['normal control 3', ['price-put', [80.04, 94.93, None, 116.64, 87.99, None], 100, 114.94], 6.246667]], [['regression leading disruption 1', ['price-call', [None, 81.2, None, 82.68, 83.48, 83.72, 115.81, 110.77], 90, 93.39], 0.0075], ['regression leading disruption 2', ['strike-call', [None, 81.52, None, None, 94.05], 110, 84.49], 0.464], ['partial repair probe 1', ['price-put', [None, None, 94.96, 105.69, 84.52, None], 90, 83.58], 0.0], ['partial repair probe 2', ['price-call', [None, None, 96.59, None, 80.55, 118.41], 100, 119.18], 0.0], ['boundary control 1', ['strike-call', [100.0, None, 90.0], 100, 120.0], 23.333333], ['normal control 1', ['strike-call', [None, None, 109.7, 99.56, 90.86, 92.96, 99.08, None], 110, 90.22], 0.0], ['normal control 2', ['strike-call', [92.29, 83.29, None, 108.83, None, None, 117.63, 114.49], 110, 88.43], 0.0], ['normal control 3', ['strike-call', [108.57, 82.78, 81.2, 97.9], 100, 107.11], 14.4975]]]
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 leading disruption 1 | 2.526667 | 13.733333 | Failed |
| regression leading disruption 2 | 6.13 | 6.13 | Passed |
| partial repair probe 1 | 6.666667 | 3.333333 | Failed |
| partial repair probe 2 | 2.26 | 0.0 | Failed |
| boundary control 1 | 23.333333 | 23.333333 | Passed |
| normal control 1 | 0.0 | 0.0 | Passed |
| normal control 2 | 0.0 | 0.0 | Passed |
| normal control 3 | 0.0 | 0.0 | Passed |
SHA-256 / e7c6a7fbadc7700fdf351692748cae89202c7467f8970bc41c98dbf49adc1bab
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(kind, fixings, strike, final):
vals = list(fixings)
filled = []
for i, v in enumerate(vals):
if v is None:
prev = [x for x in filled if x is not None]
later = [x for x in vals[i + 1:] if x is not None]
v = prev[-1] if prev else later[0]
filled.append(v)
avg = sum(filled) / len(filled)
if kind == 'price-call':
pay = max(avg - strike, 0.0)
elif kind == 'price-put':
pay = max(strike - avg, 0.0)
else:
pay = max(final - avg, 0.0)
return round(pay, 6)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression leading disruption 1', ['price-call', [None, 114.94, 81.32], 90, 117.66], 13.733333], ['regression leading disruption 2', ['strike-call', [None, 100.0], 110, 106.13], 6.13], ['partial repair probe 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['partial repair probe 2', ['price-put', [None, None, None, 116.32, None, 103.45], 110, 84.85], 0.0], ['boundary control 1', ['strike-call', [100.0, None, 90.0], 100, 120.0], 23.333333], ['normal control 1', ['strike-call', [108.09, 82.6, 92.83, None, 118.91, 91.14, None], 100, 85.45], 0.0], ['normal control 2', ['price-call', [85.01, 94.18, 82.1], 105, 116.61], 0.0], ['normal control 3', ['price-call', [100.84, 82.19, 118.59, 115.87, 90.57], 110, 107.03], 0.0]], [['regression leading disruption 1', ['price-call', [None, 91.48, 111.91, 116.49, 84.19, None, 82.85, 115.29], 90, 118.37], 7.235], ['regression leading disruption 2', ['price-put', [None, 108.91, 94.86], 105, 114.49], 0.773333], ['partial repair probe 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['partial repair probe 2', ['price-put', [None, None, 108.41, None, None, 89.67], 100, 105.65], 0.0], ['boundary control 1', ['strike-call', [100.0, None, 90.0], 100, 120.0], 23.333333], ['normal control 1', ['strike-call', [119.84, 86.93, 84.4, 83.0, 99.72], 100, 106.94], 12.162], ['normal control 2', ['price-put', [85.42, 115.2, None, 88.06], 110, 85.92], 9.03], ['normal control 3', ['price-call', [None, None, 98.45, 106.81], 105, 119.97], 0.0]], [['regression leading disruption 1', ['price-put', [None, None, 85.09], 100, 101.35], 14.91], ['regression leading disruption 2', ['price-call', [None, 95.74, 111.04, None, 108.46, 115.32, 119.28, 106.15], 105, 114.03], 2.84625], ['partial repair probe 1', ['strike-call', [None, 119.28, 84.0], 110, 100.97], 0.0], ['partial repair probe 2', ['strike-call', [None, 114.32, 85.85, 84.03, 87.82, 93.76, 99.3, 80.58], 110, 93.61], 0.0], ['boundary control 1', ['strike-call', [100.0, None, 90.0], 100, 120.0], 23.333333], ['normal control 1', ['price-call', [83.94, None, 90.41, None], 110, 81.54], 0.0], ['normal control 2', ['price-put', [99.0, None, 90.73, None, None, 112.43], 90, 80.77], 0.0], ['normal control 3', ['price-put', [115.63, None, None], 110, 88.42], 0.0]], [['regression leading disruption 1', ['strike-call', [None, None, 103.2, 95.9, 102.92], 90, 119.13], 17.446], ['regression leading disruption 2', ['strike-call', [None, 91.0, 85.59, None, 86.07, None, None], 110, 104.25], 16.908571], ['partial repair probe 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['partial repair probe 2', ['strike-call', [None, 110.67, 87.17, None, None, 85.45], 90, 91.16], 0.0], ['boundary control 1', ['strike-call', [100.0, None, 90.0], 100, 120.0], 23.333333], ['normal control 1', ['price-call', [108.9, None, 106.45], 100, 117.18], 8.083333], ['normal control 2', ['strike-call', [93.3, 112.85], 90, 109.53], 6.455], ['normal control 3', ['price-put', [80.04, 94.93, None, 116.64, 87.99, None], 100, 114.94], 6.246667]], [['regression leading disruption 1', ['price-call', [None, 81.2, None, 82.68, 83.48, 83.72, 115.81, 110.77], 90, 93.39], 0.0075], ['regression leading disruption 2', ['strike-call', [None, 81.52, None, None, 94.05], 110, 84.49], 0.464], ['partial repair probe 1', ['price-put', [None, None, 94.96, 105.69, 84.52, None], 90, 83.58], 0.0], ['partial repair probe 2', ['price-call', [None, None, 96.59, None, 80.55, 118.41], 100, 119.18], 0.0], ['boundary control 1', ['strike-call', [100.0, None, 90.0], 100, 120.0], 23.333333], ['normal control 1', ['strike-call', [None, None, 109.7, 99.56, 90.86, 92.96, 99.08, None], 110, 90.22], 0.0], ['normal control 2', ['strike-call', [92.29, 83.29, None, 108.83, None, None, 117.63, 114.49], 110, 88.43], 0.0], ['normal control 3', ['strike-call', [108.57, 82.78, 81.2, 97.9], 100, 107.11], 14.4975]]]
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 leading disruption 1 | 13.733333 | 13.733333 | Passed |
| regression leading disruption 2 | 6.13 | 6.13 | Passed |
| partial repair probe 1 | 3.333333 | 3.333333 | Passed |
| partial repair probe 2 | 0.0 | 0.0 | Passed |
| boundary control 1 | 23.333333 | 23.333333 | Passed |
| normal control 1 | 0.0 | 0.0 | Passed |
| normal control 2 | 0.0 | 0.0 | Passed |
| normal control 3 | 0.0 | 0.0 | Passed |
SHA-256 / 463358c5e470ecd6683ccac93c428ec63ae60fc5bbc1c3954ad1141a4fff9db1
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:55.101289+00:00.
Case digest / c8ada5449f1da788f3b31a9f3c12f17c8830add536fd28a550a99eaac9cd99c2