From a1bf760a28e9e0d7f3dd0c43d4c5d7dc3862e0f1 Mon Sep 17 00:00:00 2001 From: dekun Date: Tue, 11 Aug 2026 16:51:50 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=9C=9F=E6=9C=9F=E7=9B=88?= =?UTF-8?q?=E4=BA=8F=E6=AF=94=E5=8F=A3=E5=BE=84=EF=BC=9A=E6=8C=89=E6=80=BB?= =?UTF-8?q?=E6=9D=83=E5=88=A9=E9=87=91=E8=AE=A1=E7=AE=97=EF=BC=8C=E6=AE=8B?= =?UTF-8?q?=E5=80=BC=E6=8C=89=E6=9C=AC=E5=90=88=E7=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 盈利腿触发改为盈利金额/总权利金;亏损腿残值20%仍相对该合约自身权利金。 Co-authored-by: Cursor --- lib/common/static/hedge_plan.js | 4 +- lib/common/static/options_panel.js | 2 +- lib/hedge_plan/hedge_plan_calc_lib.py | 20 ++++---- lib/hedge_plan/hedge_plan_db.py | 4 +- lib/hedge_plan/hedge_plan_monitor_lib.py | 49 ++++++++++++++----- lib/hedge_plan/hedge_plan_notify_lib.py | 6 +-- .../templates/hedge_plan_panel.html | 6 +-- tests/test_hedge_plan_calc.py | 2 +- 8 files changed, 58 insertions(+), 35 deletions(-) diff --git a/lib/common/static/hedge_plan.js b/lib/common/static/hedge_plan.js index ea895c0..46847a0 100644 --- a/lib/common/static/hedge_plan.js +++ b/lib/common/static/hedge_plan.js @@ -1717,7 +1717,7 @@ rrLine = " · 目标盈亏比 " + fmt(rrTarget, 2) + - '(盈利金额/初始权利金)'; + '(盈利金额/总权利金)'; } else if (s.rr_at_up != null || s.rr_at_down != null) { rrLine = " · 盈亏比 上破 " + @@ -2410,7 +2410,7 @@ html += "
盈亏比 " + fmt(p.profit_rr, 2) + - " (盈利金额/初始权利金)
"; + " (盈利金额/总权利金)"; } else { html += "
目标价 上破 " + diff --git a/lib/common/static/options_panel.js b/lib/common/static/options_panel.js index 1fe04c8..1dfaeac 100644 --- a/lib/common/static/options_panel.js +++ b/lib/common/static/options_panel.js @@ -1455,7 +1455,7 @@ " · 盈亏比 " + fmt(rr, 2) + "" + - '进行中 · 达盈亏比仅平盈利腿;亏损腿残值平或到期平' + + '进行中 · 盈利达总权利金×盈亏比仅平盈利腿;亏损腿按本合约残值平或到期平' + "
" ); } diff --git a/lib/hedge_plan/hedge_plan_calc_lib.py b/lib/hedge_plan/hedge_plan_calc_lib.py index c1aa86e..ca44c44 100644 --- a/lib/hedge_plan/hedge_plan_calc_lib.py +++ b/lib/hedge_plan/hedge_plan_calc_lib.py @@ -454,7 +454,8 @@ def build_options_options_preview( ) -> dict[str, Any]: """期期情景:盈亏比达标 / 到期现价 / 最大保费损耗. - 新口径优先 profit_rr(盈利金额/初始权利金);若未传则兼容旧上/下破目标价. + 新口径优先 profit_rr(盈利金额/总权利金);若未传则兼容旧上/下破目标价. + 残值按亏损腿本合约权利金的 20% 计. """ def _leg_pnl(leg: dict[str, Any], spot: float) -> float: @@ -474,13 +475,12 @@ def build_options_options_preview( # 新:盈亏比情景(不依赖指数上下破价) if rr is not None and rr > 0: - # 盈利腿达 RR:盈利 = rr × 该腿权利金;亏损腿按全亏 / 残值20%回收两种 - a_win = rr * prem_a - b_win = rr * prem_b - a_at_a = a_win + # 盈利腿达 RR:盈利金额 = rr × 总权利金;亏损腿按全亏 / 本合约残值20%回收 + win_profit = rr * prem + a_at_a = win_profit b_at_a_full = -prem_b - b_at_a_res = -prem_b * 0.8 # 回收 20% - b_at_b = b_win + b_at_a_res = -prem_b * 0.8 # 本合约回收 20% + b_at_b = win_profit a_at_b_full = -prem_a a_at_b_res = -prem_a * 0.8 @@ -506,7 +506,7 @@ def build_options_options_preview( "leg_a_pnl": round(a_at_a, 4), "leg_b_pnl": round(b_at_a_full, 4), "total": round(a_at_a + b_at_a_full, 4), - "note": "盈利腿按盈亏比兑现;亏损腿权利金全亏", + "note": "盈利=总权利金×盈亏比;亏损腿本合约全亏", }, { "id": "rr_leg_b_full", @@ -515,7 +515,7 @@ def build_options_options_preview( "leg_a_pnl": round(a_at_b_full, 4), "leg_b_pnl": round(b_at_b, 4), "total": round(a_at_b_full + b_at_b, 4), - "note": "盈利腿按盈亏比兑现;亏损腿权利金全亏", + "note": "盈利=总权利金×盈亏比;亏损腿本合约全亏", }, { "id": "rr_leg_a_residual", @@ -524,7 +524,7 @@ def build_options_options_preview( "leg_a_pnl": round(a_at_a, 4), "leg_b_pnl": round(b_at_a_res, 4), "total": round(a_at_a + b_at_a_res, 4), - "note": "亏损腿买一回收约初始权利金20%", + "note": "亏损腿买一回收约本合约权利金20%", }, { "id": "expiry_flat", diff --git a/lib/hedge_plan/hedge_plan_db.py b/lib/hedge_plan/hedge_plan_db.py index 687bc47..319452f 100644 --- a/lib/hedge_plan/hedge_plan_db.py +++ b/lib/hedge_plan/hedge_plan_db.py @@ -72,9 +72,9 @@ def init_hedge_plan_tables(conn: sqlite3.Connection) -> None: ) _ensure_column(conn, "hedge_plans", "target_price_up", "REAL") _ensure_column(conn, "hedge_plans", "target_price_down", "REAL") - # 期期出场:盈利金额/初始权利金(默认2);有值则走盈亏比监控,旧单仍用上/下破价 + # 期期出场:盈利金额/总权利金(默认2);有值则走盈亏比监控,旧单仍用上/下破价 _ensure_column(conn, "hedge_plans", "profit_rr", "REAL") - # close_all=残值平(权利金≤初始20%且有买一);hold_expiry=残腿持有至到期 + # close_all=残值平(本合约权利金≤20%且有买一);hold_expiry=残腿持有至到期 _ensure_column(conn, "hedge_plans", "oo_close_mode", "TEXT") # 永期「以期权为主」 _ensure_column(conn, "hedge_plans", "option_primary", "INTEGER") diff --git a/lib/hedge_plan/hedge_plan_monitor_lib.py b/lib/hedge_plan/hedge_plan_monitor_lib.py index a2f4356..9dddff5 100644 --- a/lib/hedge_plan/hedge_plan_monitor_lib.py +++ b/lib/hedge_plan/hedge_plan_monitor_lib.py @@ -190,9 +190,9 @@ def resolve_oo_rest_close_mode(plan: dict[str, Any]) -> str: return "close_all" -# 期期亏损腿残值平:当前买一回收 ≤ 初始权利金 × 该比例 +# 期期亏损腿残值平:当前买一回收 ≤ 本合约初始权利金 × 该比例 OO_LOSS_LEG_RESIDUAL_RATIO = 0.20 -# 期期默认盈亏比:盈利金额 / 初始权利金 +# 期期默认盈亏比:盈利金额 / 总权利金 OO_DEFAULT_PROFIT_RR = 2.0 @@ -230,15 +230,30 @@ def _oo_leg_mark_value(leg: dict[str, Any], bid: Optional[float]) -> Optional[fl return float(b) * sheets * ct -def _oo_leg_profit_rr(leg: dict[str, Any], bid: Optional[float]) -> Optional[float]: - """盈亏比 = 盈利金额 / 初始权利金;盈利金额 = 买一回收 − 初始权利金.""" - premium = float(leg.get("premium") or 0) - if premium <= 0: +def _oo_plan_premium_total(plan: dict[str, Any], legs: list[dict[str, Any]]) -> float: + """双腿总权利金:优先计划字段,否则对期权腿 premium 求和.""" + total = _sf(plan.get("premium_total")) + if total is not None and total > 0: + return float(total) + s = 0.0 + for leg in legs: + if not str(leg.get("leg_role") or "").startswith("option"): + continue + s += float(leg.get("premium") or 0) + return s + + +def _oo_leg_profit_rr( + leg: dict[str, Any], bid: Optional[float], *, total_premium: float +) -> Optional[float]: + """盈亏比 = 该腿盈利金额 / 总权利金;盈利金额 = 买一回收 − 本腿权利金.""" + if total_premium <= 0: return None + leg_prem = float(leg.get("premium") or 0) value = _oo_leg_mark_value(leg, bid) if value is None: return None - return (value - premium) / premium + return (value - leg_prem) / total_premium def _oo_resolve_profit_rr(plan: dict[str, Any]) -> Optional[float]: @@ -1035,7 +1050,7 @@ def _tick_oo_close_rest( ) -> Optional[dict[str, Any]]: """盈利腿已平后:残值平模式清亏损腿. - 条件:买一回收 ≤ 初始权利金×20%,且买一有流动性;失败或未达条件则下轮重试. + 条件:买一回收 ≤ 本合约初始权利金×20%,且买一有流动性;失败或未达条件则下轮重试. """ from lib.hedge_plan.hedge_plan_option_primary_lib import option_bid_liquidity_ok @@ -1069,7 +1084,7 @@ def _tick_oo_close_rest( premium = float(leg.get("premium") or 0) bid, bid_sz = _oo_quote_bid(cfg, inst_id) value = _oo_leg_mark_value(leg, bid) - # 残值门槛:当前买一回收须 ≤ 初始权利金的 20% + # 残值门槛:相对本合约初始权利金,买一回收须 ≤ 20% if premium > 0: if value is None: waiting = True @@ -1113,7 +1128,7 @@ def _tick_oo_close_rest( if not acted: if waiting: update_plan(conn, int(plan["id"]), close_reason="oo_rest_closing") - return {"plan_id": plan["id"], "msg": "残值平等待权利金≤20%", "waiting": True} + return {"plan_id": plan["id"], "msg": "残值平等待本合约权利金≤20%", "waiting": True} return None legs2 = get_plan_legs(conn, int(plan["id"])) still_open = _oo_option_legs(legs2, statuses=("open", "hold_to_expiry")) @@ -1173,17 +1188,20 @@ def _after_oo_winner_closed( def _tick_oo_profit_rr( cfg: dict[str, Any], conn: Any, plan: dict[str, Any], legs: list[dict[str, Any]], *, rr_target: float ) -> Optional[dict[str, Any]]: - """期期:任一开仓腿盈亏比(盈利金额/初始权利金)达目标 → 平盈利腿.""" + """期期:任一开仓腿盈亏比(该腿盈利金额/总权利金)达目标 → 平盈利腿.""" if not _env_bool("HEDGE_PLAN_OO_CLOSE_WINNER_ONLY", True): return None open_legs = _oo_option_legs(legs, statuses=("open",)) if len(open_legs) < 2: return None + total_prem = _oo_plan_premium_total(plan, legs) + if total_prem <= 0: + return None ranked: list[tuple[float, float, dict[str, Any]]] = [] for leg in open_legs: bid, _bid_sz = _oo_quote_bid(cfg, str(leg.get("inst_id") or "")) - rr = _oo_leg_profit_rr(leg, bid) + rr = _oo_leg_profit_rr(leg, bid, total_premium=total_prem) if rr is None: continue value = _oo_leg_mark_value(leg, bid) or 0.0 @@ -1225,7 +1243,12 @@ def _tick_oo_profit_rr( open_legs, best, reason=reason, - extra={"profit_rr": best_rr, "rr_target": float(rr_target), "index": idx}, + extra={ + "profit_rr": best_rr, + "rr_target": float(rr_target), + "total_premium": total_prem, + "index": idx, + }, ) diff --git a/lib/hedge_plan/hedge_plan_notify_lib.py b/lib/hedge_plan/hedge_plan_notify_lib.py index beefde5..03f8848 100644 --- a/lib/hedge_plan/hedge_plan_notify_lib.py +++ b/lib/hedge_plan/hedge_plan_notify_lib.py @@ -50,7 +50,7 @@ def build_hedge_start_message(plan: dict[str, Any], *, legs: Optional[list[dict[ if rr not in (None, ""): lines.extend( [ - f"🎯 盈亏比:{_fmt(rr)} (盈利金额/初始权利金)", + f"🎯 盈亏比:{_fmt(rr)} (盈利金额/总权利金)", f"💎 期权保费合计:{_fmt(plan.get('premium_total'), 4)} USDC", ] ) @@ -176,12 +176,12 @@ def notify_plan_end(cfg: dict[str, Any], conn: Any, plan: dict[str, Any]) -> boo side = "目标" mode = (plan.get("oo_close_mode") or "").strip().lower() if mode in ("close_all", "全平", "残值平"): - rest_txt = "另一腿残值平(权利金≤初始20%且有买一,失败重试)" + rest_txt = "另一腿残值平(本合约权利金≤20%且有买一,失败重试)" else: rest_txt = "另一腿到期平(持有至到期结算)" rr = plan.get("profit_rr") if rr not in (None, ""): - detail = f"盈亏比 {_fmt(rr)} (盈利金额/初始权利金)" + detail = f"盈亏比 {_fmt(rr)} (盈利金额/总权利金)" else: detail = ( f"上破 {_fmt(plan.get('target_price_up') or plan.get('target_price'))}" diff --git a/lib/hedge_plan/templates/hedge_plan_panel.html b/lib/hedge_plan/templates/hedge_plan_panel.html index e025caf..5c809ef 100644 --- a/lib/hedge_plan/templates/hedge_plan_panel.html +++ b/lib/hedge_plan/templates/hedge_plan_panel.html @@ -213,7 +213,7 @@

账户:两腿都在期权账户。可用预算 = min(交易 USDC × 对冲缓冲 {{ '%.2f'|format(hedge_plan_budget_buffer|default(0.95)|float) }}, 单笔预算);可在 env「对冲预算缓冲比例」改。

下单:选 Call + Put 后「计算」再「启动」。启动会再拉卖一并按最新价重算张数,IOC 完全成交才算成功;资金不足可在右侧划转。

-

板块:左填盈亏比(盈利金额÷初始权利金,默认2)与张数模式(同张数/做多/做空);右 T 型选腿。两腿仅允许平值或虚值(禁实值)。出场:盈利腿达盈亏比即平;亏损腿「残值平」=权利金跌至初始20%且有买一时平,「到期平」=持有至到期。

+

板块:左填盈亏比(盈利金额÷总权利金,默认2)与张数模式(同张数/做多/做空);右 T 型选腿。两腿仅允许平值或虚值(禁实值)。出场:盈利腿达盈亏比即平;亏损腿「残值平」=本合约权利金跌至20%且有买一时平,「到期平」=持有至到期。

@@ -221,7 +221,7 @@
- + 指数 —
@@ -236,7 +236,7 @@
平仓
- +
diff --git a/tests/test_hedge_plan_calc.py b/tests/test_hedge_plan_calc.py index 73943ea..76fe54c 100644 --- a/tests/test_hedge_plan_calc.py +++ b/tests/test_hedge_plan_calc.py @@ -110,7 +110,7 @@ class TestHedgePlanCalc(unittest.TestCase): self.assertEqual(p["summary"]["premium_paid"], 10) self.assertTrue(p["summary"]["expiry_is_loss"]) self.assertEqual(p["summary"]["profit_rr"], 2) - self.assertEqual(p["summary"]["at_rr_a_full_total"], 5) # 2*5 - 5 + self.assertEqual(p["summary"]["at_rr_a_full_total"], 15) # 盈利=2*10, 亏腿-5 self.assertEqual(len(p["scenarios"]), 5) self.assertEqual(p["scenarios"][0]["id"], "rr_leg_a_full") self.assertEqual(p["scenarios"][1]["id"], "rr_leg_b_full")