diff --git a/install_trading.py b/install_trading.py index c4ce3a6..5c9b73e 100644 --- a/install_trading.py +++ b/install_trading.py @@ -443,7 +443,6 @@ def install_trading(app, *, login_required, require_nav, get_db, get_setting, se mode = get_trading_mode(get_setting) ctp_st = ctp_status(mode) _sync_trade_monitors_with_ctp(conn, mode) - sync_all_sl_tp_orders(conn, mode) rows = _build_trading_live_rows(conn) pending_orders = _build_pending_orders(conn, mode) capital = _capital(conn) diff --git a/sl_tp_guard.py b/sl_tp_guard.py index ec77431..9b2a869 100644 --- a/sl_tp_guard.py +++ b/sl_tp_guard.py @@ -18,6 +18,9 @@ from vnpy_bridge import ( logger = logging.getLogger(__name__) CHECK_INTERVAL_SEC = 20 +PLACE_COOLDOWN_SEC = 120 + +_last_place_attempt: dict[tuple[int, str], float] = {} MONITOR_ORDER_COLUMNS = ( "ALTER TABLE trade_order_monitors ADD COLUMN sl_vt_order_id TEXT", @@ -92,6 +95,15 @@ def _find_position(positions: list[dict], ths_code: str, direction: str) -> Opti return None +def _can_place_now(monitor_id: int, kind: str, *, cooldown: int = PLACE_COOLDOWN_SEC) -> bool: + last = _last_place_attempt.get((monitor_id, kind), 0.0) + return (time.time() - last) >= cooldown + + +def _mark_place_attempt(monitor_id: int, kind: str) -> None: + _last_place_attempt[(monitor_id, kind)] = time.time() + + def _order_still_active(active_orders: list[dict], vt_order_id: str) -> bool: if not vt_order_id: return False @@ -139,6 +151,8 @@ def place_monitor_exit_orders( placed: list[str] = [] updates: dict[str, Optional[str]] = {} + mid = int(mon.get("id") or 0) + def _maybe_place(kind: str, price: Optional[float], stored_id: str) -> None: if price is None or price <= 0: return @@ -150,19 +164,27 @@ def place_monitor_exit_orders( return if stored_id and _order_still_active(active, stored_id) and not force: return - result = execute_order( - conn, - mode=mode, - offset=offset, - symbol=sym, - direction=direction, - lots=lots, - price=price, - order_type="limit", - ) + if mid > 0 and not force and not _can_place_now(mid, kind): + return + try: + _mark_place_attempt(mid, kind) + result = execute_order( + conn, + mode=mode, + offset=offset, + symbol=sym, + direction=direction, + lots=lots, + price=price, + order_type="limit", + ) + except Exception as exc: + logger.warning("SL/TP place %s monitor=%s failed: %s", kind, mid, exc) + return oid = str(result.get("order_id") or "") - updates[f"{kind}_vt_order_id"] = oid - placed.append(f"{kind}@{price}") + if oid: + updates[f"{kind}_vt_order_id"] = oid + placed.append(f"{kind}@{price}") sl_id = str(mon.get("sl_vt_order_id") or "") tp_id = str(mon.get("tp_vt_order_id") or "") diff --git a/vnpy_bridge.py b/vnpy_bridge.py index 5ecd850..b0d97f0 100644 --- a/vnpy_bridge.py +++ b/vnpy_bridge.py @@ -298,10 +298,15 @@ class CtpBridge: def _resolve_close_offset(self, sym: str, ex_name: str, hold_direction: str, lots: int) -> Any: from vnpy.trader.constant import Offset - if ex_name not in ("CZCE", "CFFEX"): + ex_u = (ex_name or "").upper() + # 上期所/能源中心/郑商所/中金所须区分平今/平昨;大商所等可用通用 CLOSE + if ex_u not in ("CZCE", "CFFEX", "SHFE", "INE"): return Offset.CLOSE - pos = self._find_position(sym, ex_name, hold_direction) + pos = self._find_position(sym, ex_u, hold_direction) if not pos: + # 找不到持仓明细时,日盘新开仓优先平今(避免 SHFE「平昨仓位不足」) + if ex_u in ("SHFE", "INE", "CZCE"): + return Offset.CLOSETODAY return Offset.CLOSE vol = int(getattr(pos, "volume", 0) or 0) yd = int(getattr(pos, "yd_volume", 0) or 0)