feat(hedge): replace OO breakout targets with premium profit RR
期期改用盈亏比×权利金止盈(默认2);不达标持有至到期。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -444,6 +444,7 @@ def _hedge_ratio(opt_pnl: float, perp_pnl: float) -> Optional[float]:
|
||||
|
||||
def build_options_options_preview(
|
||||
*,
|
||||
profit_rr: float | None = None,
|
||||
target_price: float | None = None,
|
||||
target_price_up: float | None = None,
|
||||
target_price_down: float | None = None,
|
||||
@@ -451,7 +452,11 @@ def build_options_options_preview(
|
||||
leg_a: dict[str, Any],
|
||||
leg_b: dict[str, Any],
|
||||
) -> dict[str, Any]:
|
||||
"""期期情景:上破/下破目标价 / 到期现价 / 最大保费损耗."""
|
||||
"""期期情景:盈亏比达标 / 到期现价 / 最大保费损耗.
|
||||
|
||||
profit_rr=2 表示目标盈利=2×权利金;中途不达标则等到期.
|
||||
仍接受旧上破/下破参数仅作兼容测算.
|
||||
"""
|
||||
|
||||
def _leg_pnl(leg: dict[str, Any], spot: float) -> float:
|
||||
return option_expiry_pnl(
|
||||
@@ -463,15 +468,73 @@ def build_options_options_preview(
|
||||
premium_paid=float(leg.get("premium_paid") or 0),
|
||||
)
|
||||
|
||||
# 兼容旧单目标:若未传上下目标则用 target_price 填两边
|
||||
prem = float(leg_a.get("premium_paid") or 0) + float(leg_b.get("premium_paid") or 0)
|
||||
a_flat = _leg_pnl(leg_a, index_px)
|
||||
b_flat = _leg_pnl(leg_b, index_px)
|
||||
flat_total = a_flat + b_flat
|
||||
|
||||
rr = None
|
||||
if profit_rr not in (None, ""):
|
||||
try:
|
||||
rr = float(profit_rr)
|
||||
except (TypeError, ValueError):
|
||||
rr = None
|
||||
if rr is not None and rr > 0:
|
||||
target_pnl = rr * prem
|
||||
return {
|
||||
"plan_type": "options_options",
|
||||
"premium_paid": round(prem, 6),
|
||||
"oo_profit_rr": round(rr, 4),
|
||||
"target_profit": round(target_pnl, 4),
|
||||
"scenarios": [
|
||||
{
|
||||
"id": "rr_target",
|
||||
"label": f"盈亏比×{rr:g}",
|
||||
"spot": None,
|
||||
"leg_a_pnl": None,
|
||||
"leg_b_pnl": None,
|
||||
"total": round(target_pnl, 4),
|
||||
"note": f"两腿合计浮盈≥{rr:g}×权利金({round(prem, 4)})时全平;不达标等到期",
|
||||
},
|
||||
{
|
||||
"id": "expiry_flat",
|
||||
"label": "到期·现价(未达标)",
|
||||
"spot": index_px,
|
||||
"leg_a_pnl": round(a_flat, 4),
|
||||
"leg_b_pnl": round(b_flat, 4),
|
||||
"total": round(flat_total, 4),
|
||||
"note": "中途未达盈亏比则持有至到期结算",
|
||||
},
|
||||
{
|
||||
"id": "max_premium_loss",
|
||||
"label": "最大保费损耗",
|
||||
"spot": None,
|
||||
"leg_a_pnl": round(-float(leg_a.get("premium_paid") or 0), 4),
|
||||
"leg_b_pnl": round(-float(leg_b.get("premium_paid") or 0), 4),
|
||||
"total": round(-prem, 4),
|
||||
"note": "双腿权利金全部损失",
|
||||
},
|
||||
],
|
||||
"summary": {
|
||||
"oo_profit_rr": round(rr, 4),
|
||||
"target_profit": round(target_pnl, 4),
|
||||
"at_target_total": round(target_pnl, 4),
|
||||
"expiry_flat_total": round(flat_total, 4),
|
||||
"premium_paid": round(prem, 6),
|
||||
"expiry_is_loss": flat_total <= 0,
|
||||
"rr_risk_premium": round(prem, 6),
|
||||
"rr_target": round(rr, 4),
|
||||
},
|
||||
}
|
||||
|
||||
# 兼容旧上破/下破测算
|
||||
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("缺少上破/下破目标价")
|
||||
raise ValueError("请填写盈亏比(相对权利金,默认2)")
|
||||
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_up = _leg_pnl(leg_a, up_f)
|
||||
b_up = _leg_pnl(leg_b, up_f)
|
||||
at_up = a_up + b_up
|
||||
@@ -482,15 +545,10 @@ def build_options_options_preview(
|
||||
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
|
||||
expiry_loss = flat_total if flat_total <= 0 else flat_total
|
||||
|
||||
return {
|
||||
"plan_type": "options_options",
|
||||
"premium_paid": round(prem, 6),
|
||||
"target_price": up_f, # 兼容旧字段,取上破
|
||||
"target_price": up_f,
|
||||
"target_price_up": up_f,
|
||||
"target_price_down": down_f,
|
||||
"winner_at_up": win_up,
|
||||
@@ -538,10 +596,9 @@ def build_options_options_preview(
|
||||
"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),
|
||||
"expiry_flat_total": round(flat_total, 4),
|
||||
"premium_paid": round(prem, 6),
|
||||
"expiry_is_loss": flat_total <= 0,
|
||||
# 盈亏比:盈利/全亏保费(风险=权利金全损)
|
||||
"rr_risk_premium": round(prem, 6),
|
||||
"rr_at_up": round(at_up / prem, 4) if prem > 0 else None,
|
||||
"rr_at_down": round(at_dn / prem, 4) if prem > 0 else None,
|
||||
|
||||
Reference in New Issue
Block a user