feat: strategy builder
This commit is contained in:
@@ -56,7 +56,14 @@ def _quote_spread_pct(quote: Optional[Dict[str, Any]]) -> float:
|
||||
|
||||
|
||||
def entry_price(leg: Dict[str, Any], chain_slice: Dict[str, Any], surface_now: Surface, r: float) -> Dict[str, float]:
|
||||
"""Real execution price (crossing the spread) + theoretical mid, for one leg today."""
|
||||
"""Real execution price (crossing the spread) + theoretical mid, for one leg today.
|
||||
A "stock" leg (Covered Call/Protective Put/Collar's underlying position, not an
|
||||
option) has no strike/vol at all — its price IS the spot, no spread modeled (the
|
||||
underlying's own spread is typically far tighter than any option on it, and this
|
||||
tool doesn't have a live quote for it the way find_quote does for options)."""
|
||||
if leg["option_type"] == "stock":
|
||||
S = chain_slice["spot"]
|
||||
return {"exec_price": S, "mid": S, "spread_pct": 0.0}
|
||||
quote = find_quote(chain_slice, leg["expiry_date"], leg["strike"], leg["option_type"])
|
||||
T = max(leg["days_to_expiry"], 0.001) / 365
|
||||
sigma = surface_now.iv_at(leg["strike"], leg["days_to_expiry"])
|
||||
@@ -87,14 +94,17 @@ def value_at(
|
||||
computed and discarded on every one of those calls."""
|
||||
total = 0.0
|
||||
for leg in legs:
|
||||
remaining = leg["days_to_expiry"] - eval_days_from_now
|
||||
qty = leg.get("quantity", 1)
|
||||
sign = _sign(leg)
|
||||
if remaining <= 0:
|
||||
price = _intrinsic(S, leg["strike"], leg["option_type"])
|
||||
if leg["option_type"] == "stock":
|
||||
price = S # a share/lot of the underlying is worth exactly the spot, always
|
||||
else:
|
||||
sigma = surface.iv_at(leg["strike"], remaining)
|
||||
price = black_scholes(S, leg["strike"], remaining / 365, r, sigma, leg["option_type"], include_second_order=False)["price"]
|
||||
remaining = leg["days_to_expiry"] - eval_days_from_now
|
||||
if remaining <= 0:
|
||||
price = _intrinsic(S, leg["strike"], leg["option_type"])
|
||||
else:
|
||||
sigma = surface.iv_at(leg["strike"], remaining)
|
||||
price = black_scholes(S, leg["strike"], remaining / 365, r, sigma, leg["option_type"], include_second_order=False)["price"]
|
||||
total += sign * price * qty * contract_size
|
||||
return total
|
||||
|
||||
@@ -105,9 +115,14 @@ def greeks_at(legs: List[Dict[str, Any]], S: float, eval_days_from_now: float, s
|
||||
"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)
|
||||
sign = _sign(leg)
|
||||
if leg["option_type"] == "stock":
|
||||
# d(spot)/d(spot) = 1, and every other Greek (gamma, theta, vega, rho, the
|
||||
# second-order ones) is exactly zero for a position in the underlying itself.
|
||||
net["delta"] += sign * qty
|
||||
continue
|
||||
remaining = max(leg["days_to_expiry"] - eval_days_from_now, 0.001)
|
||||
sigma = surface.iv_at(leg["strike"], remaining)
|
||||
g = black_scholes(S, leg["strike"], remaining / 365, r, sigma, leg["option_type"])
|
||||
for k in net:
|
||||
@@ -169,14 +184,17 @@ def price_combo(
|
||||
scenario_mid = 0.0
|
||||
scenario_exec = 0.0
|
||||
for leg in legs:
|
||||
remaining = max(leg["days_to_expiry"] - horizon_days, 0.001)
|
||||
qty = leg.get("quantity", 1)
|
||||
sign = _sign(leg)
|
||||
sigma = surface_scenario.iv_at(leg["strike"], remaining)
|
||||
theo = black_scholes(spot_scenario, leg["strike"], remaining / 365, r, sigma, leg["option_type"])["price"]
|
||||
quote = find_quote(chain_slice, leg["expiry_date"], leg["strike"], leg["option_type"])
|
||||
spread_pct = _quote_spread_pct(quote)
|
||||
exec_price = theo * (1 - spread_pct / 2) if leg.get("position", "long") == "long" else theo * (1 + spread_pct / 2)
|
||||
if leg["option_type"] == "stock":
|
||||
theo = exec_price = spot_scenario # no spread modeled for the underlying itself
|
||||
else:
|
||||
remaining = max(leg["days_to_expiry"] - horizon_days, 0.001)
|
||||
sigma = surface_scenario.iv_at(leg["strike"], remaining)
|
||||
theo = black_scholes(spot_scenario, leg["strike"], remaining / 365, r, sigma, leg["option_type"])["price"]
|
||||
quote = find_quote(chain_slice, leg["expiry_date"], leg["strike"], leg["option_type"])
|
||||
spread_pct = _quote_spread_pct(quote)
|
||||
exec_price = theo * (1 - spread_pct / 2) if leg.get("position", "long") == "long" else theo * (1 + spread_pct / 2)
|
||||
scenario_mid += sign * theo * qty * contract_size
|
||||
scenario_exec += sign * exec_price * qty * contract_size
|
||||
|
||||
|
||||
Reference in New Issue
Block a user