FAILURE MAP
← Case archive

FA-61451 / Options payoff and settlement / Open access

Discretely monitored barrier option payoff: a knock-in that never activates pays nothing · case 01

Holders of knock-in options with a rebate receive zero when the barrier is not hit.

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

ROOT CAUSE

The knock-in branch returns 0 instead of the rebate when not hit.

VERIFIED REPAIR

Pay the rebate when a knock-in option never activates.

Unsuccessful approach: Paying the rebate only for up-and-in options still drops it for down-and-in.

Case contract

Inputs kind (up/down, in/out, call/put), observed path including initial and final price, strike, barrier and rebate. The barrier is hit if any observation is >= barrier (up) or <= barrier (down). Knock-out: rebate if hit else vanilla on the final price. Knock-in: vanilla if hit else rebate. 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, path, strike, barrier, rebate):
    up = kind.startswith('up')
    hit = any(p >= barrier for p in path) if up else any(p <= barrier for p in path)
    final = path[-1]
    vanilla = max(final - strike, 0) if kind.endswith('call') else max(strike - final, 0)
    if '-out-' in kind:
        pay = rebate if hit else vanilla
    else:
        pay = vanilla if hit else 0.0
    return round(pay, 6)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression knock-in rebate 1', ['down-in-put', [100.0, 93.0, 102.3, 107.42, 112.79, 124.07, 111.66], 100, 85.0, 1.5], 1.5], ['regression knock-in rebate 2', ['down-in-put', [100.0, 90.0, 83.7, 92.07, 99.44, 89.5], 105, 80.0, 1.5], 1.5], ['partial repair probe 1', ['down-in-call', [100.0, 90.0, 94.5, 102.06, 94.92, 94.92, 88.28, 95.34], 105, 80.0, 3.0], 3.0], ['partial repair probe 2', ['down-in-put', [100.0, 108.0, 116.64, 108.48, 97.63, 102.51], 100, 85.0, 1.5], 1.5], ['boundary control 1', ['up-out-call', [100.0, 110.0, 105.0], 100, 110.0, 2.0], 2.0], ['normal control 1', ['up-out-call', [100.0, 100.0, 105.0, 97.65, 105.46], 95, 110.0, 3.0], 10.46], ['normal control 2', ['up-in-call', [100.0, 90.0, 90.0, 99.0, 89.1], 100, 90.0, 1.5], 0], ['normal control 3', ['down-out-put', [100.0, 100.0, 100.0, 95.0], 100, 85.0, 0.0], 5.0]], [['regression knock-in rebate 1', ['down-in-call', [100.0, 95.0, 99.75, 94.76], 105, 80.0, 1.5], 1.5], ['regression knock-in rebate 2', ['down-in-call', [100.0, 105.0, 113.4, 107.73], 105, 90.0, 1.5], 1.5], ['partial repair probe 1', ['down-in-call', [100.0, 110.0, 115.5, 103.95, 112.27, 101.04, 109.12], 100, 90.0, 3.0], 3.0], ['partial repair probe 2', ['down-in-call', [100.0, 108.0, 97.2, 104.98, 94.48, 102.04, 96.94, 87.25], 95, 85.0, 3.0], 3.0], ['boundary control 1', ['up-out-call', [100.0, 110.0, 105.0], 100, 110.0, 2.0], 2.0], ['normal control 1', ['down-out-put', [100.0, 110.0, 115.5, 103.95], 100, 90.0, 0.0], 0], ['normal control 2', ['up-out-put', [100.0, 93.0, 102.3, 112.53, 106.9, 106.9], 95, 110.0, 0.0], 0.0], ['normal control 3', ['up-out-call', [100.0, 108.0, 116.64], 95, 115.0, 1.5], 1.5]], [['regression knock-in rebate 1', ['up-in-call', [100.0, 110.0, 110.0], 105, 115.0, 1.5], 1.5], ['regression knock-in rebate 2', ['down-in-call', [100.0, 95.0, 90.25, 97.47, 97.47, 102.34], 105, 90.0, 1.5], 1.5], ['partial repair probe 1', ['down-in-put', [100.0, 95.0, 99.75], 105, 85.0, 1.5], 1.5], ['partial repair probe 2', ['down-in-call', [100.0, 95.0, 95.0, 95.0, 85.5, 89.78, 85.29, 92.11], 100, 85.0, 3.0], 3.0], ['boundary control 1', ['up-out-call', [100.0, 110.0, 105.0], 100, 110.0, 2.0], 2.0], ['normal control 1', ['up-out-call', [100.0, 110.0, 99.0, 103.95, 114.35], 100, 110.0, 3.0], 3.0], ['normal control 2', ['down-out-put', [100.0, 110.0, 118.8, 112.86, 121.89, 134.08], 105, 90.0, 3.0], 0], ['normal control 3', ['down-out-put', [100.0, 100.0, 95.0, 99.75, 94.76, 94.76, 102.34, 110.53], 100, 80.0, 3.0], 0]], [['regression knock-in rebate 1', ['up-in-call', [100.0, 105.0, 94.5, 103.95, 93.56], 95, 115.0, 3.0], 3.0], ['regression knock-in rebate 2', ['down-in-call', [100.0, 108.0, 97.2], 100, 85.0, 1.5], 1.5], ['partial repair probe 1', ['down-in-put', [100.0, 108.0, 118.8, 112.86], 100, 90.0, 1.5], 1.5], ['partial repair probe 2', ['down-in-call', [100.0, 100.0, 93.0, 88.35], 95, 85.0, 3.0], 3.0], ['boundary control 1', ['up-out-call', [100.0, 110.0, 105.0], 100, 110.0, 2.0], 2.0], ['normal control 1', ['down-in-put', [100.0, 100.0, 105.0, 115.5, 107.42, 99.9, 107.89], 100, 99.9, 1.5], 0], ['normal control 2', ['down-in-call', [100.0, 90.0, 97.2, 104.98], 105, 97.2, 0.0], 0], ['normal control 3', ['up-out-put', [100.0, 108.0, 116.64, 108.48, 103.06, 97.91, 102.81], 95, 108.48, 3.0], 3.0]], [['regression knock-in rebate 1', ['down-in-put', [100.0, 110.0, 99.0], 105, 90.0, 1.5], 1.5], ['regression knock-in rebate 2', ['down-in-put', [100.0, 95.0, 88.35, 95.42, 100.19], 95, 80.0, 3.0], 3.0], ['partial repair probe 1', ['down-in-put', [100.0, 110.0, 110.0, 110.0, 102.3, 110.48, 104.96], 100, 90.0, 1.5], 1.5], ['partial repair probe 2', ['down-in-call', [100.0, 95.0, 104.5, 99.27, 94.31, 101.85], 100, 90.0, 1.5], 1.5], ['boundary control 1', ['up-out-call', [100.0, 110.0, 105.0], 100, 110.0, 2.0], 2.0], ['normal control 1', ['up-out-call', [100.0, 105.0, 113.4, 105.46], 105, 115.0, 1.5], 0.46], ['normal control 2', ['up-out-call', [100.0, 108.0, 102.6, 102.6, 110.81, 119.67], 95, 95.0, 3.0], 3.0], ['normal control 3', ['down-in-call', [100.0, 105.0, 115.5, 109.72, 120.69, 108.62, 108.62], 100, 85.0, 0.0], 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 knock-in rebate 10.01.5Failed
regression knock-in rebate 20.01.5Failed
partial repair probe 10.03.0Failed
partial repair probe 20.01.5Failed
boundary control 12.02.0Passed
normal control 110.4610.46Passed
normal control 200Passed
normal control 35.05.0Passed

SHA-256 / 8f03621ad0db0619f502e4a3fc06ed5e3c08dcb86284a74e9550a4547b61e274

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(kind, path, strike, barrier, rebate):
    up = kind.startswith('up')
    hit = any(p >= barrier for p in path) if up else any(p <= barrier for p in path)
    final = path[-1]
    vanilla = max(final - strike, 0) if kind.endswith('call') else max(strike - final, 0)
    if '-out-' in kind:
        pay = rebate if hit else vanilla
    else:
        pay = vanilla if hit else (rebate if up else 0.0)
    return round(pay, 6)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression knock-in rebate 1', ['down-in-put', [100.0, 93.0, 102.3, 107.42, 112.79, 124.07, 111.66], 100, 85.0, 1.5], 1.5], ['regression knock-in rebate 2', ['down-in-put', [100.0, 90.0, 83.7, 92.07, 99.44, 89.5], 105, 80.0, 1.5], 1.5], ['partial repair probe 1', ['down-in-call', [100.0, 90.0, 94.5, 102.06, 94.92, 94.92, 88.28, 95.34], 105, 80.0, 3.0], 3.0], ['partial repair probe 2', ['down-in-put', [100.0, 108.0, 116.64, 108.48, 97.63, 102.51], 100, 85.0, 1.5], 1.5], ['boundary control 1', ['up-out-call', [100.0, 110.0, 105.0], 100, 110.0, 2.0], 2.0], ['normal control 1', ['up-out-call', [100.0, 100.0, 105.0, 97.65, 105.46], 95, 110.0, 3.0], 10.46], ['normal control 2', ['up-in-call', [100.0, 90.0, 90.0, 99.0, 89.1], 100, 90.0, 1.5], 0], ['normal control 3', ['down-out-put', [100.0, 100.0, 100.0, 95.0], 100, 85.0, 0.0], 5.0]], [['regression knock-in rebate 1', ['down-in-call', [100.0, 95.0, 99.75, 94.76], 105, 80.0, 1.5], 1.5], ['regression knock-in rebate 2', ['down-in-call', [100.0, 105.0, 113.4, 107.73], 105, 90.0, 1.5], 1.5], ['partial repair probe 1', ['down-in-call', [100.0, 110.0, 115.5, 103.95, 112.27, 101.04, 109.12], 100, 90.0, 3.0], 3.0], ['partial repair probe 2', ['down-in-call', [100.0, 108.0, 97.2, 104.98, 94.48, 102.04, 96.94, 87.25], 95, 85.0, 3.0], 3.0], ['boundary control 1', ['up-out-call', [100.0, 110.0, 105.0], 100, 110.0, 2.0], 2.0], ['normal control 1', ['down-out-put', [100.0, 110.0, 115.5, 103.95], 100, 90.0, 0.0], 0], ['normal control 2', ['up-out-put', [100.0, 93.0, 102.3, 112.53, 106.9, 106.9], 95, 110.0, 0.0], 0.0], ['normal control 3', ['up-out-call', [100.0, 108.0, 116.64], 95, 115.0, 1.5], 1.5]], [['regression knock-in rebate 1', ['up-in-call', [100.0, 110.0, 110.0], 105, 115.0, 1.5], 1.5], ['regression knock-in rebate 2', ['down-in-call', [100.0, 95.0, 90.25, 97.47, 97.47, 102.34], 105, 90.0, 1.5], 1.5], ['partial repair probe 1', ['down-in-put', [100.0, 95.0, 99.75], 105, 85.0, 1.5], 1.5], ['partial repair probe 2', ['down-in-call', [100.0, 95.0, 95.0, 95.0, 85.5, 89.78, 85.29, 92.11], 100, 85.0, 3.0], 3.0], ['boundary control 1', ['up-out-call', [100.0, 110.0, 105.0], 100, 110.0, 2.0], 2.0], ['normal control 1', ['up-out-call', [100.0, 110.0, 99.0, 103.95, 114.35], 100, 110.0, 3.0], 3.0], ['normal control 2', ['down-out-put', [100.0, 110.0, 118.8, 112.86, 121.89, 134.08], 105, 90.0, 3.0], 0], ['normal control 3', ['down-out-put', [100.0, 100.0, 95.0, 99.75, 94.76, 94.76, 102.34, 110.53], 100, 80.0, 3.0], 0]], [['regression knock-in rebate 1', ['up-in-call', [100.0, 105.0, 94.5, 103.95, 93.56], 95, 115.0, 3.0], 3.0], ['regression knock-in rebate 2', ['down-in-call', [100.0, 108.0, 97.2], 100, 85.0, 1.5], 1.5], ['partial repair probe 1', ['down-in-put', [100.0, 108.0, 118.8, 112.86], 100, 90.0, 1.5], 1.5], ['partial repair probe 2', ['down-in-call', [100.0, 100.0, 93.0, 88.35], 95, 85.0, 3.0], 3.0], ['boundary control 1', ['up-out-call', [100.0, 110.0, 105.0], 100, 110.0, 2.0], 2.0], ['normal control 1', ['down-in-put', [100.0, 100.0, 105.0, 115.5, 107.42, 99.9, 107.89], 100, 99.9, 1.5], 0], ['normal control 2', ['down-in-call', [100.0, 90.0, 97.2, 104.98], 105, 97.2, 0.0], 0], ['normal control 3', ['up-out-put', [100.0, 108.0, 116.64, 108.48, 103.06, 97.91, 102.81], 95, 108.48, 3.0], 3.0]], [['regression knock-in rebate 1', ['down-in-put', [100.0, 110.0, 99.0], 105, 90.0, 1.5], 1.5], ['regression knock-in rebate 2', ['down-in-put', [100.0, 95.0, 88.35, 95.42, 100.19], 95, 80.0, 3.0], 3.0], ['partial repair probe 1', ['down-in-put', [100.0, 110.0, 110.0, 110.0, 102.3, 110.48, 104.96], 100, 90.0, 1.5], 1.5], ['partial repair probe 2', ['down-in-call', [100.0, 95.0, 104.5, 99.27, 94.31, 101.85], 100, 90.0, 1.5], 1.5], ['boundary control 1', ['up-out-call', [100.0, 110.0, 105.0], 100, 110.0, 2.0], 2.0], ['normal control 1', ['up-out-call', [100.0, 105.0, 113.4, 105.46], 105, 115.0, 1.5], 0.46], ['normal control 2', ['up-out-call', [100.0, 108.0, 102.6, 102.6, 110.81, 119.67], 95, 95.0, 3.0], 3.0], ['normal control 3', ['down-in-call', [100.0, 105.0, 115.5, 109.72, 120.69, 108.62, 108.62], 100, 85.0, 0.0], 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 knock-in rebate 10.01.5Failed
regression knock-in rebate 20.01.5Failed
partial repair probe 10.03.0Failed
partial repair probe 20.01.5Failed
boundary control 12.02.0Passed
normal control 110.4610.46Passed
normal control 200Passed
normal control 35.05.0Passed

SHA-256 / bab686786f0ecb8648cbf7aacf0efd7d4c5a9794712ad637d858489eebeca9ff

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(kind, path, strike, barrier, rebate):
    up = kind.startswith('up')
    hit = any(p >= barrier for p in path) if up else any(p <= barrier for p in path)
    final = path[-1]
    vanilla = max(final - strike, 0) if kind.endswith('call') else max(strike - final, 0)
    if '-out-' in kind:
        pay = rebate if hit else vanilla
    else:
        pay = vanilla if hit else rebate
    return round(pay, 6)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression knock-in rebate 1', ['down-in-put', [100.0, 93.0, 102.3, 107.42, 112.79, 124.07, 111.66], 100, 85.0, 1.5], 1.5], ['regression knock-in rebate 2', ['down-in-put', [100.0, 90.0, 83.7, 92.07, 99.44, 89.5], 105, 80.0, 1.5], 1.5], ['partial repair probe 1', ['down-in-call', [100.0, 90.0, 94.5, 102.06, 94.92, 94.92, 88.28, 95.34], 105, 80.0, 3.0], 3.0], ['partial repair probe 2', ['down-in-put', [100.0, 108.0, 116.64, 108.48, 97.63, 102.51], 100, 85.0, 1.5], 1.5], ['boundary control 1', ['up-out-call', [100.0, 110.0, 105.0], 100, 110.0, 2.0], 2.0], ['normal control 1', ['up-out-call', [100.0, 100.0, 105.0, 97.65, 105.46], 95, 110.0, 3.0], 10.46], ['normal control 2', ['up-in-call', [100.0, 90.0, 90.0, 99.0, 89.1], 100, 90.0, 1.5], 0], ['normal control 3', ['down-out-put', [100.0, 100.0, 100.0, 95.0], 100, 85.0, 0.0], 5.0]], [['regression knock-in rebate 1', ['down-in-call', [100.0, 95.0, 99.75, 94.76], 105, 80.0, 1.5], 1.5], ['regression knock-in rebate 2', ['down-in-call', [100.0, 105.0, 113.4, 107.73], 105, 90.0, 1.5], 1.5], ['partial repair probe 1', ['down-in-call', [100.0, 110.0, 115.5, 103.95, 112.27, 101.04, 109.12], 100, 90.0, 3.0], 3.0], ['partial repair probe 2', ['down-in-call', [100.0, 108.0, 97.2, 104.98, 94.48, 102.04, 96.94, 87.25], 95, 85.0, 3.0], 3.0], ['boundary control 1', ['up-out-call', [100.0, 110.0, 105.0], 100, 110.0, 2.0], 2.0], ['normal control 1', ['down-out-put', [100.0, 110.0, 115.5, 103.95], 100, 90.0, 0.0], 0], ['normal control 2', ['up-out-put', [100.0, 93.0, 102.3, 112.53, 106.9, 106.9], 95, 110.0, 0.0], 0.0], ['normal control 3', ['up-out-call', [100.0, 108.0, 116.64], 95, 115.0, 1.5], 1.5]], [['regression knock-in rebate 1', ['up-in-call', [100.0, 110.0, 110.0], 105, 115.0, 1.5], 1.5], ['regression knock-in rebate 2', ['down-in-call', [100.0, 95.0, 90.25, 97.47, 97.47, 102.34], 105, 90.0, 1.5], 1.5], ['partial repair probe 1', ['down-in-put', [100.0, 95.0, 99.75], 105, 85.0, 1.5], 1.5], ['partial repair probe 2', ['down-in-call', [100.0, 95.0, 95.0, 95.0, 85.5, 89.78, 85.29, 92.11], 100, 85.0, 3.0], 3.0], ['boundary control 1', ['up-out-call', [100.0, 110.0, 105.0], 100, 110.0, 2.0], 2.0], ['normal control 1', ['up-out-call', [100.0, 110.0, 99.0, 103.95, 114.35], 100, 110.0, 3.0], 3.0], ['normal control 2', ['down-out-put', [100.0, 110.0, 118.8, 112.86, 121.89, 134.08], 105, 90.0, 3.0], 0], ['normal control 3', ['down-out-put', [100.0, 100.0, 95.0, 99.75, 94.76, 94.76, 102.34, 110.53], 100, 80.0, 3.0], 0]], [['regression knock-in rebate 1', ['up-in-call', [100.0, 105.0, 94.5, 103.95, 93.56], 95, 115.0, 3.0], 3.0], ['regression knock-in rebate 2', ['down-in-call', [100.0, 108.0, 97.2], 100, 85.0, 1.5], 1.5], ['partial repair probe 1', ['down-in-put', [100.0, 108.0, 118.8, 112.86], 100, 90.0, 1.5], 1.5], ['partial repair probe 2', ['down-in-call', [100.0, 100.0, 93.0, 88.35], 95, 85.0, 3.0], 3.0], ['boundary control 1', ['up-out-call', [100.0, 110.0, 105.0], 100, 110.0, 2.0], 2.0], ['normal control 1', ['down-in-put', [100.0, 100.0, 105.0, 115.5, 107.42, 99.9, 107.89], 100, 99.9, 1.5], 0], ['normal control 2', ['down-in-call', [100.0, 90.0, 97.2, 104.98], 105, 97.2, 0.0], 0], ['normal control 3', ['up-out-put', [100.0, 108.0, 116.64, 108.48, 103.06, 97.91, 102.81], 95, 108.48, 3.0], 3.0]], [['regression knock-in rebate 1', ['down-in-put', [100.0, 110.0, 99.0], 105, 90.0, 1.5], 1.5], ['regression knock-in rebate 2', ['down-in-put', [100.0, 95.0, 88.35, 95.42, 100.19], 95, 80.0, 3.0], 3.0], ['partial repair probe 1', ['down-in-put', [100.0, 110.0, 110.0, 110.0, 102.3, 110.48, 104.96], 100, 90.0, 1.5], 1.5], ['partial repair probe 2', ['down-in-call', [100.0, 95.0, 104.5, 99.27, 94.31, 101.85], 100, 90.0, 1.5], 1.5], ['boundary control 1', ['up-out-call', [100.0, 110.0, 105.0], 100, 110.0, 2.0], 2.0], ['normal control 1', ['up-out-call', [100.0, 105.0, 113.4, 105.46], 105, 115.0, 1.5], 0.46], ['normal control 2', ['up-out-call', [100.0, 108.0, 102.6, 102.6, 110.81, 119.67], 95, 95.0, 3.0], 3.0], ['normal control 3', ['down-in-call', [100.0, 105.0, 115.5, 109.72, 120.69, 108.62, 108.62], 100, 85.0, 0.0], 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 knock-in rebate 11.51.5Passed
regression knock-in rebate 21.51.5Passed
partial repair probe 13.03.0Passed
partial repair probe 21.51.5Passed
boundary control 12.02.0Passed
normal control 110.4610.46Passed
normal control 200Passed
normal control 35.05.0Passed

SHA-256 / 6f9a57d67c8edc40bd41ccf2a46654576bf24996d081a0ebf28b6e8633d829a3

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

Case digest / e874af908eb13d928a25e98e1d390e7bb3819e73182c9d41793a61db50d4c00f