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.
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression continuation weighting 1 | 18.397987 | 13.005948 | Failed |
| regression continuation weighting 2 | 0.465021 | 0.588112 | Failed |
| partial repair probe 1 | 21.231316 | 23.588558 | Failed |
| partial repair probe 2 | 31.269188 | 23.088626 | Failed |
| normal control 1 | 25.0 | 25.0 | Passed |
| normal control 2 | 0.0 | 0.0 | Passed |
| normal control 3 | 0.0 | 0.0 | Passed |
| normal control 4 | 0.0 | 0.0 | Passed |
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression continuation weighting 1 | 15.552113 | 13.005948 | Failed |
| regression continuation weighting 2 | 0.523556 | 0.588112 | Failed |
| partial repair probe 1 | 22.434645 | 23.588558 | Failed |
| partial repair probe 2 | 26.951753 | 23.088626 | Failed |
| normal control 1 | 25.0 | 25.0 | Passed |
| normal control 2 | 0.0 | 0.0 | Passed |
| normal control 3 | 0.0 | 0.0 | Passed |
| normal control 4 | 0.0 | 0.0 | Passed |
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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| regression continuation weighting 1 | 13.005948 | 13.005948 | Passed |
| regression continuation weighting 2 | 0.588112 | 0.588112 | Passed |
| partial repair probe 1 | 23.588558 | 23.588558 | Passed |
| partial repair probe 2 | 23.088626 | 23.088626 | Passed |
| normal control 1 | 25.0 | 25.0 | Passed |
| normal control 2 | 0.0 | 0.0 | Passed |
| normal control 3 | 0.0 | 0.0 | Passed |
| normal control 4 | 0.0 | 0.0 | Passed |
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