Add HEDGE_PO_MODE instance modes with shared sim/live path.

Lock one mode per instance/subaccount, cross-asset BTC option/ETH perp sizing, combo-U exit, and ship docs/tests/audit.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-10-14 20:05:25 +08:00
parent ab7a2309ad
commit 5d161b4220
18 changed files with 1215 additions and 115 deletions
+124 -11
View File
@@ -141,8 +141,14 @@ def size_from_premium(
ratio: float,
contract_size: float,
exec_factor: float = PREMIUM_EXEC_FACTOR,
option_index: Optional[float] = None,
perp_index: Optional[float] = None,
) -> dict[str, Any]:
"""权利金×0.95 → ETH 两位小数 → 期权张 → 永续跟比例."""
"""权利金×0.95 → 币两位小数 → 期权张 → 永续跟比例.
同币:永续币 = 期权币 / ratio.
跨币(传入 option_index 与 perp_index):永续名义U = 期权名义U × (1/ratio).
"""
budget = float(premium_budget or 0)
a = float(ask or 0)
ct = float(ct_mult or 0.01)
@@ -155,23 +161,25 @@ def size_from_premium(
"msg": "定仓参数无效",
"usable_premium": round(usable, 4),
"eth_qty": 0.0,
"coin_qty": 0.0,
"sheets": 0.0,
"perp_eth": 0.0,
"contracts": 0.0,
}
# ask 为每 1 币权利金;ETH 数量 = usable / ask
eth_qty = floor2(usable / a)
if eth_qty <= 0:
# ask 为每 1 币权利金;币数量 = usable / ask
coin_qty = floor2(usable / a)
if coin_qty <= 0:
return {
"ok": False,
"msg": "权利金不足以买入 0.01 ETH 名义期权",
"msg": "权利金不足以买入 0.01 币名义期权",
"usable_premium": round(usable, 4),
"eth_qty": 0.0,
"coin_qty": 0.0,
"sheets": 0.0,
"perp_eth": 0.0,
"contracts": 0.0,
}
sheets = eth_qty / ct
sheets = coin_qty / ct
# 张数向下取整到整数张(OKX 期权常见整张)
sheets_i = float(math.floor(sheets + 1e-12))
if sheets_i <= 0:
@@ -179,27 +187,132 @@ def size_from_premium(
"ok": False,
"msg": "换算期权张数不足 1 张",
"usable_premium": round(usable, 4),
"eth_qty": eth_qty,
"eth_qty": coin_qty,
"coin_qty": coin_qty,
"sheets": 0.0,
"perp_eth": 0.0,
"contracts": 0.0,
}
# 用整张回写 ETH,保持与下单一致
eth_qty = round(sheets_i * ct, 2)
perp_eth = eth_qty / r
# 用整张回写币量,保持与下单一致
coin_qty = round(sheets_i * ct, 2)
oi = _sf(option_index)
pi = _sf(perp_index)
cross = oi is not None and pi is not None and oi > 0 and pi > 0 and abs(oi - pi) > 1e-6
if cross or (oi is not None and pi is not None and oi > 0 and pi > 0):
from lib.hedge_plan.hedge_po_mode_lib import size_cross_notional
cross_sz = size_cross_notional(
option_coin_qty=coin_qty,
option_index=float(oi),
perp_index=float(pi),
contract_size=cs,
notional_k=(1.0 / r) if r > 0 else None,
)
if not cross_sz.get("ok"):
return {
"ok": False,
"msg": cross_sz.get("msg") or "跨币定仓失败",
"usable_premium": round(usable, 4),
"eth_qty": coin_qty,
"coin_qty": coin_qty,
"sheets": sheets_i,
"perp_eth": 0.0,
"contracts": 0.0,
}
perp_coin = float(cross_sz["perp_coin"])
contracts = float(cross_sz["contracts"])
premium_est = a * sheets_i * ct
return {
"ok": True,
"msg": "",
"usable_premium": round(usable, 4),
"eth_qty": coin_qty,
"coin_qty": coin_qty,
"sheets": sheets_i,
"perp_eth": round(perp_coin, 6),
"contracts": contracts,
"premium_est": round(premium_est, 4),
"ratio": r,
"exec_factor": float(exec_factor or PREMIUM_EXEC_FACTOR),
"option_notional_usdt": cross_sz.get("option_notional_usdt"),
"perp_notional_usdt": cross_sz.get("perp_notional_usdt"),
"notional_k": cross_sz.get("notional_k"),
"cross_asset": bool(cross),
}
perp_eth = coin_qty / r
contracts = perp_eth / cs
premium_est = a * sheets_i * ct
return {
"ok": True,
"msg": "",
"usable_premium": round(usable, 4),
"eth_qty": eth_qty,
"eth_qty": coin_qty,
"coin_qty": coin_qty,
"sheets": sheets_i,
"perp_eth": round(perp_eth, 6),
"contracts": contracts,
"premium_est": round(premium_est, 4),
"ratio": r,
"exec_factor": float(exec_factor or PREMIUM_EXEC_FACTOR),
"cross_asset": False,
}
def contracts_from_option_sheets(
*,
sheets: float,
ct_mult: float,
ratio: float,
contract_size: float,
option_index: Optional[float] = None,
perp_index: Optional[float] = None,
) -> dict[str, Any]:
"""手动张数 → 永续张数(同币或跨币名义)."""
sheets_i = float(int(float(sheets or 0)))
ct = float(ct_mult or 0.01)
r = float(ratio or 0)
cs = float(contract_size or 0.01)
if sheets_i <= 0 or ct <= 0 or r <= 0 or cs <= 0:
return {"ok": False, "msg": "定仓参数无效", "coin_qty": 0.0, "contracts": 0.0}
coin_qty = round(sheets_i * ct, 2)
oi = _sf(option_index)
pi = _sf(perp_index)
if oi is not None and pi is not None and oi > 0 and pi > 0:
from lib.hedge_plan.hedge_po_mode_lib import size_cross_notional
cross_sz = size_cross_notional(
option_coin_qty=coin_qty,
option_index=float(oi),
perp_index=float(pi),
contract_size=cs,
notional_k=(1.0 / r) if r > 0 else None,
)
if not cross_sz.get("ok"):
return {"ok": False, "msg": cross_sz.get("msg") or "跨币定仓失败", "coin_qty": coin_qty}
return {
"ok": True,
"msg": "",
"sheets": sheets_i,
"coin_qty": coin_qty,
"eth_qty": coin_qty,
"contracts": float(cross_sz["contracts"]),
"perp_coin": float(cross_sz["perp_coin"]),
"option_notional_usdt": cross_sz.get("option_notional_usdt"),
"perp_notional_usdt": cross_sz.get("perp_notional_usdt"),
"ratio": r,
"cross_asset": abs(float(oi) - float(pi)) > 1e-6,
}
perp_coin = coin_qty / r
return {
"ok": True,
"msg": "",
"sheets": sheets_i,
"coin_qty": coin_qty,
"eth_qty": coin_qty,
"contracts": perp_coin / cs,
"perp_coin": perp_coin,
"ratio": r,
"cross_asset": False,
}