Use daily open as perp-hedge entry and toggle buy-straddle vs perp overlays.

Amp-stats now prices premium from each day's open, and the form switches mutually between straddle and perpetual-options对照.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-28 14:31:17 +08:00
parent d049c5d317
commit 2ce67da8e8
6 changed files with 191 additions and 97 deletions
+71 -43
View File
@@ -279,7 +279,7 @@ def enrich_rows_pnl(
change=float(item.get("change") or 0),
open_px=float(item.get("open") or 0),
close_px=float(item.get("close") or 0),
premium_total=float(hedge["premium_total"]),
option_leverage=float(hedge["option_leverage"]),
opt_coins=float(hedge["opt_coins"]),
)
else:
@@ -291,22 +291,21 @@ def enrich_rows_pnl(
def normalize_perp_hedge_params(raw: Any) -> Optional[dict[str, float]]:
"""永期对冲对照参数.缺必填则返回 None(不做对照).
接受 dict 或带 spot/target_profit_u/perp_leverage/option_leverage 的对象字段.
入场价按日开盘;表单只需目标盈利/杠杆/比例.旧字段 spot 可忽略.
"""
if raw is None or raw == "":
return None
if not isinstance(raw, dict):
return None
spot = _safe_float(raw.get("spot"))
target = _safe_float(raw.get("target_profit_u") if "target_profit_u" in raw else raw.get("target"))
p_lev = _safe_float(raw.get("perp_leverage"))
o_lev = _safe_float(raw.get("option_leverage"))
rp = _safe_float(raw.get("ratio_perp"))
ro = _safe_float(raw.get("ratio_opt"))
ct = _safe_float(raw.get("ct_mult"))
if spot is None or target is None or p_lev is None or o_lev is None:
if target is None or p_lev is None or o_lev is None:
return None
if spot <= 0 or target < 0 or p_lev <= 0 or o_lev <= 0:
if target < 0 or p_lev <= 0 or o_lev <= 0:
return None
if rp is None or rp <= 0:
rp = 1.0
@@ -314,33 +313,38 @@ def normalize_perp_hedge_params(raw: Any) -> Optional[dict[str, float]]:
ro = 2.0
if ct is None or ct <= 0:
ct = 0.01
prem_per_coin = spot / o_lev
opt_coins = 1.0 * (ro / rp)
premium_total = opt_coins * prem_per_coin
return {
"spot": spot,
"target_profit_u": target,
"perp_leverage": p_lev,
"option_leverage": o_lev,
"ratio_perp": rp,
"ratio_opt": ro,
"ct_mult": ct,
"prem_per_coin": prem_per_coin,
"opt_coins": opt_coins,
"opt_sheets": opt_coins / ct,
"premium_total": premium_total,
}
def perp_hedge_day_premium(*, open_px: float, option_leverage: float, opt_coins: float) -> float:
"""单日权利金总额 = 开盘 / 期权杠杆 × 期权币数."""
o = float(open_px or 0)
lev = float(option_leverage or 0)
coins = float(opt_coins or 0)
if o <= 0 or lev <= 0 or coins < 0:
return 0.0
return coins * (o / lev)
def perp_hedge_day_pnl(
*,
change: float,
open_px: float,
close_px: float,
premium_total: float,
option_leverage: float,
opt_coins: float,
) -> float:
"""单日组合净利(永续多1币 + 买期权).
"""单日组合净利(永续多1币 + 买期权);入场/权利金按当日开盘.
上涨: change − 权利金 − 永续开平手续费
下跌: |change|×(opt_coins1) 权利金
@@ -348,7 +352,9 @@ def perp_hedge_day_pnl(
from lib.trade.trade_fee_lib import estimate_roundtrip_fee_usdt
chg = float(change or 0)
prem = float(premium_total or 0)
prem = perp_hedge_day_premium(
open_px=open_px, option_leverage=option_leverage, opt_coins=opt_coins
)
coins = float(opt_coins or 0)
if chg >= 0:
fee = 0.0
@@ -363,54 +369,74 @@ def perp_hedge_stats(
rows: list[dict[str, Any]],
hedge: dict[str, float],
) -> dict[str, Any]:
"""永期对冲:所需点数达标 + 按日组合盈亏汇总."""
"""永期对冲:所需点数达标 + 按日组合盈亏汇总.
日盈亏权利金按当日开盘;推所需点数用样本开盘中位数作入场参照.
"""
from lib.hub.hub_perp_options_calc_lib import calc_perp_options_points
points_data, points_err = calc_perp_options_points(
base="ETH",
spot=hedge["spot"],
capital_usdt=max(hedge["spot"] / hedge["perp_leverage"] * 2, 1000.0),
target_profit_u=hedge["target_profit_u"],
perp_leverage=hedge["perp_leverage"],
option_leverage=hedge["option_leverage"],
ratio_perp=hedge["ratio_perp"],
ratio_opt=hedge["ratio_opt"],
ct_mult=hedge["ct_mult"],
opens = [float(r.get("open") or 0) for r in (rows or []) if float(r.get("open") or 0) > 0]
spot_ref = statistics.median(opens) if opens else None
prem_ref = (
perp_hedge_day_premium(
open_px=spot_ref,
option_leverage=hedge["option_leverage"],
opt_coins=hedge["opt_coins"],
)
if spot_ref is not None
else None
)
prem_per_coin_ref = (spot_ref / hedge["option_leverage"]) if spot_ref is not None else None
move_a = None
move_b = None
if points_data:
move_a = float((points_data.get("case_a") or {}).get("move_points") or 0) or None
mb = (points_data.get("case_b") or {}).get("move_points_portfolio")
move_b = float(mb) if mb is not None else None
points_err = None
if spot_ref is not None and spot_ref > 0:
points_data, points_err = calc_perp_options_points(
base="ETH",
spot=spot_ref,
capital_usdt=max(spot_ref / hedge["perp_leverage"] * 2, 1000.0),
target_profit_u=hedge["target_profit_u"],
perp_leverage=hedge["perp_leverage"],
option_leverage=hedge["option_leverage"],
ratio_perp=hedge["ratio_perp"],
ratio_opt=hedge["ratio_opt"],
ct_mult=hedge["ct_mult"],
)
if points_data:
move_a = float((points_data.get("case_a") or {}).get("move_points") or 0) or None
mb = (points_data.get("case_b") or {}).get("move_points_portfolio")
move_b = float(mb) if mb is not None else None
else:
points_err = "样本无有效开盘价,无法推所需点数"
# 使用已 enrich 的 perp_hedge_pnl;若无则当场补算
# 按日开盘重算盈亏(不沿用固定权利金)
work: list[dict[str, Any]] = []
for r in rows or []:
item = dict(r)
if item.get("perp_hedge_pnl") is None:
item["perp_hedge_pnl"] = perp_hedge_day_pnl(
change=float(item.get("change") or 0),
open_px=float(item.get("open") or 0),
close_px=float(item.get("close") or 0),
premium_total=float(hedge["premium_total"]),
opt_coins=float(hedge["opt_coins"]),
)
item["perp_hedge_pnl"] = perp_hedge_day_pnl(
change=float(item.get("change") or 0),
open_px=float(item.get("open") or 0),
close_px=float(item.get("close") or 0),
option_leverage=float(hedge["option_leverage"]),
opt_coins=float(hedge["opt_coins"]),
)
work.append(item)
n = len(work)
empty = {
"enabled": True,
"spot": round(hedge["spot"], 4),
"entry": "open",
"spot": None if spot_ref is None else round(spot_ref, 4),
"target_profit_u": round(hedge["target_profit_u"], 4),
"perp_leverage": round(hedge["perp_leverage"], 4),
"option_leverage": round(hedge["option_leverage"], 4),
"ratio_perp": round(hedge["ratio_perp"], 4),
"ratio_opt": round(hedge["ratio_opt"], 4),
"ratio_label": f"{hedge['ratio_perp']:g}:{hedge['ratio_opt']:g}",
"prem_per_coin": round(hedge["prem_per_coin"], 4),
"prem_per_coin": None if prem_per_coin_ref is None else round(prem_per_coin_ref, 4),
"opt_coins": round(hedge["opt_coins"], 4),
"opt_sheets": round(hedge["opt_sheets"], 4),
"premium_total": round(hedge["premium_total"], 4),
"premium_total": None if prem_ref is None else round(prem_ref, 4),
"move_a": None if move_a is None else round(move_a, 4),
"move_b": None if move_b is None else round(move_b, 4),
"points_error": points_err,
@@ -995,7 +1021,9 @@ def build_export_csv(payload: dict[str, Any]) -> str:
"【永期对冲对照】",
"比例",
ph.get("ratio_label"),
"现价",
"入场",
"按日开盘",
"推点数开盘中位",
ph.get("spot"),
"目标",
ph.get("target_profit_u"),
@@ -1003,9 +1031,9 @@ def build_export_csv(payload: dict[str, Any]) -> str:
)
w.writerow(
[
"单币权利金",
"单币权利金(开盘中位)",
ph.get("prem_per_coin"),
"权利金总额",
"权利金总额(开盘中位)",
ph.get("premium_total"),
"期权币数",
ph.get("opt_coins"),