FAILURE MAP
← Case archive

FA-61536 / Options payoff and settlement / Open access

Cox-Ross-Rubinstein American option tree: up and down probabilities are attached to the wrong children · case 01

Continuation values are biased toward the down state.

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

ROOT CAUSE

The continuation weights p on vals[j] and 1-p on vals[j+1].

VERIFIED REPAIR

Weight the up child vals[j+1] by p.

Unsuccessful approach: Equal weights ignore the risk-neutral probability.

Case contract

Inputs kind, spot S, strike K, rate r, volatility sigma, maturity T in years and steps. dt=T/steps, u=exp(sigma*sqrt(dt)), d=1/u, p=(exp(r*dt)-d)/(u-d), disc=exp(-r*dt). Roll back from terminal payoffs; at each node take max(continuation, immediate exercise at that node's price). Return the root rounded 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
import math
N = 1
observations = []
def solve(kind, S, K, r, sigma, T, steps):
    dt = T / steps
    u = math.exp(sigma * math.sqrt(dt))
    d = 1 / u
    p = (math.exp(r * dt) - d) / (u - d)
    disc = math.exp(-r * dt)
    def pay(s):
        return max(s - K, 0.0) if kind == 'C' else max(K - s, 0.0)
    vals = [pay(S * u ** j * d ** (steps - j)) for j in range(steps + 1)]
    for i in range(steps - 1, -1, -1):
        vals = [max(disc * (p * vals[j] + (1 - p) * vals[j + 1]), pay(S * u ** j * d ** (i - j))) for j in range(i + 1)]
    return round(vals[0], 6)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression continuation weighting 1', ['C', 95.0, 90.0, 0.0, 0.4, 0.5, 25], 13.005948], ['regression continuation weighting 2', ['C', 80.0, 105.0, 0.05, 0.25, 0.5, 12], 0.588112], ['partial repair probe 1', ['P', 80.0, 100.0, 0.01, 0.25, 2.0, 3], 23.588558], ['partial repair probe 2', ['C', 95.0, 100.0, 0.05, 0.4, 2.0, 25], 23.088626], ['normal control 1', ['P', 80.0, 105.0, 0.05, 0.25, 0.25, 12], 25.0], ['normal control 2', ['P', 110.0, 90.0, 0.05, 0.15, 0.25, 3], 0.0], ['normal control 3', ['P', 110.0, 90.0, 0.05, 0.15, 0.5, 3], 0.0], ['normal control 4', ['C', 80.0, 105.0, 0.08, 0.15, 0.25, 12], 0.0]], [['regression continuation weighting 1', ['P', 80.0, 100.0, 0.05, 0.4, 0.25, 5], 20.610754], ['regression continuation weighting 2', ['P', 110.0, 100.0, 0.05, 0.15, 1.0, 12], 1.445712], ['partial repair probe 1', ['C', 110.0, 90.0, 0.01, 0.25, 1.0, 12], 23.733528], ['partial repair probe 2', ['P', 100.0, 100.0, 0.05, 0.15, 0.5, 12], 3.26077], ['normal control 1', ['P', 80.0, 100.0, 0.01, 0.15, 0.5, 25], 20.0], ['normal control 2', ['P', 80.0, 105.0, 0.01, 0.15, 0.25, 25], 25.0], ['normal control 3', ['P', 80.0, 105.0, 0.08, 0.4, 0.25, 3], 25.0], ['normal control 4', ['P', 80.0, 105.0, 0.05, 0.25, 0.25, 3], 25.0]], [['regression continuation weighting 1', ['P', 110.0, 100.0, 0.01, 0.15, 0.5, 3], 1.063038], ['regression continuation weighting 2', ['P', 95.0, 105.0, 0.01, 0.25, 0.25, 25], 11.367995], ['partial repair probe 1', ['C', 80.0, 105.0, 0.08, 0.4, 0.25, 3], 0.995113], ['partial repair probe 2', ['P', 95.0, 105.0, 0.05, 0.15, 2.0, 5], 10.529554], ['normal control 1', ['P', 80.0, 100.0, 0.0, 0.15, 0.5, 3], 20.0], ['normal control 2', ['P', 80.0, 105.0, 0.05, 0.25, 0.5, 3], 25.0], ['normal control 3', ['P', 80.0, 105.0, 0.01, 0.15, 0.5, 3], 25.0], ['normal control 4', ['C', 80.0, 105.0, 0.05, 0.15, 0.25, 5], 0.0]], [['regression continuation weighting 1', ['P', 95.0, 105.0, 0.01, 0.15, 1.0, 25], 11.69818], ['regression continuation weighting 2', ['C', 95.0, 100.0, 0.01, 0.4, 1.0, 3], 14.601622], ['partial repair probe 1', ['P', 110.0, 90.0, 0.0, 0.25, 0.25, 5], 0.244718], ['partial repair probe 2', ['P', 110.0, 105.0, 0.08, 0.15, 1.0, 5], 2.188872], ['normal control 1', ['P', 80.0, 105.0, 0.01, 0.15, 1.0, 5], 25.0], ['normal control 2', ['C', 80.0, 105.0, 0.01, 0.15, 1.0, 3], 0.0], ['normal control 3', ['P', 80.0, 105.0, 0.0, 0.15, 1.0, 3], 25.0], ['normal control 4', ['P', 80.0, 105.0, 0.01, 0.15, 1.0, 12], 25.0]], [['regression continuation weighting 1', ['C', 110.0, 90.0, 0.0, 0.25, 1.0, 3], 22.873702], ['regression continuation weighting 2', ['P', 110.0, 100.0, 0.0, 0.25, 1.0, 5], 5.971362], ['partial repair probe 1', ['P', 80.0, 90.0, 0.05, 0.15, 2.0, 25], 10.363792], ['partial repair probe 2', ['P', 100.0, 90.0, 0.08, 0.4, 0.5, 25], 5.322102], ['normal control 1', ['P', 80.0, 100.0, 0.01, 0.15, 1.0, 5], 20.0], ['normal control 2', ['P', 80.0, 105.0, 0.0, 0.15, 1.0, 3], 25.0], ['normal control 3', ['P', 80.0, 100.0, 0.05, 0.25, 0.25, 3], 20.0], ['normal control 4', ['C', 80.0, 105.0, 0.08, 0.15, 0.25, 5], 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 continuation weighting 118.39798713.005948Failed
regression continuation weighting 20.4650210.588112Failed
partial repair probe 121.23131623.588558Failed
partial repair probe 231.26918823.088626Failed
normal control 125.025.0Passed
normal control 20.00.0Passed
normal control 30.00.0Passed
normal control 40.00.0Passed

SHA-256 / 1a74248fad7aa3b4c60bc2302205e179153f7a1ee3adfe31e4282710232c03f0

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(kind, S, K, r, sigma, T, steps):
    dt = T / steps
    u = math.exp(sigma * math.sqrt(dt))
    d = 1 / u
    p = (math.exp(r * dt) - d) / (u - d)
    disc = math.exp(-r * dt)
    def pay(s):
        return max(s - K, 0.0) if kind == 'C' else max(K - s, 0.0)
    vals = [pay(S * u ** j * d ** (steps - j)) for j in range(steps + 1)]
    for i in range(steps - 1, -1, -1):
        vals = [max(disc * 0.5 * (vals[j] + vals[j + 1]), pay(S * u ** j * d ** (i - j))) for j in range(i + 1)]
    return round(vals[0], 6)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression continuation weighting 1', ['C', 95.0, 90.0, 0.0, 0.4, 0.5, 25], 13.005948], ['regression continuation weighting 2', ['C', 80.0, 105.0, 0.05, 0.25, 0.5, 12], 0.588112], ['partial repair probe 1', ['P', 80.0, 100.0, 0.01, 0.25, 2.0, 3], 23.588558], ['partial repair probe 2', ['C', 95.0, 100.0, 0.05, 0.4, 2.0, 25], 23.088626], ['normal control 1', ['P', 80.0, 105.0, 0.05, 0.25, 0.25, 12], 25.0], ['normal control 2', ['P', 110.0, 90.0, 0.05, 0.15, 0.25, 3], 0.0], ['normal control 3', ['P', 110.0, 90.0, 0.05, 0.15, 0.5, 3], 0.0], ['normal control 4', ['C', 80.0, 105.0, 0.08, 0.15, 0.25, 12], 0.0]], [['regression continuation weighting 1', ['P', 80.0, 100.0, 0.05, 0.4, 0.25, 5], 20.610754], ['regression continuation weighting 2', ['P', 110.0, 100.0, 0.05, 0.15, 1.0, 12], 1.445712], ['partial repair probe 1', ['C', 110.0, 90.0, 0.01, 0.25, 1.0, 12], 23.733528], ['partial repair probe 2', ['P', 100.0, 100.0, 0.05, 0.15, 0.5, 12], 3.26077], ['normal control 1', ['P', 80.0, 100.0, 0.01, 0.15, 0.5, 25], 20.0], ['normal control 2', ['P', 80.0, 105.0, 0.01, 0.15, 0.25, 25], 25.0], ['normal control 3', ['P', 80.0, 105.0, 0.08, 0.4, 0.25, 3], 25.0], ['normal control 4', ['P', 80.0, 105.0, 0.05, 0.25, 0.25, 3], 25.0]], [['regression continuation weighting 1', ['P', 110.0, 100.0, 0.01, 0.15, 0.5, 3], 1.063038], ['regression continuation weighting 2', ['P', 95.0, 105.0, 0.01, 0.25, 0.25, 25], 11.367995], ['partial repair probe 1', ['C', 80.0, 105.0, 0.08, 0.4, 0.25, 3], 0.995113], ['partial repair probe 2', ['P', 95.0, 105.0, 0.05, 0.15, 2.0, 5], 10.529554], ['normal control 1', ['P', 80.0, 100.0, 0.0, 0.15, 0.5, 3], 20.0], ['normal control 2', ['P', 80.0, 105.0, 0.05, 0.25, 0.5, 3], 25.0], ['normal control 3', ['P', 80.0, 105.0, 0.01, 0.15, 0.5, 3], 25.0], ['normal control 4', ['C', 80.0, 105.0, 0.05, 0.15, 0.25, 5], 0.0]], [['regression continuation weighting 1', ['P', 95.0, 105.0, 0.01, 0.15, 1.0, 25], 11.69818], ['regression continuation weighting 2', ['C', 95.0, 100.0, 0.01, 0.4, 1.0, 3], 14.601622], ['partial repair probe 1', ['P', 110.0, 90.0, 0.0, 0.25, 0.25, 5], 0.244718], ['partial repair probe 2', ['P', 110.0, 105.0, 0.08, 0.15, 1.0, 5], 2.188872], ['normal control 1', ['P', 80.0, 105.0, 0.01, 0.15, 1.0, 5], 25.0], ['normal control 2', ['C', 80.0, 105.0, 0.01, 0.15, 1.0, 3], 0.0], ['normal control 3', ['P', 80.0, 105.0, 0.0, 0.15, 1.0, 3], 25.0], ['normal control 4', ['P', 80.0, 105.0, 0.01, 0.15, 1.0, 12], 25.0]], [['regression continuation weighting 1', ['C', 110.0, 90.0, 0.0, 0.25, 1.0, 3], 22.873702], ['regression continuation weighting 2', ['P', 110.0, 100.0, 0.0, 0.25, 1.0, 5], 5.971362], ['partial repair probe 1', ['P', 80.0, 90.0, 0.05, 0.15, 2.0, 25], 10.363792], ['partial repair probe 2', ['P', 100.0, 90.0, 0.08, 0.4, 0.5, 25], 5.322102], ['normal control 1', ['P', 80.0, 100.0, 0.01, 0.15, 1.0, 5], 20.0], ['normal control 2', ['P', 80.0, 105.0, 0.0, 0.15, 1.0, 3], 25.0], ['normal control 3', ['P', 80.0, 100.0, 0.05, 0.25, 0.25, 3], 20.0], ['normal control 4', ['C', 80.0, 105.0, 0.08, 0.15, 0.25, 5], 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 continuation weighting 115.55211313.005948Failed
regression continuation weighting 20.5235560.588112Failed
partial repair probe 122.43464523.588558Failed
partial repair probe 226.95175323.088626Failed
normal control 125.025.0Passed
normal control 20.00.0Passed
normal control 30.00.0Passed
normal control 40.00.0Passed

SHA-256 / 5cb811a975debc4f7c9b30960b8738895d25bbf1ef4c702a03872b74bb4ba808

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json
import math
N = 1
observations = []
def solve(kind, S, K, r, sigma, T, steps):
    dt = T / steps
    u = math.exp(sigma * math.sqrt(dt))
    d = 1 / u
    p = (math.exp(r * dt) - d) / (u - d)
    disc = math.exp(-r * dt)
    def pay(s):
        return max(s - K, 0.0) if kind == 'C' else max(K - s, 0.0)
    vals = [pay(S * u ** j * d ** (steps - j)) for j in range(steps + 1)]
    for i in range(steps - 1, -1, -1):
        vals = [max(disc * (p * vals[j + 1] + (1 - p) * vals[j]), pay(S * u ** j * d ** (i - j))) for j in range(i + 1)]
    return round(vals[0], 6)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression continuation weighting 1', ['C', 95.0, 90.0, 0.0, 0.4, 0.5, 25], 13.005948], ['regression continuation weighting 2', ['C', 80.0, 105.0, 0.05, 0.25, 0.5, 12], 0.588112], ['partial repair probe 1', ['P', 80.0, 100.0, 0.01, 0.25, 2.0, 3], 23.588558], ['partial repair probe 2', ['C', 95.0, 100.0, 0.05, 0.4, 2.0, 25], 23.088626], ['normal control 1', ['P', 80.0, 105.0, 0.05, 0.25, 0.25, 12], 25.0], ['normal control 2', ['P', 110.0, 90.0, 0.05, 0.15, 0.25, 3], 0.0], ['normal control 3', ['P', 110.0, 90.0, 0.05, 0.15, 0.5, 3], 0.0], ['normal control 4', ['C', 80.0, 105.0, 0.08, 0.15, 0.25, 12], 0.0]], [['regression continuation weighting 1', ['P', 80.0, 100.0, 0.05, 0.4, 0.25, 5], 20.610754], ['regression continuation weighting 2', ['P', 110.0, 100.0, 0.05, 0.15, 1.0, 12], 1.445712], ['partial repair probe 1', ['C', 110.0, 90.0, 0.01, 0.25, 1.0, 12], 23.733528], ['partial repair probe 2', ['P', 100.0, 100.0, 0.05, 0.15, 0.5, 12], 3.26077], ['normal control 1', ['P', 80.0, 100.0, 0.01, 0.15, 0.5, 25], 20.0], ['normal control 2', ['P', 80.0, 105.0, 0.01, 0.15, 0.25, 25], 25.0], ['normal control 3', ['P', 80.0, 105.0, 0.08, 0.4, 0.25, 3], 25.0], ['normal control 4', ['P', 80.0, 105.0, 0.05, 0.25, 0.25, 3], 25.0]], [['regression continuation weighting 1', ['P', 110.0, 100.0, 0.01, 0.15, 0.5, 3], 1.063038], ['regression continuation weighting 2', ['P', 95.0, 105.0, 0.01, 0.25, 0.25, 25], 11.367995], ['partial repair probe 1', ['C', 80.0, 105.0, 0.08, 0.4, 0.25, 3], 0.995113], ['partial repair probe 2', ['P', 95.0, 105.0, 0.05, 0.15, 2.0, 5], 10.529554], ['normal control 1', ['P', 80.0, 100.0, 0.0, 0.15, 0.5, 3], 20.0], ['normal control 2', ['P', 80.0, 105.0, 0.05, 0.25, 0.5, 3], 25.0], ['normal control 3', ['P', 80.0, 105.0, 0.01, 0.15, 0.5, 3], 25.0], ['normal control 4', ['C', 80.0, 105.0, 0.05, 0.15, 0.25, 5], 0.0]], [['regression continuation weighting 1', ['P', 95.0, 105.0, 0.01, 0.15, 1.0, 25], 11.69818], ['regression continuation weighting 2', ['C', 95.0, 100.0, 0.01, 0.4, 1.0, 3], 14.601622], ['partial repair probe 1', ['P', 110.0, 90.0, 0.0, 0.25, 0.25, 5], 0.244718], ['partial repair probe 2', ['P', 110.0, 105.0, 0.08, 0.15, 1.0, 5], 2.188872], ['normal control 1', ['P', 80.0, 105.0, 0.01, 0.15, 1.0, 5], 25.0], ['normal control 2', ['C', 80.0, 105.0, 0.01, 0.15, 1.0, 3], 0.0], ['normal control 3', ['P', 80.0, 105.0, 0.0, 0.15, 1.0, 3], 25.0], ['normal control 4', ['P', 80.0, 105.0, 0.01, 0.15, 1.0, 12], 25.0]], [['regression continuation weighting 1', ['C', 110.0, 90.0, 0.0, 0.25, 1.0, 3], 22.873702], ['regression continuation weighting 2', ['P', 110.0, 100.0, 0.0, 0.25, 1.0, 5], 5.971362], ['partial repair probe 1', ['P', 80.0, 90.0, 0.05, 0.15, 2.0, 25], 10.363792], ['partial repair probe 2', ['P', 100.0, 90.0, 0.08, 0.4, 0.5, 25], 5.322102], ['normal control 1', ['P', 80.0, 100.0, 0.01, 0.15, 1.0, 5], 20.0], ['normal control 2', ['P', 80.0, 105.0, 0.0, 0.15, 1.0, 3], 25.0], ['normal control 3', ['P', 80.0, 100.0, 0.05, 0.25, 0.25, 3], 20.0], ['normal control 4', ['C', 80.0, 105.0, 0.08, 0.15, 0.25, 5], 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 continuation weighting 113.00594813.005948Passed
regression continuation weighting 20.5881120.588112Passed
partial repair probe 123.58855823.588558Passed
partial repair probe 223.08862623.088626Passed
normal control 125.025.0Passed
normal control 20.00.0Passed
normal control 30.00.0Passed
normal control 40.00.0Passed

SHA-256 / 651d7b2bfb56cd0377ed6fac09770cb87a5c625e166b1d7a69f1af96b3eaecc1

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

Case digest / 0427dc184e7139995633e17b2000cb3c159614feab1520af4aa006a5ba0a90f0