feat: strategy builder

This commit is contained in:
OpenSquared
2026-07-27 18:58:02 +02:00
parent ce09159bfb
commit 568414ca0c
18 changed files with 1315 additions and 63 deletions

View File

@@ -97,7 +97,10 @@ def value_at(
def greeks_at(legs: List[Dict[str, Any]], S: float, eval_days_from_now: float, surface: Any, r: float) -> Dict[str, float]:
net = {"delta": 0.0, "gamma": 0.0, "theta": 0.0, "vega": 0.0}
net = {
"delta": 0.0, "gamma": 0.0, "theta": 0.0, "vega": 0.0, "rho": 0.0,
"vanna": 0.0, "charm": 0.0, "vomma": 0.0, "veta": 0.0, "speed": 0.0, "color": 0.0, "zomma": 0.0,
}
for leg in legs:
remaining = max(leg["days_to_expiry"] - eval_days_from_now, 0.001)
qty = leg.get("quantity", 1)
@@ -106,7 +109,34 @@ def greeks_at(legs: List[Dict[str, Any]], S: float, eval_days_from_now: float, s
g = black_scholes(S, leg["strike"], remaining / 365, r, sigma, leg["option_type"])
for k in net:
net[k] += g[k] * qty * sign
return {k: round(v, 4) for k, v in net.items()}
return {k: round(v, 6) for k, v in net.items()}
def vanna_simulation(
legs: List[Dict[str, Any]], S: float, eval_days_from_now: float, surface: Any, r: float,
spot_shock_pct: float = -5.0, iv_shock_pts: float = 8.0,
) -> Dict[str, float]:
"""A concrete joint spot+IV shock reprice — "if spot drops 5% and IV jumps 8pts, what
actually happens to my net delta" — rather than a bare "vanna is positive/negative"
label. Uses a real Black-Scholes reprice (not the linear vanna approximation) so it's
accurate for shocks this large, matching the same "show a simulation, not a sign"
principle the payoff diagram already uses elsewhere in Strategy Builder."""
delta_before = greeks_at(legs, S, eval_days_from_now, surface, r)["delta"]
class _ShockedSurface:
def iv_at(self, strike: float, days: float) -> float:
return max(0.01, surface.iv_at(strike, days) + iv_shock_pts / 100.0)
S_shocked = S * (1 + spot_shock_pct / 100.0)
delta_after = greeks_at(legs, S_shocked, eval_days_from_now, _ShockedSurface(), r)["delta"]
return {
"spot_shock_pct": spot_shock_pct,
"iv_shock_pts": iv_shock_pts,
"delta_before": delta_before,
"delta_after": delta_after,
"delta_change": round(delta_after - delta_before, 6),
}
def price_combo(
@@ -176,6 +206,9 @@ def price_combo(
"greeks_scenario": greeks_at(legs, spot_scenario, horizon_days, surface_scenario, r),
"net_delta_now": delta_now,
"net_delta_scenario": delta_scenario,
# Skipped during the optimizer's bulk scan (precise=False, hundreds of candidates
# per request) — only computed for the single position actually loaded/priced.
"vanna_simulation": vanna_simulation(legs, spot_now, 0, surface_now, r) if precise else None,
})