FA-61541 / Options payoff and settlement / Open access
European option value with continuous dividend yield: the dividend yield is added to the drift · case 01
Options on dividend payers are priced as if dividends increased the forward.
ROOT CAUSE
d1 uses r + q instead of r - q.
VERIFIED REPAIR
Use r - q in the drift term of d1.
Unsuccessful approach: Dropping q from d1 while keeping the e^{-qT} factor is internally inconsistent.
Case contract
Inputs kind, spot S, strike K, rate r, dividend yield q, volatility sigma and calendar days to expiry. T = days/365. At days == 0 return intrinsic value. Otherwise d1 = (ln(S/K) + (r - q + sigma^2/2)T)/(sigma sqrt T), d2 = d1 - sigma sqrt T, call = S e^{-qT} N(d1) - K e^{-rT} N(d2), put = K e^{-rT} N(-d2) - S e^{-qT} N(-d1). 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
import math
N = 1
observations = []
def solve(kind, S, K, r, q, sigma, days):
def N(x):
return 0.5 * (1 + math.erf(x / math.sqrt(2)))
T = days / 365
if days == 0:
return round(max(S - K, 0.0) if kind == 'C' else max(K - S, 0.0), 6)
d1 = (math.log(S / K) + (r + q + 0.5 * sigma ** 2) * T) / (sigma * math.sqrt(T))
d2 = d1 - sigma * math.sqrt(T)
if kind == 'C':
v = S * math.exp(-q * T) * N(d1) - K * math.exp(-r * T) * N(d2)
else:
v = K * math.exp(-r * T) * N(-d2) - S * math.exp(-q * T) * N(-d1)
return round(v, 6)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression dividend drift 1', ['P', 110.0, 105.0, 0.0, 0.03, 0.35, 30], 2.338617], ['regression dividend drift 2', ['C', 110.0, 105.0, 0.0, 0.015, 0.35, 91], 9.987818], ['partial repair probe 1', ['C', 100.0, 105.0, 0.05, 0.015, 0.35, 30], 2.164477], ['partial repair probe 2', ['P', 90.0, 105.0, 0.02, 0.03, 0.1, 730], 16.810523], ['boundary control 1', ['C', 110.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['boundary control 2', ['P', 90.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['normal control 1', ['C', 100.0, 105.0, 0.05, 0.015, 0.2, 1], 0.0], ['normal control 2', ['P', 100.0, 105.0, 0.05, 0.0, 0.35, 0], 5.0]], [['regression dividend drift 1', ['C', 100.0, 100.0, 0.02, 0.015, 0.1, 182], 2.917342], ['regression dividend drift 2', ['P', 90.0, 100.0, 0.0, 0.015, 0.35, 730], 25.3049], ['partial repair probe 1', ['P', 100.0, 95.0, 0.02, 0.015, 0.2, 30], 0.558539], ['partial repair probe 2', ['P', 100.0, 105.0, 0.05, 0.03, 0.1, 730], 5.758366], ['boundary control 1', ['C', 110.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['boundary control 2', ['P', 90.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['normal control 1', ['P', 100.0, 105.0, 0.02, 0.0, 0.2, 30], 5.505807], ['normal control 2', ['C', 110.0, 100.0, 0.0, 0.0, 0.2, 730], 17.46773]], [['regression dividend drift 1', ['C', 110.0, 100.0, 0.0, 0.015, 0.35, 365], 19.029274], ['regression dividend drift 2', ['P', 100.0, 95.0, 0.0, 0.03, 0.35, 7], 0.359876], ['partial repair probe 1', ['P', 90.0, 105.0, 0.05, 0.015, 0.1, 182], 13.153147], ['partial repair probe 2', ['P', 100.0, 105.0, 0.0, 0.015, 0.35, 91], 10.129304], ['boundary control 1', ['P', 90.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['boundary control 2', ['C', 110.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['normal control 1', ['P', 110.0, 100.0, 0.02, 0.0, 0.2, 730], 5.917793], ['normal control 2', ['C', 100.0, 105.0, 0.0, 0.0, 0.2, 365], 5.905593]], [['regression dividend drift 1', ['P', 100.0, 100.0, 0.02, 0.03, 0.1, 182], 3.034769], ['regression dividend drift 2', ['C', 110.0, 95.0, 0.0, 0.015, 0.35, 91], 16.662902], ['partial repair probe 1', ['P', 110.0, 105.0, 0.05, 0.03, 0.35, 91], 4.98959], ['partial repair probe 2', ['C', 110.0, 100.0, 0.02, 0.03, 0.35, 30], 10.869205], ['boundary control 1', ['P', 90.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['boundary control 2', ['C', 110.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['normal control 1', ['C', 100.0, 95.0, 0.05, 0.03, 0.35, 0], 5.0], ['normal control 2', ['C', 110.0, 95.0, 0.02, 0.0, 0.1, 30], 15.156036]], [['regression dividend drift 1', ['C', 90.0, 100.0, 0.05, 0.015, 0.1, 365], 1.303213], ['regression dividend drift 2', ['C', 110.0, 100.0, 0.05, 0.03, 0.1, 365], 12.247458], ['partial repair probe 1', ['C', 100.0, 105.0, 0.02, 0.03, 0.1, 91], 0.402157], ['partial repair probe 2', ['P', 90.0, 105.0, 0.02, 0.03, 0.35, 7], 15.012424], ['boundary control 1', ['C', 110.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['boundary control 2', ['P', 90.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['normal control 1', ['P', 90.0, 95.0, 0.0, 0.0, 0.35, 7], 5.298184], ['normal control 2', ['P', 100.0, 105.0, 0.05, 0.0, 0.35, 365], 13.823265]]]
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 dividend drift 1 | 2.333981 | 2.338617 | Failed |
| regression dividend drift 2 | 9.981245 | 9.987818 | Failed |
| partial repair probe 1 | 2.163358 | 2.164477 | Failed |
| partial repair probe 2 | 15.323725 | 16.810523 | Failed |
| boundary control 1 | 10.0 | 10.0 | Passed |
| boundary control 2 | 10.0 | 10.0 | Passed |
| normal control 1 | 0.0 | 0.0 | Passed |
| normal control 2 | 5.0 | 5.0 | Passed |
SHA-256 / e9cf4fcc2b17aa052aa35dfd603f29419444aa0b4facd3b287208af77d6f6a05
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, q, sigma, days):
def N(x):
return 0.5 * (1 + math.erf(x / math.sqrt(2)))
T = days / 365
if days == 0:
return round(max(S - K, 0.0) if kind == 'C' else max(K - S, 0.0), 6)
d1 = (math.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * math.sqrt(T))
d2 = d1 - sigma * math.sqrt(T)
if kind == 'C':
v = S * math.exp(-q * T) * N(d1) - K * math.exp(-r * T) * N(d2)
else:
v = K * math.exp(-r * T) * N(-d2) - S * math.exp(-q * T) * N(-d1)
return round(v, 6)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression dividend drift 1', ['P', 110.0, 105.0, 0.0, 0.03, 0.35, 30], 2.338617], ['regression dividend drift 2', ['C', 110.0, 105.0, 0.0, 0.015, 0.35, 91], 9.987818], ['partial repair probe 1', ['C', 100.0, 105.0, 0.05, 0.015, 0.35, 30], 2.164477], ['partial repair probe 2', ['P', 90.0, 105.0, 0.02, 0.03, 0.1, 730], 16.810523], ['boundary control 1', ['C', 110.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['boundary control 2', ['P', 90.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['normal control 1', ['C', 100.0, 105.0, 0.05, 0.015, 0.2, 1], 0.0], ['normal control 2', ['P', 100.0, 105.0, 0.05, 0.0, 0.35, 0], 5.0]], [['regression dividend drift 1', ['C', 100.0, 100.0, 0.02, 0.015, 0.1, 182], 2.917342], ['regression dividend drift 2', ['P', 90.0, 100.0, 0.0, 0.015, 0.35, 730], 25.3049], ['partial repair probe 1', ['P', 100.0, 95.0, 0.02, 0.015, 0.2, 30], 0.558539], ['partial repair probe 2', ['P', 100.0, 105.0, 0.05, 0.03, 0.1, 730], 5.758366], ['boundary control 1', ['C', 110.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['boundary control 2', ['P', 90.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['normal control 1', ['P', 100.0, 105.0, 0.02, 0.0, 0.2, 30], 5.505807], ['normal control 2', ['C', 110.0, 100.0, 0.0, 0.0, 0.2, 730], 17.46773]], [['regression dividend drift 1', ['C', 110.0, 100.0, 0.0, 0.015, 0.35, 365], 19.029274], ['regression dividend drift 2', ['P', 100.0, 95.0, 0.0, 0.03, 0.35, 7], 0.359876], ['partial repair probe 1', ['P', 90.0, 105.0, 0.05, 0.015, 0.1, 182], 13.153147], ['partial repair probe 2', ['P', 100.0, 105.0, 0.0, 0.015, 0.35, 91], 10.129304], ['boundary control 1', ['P', 90.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['boundary control 2', ['C', 110.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['normal control 1', ['P', 110.0, 100.0, 0.02, 0.0, 0.2, 730], 5.917793], ['normal control 2', ['C', 100.0, 105.0, 0.0, 0.0, 0.2, 365], 5.905593]], [['regression dividend drift 1', ['P', 100.0, 100.0, 0.02, 0.03, 0.1, 182], 3.034769], ['regression dividend drift 2', ['C', 110.0, 95.0, 0.0, 0.015, 0.35, 91], 16.662902], ['partial repair probe 1', ['P', 110.0, 105.0, 0.05, 0.03, 0.35, 91], 4.98959], ['partial repair probe 2', ['C', 110.0, 100.0, 0.02, 0.03, 0.35, 30], 10.869205], ['boundary control 1', ['P', 90.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['boundary control 2', ['C', 110.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['normal control 1', ['C', 100.0, 95.0, 0.05, 0.03, 0.35, 0], 5.0], ['normal control 2', ['C', 110.0, 95.0, 0.02, 0.0, 0.1, 30], 15.156036]], [['regression dividend drift 1', ['C', 90.0, 100.0, 0.05, 0.015, 0.1, 365], 1.303213], ['regression dividend drift 2', ['C', 110.0, 100.0, 0.05, 0.03, 0.1, 365], 12.247458], ['partial repair probe 1', ['C', 100.0, 105.0, 0.02, 0.03, 0.1, 91], 0.402157], ['partial repair probe 2', ['P', 90.0, 105.0, 0.02, 0.03, 0.35, 7], 15.012424], ['boundary control 1', ['C', 110.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['boundary control 2', ['P', 90.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['normal control 1', ['P', 90.0, 95.0, 0.0, 0.0, 0.35, 7], 5.298184], ['normal control 2', ['P', 100.0, 105.0, 0.05, 0.0, 0.35, 365], 13.823265]]]
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 dividend drift 1 | 2.337449 | 2.338617 | Failed |
| regression dividend drift 2 | 9.986169 | 9.987818 | Failed |
| partial repair probe 1 | 2.164199 | 2.164477 | Failed |
| partial repair probe 2 | 16.511929 | 16.810523 | Failed |
| boundary control 1 | 10.0 | 10.0 | Passed |
| boundary control 2 | 10.0 | 10.0 | Passed |
| normal control 1 | 0.0 | 0.0 | Passed |
| normal control 2 | 5.0 | 5.0 | Passed |
SHA-256 / ff347af977efa352312b2ba5f20ea6f31a88213e2fb35335e7c022e42c02cf6f
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, q, sigma, days):
def N(x):
return 0.5 * (1 + math.erf(x / math.sqrt(2)))
T = days / 365
if days == 0:
return round(max(S - K, 0.0) if kind == 'C' else max(K - S, 0.0), 6)
d1 = (math.log(S / K) + (r - q + 0.5 * sigma ** 2) * T) / (sigma * math.sqrt(T))
d2 = d1 - sigma * math.sqrt(T)
if kind == 'C':
v = S * math.exp(-q * T) * N(d1) - K * math.exp(-r * T) * N(d2)
else:
v = K * math.exp(-r * T) * N(-d2) - S * math.exp(-q * T) * N(-d1)
return round(v, 6)
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['regression dividend drift 1', ['P', 110.0, 105.0, 0.0, 0.03, 0.35, 30], 2.338617], ['regression dividend drift 2', ['C', 110.0, 105.0, 0.0, 0.015, 0.35, 91], 9.987818], ['partial repair probe 1', ['C', 100.0, 105.0, 0.05, 0.015, 0.35, 30], 2.164477], ['partial repair probe 2', ['P', 90.0, 105.0, 0.02, 0.03, 0.1, 730], 16.810523], ['boundary control 1', ['C', 110.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['boundary control 2', ['P', 90.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['normal control 1', ['C', 100.0, 105.0, 0.05, 0.015, 0.2, 1], 0.0], ['normal control 2', ['P', 100.0, 105.0, 0.05, 0.0, 0.35, 0], 5.0]], [['regression dividend drift 1', ['C', 100.0, 100.0, 0.02, 0.015, 0.1, 182], 2.917342], ['regression dividend drift 2', ['P', 90.0, 100.0, 0.0, 0.015, 0.35, 730], 25.3049], ['partial repair probe 1', ['P', 100.0, 95.0, 0.02, 0.015, 0.2, 30], 0.558539], ['partial repair probe 2', ['P', 100.0, 105.0, 0.05, 0.03, 0.1, 730], 5.758366], ['boundary control 1', ['C', 110.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['boundary control 2', ['P', 90.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['normal control 1', ['P', 100.0, 105.0, 0.02, 0.0, 0.2, 30], 5.505807], ['normal control 2', ['C', 110.0, 100.0, 0.0, 0.0, 0.2, 730], 17.46773]], [['regression dividend drift 1', ['C', 110.0, 100.0, 0.0, 0.015, 0.35, 365], 19.029274], ['regression dividend drift 2', ['P', 100.0, 95.0, 0.0, 0.03, 0.35, 7], 0.359876], ['partial repair probe 1', ['P', 90.0, 105.0, 0.05, 0.015, 0.1, 182], 13.153147], ['partial repair probe 2', ['P', 100.0, 105.0, 0.0, 0.015, 0.35, 91], 10.129304], ['boundary control 1', ['P', 90.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['boundary control 2', ['C', 110.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['normal control 1', ['P', 110.0, 100.0, 0.02, 0.0, 0.2, 730], 5.917793], ['normal control 2', ['C', 100.0, 105.0, 0.0, 0.0, 0.2, 365], 5.905593]], [['regression dividend drift 1', ['P', 100.0, 100.0, 0.02, 0.03, 0.1, 182], 3.034769], ['regression dividend drift 2', ['C', 110.0, 95.0, 0.0, 0.015, 0.35, 91], 16.662902], ['partial repair probe 1', ['P', 110.0, 105.0, 0.05, 0.03, 0.35, 91], 4.98959], ['partial repair probe 2', ['C', 110.0, 100.0, 0.02, 0.03, 0.35, 30], 10.869205], ['boundary control 1', ['P', 90.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['boundary control 2', ['C', 110.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['normal control 1', ['C', 100.0, 95.0, 0.05, 0.03, 0.35, 0], 5.0], ['normal control 2', ['C', 110.0, 95.0, 0.02, 0.0, 0.1, 30], 15.156036]], [['regression dividend drift 1', ['C', 90.0, 100.0, 0.05, 0.015, 0.1, 365], 1.303213], ['regression dividend drift 2', ['C', 110.0, 100.0, 0.05, 0.03, 0.1, 365], 12.247458], ['partial repair probe 1', ['C', 100.0, 105.0, 0.02, 0.03, 0.1, 91], 0.402157], ['partial repair probe 2', ['P', 90.0, 105.0, 0.02, 0.03, 0.35, 7], 15.012424], ['boundary control 1', ['C', 110.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['boundary control 2', ['P', 90.0, 100.0, 0.05, 0.0, 0.2, 0], 10.0], ['normal control 1', ['P', 90.0, 95.0, 0.0, 0.0, 0.35, 7], 5.298184], ['normal control 2', ['P', 100.0, 105.0, 0.05, 0.0, 0.35, 365], 13.823265]]]
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 dividend drift 1 | 2.338617 | 2.338617 | Passed |
| regression dividend drift 2 | 9.987818 | 9.987818 | Passed |
| partial repair probe 1 | 2.164477 | 2.164477 | Passed |
| partial repair probe 2 | 16.810523 | 16.810523 | Passed |
| boundary control 1 | 10.0 | 10.0 | Passed |
| boundary control 2 | 10.0 | 10.0 | Passed |
| normal control 1 | 0.0 | 0.0 | Passed |
| normal control 2 | 5.0 | 5.0 | Passed |
SHA-256 / 6bbbc6ca1b6865e64b6c7df5e0d4bcf6657b83a53680644ff2c9aefced56f702
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.130363+00:00.
Case digest / cf3dd773e22b78fd0b83f540605f385454ec7e64debd8b92bb47734d902089cc