FA-61436 / Options payoff and settlement / Open access
Arithmetic Asian payoff with disrupted fixings: the final price is added to the fixing average · case 01
The final observation is double counted in the average.
ROOT CAUSE
The average includes final as an extra observation.
VERIFIED REPAIR
Average exactly the scheduled fixings.
Unsuccessful approach: Dropping the last fixing removes a scheduled observation instead.
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 later[0]
filled.append(v)
avg = (sum(filled) + final) / (len(filled) + 1)
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 averaging set 1', ['strike-call', [80.18, 83.29, 83.37, 113.46, 117.92, 113.69, 91.1], 90, 101.49], 3.917143], ['regression averaging set 2', ['price-call', [None, 93.86, 86.82, 114.78, 116.01, 106.72, 113.09, 99.31], 100, 97.7], 3.05625], ['partial repair probe 1', ['price-put', [85.47, None, 89.36, 119.71], 90, 108.07], 0.0], ['partial repair probe 2', ['price-put', [94.35, 82.66, 99.58], 90, 119.87], 0.0], ['normal control 1', ['price-put', [119.18, 101.58, None, 97.92, None, 103.75, 115.65, 99.76], 90, 86.86], 0.0], ['normal control 2', ['strike-call', [101.08, 83.38, None, 111.97, 96.17, 119.36, None, 112.17], 110, 97.0], 0.0], ['normal control 3', ['price-put', [109.69, 95.98, 111.39, None, 88.5, 86.77, 109.74], 100, 105.11], 0.0], ['normal control 4', ['price-put', [106.22, 117.4], 100, 88.61], 0.0]], [['regression averaging set 1', ['price-put', [None, None, 112.93, 108.05, 83.92, None], 105, 112.17], 2.553333], ['regression averaging set 2', ['price-put', [105.66, 81.85, 81.44], 105, 87.44], 15.35], ['partial repair probe 1', ['strike-call', [None, 103.34, 92.64, 96.71, 88.34, 81.7, 89.42, 115.57], 105, 96.03], 0.0], ['partial repair probe 2', ['price-put', [119.7, 95.16, None, 116.26], 105, 104.79], 0.0], ['normal control 1', ['strike-call', [None, 103.36, 82.5, 116.07], 90, 85.2], 0.0], ['normal control 2', ['price-put', [85.46, 80.82, 107.2, 114.95, None, 110.68, 98.77, 101.95], 100, 103.02], 0.0], ['normal control 3', ['price-put', [100.59, 114.99, None, 90.36, 118.38], 100, 91.41], 0.0], ['normal control 4', ['strike-call', [86.04, 112.95, None, 116.44, 114.1, 97.61], 100, 81.41], 0.0]], [['regression averaging set 1', ['price-call', [None, None, None, 110.46], 90, 114.65], 20.46], ['regression averaging set 2', ['price-put', [116.89, 107.33, 103.44], 110, 109.18], 0.78], ['partial repair probe 1', ['price-call', [116.24, 96.14, 118.53, 80.19], 110, 110.74], 0.0], ['partial repair probe 2', ['strike-call', [None, 92.85, 108.32], 90, 93.35], 0.0], ['normal control 1', ['strike-call', [104.59, None, None, 82.73], 100, 94.24], 0.0], ['normal control 2', ['price-call', [None, 86.46, 94.24, 98.57, 80.49, 97.12], 100, 113.48], 0.0], ['normal control 3', ['strike-call', [97.17, 95.11, 96.49, 111.33, 93.94, 102.96, None, 84.02], 100, 89.71], 0.0], ['normal control 4', ['strike-call', [None, 96.59, None, 113.0, 114.72, 97.55], 110, 99.24], 0.0]], [['regression averaging set 1', ['price-put', [109.13, None], 110, 105.91], 0.87], ['regression averaging set 2', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['partial repair probe 1', ['strike-call', [102.88, 113.6, None], 105, 109.21], 0.0], ['partial repair probe 2', ['price-put', [83.4, 115.69, 97.25, None, 92.32, 111.92, None], 100, 103.18], 0.0], ['normal control 1', ['strike-call', [None, 100.74], 100, 80.87], 0.0], ['normal control 2', ['price-put', [None, 117.64, 104.34, 103.25, 96.88, None], 90, 117.22], 0.0], ['normal control 3', ['strike-call', [117.52, None, 102.65, 89.79, None, 113.85, 95.03], 100, 86.43], 0.0], ['normal control 4', ['price-put', [116.99, None, 112.3, 80.06, 83.34, 105.61, 104.54], 90, 119.98], 0.0]], [['regression averaging set 1', ['price-put', [None, 89.31, 115.64, None, 109.9, None], 110, 110.17], 5.05], ['regression averaging set 2', ['price-call', [92.38, 89.49, 108.11], 90, 88.72], 6.66], ['partial repair probe 1', ['price-call', [111.39, 81.66], 100, 81.74], 0.0], ['partial repair probe 2', ['strike-call', [None, 94.49, 102.83], 100, 95.06], 0.0], ['normal control 1', ['price-call', [None, 104.44, 84.71, 91.43, 87.51], 110, 116.96], 0.0], ['normal control 2', ['price-call', [82.28, 99.04, 80.7, 106.55, None, None], 110, 81.57], 0.0], ['normal control 3', ['price-call', [99.04, None, 87.34, None, 102.98, 83.37, 84.75], 110, 97.22], 0.0], ['normal control 4', ['strike-call', [108.54, 108.11, 96.0, 97.59, 80.67], 110, 95.07], 0.0]]]
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 averaging set 1 | 3.4275 | 3.917143 | Failed |
| regression averaging set 2 | 2.461111 | 3.05625 | Failed |
| partial repair probe 1 | 0.0 | 0.0 | Passed |
| partial repair probe 2 | 0.0 | 0.0 | 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 |
| normal control 4 | 0.0 | 0.0 | Passed |
SHA-256 / ec96ba11f11ca7c7926d310afb93d75426860354b06d69f2a7b500f1bf81f09c
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[0]
filled.append(v)
avg = sum(filled[:-1]) / (len(filled) - 1)
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 averaging set 1', ['strike-call', [80.18, 83.29, 83.37, 113.46, 117.92, 113.69, 91.1], 90, 101.49], 3.917143], ['regression averaging set 2', ['price-call', [None, 93.86, 86.82, 114.78, 116.01, 106.72, 113.09, 99.31], 100, 97.7], 3.05625], ['partial repair probe 1', ['price-put', [85.47, None, 89.36, 119.71], 90, 108.07], 0.0], ['partial repair probe 2', ['price-put', [94.35, 82.66, 99.58], 90, 119.87], 0.0], ['normal control 1', ['price-put', [119.18, 101.58, None, 97.92, None, 103.75, 115.65, 99.76], 90, 86.86], 0.0], ['normal control 2', ['strike-call', [101.08, 83.38, None, 111.97, 96.17, 119.36, None, 112.17], 110, 97.0], 0.0], ['normal control 3', ['price-put', [109.69, 95.98, 111.39, None, 88.5, 86.77, 109.74], 100, 105.11], 0.0], ['normal control 4', ['price-put', [106.22, 117.4], 100, 88.61], 0.0]], [['regression averaging set 1', ['price-put', [None, None, 112.93, 108.05, 83.92, None], 105, 112.17], 2.553333], ['regression averaging set 2', ['price-put', [105.66, 81.85, 81.44], 105, 87.44], 15.35], ['partial repair probe 1', ['strike-call', [None, 103.34, 92.64, 96.71, 88.34, 81.7, 89.42, 115.57], 105, 96.03], 0.0], ['partial repair probe 2', ['price-put', [119.7, 95.16, None, 116.26], 105, 104.79], 0.0], ['normal control 1', ['strike-call', [None, 103.36, 82.5, 116.07], 90, 85.2], 0.0], ['normal control 2', ['price-put', [85.46, 80.82, 107.2, 114.95, None, 110.68, 98.77, 101.95], 100, 103.02], 0.0], ['normal control 3', ['price-put', [100.59, 114.99, None, 90.36, 118.38], 100, 91.41], 0.0], ['normal control 4', ['strike-call', [86.04, 112.95, None, 116.44, 114.1, 97.61], 100, 81.41], 0.0]], [['regression averaging set 1', ['price-call', [None, None, None, 110.46], 90, 114.65], 20.46], ['regression averaging set 2', ['price-put', [116.89, 107.33, 103.44], 110, 109.18], 0.78], ['partial repair probe 1', ['price-call', [116.24, 96.14, 118.53, 80.19], 110, 110.74], 0.0], ['partial repair probe 2', ['strike-call', [None, 92.85, 108.32], 90, 93.35], 0.0], ['normal control 1', ['strike-call', [104.59, None, None, 82.73], 100, 94.24], 0.0], ['normal control 2', ['price-call', [None, 86.46, 94.24, 98.57, 80.49, 97.12], 100, 113.48], 0.0], ['normal control 3', ['strike-call', [97.17, 95.11, 96.49, 111.33, 93.94, 102.96, None, 84.02], 100, 89.71], 0.0], ['normal control 4', ['strike-call', [None, 96.59, None, 113.0, 114.72, 97.55], 110, 99.24], 0.0]], [['regression averaging set 1', ['price-put', [109.13, None], 110, 105.91], 0.87], ['regression averaging set 2', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['partial repair probe 1', ['strike-call', [102.88, 113.6, None], 105, 109.21], 0.0], ['partial repair probe 2', ['price-put', [83.4, 115.69, 97.25, None, 92.32, 111.92, None], 100, 103.18], 0.0], ['normal control 1', ['strike-call', [None, 100.74], 100, 80.87], 0.0], ['normal control 2', ['price-put', [None, 117.64, 104.34, 103.25, 96.88, None], 90, 117.22], 0.0], ['normal control 3', ['strike-call', [117.52, None, 102.65, 89.79, None, 113.85, 95.03], 100, 86.43], 0.0], ['normal control 4', ['price-put', [116.99, None, 112.3, 80.06, 83.34, 105.61, 104.54], 90, 119.98], 0.0]], [['regression averaging set 1', ['price-put', [None, 89.31, 115.64, None, 109.9, None], 110, 110.17], 5.05], ['regression averaging set 2', ['price-call', [92.38, 89.49, 108.11], 90, 88.72], 6.66], ['partial repair probe 1', ['price-call', [111.39, 81.66], 100, 81.74], 0.0], ['partial repair probe 2', ['strike-call', [None, 94.49, 102.83], 100, 95.06], 0.0], ['normal control 1', ['price-call', [None, 104.44, 84.71, 91.43, 87.51], 110, 116.96], 0.0], ['normal control 2', ['price-call', [82.28, 99.04, 80.7, 106.55, None, None], 110, 81.57], 0.0], ['normal control 3', ['price-call', [99.04, None, 87.34, None, 102.98, 83.37, 84.75], 110, 97.22], 0.0], ['normal control 4', ['strike-call', [108.54, 108.11, 96.0, 97.59, 80.67], 110, 95.07], 0.0]]]
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 averaging set 1 | 2.838333 | 3.917143 | Failed |
| regression averaging set 2 | 3.591429 | 3.05625 | Failed |
| partial repair probe 1 | 3.233333 | 0.0 | Failed |
| partial repair probe 2 | 1.495 | 0.0 | Failed |
| normal control 1 | 0.0 | 0.0 | Passed |
| normal control 2 | 0.0 | 0.0 | Passed |
| normal control 3 | 0.0 | 0.0 | Passed |
| normal control 4 | 0.0 | 0.0 | Passed |
SHA-256 / f94d56adb1d193ee43d4feda55abcc6a56efc0890dc27a2ffa6a18f912a42f43
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 averaging set 1', ['strike-call', [80.18, 83.29, 83.37, 113.46, 117.92, 113.69, 91.1], 90, 101.49], 3.917143], ['regression averaging set 2', ['price-call', [None, 93.86, 86.82, 114.78, 116.01, 106.72, 113.09, 99.31], 100, 97.7], 3.05625], ['partial repair probe 1', ['price-put', [85.47, None, 89.36, 119.71], 90, 108.07], 0.0], ['partial repair probe 2', ['price-put', [94.35, 82.66, 99.58], 90, 119.87], 0.0], ['normal control 1', ['price-put', [119.18, 101.58, None, 97.92, None, 103.75, 115.65, 99.76], 90, 86.86], 0.0], ['normal control 2', ['strike-call', [101.08, 83.38, None, 111.97, 96.17, 119.36, None, 112.17], 110, 97.0], 0.0], ['normal control 3', ['price-put', [109.69, 95.98, 111.39, None, 88.5, 86.77, 109.74], 100, 105.11], 0.0], ['normal control 4', ['price-put', [106.22, 117.4], 100, 88.61], 0.0]], [['regression averaging set 1', ['price-put', [None, None, 112.93, 108.05, 83.92, None], 105, 112.17], 2.553333], ['regression averaging set 2', ['price-put', [105.66, 81.85, 81.44], 105, 87.44], 15.35], ['partial repair probe 1', ['strike-call', [None, 103.34, 92.64, 96.71, 88.34, 81.7, 89.42, 115.57], 105, 96.03], 0.0], ['partial repair probe 2', ['price-put', [119.7, 95.16, None, 116.26], 105, 104.79], 0.0], ['normal control 1', ['strike-call', [None, 103.36, 82.5, 116.07], 90, 85.2], 0.0], ['normal control 2', ['price-put', [85.46, 80.82, 107.2, 114.95, None, 110.68, 98.77, 101.95], 100, 103.02], 0.0], ['normal control 3', ['price-put', [100.59, 114.99, None, 90.36, 118.38], 100, 91.41], 0.0], ['normal control 4', ['strike-call', [86.04, 112.95, None, 116.44, 114.1, 97.61], 100, 81.41], 0.0]], [['regression averaging set 1', ['price-call', [None, None, None, 110.46], 90, 114.65], 20.46], ['regression averaging set 2', ['price-put', [116.89, 107.33, 103.44], 110, 109.18], 0.78], ['partial repair probe 1', ['price-call', [116.24, 96.14, 118.53, 80.19], 110, 110.74], 0.0], ['partial repair probe 2', ['strike-call', [None, 92.85, 108.32], 90, 93.35], 0.0], ['normal control 1', ['strike-call', [104.59, None, None, 82.73], 100, 94.24], 0.0], ['normal control 2', ['price-call', [None, 86.46, 94.24, 98.57, 80.49, 97.12], 100, 113.48], 0.0], ['normal control 3', ['strike-call', [97.17, 95.11, 96.49, 111.33, 93.94, 102.96, None, 84.02], 100, 89.71], 0.0], ['normal control 4', ['strike-call', [None, 96.59, None, 113.0, 114.72, 97.55], 110, 99.24], 0.0]], [['regression averaging set 1', ['price-put', [109.13, None], 110, 105.91], 0.87], ['regression averaging set 2', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['partial repair probe 1', ['strike-call', [102.88, 113.6, None], 105, 109.21], 0.0], ['partial repair probe 2', ['price-put', [83.4, 115.69, 97.25, None, 92.32, 111.92, None], 100, 103.18], 0.0], ['normal control 1', ['strike-call', [None, 100.74], 100, 80.87], 0.0], ['normal control 2', ['price-put', [None, 117.64, 104.34, 103.25, 96.88, None], 90, 117.22], 0.0], ['normal control 3', ['strike-call', [117.52, None, 102.65, 89.79, None, 113.85, 95.03], 100, 86.43], 0.0], ['normal control 4', ['price-put', [116.99, None, 112.3, 80.06, 83.34, 105.61, 104.54], 90, 119.98], 0.0]], [['regression averaging set 1', ['price-put', [None, 89.31, 115.64, None, 109.9, None], 110, 110.17], 5.05], ['regression averaging set 2', ['price-call', [92.38, 89.49, 108.11], 90, 88.72], 6.66], ['partial repair probe 1', ['price-call', [111.39, 81.66], 100, 81.74], 0.0], ['partial repair probe 2', ['strike-call', [None, 94.49, 102.83], 100, 95.06], 0.0], ['normal control 1', ['price-call', [None, 104.44, 84.71, 91.43, 87.51], 110, 116.96], 0.0], ['normal control 2', ['price-call', [82.28, 99.04, 80.7, 106.55, None, None], 110, 81.57], 0.0], ['normal control 3', ['price-call', [99.04, None, 87.34, None, 102.98, 83.37, 84.75], 110, 97.22], 0.0], ['normal control 4', ['strike-call', [108.54, 108.11, 96.0, 97.59, 80.67], 110, 95.07], 0.0]]]
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 averaging set 1 | 3.917143 | 3.917143 | Passed |
| regression averaging set 2 | 3.05625 | 3.05625 | Passed |
| partial repair probe 1 | 0.0 | 0.0 | Passed |
| partial repair probe 2 | 0.0 | 0.0 | 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 |
| normal control 4 | 0.0 | 0.0 | Passed |
SHA-256 / a03994462a904f3d27bb8f29df0ae9b21579b0f4ab508fda8504f983d784a813
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.181582+00:00.
Case digest / b7b24126d072aea39c6bcfbd1bf07925ae78661699889cc54e0884a757cf5f62