39 lines
995 B
Python
39 lines
995 B
Python
from __future__ import annotations
|
|
|
|
import math
|
|
|
|
|
|
def _norm_cdf(z: float) -> float:
|
|
# Standard normal CDF via erf (no scipy dependency)
|
|
return 0.5 * (1.0 + math.erf(z / math.sqrt(2.0)))
|
|
|
|
|
|
def fair_prob_threshold(
|
|
*,
|
|
spot: float,
|
|
strike: float,
|
|
sigma_per_second: float,
|
|
time_remaining_seconds: float,
|
|
resolves_yes_if_spot_ge_strike: bool,
|
|
) -> float:
|
|
"""
|
|
Conservative approximation:
|
|
spot(t) ~ Normal(spot, spot*sigma*sqrt(t))
|
|
and compute P(spot_T >= strike) or P(spot_T <= strike).
|
|
"""
|
|
if spot <= 0 or strike <= 0:
|
|
return 0.5
|
|
t = max(1.0, float(time_remaining_seconds))
|
|
sigma = max(1e-9, float(sigma_per_second))
|
|
stdev = spot * sigma * math.sqrt(t)
|
|
if stdev <= 0:
|
|
return 0.5
|
|
|
|
z = (spot - strike) / stdev
|
|
p_ge = _norm_cdf(z) # P(spot_T >= strike)
|
|
|
|
fair = p_ge if resolves_yes_if_spot_ge_strike else (1.0 - p_ge)
|
|
|
|
# clip away from certainty (tail risk)
|
|
return max(0.03, min(0.97, fair))
|