Support dual breakout targets for options-options hedges.
Replace single S* with up/down targets across UI, preview, persist, monitor, and alerts so ranging breakouts can close the winner either way. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -223,12 +223,14 @@ def _hedge_ratio(opt_pnl: float, perp_pnl: float) -> Optional[float]:
|
||||
|
||||
def build_options_options_preview(
|
||||
*,
|
||||
target_price: float,
|
||||
target_price: float | None = None,
|
||||
target_price_up: float | None = None,
|
||||
target_price_down: float | None = None,
|
||||
index_px: float,
|
||||
leg_a: dict[str, Any],
|
||||
leg_b: dict[str, Any],
|
||||
) -> dict[str, Any]:
|
||||
"""期期情景:目标价 / 到期现价 / 到期两边."""
|
||||
"""期期情景:上破/下破目标价 / 到期现价 / 最大保费损耗."""
|
||||
|
||||
def _leg_pnl(leg: dict[str, Any], spot: float) -> float:
|
||||
return option_expiry_pnl(
|
||||
@@ -240,11 +242,25 @@ def build_options_options_preview(
|
||||
premium_paid=float(leg.get("premium_paid") or 0),
|
||||
)
|
||||
|
||||
# 兼容旧单目标:若未传上下目标则用 target_price 填两边
|
||||
up = target_price_up if target_price_up is not None else target_price
|
||||
down = target_price_down if target_price_down is not None else target_price
|
||||
if up is None or down is None:
|
||||
raise ValueError("缺少上破/下破目标价")
|
||||
up_f = float(up)
|
||||
down_f = float(down)
|
||||
|
||||
prem = float(leg_a.get("premium_paid") or 0) + float(leg_b.get("premium_paid") or 0)
|
||||
a_t = _leg_pnl(leg_a, target_price)
|
||||
b_t = _leg_pnl(leg_b, target_price)
|
||||
at_target = a_t + b_t
|
||||
win_leg = "a" if a_t >= b_t else "b"
|
||||
a_up = _leg_pnl(leg_a, up_f)
|
||||
b_up = _leg_pnl(leg_b, up_f)
|
||||
at_up = a_up + b_up
|
||||
win_up = "a" if a_up >= b_up else "b"
|
||||
|
||||
a_dn = _leg_pnl(leg_a, down_f)
|
||||
b_dn = _leg_pnl(leg_b, down_f)
|
||||
at_dn = a_dn + b_dn
|
||||
win_dn = "a" if a_dn >= b_dn else "b"
|
||||
|
||||
a_flat = _leg_pnl(leg_a, index_px)
|
||||
b_flat = _leg_pnl(leg_b, index_px)
|
||||
flat_total = a_flat + b_flat
|
||||
@@ -253,17 +269,30 @@ def build_options_options_preview(
|
||||
return {
|
||||
"plan_type": "options_options",
|
||||
"premium_paid": round(prem, 6),
|
||||
"target_price": target_price,
|
||||
"winner_at_target": win_leg,
|
||||
"target_price": up_f, # 兼容旧字段,取上破
|
||||
"target_price_up": up_f,
|
||||
"target_price_down": down_f,
|
||||
"winner_at_up": win_up,
|
||||
"winner_at_down": win_dn,
|
||||
"winner_at_target": win_up,
|
||||
"scenarios": [
|
||||
{
|
||||
"id": "target",
|
||||
"label": "到达目标价",
|
||||
"spot": target_price,
|
||||
"leg_a_pnl": round(a_t, 4),
|
||||
"leg_b_pnl": round(b_t, 4),
|
||||
"total": round(at_target, 4),
|
||||
"note": f"盈利方≈腿{win_leg.upper()}(可平);亏损方默认到期",
|
||||
"id": "target_up",
|
||||
"label": "上破目标",
|
||||
"spot": up_f,
|
||||
"leg_a_pnl": round(a_up, 4),
|
||||
"leg_b_pnl": round(b_up, 4),
|
||||
"total": round(at_up, 4),
|
||||
"note": f"盈利方≈腿{win_up.upper()}(可平);亏损方默认到期",
|
||||
},
|
||||
{
|
||||
"id": "target_down",
|
||||
"label": "下破目标",
|
||||
"spot": down_f,
|
||||
"leg_a_pnl": round(a_dn, 4),
|
||||
"leg_b_pnl": round(b_dn, 4),
|
||||
"total": round(at_dn, 4),
|
||||
"note": f"盈利方≈腿{win_dn.upper()}(可平);亏损方默认到期",
|
||||
},
|
||||
{
|
||||
"id": "expiry_flat",
|
||||
@@ -285,9 +314,11 @@ def build_options_options_preview(
|
||||
},
|
||||
],
|
||||
"summary": {
|
||||
"at_target_total": round(at_target, 4),
|
||||
"at_target_up_total": round(at_up, 4),
|
||||
"at_target_down_total": round(at_dn, 4),
|
||||
"at_target_total": round(at_up, 4),
|
||||
"expiry_flat_total": round(expiry_loss, 4),
|
||||
"premium_paid": round(prem, 4),
|
||||
"premium_paid": round(prem, 6),
|
||||
"expiry_is_loss": flat_total <= 0,
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user