FA-61431 / Options payoff and settlement / Open access
Arithmetic Asian payoff with disrupted fixings: the floating-strike call pays the average over the final price · case 01
Floating-strike calls pay when the final price is below the average.
ROOT CAUSE
The payoff subtracts final from average.
VERIFIED REPAIR
Pay max(final - avg, 0).
Unsuccessful approach: Using the fixed strike turns the floating-strike call into a vanilla call.
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) / 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(avg - final, 0.0)
return round(pay, 6)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression floating strike leg 1', ['strike-call', [103.05, None, None], 110, 93.79], 0.0], ['regression floating strike leg 2', ['strike-call', [None, 90.77, 100.31, 111.32, 86.96, 81.33], 110, 110.43], 16.853333], ['partial repair probe 1', ['strike-call', [None, 96.42, 91.29, 85.33, 103.29, None, 100.83, 98.19], 110, 100.35], 3.4675], ['partial repair probe 2', ['strike-call', [101.79, None, 97.24, 102.75, None], 90, 119.68], 18.416], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-put', [None, None, 90.1, None], 105, 111.03], 14.9], ['normal control 2', ['price-put', [89.66, None, 87.62, 86.54], 100, 115.18], 11.63], ['normal control 3', ['price-put', [111.73, 95.4, 91.7, 102.94, 89.69, 102.29, 99.37, 110.62], 110, 110.4], 9.5325]], [['regression floating strike leg 1', ['strike-call', [97.05, None, 91.77, None, None, 82.64, 96.57, 112.36], 100, 82.53], 0.0], ['regression floating strike leg 2', ['strike-call', [None, 87.57, 95.18, 82.58, None], 110, 80.04], 0.0], ['partial repair probe 1', ['strike-call', [87.17, 102.54, 83.6], 110, 103.05], 11.946667], ['partial repair probe 2', ['strike-call', [117.37, 113.79], 100, 105.4], 0.0], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-call', [115.75, 98.8, None, None], 90, 102.14], 13.0375], ['normal control 2', ['price-put', [None, 102.49], 110, 90.49], 7.51], ['normal control 3', ['price-put', [81.06, None, 82.46, 117.08, 94.93, 115.96], 110, 106.53], 14.575]], [['regression floating strike leg 1', ['strike-call', [106.31, 106.2, 110.92, 88.16], 100, 88.23], 0.0], ['regression floating strike leg 2', ['strike-call', [87.28, 99.15, 107.74, 95.33, 101.03], 105, 105.7], 7.594], ['partial repair probe 1', ['strike-call', [104.99, None, None, 92.17, None, 102.65, 88.75], 110, 118.6], 19.927143], ['partial repair probe 2', ['strike-call', [88.17, None], 90, 107.11], 18.94], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-put', [86.78, 117.58, 90.09, 111.99, 83.86, 115.07, 82.55], 90, 95.52], 0.0], ['normal control 2', ['price-call', [80.15, 119.98, 88.0, 102.79, 87.59, None, None], 100, 117.78], 0.0], ['normal control 3', ['price-call', [100.9, 119.43, 91.91, 84.18, 100.52], 110, 104.53], 0.0]], [['regression floating strike leg 1', ['strike-call', [112.97, 91.85, 111.17], 105, 96.84], 0.0], ['regression floating strike leg 2', ['strike-call', [87.61, None, 97.7, 109.43, None, None, 80.17], 100, 84.68], 0.0], ['partial repair probe 1', ['strike-call', [None, None, None, 108.09, 105.86, 80.01, None, None], 90, 100.06], 2.77875], ['partial repair probe 2', ['strike-call', [82.63, 86.84, 116.07, 94.98, None, 111.46], 90, 91.41], 0.0], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-call', [81.64, 110.92, 119.01, 112.86, 113.88, 113.24], 105, 86.69], 3.591667], ['normal control 2', ['price-call', [94.17, 108.43, 107.73, 88.19, None], 100, 80.09], 0.0], ['normal control 3', ['price-put', [81.81, 90.42, None, 86.29, 104.03, 100.86], 90, 119.4], 0.0]], [['regression floating strike leg 1', ['strike-call', [84.05, 111.33, 119.36, 95.5, 109.08, 81.91], 105, 89.98], 0.0], ['regression floating strike leg 2', ['strike-call', [88.89, None, 92.84], 100, 107.62], 17.413333], ['partial repair probe 1', ['strike-call', [85.48, 100.59, 86.54, None, 96.53, None], 110, 98.91], 6.875], ['partial repair probe 2', ['strike-call', [None, 119.94, 87.68, 92.42, None, None], 105, 114.08], 13.276667], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-call', [93.01, 112.21, 93.6, 118.27, 97.52, 89.11, None, None], 110, 85.35], 0.0], ['normal control 2', ['price-call', [None, 92.28, 115.75, None, 91.4, 106.72, 114.83, 84.89], 100, 82.1], 1.7375], ['normal control 3', ['price-put', [85.64, 111.27, 89.68, 100.6, 119.83, None, 99.77], 105, 96.0], 1.197143]]]
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 floating strike leg 1 | 9.26 | 0.0 | Failed |
| regression floating strike leg 2 | 0.0 | 16.853333 | Failed |
| partial repair probe 1 | 0.0 | 3.4675 | Failed |
| partial repair probe 2 | 0.0 | 18.416 | Failed |
| boundary control 1 | 3.333333 | 3.333333 | Passed |
| normal control 1 | 14.9 | 14.9 | Passed |
| normal control 2 | 11.63 | 11.63 | Passed |
| normal control 3 | 9.5325 | 9.5325 | Passed |
SHA-256 / b39ba3ecc03de1cf2bf4d4bc87c463144bb193b92e60c749f05c74cac55aa900
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) / 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 - strike, 0.0)
return round(pay, 6)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression floating strike leg 1', ['strike-call', [103.05, None, None], 110, 93.79], 0.0], ['regression floating strike leg 2', ['strike-call', [None, 90.77, 100.31, 111.32, 86.96, 81.33], 110, 110.43], 16.853333], ['partial repair probe 1', ['strike-call', [None, 96.42, 91.29, 85.33, 103.29, None, 100.83, 98.19], 110, 100.35], 3.4675], ['partial repair probe 2', ['strike-call', [101.79, None, 97.24, 102.75, None], 90, 119.68], 18.416], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-put', [None, None, 90.1, None], 105, 111.03], 14.9], ['normal control 2', ['price-put', [89.66, None, 87.62, 86.54], 100, 115.18], 11.63], ['normal control 3', ['price-put', [111.73, 95.4, 91.7, 102.94, 89.69, 102.29, 99.37, 110.62], 110, 110.4], 9.5325]], [['regression floating strike leg 1', ['strike-call', [97.05, None, 91.77, None, None, 82.64, 96.57, 112.36], 100, 82.53], 0.0], ['regression floating strike leg 2', ['strike-call', [None, 87.57, 95.18, 82.58, None], 110, 80.04], 0.0], ['partial repair probe 1', ['strike-call', [87.17, 102.54, 83.6], 110, 103.05], 11.946667], ['partial repair probe 2', ['strike-call', [117.37, 113.79], 100, 105.4], 0.0], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-call', [115.75, 98.8, None, None], 90, 102.14], 13.0375], ['normal control 2', ['price-put', [None, 102.49], 110, 90.49], 7.51], ['normal control 3', ['price-put', [81.06, None, 82.46, 117.08, 94.93, 115.96], 110, 106.53], 14.575]], [['regression floating strike leg 1', ['strike-call', [106.31, 106.2, 110.92, 88.16], 100, 88.23], 0.0], ['regression floating strike leg 2', ['strike-call', [87.28, 99.15, 107.74, 95.33, 101.03], 105, 105.7], 7.594], ['partial repair probe 1', ['strike-call', [104.99, None, None, 92.17, None, 102.65, 88.75], 110, 118.6], 19.927143], ['partial repair probe 2', ['strike-call', [88.17, None], 90, 107.11], 18.94], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-put', [86.78, 117.58, 90.09, 111.99, 83.86, 115.07, 82.55], 90, 95.52], 0.0], ['normal control 2', ['price-call', [80.15, 119.98, 88.0, 102.79, 87.59, None, None], 100, 117.78], 0.0], ['normal control 3', ['price-call', [100.9, 119.43, 91.91, 84.18, 100.52], 110, 104.53], 0.0]], [['regression floating strike leg 1', ['strike-call', [112.97, 91.85, 111.17], 105, 96.84], 0.0], ['regression floating strike leg 2', ['strike-call', [87.61, None, 97.7, 109.43, None, None, 80.17], 100, 84.68], 0.0], ['partial repair probe 1', ['strike-call', [None, None, None, 108.09, 105.86, 80.01, None, None], 90, 100.06], 2.77875], ['partial repair probe 2', ['strike-call', [82.63, 86.84, 116.07, 94.98, None, 111.46], 90, 91.41], 0.0], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-call', [81.64, 110.92, 119.01, 112.86, 113.88, 113.24], 105, 86.69], 3.591667], ['normal control 2', ['price-call', [94.17, 108.43, 107.73, 88.19, None], 100, 80.09], 0.0], ['normal control 3', ['price-put', [81.81, 90.42, None, 86.29, 104.03, 100.86], 90, 119.4], 0.0]], [['regression floating strike leg 1', ['strike-call', [84.05, 111.33, 119.36, 95.5, 109.08, 81.91], 105, 89.98], 0.0], ['regression floating strike leg 2', ['strike-call', [88.89, None, 92.84], 100, 107.62], 17.413333], ['partial repair probe 1', ['strike-call', [85.48, 100.59, 86.54, None, 96.53, None], 110, 98.91], 6.875], ['partial repair probe 2', ['strike-call', [None, 119.94, 87.68, 92.42, None, None], 105, 114.08], 13.276667], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-call', [93.01, 112.21, 93.6, 118.27, 97.52, 89.11, None, None], 110, 85.35], 0.0], ['normal control 2', ['price-call', [None, 92.28, 115.75, None, 91.4, 106.72, 114.83, 84.89], 100, 82.1], 1.7375], ['normal control 3', ['price-put', [85.64, 111.27, 89.68, 100.6, 119.83, None, 99.77], 105, 96.0], 1.197143]]]
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 floating strike leg 1 | 0.0 | 0.0 | Passed |
| regression floating strike leg 2 | 0.43 | 16.853333 | Failed |
| partial repair probe 1 | 0.0 | 3.4675 | Failed |
| partial repair probe 2 | 29.68 | 18.416 | Failed |
| boundary control 1 | 3.333333 | 3.333333 | Passed |
| normal control 1 | 14.9 | 14.9 | Passed |
| normal control 2 | 11.63 | 11.63 | Passed |
| normal control 3 | 9.5325 | 9.5325 | Passed |
SHA-256 / 3556aa4caadb6fad097cf8642270ec6f8753f024219d67f564c8fc40e5b4e36c
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 floating strike leg 1', ['strike-call', [103.05, None, None], 110, 93.79], 0.0], ['regression floating strike leg 2', ['strike-call', [None, 90.77, 100.31, 111.32, 86.96, 81.33], 110, 110.43], 16.853333], ['partial repair probe 1', ['strike-call', [None, 96.42, 91.29, 85.33, 103.29, None, 100.83, 98.19], 110, 100.35], 3.4675], ['partial repair probe 2', ['strike-call', [101.79, None, 97.24, 102.75, None], 90, 119.68], 18.416], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-put', [None, None, 90.1, None], 105, 111.03], 14.9], ['normal control 2', ['price-put', [89.66, None, 87.62, 86.54], 100, 115.18], 11.63], ['normal control 3', ['price-put', [111.73, 95.4, 91.7, 102.94, 89.69, 102.29, 99.37, 110.62], 110, 110.4], 9.5325]], [['regression floating strike leg 1', ['strike-call', [97.05, None, 91.77, None, None, 82.64, 96.57, 112.36], 100, 82.53], 0.0], ['regression floating strike leg 2', ['strike-call', [None, 87.57, 95.18, 82.58, None], 110, 80.04], 0.0], ['partial repair probe 1', ['strike-call', [87.17, 102.54, 83.6], 110, 103.05], 11.946667], ['partial repair probe 2', ['strike-call', [117.37, 113.79], 100, 105.4], 0.0], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-call', [115.75, 98.8, None, None], 90, 102.14], 13.0375], ['normal control 2', ['price-put', [None, 102.49], 110, 90.49], 7.51], ['normal control 3', ['price-put', [81.06, None, 82.46, 117.08, 94.93, 115.96], 110, 106.53], 14.575]], [['regression floating strike leg 1', ['strike-call', [106.31, 106.2, 110.92, 88.16], 100, 88.23], 0.0], ['regression floating strike leg 2', ['strike-call', [87.28, 99.15, 107.74, 95.33, 101.03], 105, 105.7], 7.594], ['partial repair probe 1', ['strike-call', [104.99, None, None, 92.17, None, 102.65, 88.75], 110, 118.6], 19.927143], ['partial repair probe 2', ['strike-call', [88.17, None], 90, 107.11], 18.94], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-put', [86.78, 117.58, 90.09, 111.99, 83.86, 115.07, 82.55], 90, 95.52], 0.0], ['normal control 2', ['price-call', [80.15, 119.98, 88.0, 102.79, 87.59, None, None], 100, 117.78], 0.0], ['normal control 3', ['price-call', [100.9, 119.43, 91.91, 84.18, 100.52], 110, 104.53], 0.0]], [['regression floating strike leg 1', ['strike-call', [112.97, 91.85, 111.17], 105, 96.84], 0.0], ['regression floating strike leg 2', ['strike-call', [87.61, None, 97.7, 109.43, None, None, 80.17], 100, 84.68], 0.0], ['partial repair probe 1', ['strike-call', [None, None, None, 108.09, 105.86, 80.01, None, None], 90, 100.06], 2.77875], ['partial repair probe 2', ['strike-call', [82.63, 86.84, 116.07, 94.98, None, 111.46], 90, 91.41], 0.0], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-call', [81.64, 110.92, 119.01, 112.86, 113.88, 113.24], 105, 86.69], 3.591667], ['normal control 2', ['price-call', [94.17, 108.43, 107.73, 88.19, None], 100, 80.09], 0.0], ['normal control 3', ['price-put', [81.81, 90.42, None, 86.29, 104.03, 100.86], 90, 119.4], 0.0]], [['regression floating strike leg 1', ['strike-call', [84.05, 111.33, 119.36, 95.5, 109.08, 81.91], 105, 89.98], 0.0], ['regression floating strike leg 2', ['strike-call', [88.89, None, 92.84], 100, 107.62], 17.413333], ['partial repair probe 1', ['strike-call', [85.48, 100.59, 86.54, None, 96.53, None], 110, 98.91], 6.875], ['partial repair probe 2', ['strike-call', [None, 119.94, 87.68, 92.42, None, None], 105, 114.08], 13.276667], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-call', [93.01, 112.21, 93.6, 118.27, 97.52, 89.11, None, None], 110, 85.35], 0.0], ['normal control 2', ['price-call', [None, 92.28, 115.75, None, 91.4, 106.72, 114.83, 84.89], 100, 82.1], 1.7375], ['normal control 3', ['price-put', [85.64, 111.27, 89.68, 100.6, 119.83, None, 99.77], 105, 96.0], 1.197143]]]
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 floating strike leg 1 | 0.0 | 0.0 | Passed |
| regression floating strike leg 2 | 16.853333 | 16.853333 | Passed |
| partial repair probe 1 | 3.4675 | 3.4675 | Passed |
| partial repair probe 2 | 18.416 | 18.416 | Passed |
| boundary control 1 | 3.333333 | 3.333333 | Passed |
| normal control 1 | 14.9 | 14.9 | Passed |
| normal control 2 | 11.63 | 11.63 | Passed |
| normal control 3 | 9.5325 | 9.5325 | Passed |
SHA-256 / 723bac1c3c92469e5dcf13fd59c1f36ca4afbdf63d2af524381411b9b9bc2302
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.100870+00:00.
Case digest / de174d8e8acab423442679b6b8016e2b2d1d13b1706cc6380409f8d2169b071c