Add hub perpetual-options hedge calculator tab and sizing formula.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-28 12:37:51 +08:00
parent f360242188
commit 4bcf88b5cb
6 changed files with 496 additions and 5 deletions
+146
View File
@@ -0,0 +1,146 @@
"""中控永期对冲计算器:永续 1 币 + 按目标盈利反推期权仓位(纯函数)."""
from __future__ import annotations
from typing import Any, Optional, Tuple
from lib.trade.trade_fee_lib import estimate_roundtrip_fee_usdt, taker_fee_rate
DEFAULT_CT_MULT = 0.01
PERP_COINS = 1.0
def _f(v: Any) -> Optional[float]:
if v is None or v == "":
return None
try:
return float(v)
except (TypeError, ValueError):
return None
def calc_perp_options_hedge(
*,
base: str = "ETH",
spot: float,
capital_usdt: float,
target_profit_u: float,
move_mode: str = "points",
move_value: float,
perp_leverage: float,
option_leverage: float,
ct_mult: float = DEFAULT_CT_MULT,
) -> Tuple[Optional[dict[str, Any]], Optional[str]]:
"""测算期权开仓币数/张数,并给出永续对 / 期权对两套情景.
单币权利金 = 现价 / 期权杠杆
权利金预算 = 永续毛收益 − 目标盈利 − 永续开平手续费
期权币数 = 权利金预算 / 单币权利金
"""
b = (base or "ETH").strip().upper()
if b not in ("ETH", "BTC"):
return None, "币种仅支持 BTC / ETH"
s = _f(spot)
capital = _f(capital_usdt)
target = _f(target_profit_u)
move = _f(move_value)
p_lev = _f(perp_leverage)
o_lev = _f(option_leverage)
ct = _f(ct_mult)
mode = (move_mode or "points").strip().lower()
if mode not in ("points", "pct", "percent", "rate"):
return None, "波动模式须为 points 或 pct"
if mode in ("percent", "rate"):
mode = "pct"
if s is None or capital is None or target is None or move is None or p_lev is None or o_lev is None:
return None, "参数格式错误"
if ct is None or ct <= 0:
ct = DEFAULT_CT_MULT
if s <= 0 or capital <= 0 or move <= 0 or p_lev <= 0 or o_lev <= 0:
return None, "现价、资金、波动、杠杆须大于 0"
if target < 0:
return None, "目标盈利不能为负"
# 波动对应的绝对点数(价格变动)
if mode == "pct":
move_points = s * (move / 100.0)
else:
move_points = move
if move_points <= 0:
return None, "波动对应价格变动须大于 0"
exit_px = s + move_points # 永续方向对:按上涨测算 1 币多头
perp_gross = move_points * PERP_COINS
fee = estimate_roundtrip_fee_usdt(
s,
exit_px,
qty=PERP_COINS,
contract_size=1.0,
)
fee_rate = taker_fee_rate()
prem_per_coin = s / o_lev
if prem_per_coin <= 0:
return None, "单币权利金无效"
premium_budget = perp_gross - target - fee
if premium_budget <= 0:
return None, "波动收益不足以覆盖目标盈利+手续费,无法开期权"
opt_coins = premium_budget / prem_per_coin
opt_sheets = opt_coins / ct
premium_total = opt_coins * prem_per_coin # == premium_budget
margin = (s * PERP_COINS) / p_lev
capital_ok = capital >= margin
# A: 永续方向对,期权权利金全亏
case_a_net = perp_gross - premium_total - fee
# B: 期权方向对,永续 1 币反向亏掉同等波动
opt_intrinsic = opt_coins * move_points
opt_net = opt_intrinsic - premium_total
perp_loss = -perp_gross
portfolio_net = opt_net + perp_loss
return {
"base": b,
"spot": round(s, 8),
"capital_usdt": round(capital, 8),
"target_profit_u": round(target, 8),
"move_mode": mode,
"move_value": round(move, 8),
"move_points": round(move_points, 8),
"exit_price": round(exit_px, 8),
"perp_coins": PERP_COINS,
"perp_leverage": round(p_lev, 8),
"option_leverage": round(o_lev, 8),
"ct_mult": ct,
"prem_per_coin": round(prem_per_coin, 8),
"perp_gross_u": round(perp_gross, 8),
"perp_fee_u": round(fee, 8),
"fee_rate": fee_rate,
"premium_budget_u": round(premium_budget, 8),
"opt_coins": round(opt_coins, 8),
"opt_sheets": round(opt_sheets, 8),
"premium_total_u": round(premium_total, 8),
"perp_margin_u": round(margin, 8),
"capital_ok": bool(capital_ok),
"case_a": {
"label": "永续方向对",
"perp_pnl_u": round(perp_gross, 8),
"premium_u": round(premium_total, 8),
"fee_u": round(fee, 8),
"net_u": round(case_a_net, 8),
},
"case_b": {
"label": "期权方向对",
"opt_intrinsic_u": round(opt_intrinsic, 8),
"premium_u": round(premium_total, 8),
"opt_net_u": round(opt_net, 8),
"perp_pnl_u": round(perp_loss, 8),
"portfolio_net_u": round(portfolio_net, 8),
},
}, None