Fix ghost finalize on unknown perp size and open rollback hazards.
Distinguish exchange query None from flat zero; keep opening when size unknown; idempotent half_open cash; scan options while opening; continue manage after recover. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -171,7 +171,8 @@ def assert_safe_to_open_live(executor) -> tuple[bool, str]:
|
||||
False,
|
||||
f"交易所有期权仓({opt_inst})但本地未确认持仓,禁止新开,请人工核对",
|
||||
)
|
||||
elif st in ("flat", ""):
|
||||
elif st in ("flat", "", "opening"):
|
||||
# opening 且尚未 stamp option_inst_id 时仍须扫任意期权残留
|
||||
any_opt = exchange_any_option_abs(client)
|
||||
if any_opt is None:
|
||||
return False, "无法核对交易所期权持仓"
|
||||
@@ -209,18 +210,20 @@ def perp_close_contracts_okx(
|
||||
perp_qty_eth: float,
|
||||
ct_val: float,
|
||||
allow_db_fallback: bool = True,
|
||||
) -> int:
|
||||
) -> int | None:
|
||||
"""平永续张数:优先交易所持仓。
|
||||
|
||||
allow_db_fallback=False 且交易所已空仓时返回 0(勿用 DB 数量再下单,防反向开仓)。
|
||||
返回 >0 应下单;0=已确认空仓(仅 allow_db_fallback=False);
|
||||
None=查仓失败(调用方不得当空仓 finalize)。
|
||||
"""
|
||||
ps = "long" if perp_side == "long" else "short"
|
||||
ex_sz = client.get_perp_pos_sz(perp_inst, pos_side=ps)
|
||||
if ex_sz is not None and ex_sz > _PERP_EPS:
|
||||
return max(1, int(round(ex_sz)))
|
||||
if ex_sz is not None and ex_sz <= _PERP_EPS:
|
||||
if ex_sz is None:
|
||||
if not allow_db_fallback:
|
||||
return 0
|
||||
return None
|
||||
return max(1, int(round(perp_qty_eth / ct_val)))
|
||||
if ex_sz > _PERP_EPS:
|
||||
return max(1, int(round(ex_sz)))
|
||||
if not allow_db_fallback:
|
||||
return 0
|
||||
return max(1, int(round(perp_qty_eth / ct_val)))
|
||||
@@ -233,20 +236,28 @@ def perp_close_qty_eth_binance(
|
||||
perp_side: str,
|
||||
perp_qty_eth: float,
|
||||
allow_db_fallback: bool = True,
|
||||
) -> float:
|
||||
"""平永续 ETH 数量:优先交易所持仓。交易所空且不允许 fallback → 0。"""
|
||||
) -> float | None:
|
||||
"""平永续 ETH:>0 下单;0=已确认空;None=查仓失败。"""
|
||||
ps = "LONG" if perp_side == "long" else "SHORT"
|
||||
ex_sz = client.get_perp_pos_sz(perp_inst, position_side=ps)
|
||||
if ex_sz is not None and ex_sz > _PERP_EPS:
|
||||
return float(ex_sz)
|
||||
if ex_sz is not None and ex_sz <= _PERP_EPS:
|
||||
if ex_sz is None:
|
||||
if not allow_db_fallback:
|
||||
return 0.0
|
||||
return None
|
||||
return float(perp_qty_eth)
|
||||
if ex_sz > _PERP_EPS:
|
||||
return float(ex_sz)
|
||||
if not allow_db_fallback:
|
||||
return 0.0
|
||||
return float(perp_qty_eth)
|
||||
|
||||
|
||||
def perp_open_contracts_okx(*, perp_qty_eth: float, ct_val: float) -> int:
|
||||
"""开仓张数:仅按设置名义/面值,不跟交易所残留。"""
|
||||
if ct_val <= 0:
|
||||
raise RuntimeError("ct_val invalid")
|
||||
return max(1, int(round(float(perp_qty_eth) / float(ct_val))))
|
||||
|
||||
|
||||
def recover_stuck_opening(executor) -> CloseResult | None:
|
||||
"""恢复本地 status=opening:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user