fix: SHFE止盈止损平仓改平今并限制重复报单

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-24 14:30:41 +08:00
parent f6ee13765d
commit 397e9cd9d8
3 changed files with 41 additions and 15 deletions
-1
View File
@@ -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)
+34 -12
View File
@@ -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 "")
+7 -2
View File
@@ -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)