feat: strategy builder
This commit is contained in:
@@ -6,17 +6,30 @@ import math
|
||||
|
||||
|
||||
def black_scholes(S: float, K: float, T: float, r: float, sigma: float, option_type: str = "call") -> Dict[str, float]:
|
||||
"""Black-Scholes pricing + Greeks."""
|
||||
"""Black-Scholes pricing + Greeks (first-order delta/gamma/theta/vega/rho, plus the
|
||||
second-order Greeks used by Strategy Builder's "advanced sensitivities" panel: vanna,
|
||||
charm, vomma/volga, veta, speed, color, zomma — vera deliberately omitted, see project
|
||||
memory "Strategy Builder Greeks plan"). All second-order values are scaled to match the
|
||||
convention their related first-order Greek already uses here — e.g. vanna/vomma/zomma
|
||||
are "per vol POINT" like vega already is (not per unit of raw decimal sigma), charm/
|
||||
color/veta are "per DAY" like theta already is (not per year) — every formula/scaling
|
||||
is verified against finite-difference bumps of this same function's own first-order
|
||||
outputs (see scratchpad test_second_order_greeks.py from the Phase 3 build), not just
|
||||
hand-derived from a textbook, since these third-derivative formulas are easy to get
|
||||
subtly wrong."""
|
||||
S = float(S or 100.0)
|
||||
K = float(K or S)
|
||||
T = float(T or 0.001)
|
||||
sigma = float(sigma or 0.25)
|
||||
if T <= 0 or sigma <= 0:
|
||||
intrinsic = max(0, S - K) if option_type == "call" else max(0, K - S)
|
||||
return {"price": intrinsic, "delta": 0, "gamma": 0, "theta": 0, "vega": 0, "rho": 0}
|
||||
return {"price": intrinsic, "delta": 0, "gamma": 0, "theta": 0, "vega": 0, "rho": 0,
|
||||
"vanna": 0, "charm": 0, "vomma": 0, "veta": 0, "speed": 0, "color": 0, "zomma": 0}
|
||||
|
||||
d1 = (math.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * math.sqrt(T))
|
||||
d2 = d1 - sigma * math.sqrt(T)
|
||||
sqrtT = math.sqrt(T)
|
||||
d1 = (math.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * sqrtT)
|
||||
d2 = d1 - sigma * sqrtT
|
||||
phi_d1 = norm.pdf(d1)
|
||||
|
||||
if option_type == "call":
|
||||
price = S * norm.cdf(d1) - K * math.exp(-r * T) * norm.cdf(d2)
|
||||
@@ -27,9 +40,19 @@ def black_scholes(S: float, K: float, T: float, r: float, sigma: float, option_t
|
||||
delta = norm.cdf(d1) - 1
|
||||
rho = -K * T * math.exp(-r * T) * norm.cdf(-d2) / 100
|
||||
|
||||
gamma = norm.pdf(d1) / (S * sigma * math.sqrt(T))
|
||||
theta = (-(S * norm.pdf(d1) * sigma) / (2 * math.sqrt(T)) - r * K * math.exp(-r * T) * norm.cdf(d2 if option_type == "call" else -d2)) / 365
|
||||
vega = S * norm.pdf(d1) * math.sqrt(T) / 100
|
||||
gamma = phi_d1 / (S * sigma * sqrtT)
|
||||
theta = (-(S * phi_d1 * sigma) / (2 * sqrtT) - r * K * math.exp(-r * T) * norm.cdf(d2 if option_type == "call" else -d2)) / 365
|
||||
vega = S * phi_d1 * sqrtT / 100
|
||||
|
||||
# Second-order — same for calls and puts (this pricer carries no dividend yield, so the
|
||||
# extra q-term that would otherwise make charm/veta/color differ by option_type is zero).
|
||||
vanna = (-phi_d1 * d2 / sigma) / 100
|
||||
vomma = (S * phi_d1 * sqrtT * d1 * d2 / sigma) / 10_000
|
||||
charm = (-phi_d1 * (2 * r * T - d2 * sigma * sqrtT) / (2 * T * sigma * sqrtT)) / 365
|
||||
veta = (S * phi_d1 * sqrtT * ((r * d1) / (sigma * sqrtT) - (1 + d1 * d2) / (2 * T))) / 36_500
|
||||
speed = -(gamma / S) * (d1 / (sigma * sqrtT) + 1)
|
||||
color = (phi_d1 / (2 * S * T * sigma * sqrtT) * (2 * r * T + 1 + d1 * (2 * r * T - d2 * sigma * sqrtT) / (sigma * sqrtT))) / 365
|
||||
zomma = (gamma * (d1 * d2 - 1) / sigma) / 100
|
||||
|
||||
return {
|
||||
"price": round(price, 4),
|
||||
@@ -38,6 +61,13 @@ def black_scholes(S: float, K: float, T: float, r: float, sigma: float, option_t
|
||||
"theta": round(theta, 4),
|
||||
"vega": round(vega, 4),
|
||||
"rho": round(rho, 4),
|
||||
"vanna": round(vanna, 6),
|
||||
"charm": round(charm, 6),
|
||||
"vomma": round(vomma, 6),
|
||||
"veta": round(veta, 6),
|
||||
"speed": round(speed, 8),
|
||||
"color": round(color, 8),
|
||||
"zomma": round(zomma, 6),
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user