Fix close-path SQLite lock by releasing write lock before exchange/sim ops.
Also enable WAL and busy_timeout on get_db so concurrent sim writes wait instead of failing immediately. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1591,8 +1591,14 @@ def _purge_key_monitors_if_full_margin():
|
||||
|
||||
|
||||
def get_db():
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
# timeout/busy_timeout: 并发写(平仓握锁 + sim 再开库)时等待而非立刻 database is locked
|
||||
conn = sqlite3.connect(DB_PATH, timeout=30)
|
||||
conn.row_factory = sqlite3.Row
|
||||
try:
|
||||
conn.execute("PRAGMA busy_timeout=30000")
|
||||
conn.execute("PRAGMA journal_mode=WAL")
|
||||
except Exception:
|
||||
pass
|
||||
return conn
|
||||
|
||||
|
||||
|
||||
@@ -11,6 +11,14 @@ def _now() -> str:
|
||||
return datetime.now(timezone.utc).astimezone().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
|
||||
def _release_db_write(conn: Any) -> None:
|
||||
"""提交并释放写锁,避免平仓时 sim/其它路径再开同库被 database is locked."""
|
||||
try:
|
||||
conn.commit()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _env_bool(key: str, default: bool = False) -> bool:
|
||||
raw = (os.getenv(key) or "").strip().lower()
|
||||
if not raw:
|
||||
@@ -1538,7 +1546,7 @@ def execute_manual_close_plan(cfg: dict[str, Any], conn: Any, plan_id: int) -> d
|
||||
perp_pnl = 0.0
|
||||
close_detail: dict[str, Any] = {}
|
||||
|
||||
# 1) 先平期权
|
||||
# 1) 先平期权(下单前先提交,避免握锁撞 sim 库)
|
||||
if opt:
|
||||
inst_id = str(opt.get("inst_id") or "")
|
||||
sheets = float(opt.get("size") or 1)
|
||||
@@ -1561,6 +1569,7 @@ def execute_manual_close_plan(cfg: dict[str, Any], conn: Any, plan_id: int) -> d
|
||||
"plan_id": int(plan_id),
|
||||
"need_liquidity": True,
|
||||
}
|
||||
_release_db_write(conn)
|
||||
close_r = _sell_option(cfg, inst_id=inst_id, sheets=sheets, dry_run=False)
|
||||
close_detail["option"] = close_r
|
||||
if not close_r.get("already_flat") and (not close_r.get("ok") or not close_r.get("fully_closed", True)):
|
||||
@@ -1590,6 +1599,7 @@ def execute_manual_close_plan(cfg: dict[str, Any], conn: Any, plan_id: int) -> d
|
||||
realized_pnl=round(opt_pnl, 4),
|
||||
)
|
||||
notes.append(f"期权已平 {inst_id}")
|
||||
_release_db_write(conn)
|
||||
|
||||
# 2) 再平永续
|
||||
if perp:
|
||||
@@ -1616,6 +1626,7 @@ def execute_manual_close_plan(cfg: dict[str, Any], conn: Any, plan_id: int) -> d
|
||||
)
|
||||
notes.append("永续无仓/已平")
|
||||
else:
|
||||
_release_db_write(conn)
|
||||
perp_close = _close_perp(
|
||||
cfg, symbol=symbol, direction=perp_dir, contracts=contracts, dry_run=False
|
||||
)
|
||||
@@ -1636,23 +1647,27 @@ def execute_manual_close_plan(cfg: dict[str, Any], conn: Any, plan_id: int) -> d
|
||||
"close": close_detail,
|
||||
"notes": notes,
|
||||
}
|
||||
# 粗估永续盈亏
|
||||
# 粗估永续盈亏(优先用成交回报)
|
||||
try:
|
||||
entry = float(perp.get("avg_open") or plan.get("entry_mark") or 0)
|
||||
get_m = cfg.get("get_live_position_exchange_metrics")
|
||||
mark = entry
|
||||
if callable(get_m):
|
||||
m = get_m(symbol, perp_dir) or {}
|
||||
mark = float(m.get("mark_price") or m.get("mark") or entry)
|
||||
cs = 0.01
|
||||
get_cs = cfg.get("get_contract_size")
|
||||
if callable(get_cs):
|
||||
cs = float(get_cs(symbol) or 0.01)
|
||||
coins = contracts * cs
|
||||
if perp_dir == "short":
|
||||
perp_pnl = (entry - mark) * coins
|
||||
info = (perp_close.get("order") or {}).get("info") or {}
|
||||
if info.get("pnl") is not None:
|
||||
perp_pnl = float(info.get("pnl") or 0)
|
||||
else:
|
||||
perp_pnl = (mark - entry) * coins
|
||||
entry = float(perp.get("avg_open") or plan.get("entry_mark") or 0)
|
||||
get_m = cfg.get("get_live_position_exchange_metrics")
|
||||
mark = entry
|
||||
if callable(get_m):
|
||||
m = get_m(symbol, perp_dir) or {}
|
||||
mark = float(m.get("mark_price") or m.get("mark") or entry)
|
||||
cs = 0.01
|
||||
get_cs = cfg.get("get_contract_size")
|
||||
if callable(get_cs):
|
||||
cs = float(get_cs(symbol) or 0.01)
|
||||
coins = contracts * cs
|
||||
if perp_dir == "short":
|
||||
perp_pnl = (entry - mark) * coins
|
||||
else:
|
||||
perp_pnl = (mark - entry) * coins
|
||||
except Exception:
|
||||
perp_pnl = 0.0
|
||||
update_leg(
|
||||
@@ -1664,10 +1679,12 @@ def execute_manual_close_plan(cfg: dict[str, Any], conn: Any, plan_id: int) -> d
|
||||
realized_pnl=round(perp_pnl, 4),
|
||||
)
|
||||
notes.append(f"永续已平 {symbol}")
|
||||
_release_db_write(conn)
|
||||
|
||||
# 3) 卖币回 USDT
|
||||
sell_r: dict[str, Any] = {"ok": True, "skipped": True}
|
||||
try:
|
||||
_release_db_write(conn)
|
||||
sell_ex = cfg.get("exchange_options") or cfg.get("exchange")
|
||||
if sell_ex is not None:
|
||||
sell_r = spot_market_sell_coin_to_usdt(sell_ex, underlying=uly)
|
||||
|
||||
Reference in New Issue
Block a user