FAILURE MAP
← Case archive

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.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

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 fixtureActualExpectedOutcome
regression averaging set 13.42753.917143Failed
regression averaging set 22.4611113.05625Failed
partial repair probe 10.00.0Passed
partial repair probe 20.00.0Passed
normal control 10.00.0Passed
normal control 20.00.0Passed
normal control 30.00.0Passed
normal control 40.00.0Passed

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 fixtureActualExpectedOutcome
regression averaging set 12.8383333.917143Failed
regression averaging set 23.5914293.05625Failed
partial repair probe 13.2333330.0Failed
partial repair probe 21.4950.0Failed
normal control 10.00.0Passed
normal control 20.00.0Passed
normal control 30.00.0Passed
normal control 40.00.0Passed

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 fixtureActualExpectedOutcome
regression averaging set 13.9171433.917143Passed
regression averaging set 23.056253.05625Passed
partial repair probe 10.00.0Passed
partial repair probe 20.00.0Passed
normal control 10.00.0Passed
normal control 20.00.0Passed
normal control 30.00.0Passed
normal control 40.00.0Passed

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