"""中控永期对冲计算器:永续 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