feat: strategy builder
This commit is contained in:
@@ -285,15 +285,30 @@ def payoff_curves(
|
||||
entry_ref = priced["entry_cost"]
|
||||
|
||||
lo, hi = spot * 0.6, spot * 1.4
|
||||
prices = np.linspace(lo, hi, n)
|
||||
# A calendar/ratio spread's payoff can spike sharply right at a strike — a plain
|
||||
# uniform sweep over the full 0.6x-1.4x range (n points) can straddle right over that
|
||||
# peak without ever sampling it (same issue fixed in check_bounded_risk). Blend a
|
||||
# coarse baseline (overall shape) with a dense window around the legs' own strikes.
|
||||
baseline = np.linspace(lo, hi, n)
|
||||
strikes = [l["strike"] for l in legs]
|
||||
lo_k, hi_k = max(min(strikes) * 0.9, lo), min(max(strikes) * 1.1, hi)
|
||||
near_strikes = np.linspace(lo_k, hi_k, n * 3)
|
||||
prices = np.unique(np.concatenate([baseline, near_strikes]))
|
||||
prices.sort()
|
||||
eval_days_expiry = min(l["days_to_expiry"] for l in legs)
|
||||
|
||||
# Both curves share the scenario's volatility view (surface_scenario) — only the
|
||||
# evaluation DATE differs: "à échéance" prices the day the near leg expires (matching
|
||||
# the Max gain/Max perte tile, itself computed with surface_scenario for the same
|
||||
# reason — see check_bounded_risk), "à J+8" prices your chosen scenario horizon. Using
|
||||
# surface_now here instead would silently mix in today's un-shocked vol, producing a
|
||||
# curve whose peak doesn't match the Max gain tile right next to it.
|
||||
at_expiry = [
|
||||
{"underlying": round(float(p), 2), "pnl": round(float(value_at(legs, float(p), eval_days_expiry, surface_now, r, contract_size) - entry_ref), 2)}
|
||||
{"underlying": round(float(p), 4), "pnl": round(float(value_at(legs, float(p), eval_days_expiry, surface_scenario, r, contract_size) - entry_ref), 2)}
|
||||
for p in prices
|
||||
]
|
||||
at_scenario = [
|
||||
{"underlying": round(float(p), 2), "pnl": round(float(value_at(legs, float(p), horizon_days, surface_scenario, r, contract_size) - entry_ref), 2)}
|
||||
{"underlying": round(float(p), 4), "pnl": round(float(value_at(legs, float(p), horizon_days, surface_scenario, r, contract_size) - entry_ref), 2)}
|
||||
for p in prices
|
||||
]
|
||||
return {"at_expiry": at_expiry, "at_scenario": at_scenario, **priced}
|
||||
|
||||
Reference in New Issue
Block a user