修正期期盈亏比口径:按总权利金计算,残值按本合约
盈利腿触发改为盈利金额/总权利金;亏损腿残值20%仍相对该合约自身权利金。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1717,7 +1717,7 @@
|
||||
rrLine =
|
||||
" · 目标盈亏比 " +
|
||||
fmt(rrTarget, 2) +
|
||||
'<span class="muted">(盈利金额/初始权利金)</span>';
|
||||
'<span class="muted">(盈利金额/总权利金)</span>';
|
||||
} else if (s.rr_at_up != null || s.rr_at_down != null) {
|
||||
rrLine =
|
||||
" · 盈亏比 上破 " +
|
||||
@@ -2410,7 +2410,7 @@
|
||||
html +=
|
||||
"<div><span class=\"muted\">盈亏比</span> " +
|
||||
fmt(p.profit_rr, 2) +
|
||||
" <span class=\"muted\">(盈利金额/初始权利金)</span></div>";
|
||||
" <span class=\"muted\">(盈利金额/总权利金)</span></div>";
|
||||
} else {
|
||||
html +=
|
||||
"<div><span class=\"muted\">目标价</span> 上破 " +
|
||||
|
||||
@@ -1455,7 +1455,7 @@
|
||||
" · 盈亏比 " +
|
||||
fmt(rr, 2) +
|
||||
"</span>" +
|
||||
'<span class="muted opt-target-row-hint">进行中 · 达盈亏比仅平盈利腿;亏损腿残值平或到期平</span>' +
|
||||
'<span class="muted opt-target-row-hint">进行中 · 盈利达总权利金×盈亏比仅平盈利腿;亏损腿按本合约残值平或到期平</span>' +
|
||||
"</div>"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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'))}"
|
||||
|
||||
@@ -213,7 +213,7 @@
|
||||
<div class="tip-collapse-body rule-tip">
|
||||
<p><strong>账户</strong>:两腿都在<strong>期权账户</strong>。可用预算 = min(交易 USDC × 对冲缓冲 <strong id="hp-oo-buf-ratio">{{ '%.2f'|format(hedge_plan_budget_buffer|default(0.95)|float) }}</strong>, 单笔预算);可在 env「对冲预算缓冲比例」改。</p>
|
||||
<p><strong>下单</strong>:选 Call + Put 后「计算」再「启动」。启动会再拉卖一并按最新价重算张数,IOC 完全成交才算成功;资金不足可在右侧划转。</p>
|
||||
<p><strong>板块</strong>:左填<strong>盈亏比</strong>(盈利金额÷初始权利金,默认2)与张数模式(同张数/做多/做空);右 T 型选腿。<strong>两腿仅允许平值或虚值</strong>(禁实值)。出场:盈利腿达盈亏比即平;亏损腿「残值平」=权利金跌至初始20%且有买一时平,「到期平」=持有至到期。</p>
|
||||
<p><strong>板块</strong>:左填<strong>盈亏比</strong>(盈利金额÷总权利金,默认2)与张数模式(同张数/做多/做空);右 T 型选腿。<strong>两腿仅允许平值或虚值</strong>(禁实值)。出场:盈利腿达盈亏比即平;亏损腿「残值平」=本合约权利金跌至20%且有买一时平,「到期平」=持有至到期。</p>
|
||||
</div>
|
||||
</details>
|
||||
<div class="form-row hp-uly-row">
|
||||
@@ -221,7 +221,7 @@
|
||||
<button type="button" class="btn-secondary hp-uly-btn-oo" data-uly="BTC">BTC</button>
|
||||
</div>
|
||||
<div class="form-row hp-target-row hp-oo-target-row">
|
||||
<label title="盈利金额 / 初始权利金">盈亏比 <input type="number" step="0.1" min="0.1" id="hp-profit-rr" value="2" placeholder="默认2" autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-form-type="other" /></label>
|
||||
<label title="盈利金额 / 总权利金">盈亏比 <input type="number" step="0.1" min="0.1" id="hp-profit-rr" value="2" placeholder="默认2" autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-form-type="other" /></label>
|
||||
<span id="hp-oo-index" class="hp-oo-index" aria-live="polite">指数 —</span>
|
||||
</div>
|
||||
<div class="hp-oo-controls">
|
||||
@@ -236,7 +236,7 @@
|
||||
<div class="hp-oo-ctrl" id="hp-oo-close-mode-row">
|
||||
<span class="hp-oo-ctrl-lab" title="仅控制盈利腿平掉后的另一腿">平仓</span>
|
||||
<div class="hp-oo-seg" role="group" aria-label="平仓模式">
|
||||
<button type="button" class="btn-secondary hp-oo-close-mode is-selected" data-oo-close="close_all" title="盈利腿平后:亏损腿权利金跌至初始20%且有买一时平掉(失败重试)"><span class="hp-oo-check" aria-hidden="true">✓</span>残值平</button>
|
||||
<button type="button" class="btn-secondary hp-oo-close-mode is-selected" data-oo-close="close_all" title="盈利腿平后:亏损腿本合约权利金跌至20%且有买一时平掉(失败重试)"><span class="hp-oo-check" aria-hidden="true">✓</span>残值平</button>
|
||||
<button type="button" class="btn-secondary hp-oo-close-mode" data-oo-close="hold_expiry" title="盈利腿平后另一腿持有至到期"><span class="hp-oo-check" aria-hidden="true">✓</span>到期平</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user