FAILURE MAP
← Case archive

FA-61421 / Options payoff and settlement / Open access

Arithmetic Asian payoff with disrupted fixings: disruptions prefer the next valid fixing over the previous one · case 01

Mid-schedule disruptions borrow a future price.

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

ROOT CAUSE

The fallback checks later fixings before earlier ones.

VERIFIED REPAIR

Use the last preceding filled value; look forward only when nothing precedes.

Unsuccessful approach: Averaging neighbours interpolates instead of carrying the previous value.

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 = later[0] if later else prev[-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 fallback priority 1', ['price-put', [107.56, 91.47, None, 93.63, 107.09, 85.05], 100, 107.47], 3.955], ['regression fallback priority 2', ['strike-call', [90.82, 116.42, 97.17, None, 102.9], 100, 102.74], 1.844], ['partial repair probe 1', ['strike-call', [119.87, 109.69, None], 110, 116.2], 3.116667], ['partial repair probe 2', ['price-call', [117.31, 109.18, 80.52, 110.93, None], 90, 89.18], 15.774], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-put', [115.48, 83.47, 98.82], 100, 99.98], 0.743333], ['normal control 2', ['strike-call', [111.57, None, None, None, 98.59, 116.83], 110, 84.9], 0.0], ['normal control 3', ['strike-call', [91.38, None, None], 105, 95.03], 3.65]], [['regression fallback priority 1', ['price-call', [98.02, None, 91.49, 95.03, 98.48, 97.35], 90, 119.72], 6.398333], ['regression fallback priority 2', ['price-put', [119.49, 98.97, 101.06, None, 95.58, 113.4, 91.41, 95.29], 105, 88.56], 2.9675], ['partial repair probe 1', ['strike-call', [88.95, 80.84, None], 110, 112.81], 29.266667], ['partial repair probe 2', ['price-put', [119.4, 88.69, 99.38, 103.22, 102.55, None], 110, 112.44], 7.368333], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-put', [85.84, None], 90, 114.04], 4.16], ['normal control 2', ['price-put', [None, 105.9], 105, 82.58], 0.0], ['normal control 3', ['price-put', [111.7, 101.25, 103.45, 114.56, 86.59, None, 114.39], 100, 96.18], 0.0]], [['regression fallback priority 1', ['price-call', [119.01, None, 115.28], 105, 82.02], 12.766667], ['regression fallback priority 2', ['price-call', [84.27, 118.57, None, None, None, 103.89], 100, 80.29], 10.406667], ['partial repair probe 1', ['price-put', [None, 87.07, 89.34, None], 90, 115.78], 1.795], ['partial repair probe 2', ['price-put', [110.48, 85.44, None, None, None], 110, 102.6], 19.552], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-call', [80.8, 102.44, None, 103.49, 106.76, 93.24, None], 110, 93.16], 0.0], ['normal control 2', ['price-call', [96.59, None, 81.5, 86.37, 117.86, 81.57], 100, 80.38], 0.0], ['normal control 3', ['price-put', [93.19, None, None, None, None], 110, 115.13], 16.81]], [['regression fallback priority 1', ['price-put', [85.19, None, 83.45, 104.23, 100.65, None, 102.76], 110, 109.78], 15.411429], ['regression fallback priority 2', ['strike-call', [86.53, 95.55, None, 100.69, 106.44], 90, 104.86], 7.908], ['partial repair probe 1', ['strike-call', [81.55, 101.38, 89.48, None], 100, 100.13], 9.6575], ['partial repair probe 2', ['price-put', [88.01, 87.96, None], 105, 112.32], 17.023333], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-call', [None, 112.37, None, None], 90, 98.65], 22.37], ['normal control 2', ['price-call', [None, 110.4, 80.07], 90, 95.81], 10.29], ['normal control 3', ['price-put', [110.91, None], 100, 116.16], 0.0]], [['regression fallback priority 1', ['strike-call', [100.62, None, 111.91, 86.61, None], 100, 115.98], 18.706], ['regression fallback priority 2', ['price-put', [116.55, 87.44, 91.05, None, 97.68], 110, 114.79], 13.246], ['partial repair probe 1', ['strike-call', [87.08, 93.2, None, None, None, None], 110, 94.72], 2.54], ['partial repair probe 2', ['price-call', [104.79, 89.0, None, None], 100, 101.84], 0.0], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-call', [None, 98.32, 105.85, 84.08], 100, 117.74], 0.0], ['normal control 2', ['price-call', [80.35, 102.5], 105, 119.19], 0.0], ['normal control 3', ['price-call', [97.46, 119.62, 96.51, 117.63, 87.35, 92.18, 101.58, 80.54], 90, 100.37], 9.10875]]]
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 fallback priority 13.5953.955Failed
regression fallback priority 20.6981.844Failed
partial repair probe 13.1166673.116667Passed
partial repair probe 215.77415.774Passed
boundary control 13.3333333.333333Passed
normal control 10.7433330.743333Passed
normal control 20.00.0Passed
normal control 33.653.65Passed

SHA-256 / f316b14fe4e0191f7d80a1e821bb0840b61d7304d7aad99d0cd990d1008b09fe

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] + later[0]) / 2 if prev and later else (prev or 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 fallback priority 1', ['price-put', [107.56, 91.47, None, 93.63, 107.09, 85.05], 100, 107.47], 3.955], ['regression fallback priority 2', ['strike-call', [90.82, 116.42, 97.17, None, 102.9], 100, 102.74], 1.844], ['partial repair probe 1', ['strike-call', [119.87, 109.69, None], 110, 116.2], 3.116667], ['partial repair probe 2', ['price-call', [117.31, 109.18, 80.52, 110.93, None], 90, 89.18], 15.774], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-put', [115.48, 83.47, 98.82], 100, 99.98], 0.743333], ['normal control 2', ['strike-call', [111.57, None, None, None, 98.59, 116.83], 110, 84.9], 0.0], ['normal control 3', ['strike-call', [91.38, None, None], 105, 95.03], 3.65]], [['regression fallback priority 1', ['price-call', [98.02, None, 91.49, 95.03, 98.48, 97.35], 90, 119.72], 6.398333], ['regression fallback priority 2', ['price-put', [119.49, 98.97, 101.06, None, 95.58, 113.4, 91.41, 95.29], 105, 88.56], 2.9675], ['partial repair probe 1', ['strike-call', [88.95, 80.84, None], 110, 112.81], 29.266667], ['partial repair probe 2', ['price-put', [119.4, 88.69, 99.38, 103.22, 102.55, None], 110, 112.44], 7.368333], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-put', [85.84, None], 90, 114.04], 4.16], ['normal control 2', ['price-put', [None, 105.9], 105, 82.58], 0.0], ['normal control 3', ['price-put', [111.7, 101.25, 103.45, 114.56, 86.59, None, 114.39], 100, 96.18], 0.0]], [['regression fallback priority 1', ['price-call', [119.01, None, 115.28], 105, 82.02], 12.766667], ['regression fallback priority 2', ['price-call', [84.27, 118.57, None, None, None, 103.89], 100, 80.29], 10.406667], ['partial repair probe 1', ['price-put', [None, 87.07, 89.34, None], 90, 115.78], 1.795], ['partial repair probe 2', ['price-put', [110.48, 85.44, None, None, None], 110, 102.6], 19.552], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-call', [80.8, 102.44, None, 103.49, 106.76, 93.24, None], 110, 93.16], 0.0], ['normal control 2', ['price-call', [96.59, None, 81.5, 86.37, 117.86, 81.57], 100, 80.38], 0.0], ['normal control 3', ['price-put', [93.19, None, None, None, None], 110, 115.13], 16.81]], [['regression fallback priority 1', ['price-put', [85.19, None, 83.45, 104.23, 100.65, None, 102.76], 110, 109.78], 15.411429], ['regression fallback priority 2', ['strike-call', [86.53, 95.55, None, 100.69, 106.44], 90, 104.86], 7.908], ['partial repair probe 1', ['strike-call', [81.55, 101.38, 89.48, None], 100, 100.13], 9.6575], ['partial repair probe 2', ['price-put', [88.01, 87.96, None], 105, 112.32], 17.023333], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-call', [None, 112.37, None, None], 90, 98.65], 22.37], ['normal control 2', ['price-call', [None, 110.4, 80.07], 90, 95.81], 10.29], ['normal control 3', ['price-put', [110.91, None], 100, 116.16], 0.0]], [['regression fallback priority 1', ['strike-call', [100.62, None, 111.91, 86.61, None], 100, 115.98], 18.706], ['regression fallback priority 2', ['price-put', [116.55, 87.44, 91.05, None, 97.68], 110, 114.79], 13.246], ['partial repair probe 1', ['strike-call', [87.08, 93.2, None, None, None, None], 110, 94.72], 2.54], ['partial repair probe 2', ['price-call', [104.79, 89.0, None, None], 100, 101.84], 0.0], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-call', [None, 98.32, 105.85, 84.08], 100, 117.74], 0.0], ['normal control 2', ['price-call', [80.35, 102.5], 105, 119.19], 0.0], ['normal control 3', ['price-call', [97.46, 119.62, 96.51, 117.63, 87.35, 92.18, 101.58, 80.54], 90, 100.37], 9.10875]]]
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 fallback priority 13.7753.955Failed
regression fallback priority 21.2711.844Failed
partial repair probe 10.03.116667Failed
partial repair probe 217.0515.774Failed
boundary control 13.3333333.333333Passed
normal control 10.7433330.743333Passed
normal control 20.00.0Passed
normal control 33.653.65Passed

SHA-256 / 35b0af2562e043643a737b5815134078478219a0d3dcf4f6dc5ffe4cc0723282

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 fallback priority 1', ['price-put', [107.56, 91.47, None, 93.63, 107.09, 85.05], 100, 107.47], 3.955], ['regression fallback priority 2', ['strike-call', [90.82, 116.42, 97.17, None, 102.9], 100, 102.74], 1.844], ['partial repair probe 1', ['strike-call', [119.87, 109.69, None], 110, 116.2], 3.116667], ['partial repair probe 2', ['price-call', [117.31, 109.18, 80.52, 110.93, None], 90, 89.18], 15.774], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-put', [115.48, 83.47, 98.82], 100, 99.98], 0.743333], ['normal control 2', ['strike-call', [111.57, None, None, None, 98.59, 116.83], 110, 84.9], 0.0], ['normal control 3', ['strike-call', [91.38, None, None], 105, 95.03], 3.65]], [['regression fallback priority 1', ['price-call', [98.02, None, 91.49, 95.03, 98.48, 97.35], 90, 119.72], 6.398333], ['regression fallback priority 2', ['price-put', [119.49, 98.97, 101.06, None, 95.58, 113.4, 91.41, 95.29], 105, 88.56], 2.9675], ['partial repair probe 1', ['strike-call', [88.95, 80.84, None], 110, 112.81], 29.266667], ['partial repair probe 2', ['price-put', [119.4, 88.69, 99.38, 103.22, 102.55, None], 110, 112.44], 7.368333], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-put', [85.84, None], 90, 114.04], 4.16], ['normal control 2', ['price-put', [None, 105.9], 105, 82.58], 0.0], ['normal control 3', ['price-put', [111.7, 101.25, 103.45, 114.56, 86.59, None, 114.39], 100, 96.18], 0.0]], [['regression fallback priority 1', ['price-call', [119.01, None, 115.28], 105, 82.02], 12.766667], ['regression fallback priority 2', ['price-call', [84.27, 118.57, None, None, None, 103.89], 100, 80.29], 10.406667], ['partial repair probe 1', ['price-put', [None, 87.07, 89.34, None], 90, 115.78], 1.795], ['partial repair probe 2', ['price-put', [110.48, 85.44, None, None, None], 110, 102.6], 19.552], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-call', [80.8, 102.44, None, 103.49, 106.76, 93.24, None], 110, 93.16], 0.0], ['normal control 2', ['price-call', [96.59, None, 81.5, 86.37, 117.86, 81.57], 100, 80.38], 0.0], ['normal control 3', ['price-put', [93.19, None, None, None, None], 110, 115.13], 16.81]], [['regression fallback priority 1', ['price-put', [85.19, None, 83.45, 104.23, 100.65, None, 102.76], 110, 109.78], 15.411429], ['regression fallback priority 2', ['strike-call', [86.53, 95.55, None, 100.69, 106.44], 90, 104.86], 7.908], ['partial repair probe 1', ['strike-call', [81.55, 101.38, 89.48, None], 100, 100.13], 9.6575], ['partial repair probe 2', ['price-put', [88.01, 87.96, None], 105, 112.32], 17.023333], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-call', [None, 112.37, None, None], 90, 98.65], 22.37], ['normal control 2', ['price-call', [None, 110.4, 80.07], 90, 95.81], 10.29], ['normal control 3', ['price-put', [110.91, None], 100, 116.16], 0.0]], [['regression fallback priority 1', ['strike-call', [100.62, None, 111.91, 86.61, None], 100, 115.98], 18.706], ['regression fallback priority 2', ['price-put', [116.55, 87.44, 91.05, None, 97.68], 110, 114.79], 13.246], ['partial repair probe 1', ['strike-call', [87.08, 93.2, None, None, None, None], 110, 94.72], 2.54], ['partial repair probe 2', ['price-call', [104.79, 89.0, None, None], 100, 101.84], 0.0], ['boundary control 1', ['price-call', [None, 100.0, 110.0], 100, 100.0], 3.333333], ['normal control 1', ['price-call', [None, 98.32, 105.85, 84.08], 100, 117.74], 0.0], ['normal control 2', ['price-call', [80.35, 102.5], 105, 119.19], 0.0], ['normal control 3', ['price-call', [97.46, 119.62, 96.51, 117.63, 87.35, 92.18, 101.58, 80.54], 90, 100.37], 9.10875]]]
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 fallback priority 13.9553.955Passed
regression fallback priority 21.8441.844Passed
partial repair probe 13.1166673.116667Passed
partial repair probe 215.77415.774Passed
boundary control 13.3333333.333333Passed
normal control 10.7433330.743333Passed
normal control 20.00.0Passed
normal control 33.653.65Passed

SHA-256 / 2c5c22b8f85e355cda5820d337ef2f23b59f28d56e896d75357d27a982166ce1

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.059710+00:00.

Case digest / 392e78a2d1430a85bb192f4dc7f2c5a39167d901e9a8ef191b717425bd2cc5e9