Add option-option hedge mode with SIM/LIVE parity.
Mutual hedge_mode, amplitude OTM selection, 1:1 risk sizing, win-leg/full close, dual audits and docs. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -104,17 +104,24 @@ def assess_open_capacity(
|
||||
option_ask: float | None = None,
|
||||
option_qty_eth: float | None = None,
|
||||
perp_qty_eth: float | None = None,
|
||||
call_ask: float | None = None,
|
||||
put_ask: float | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
返回永续/期权是否有足够交易账户资金开新仓。
|
||||
- 永续:交易账户 USDT >= 名义/杠杆
|
||||
- 期权:交易账户 USDC >= 卖一×名义×(1+费率)
|
||||
可选覆盖 ask/名义(选约后应用选中腿卖一,避免与 max(call,put) 打架)。
|
||||
- 期期:期权需 (call_ask+put_ask)×qty×(1+fee);永续视为不需要
|
||||
"""
|
||||
global _notified_while_short
|
||||
db = db or get_db()
|
||||
s = get_settings()
|
||||
ledger = Ledger(db)
|
||||
hedge = str(
|
||||
ledger.get_setting_str("hedge_mode", s.hedge_mode) or s.hedge_mode
|
||||
).strip().lower()
|
||||
if hedge not in ("perp_option", "option_option"):
|
||||
hedge = "perp_option"
|
||||
lev = float(ledger.get_setting_float("leverage", s.leverage) or 3)
|
||||
if lev <= 0:
|
||||
lev = 3.0
|
||||
@@ -132,10 +139,38 @@ def assess_open_capacity(
|
||||
|
||||
idx, ask_book = _index_and_option_ask()
|
||||
ask = float(option_ask) if option_ask is not None and float(option_ask) > 0 else ask_book
|
||||
margin_need = (float(idx) * perp_qty / lev) if idx and idx > 0 else None
|
||||
premium_need = (
|
||||
float(ask) * opt_qty * (1.0 + fee_rate) if ask is not None and ask > 0 else None
|
||||
)
|
||||
if hedge == "option_option":
|
||||
ca = float(call_ask) if call_ask is not None and float(call_ask) > 0 else None
|
||||
pa = float(put_ask) if put_ask is not None and float(put_ask) > 0 else None
|
||||
if ca is None or pa is None:
|
||||
# 回退:用监控对 call/put 卖一
|
||||
try:
|
||||
from .session import get_session
|
||||
|
||||
snap = get_session().snapshot()
|
||||
if ca is None and snap.call and snap.call.ask:
|
||||
ca = float(snap.call.ask)
|
||||
if pa is None and snap.put and snap.put.ask:
|
||||
pa = float(snap.put.ask)
|
||||
except Exception:
|
||||
pass
|
||||
cush = float(
|
||||
ledger.get_setting_float("oo_budget_cushion", s.oo_budget_cushion)
|
||||
or s.oo_budget_cushion
|
||||
)
|
||||
cush = min(1.0, max(0.5, cush))
|
||||
if ca is not None and pa is not None and ca > 0 and pa > 0:
|
||||
# 与定仓一致:按预留后的权利金需求估资金门
|
||||
premium_need = (ca + pa) * opt_qty * (1.0 + fee_rate) * cush
|
||||
else:
|
||||
premium_need = None
|
||||
margin_need = 0.0
|
||||
perp_qty = 0.0
|
||||
else:
|
||||
margin_need = (float(idx) * perp_qty / lev) if idx and idx > 0 else None
|
||||
premium_need = (
|
||||
float(ask) * opt_qty * (1.0 + fee_rate) if ask is not None and ask > 0 else None
|
||||
)
|
||||
|
||||
if s.is_sim:
|
||||
bal = _sim_balances(db)
|
||||
@@ -149,24 +184,31 @@ def assess_open_capacity(
|
||||
have_opt = float(t_usdc) if t_usdc is not None else None
|
||||
|
||||
perp_ok: bool | None
|
||||
if margin_need is None or have_perp is None:
|
||||
if hedge == "option_option":
|
||||
perp_ok = True
|
||||
elif margin_need is None or have_perp is None:
|
||||
perp_ok = None
|
||||
else:
|
||||
perp_ok = have_perp + 1e-9 >= margin_need
|
||||
perp_ok = float(have_perp) + 1e-9 >= float(margin_need)
|
||||
|
||||
opt_ok: bool | None
|
||||
if premium_need is None or have_opt is None:
|
||||
opt_ok = None
|
||||
else:
|
||||
opt_ok = have_opt + 1e-9 >= premium_need
|
||||
opt_ok = float(have_opt) + 1e-9 >= float(premium_need)
|
||||
|
||||
funds_ok = perp_ok is True and opt_ok is True
|
||||
if hedge == "option_option":
|
||||
funds_ok = opt_ok is True
|
||||
else:
|
||||
funds_ok = perp_ok is True and opt_ok is True
|
||||
# 资金恢复后允许下次不足再通知一次
|
||||
if funds_ok:
|
||||
_notified_while_short = False
|
||||
|
||||
lev_i = int(round(lev)) if abs(lev - round(lev)) < 1e-9 else lev
|
||||
if perp_ok is True:
|
||||
if hedge == "option_option":
|
||||
perp_label = "永续 —(期期)"
|
||||
elif perp_ok is True:
|
||||
perp_label = f"永续{lev_i}x 可开"
|
||||
elif perp_ok is False:
|
||||
perp_label = f"永续{lev_i}x 不可开"
|
||||
@@ -181,6 +223,7 @@ def assess_open_capacity(
|
||||
opt_label = "期权 —"
|
||||
|
||||
return {
|
||||
"hedge_mode": hedge,
|
||||
"leverage": lev,
|
||||
"perp_qty_eth": perp_qty,
|
||||
"option_qty_eth": opt_qty,
|
||||
@@ -201,11 +244,22 @@ def assess_open_capacity(
|
||||
|
||||
def funds_gate_blocks(cap: dict[str, Any] | None) -> tuple[bool, str]:
|
||||
"""
|
||||
Fail-closed:仅当永续与期权均为 True 才放行。
|
||||
None(未知,如币安未接余额)或 False → 拦截。
|
||||
Fail-closed:永期需永续+期权均为 True;期期仅需期权为 True。
|
||||
None(未知)或 False → 拦截。
|
||||
"""
|
||||
if not cap:
|
||||
return True, "资金可开判定结果为空,拒绝开仓"
|
||||
hedge = str(cap.get("hedge_mode") or "perp_option").strip().lower()
|
||||
if hedge == "option_option":
|
||||
if cap.get("option_can_open") is not True:
|
||||
detail = (
|
||||
f"{cap.get('option_label')};"
|
||||
f"期权需≈{cap.get('option_need_usdc')}U/有{cap.get('option_have_usdc')}U"
|
||||
)
|
||||
if cap.get("option_can_open") is None:
|
||||
detail += "(余额/盘口未知,fail-closed 拒绝开仓)"
|
||||
return True, f"资金不足或状态未知,暂不可开新仓:{detail}"
|
||||
return False, ""
|
||||
if cap.get("perp_can_open") is not True or cap.get("option_can_open") is not True:
|
||||
detail = (
|
||||
f"{cap.get('perp_label')} · {cap.get('option_label')};"
|
||||
|
||||
Reference in New Issue
Block a user