fix: SHFE止盈止损平仓改平今并限制重复报单
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user