Harden strategy SoT: fail-closed funds gate, shared open pipeline, LIVE switch guards.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -98,11 +98,18 @@ def _sim_balances(db: Database) -> dict[str, float]:
|
||||
}
|
||||
|
||||
|
||||
def assess_open_capacity(db: Database | None = None) -> dict[str, Any]:
|
||||
def assess_open_capacity(
|
||||
db: Database | None = None,
|
||||
*,
|
||||
option_ask: float | None = None,
|
||||
option_qty_eth: float | None = None,
|
||||
perp_qty_eth: float | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
返回永续/期权是否有足够交易账户资金开新仓。
|
||||
- 永续:交易账户 USDT >= 名义/杠杆
|
||||
- 期权:交易账户 USDC >= 卖一×名义×(1+费率)
|
||||
可选覆盖 ask/名义(选约后应用选中腿卖一,避免与 max(call,put) 打架)。
|
||||
"""
|
||||
global _notified_while_short
|
||||
db = db or get_db()
|
||||
@@ -111,11 +118,20 @@ def assess_open_capacity(db: Database | None = None) -> dict[str, Any]:
|
||||
lev = float(ledger.get_setting_float("leverage", s.leverage) or 3)
|
||||
if lev <= 0:
|
||||
lev = 3.0
|
||||
perp_qty = float(ledger.get_setting_float("perp_qty_eth", s.perp_qty_eth) or 1)
|
||||
opt_qty = float(ledger.get_setting_float("option_qty_eth", s.option_qty_eth) or 2)
|
||||
perp_qty = float(
|
||||
perp_qty_eth
|
||||
if perp_qty_eth is not None
|
||||
else (ledger.get_setting_float("perp_qty_eth", s.perp_qty_eth) or 1)
|
||||
)
|
||||
opt_qty = float(
|
||||
option_qty_eth
|
||||
if option_qty_eth is not None
|
||||
else (ledger.get_setting_float("option_qty_eth", s.option_qty_eth) or 2)
|
||||
)
|
||||
fee_rate = float(ledger.get_setting_float("fee_rate", s.fee_rate) or 0.0005)
|
||||
|
||||
idx, ask = _index_and_option_ask()
|
||||
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
|
||||
@@ -183,6 +199,25 @@ def assess_open_capacity(db: Database | None = None) -> dict[str, Any]:
|
||||
}
|
||||
|
||||
|
||||
def funds_gate_blocks(cap: dict[str, Any] | None) -> tuple[bool, str]:
|
||||
"""
|
||||
Fail-closed:仅当永续与期权均为 True 才放行。
|
||||
None(未知,如币安未接余额)或 False → 拦截。
|
||||
"""
|
||||
if not cap:
|
||||
return True, "资金可开判定结果为空,拒绝开仓"
|
||||
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')};"
|
||||
f"永续需≈{cap.get('perp_need_usdt')}U/有{cap.get('perp_have_usdt')}U,"
|
||||
f"期权需≈{cap.get('option_need_usdc')}U/有{cap.get('option_have_usdc')}U"
|
||||
)
|
||||
if cap.get("perp_can_open") is None or cap.get("option_can_open") is None:
|
||||
detail += "(余额/盘口未知,fail-closed 拒绝开仓)"
|
||||
return True, f"资金不足或状态未知,暂不可开新仓:{detail}"
|
||||
return False, ""
|
||||
|
||||
|
||||
def maybe_notify_funds_short(cap: dict[str, Any] | None = None) -> None:
|
||||
"""仅在「不能开」时推送一次;能开绝不通知。"""
|
||||
global _notified_while_short
|
||||
|
||||
Reference in New Issue
Block a user