"""趋势策略委托簿,对应 Go 客户端的 ``logic/order.go``。""" from __future__ import annotations import secrets from dataclasses import dataclass from datetime import datetime, timedelta from threading import Lock from typing import Any from sdk import ORDER_SIDE_BY_OFFSET, Client, OrderItem # 表示委托仍在处理、可能继续成交的 QMT 状态。 BUSY_STATUSES = {"48", "49", "50", "51", "52", "55"} COMPLETED_STATUSES = {"56"} TRACKED_STATUSES = BUSY_STATUSES | COMPLETED_STATUSES CANCELABLE_STATUSES = {"49", "50", "51", "52"} @dataclass(frozen=True, slots=True) class PlaceOrderRequest: """``OrderBook.place`` 提交委托所需的全部参数。""" client: Any op: int code: str volume: int order_id: str strategy_name: str class OrderBook: """线程安全的活动委托缓存。""" def __init__(self, lock_timeout_sec: float = 180, cancel_timeout_sec: float = 10) -> None: self.lock_timeout_sec = max(0.0, float(lock_timeout_sec)) self.cancel_timeout_sec = timedelta(seconds=cancel_timeout_sec) self.data: list[OrderItem] = [] self.lock: dict[str, float] = {} self.mutex = Lock() @staticmethod def new_order_id(leg: str) -> str: """生成短订单号,为 QMT 备注中的信号键预留空间。""" return f"zt-{leg[:1]}-{secrets.token_hex(4)}" def busy(self, code: str, side: str) -> bool: """判断证券是否存在仍在处理中的同方向委托。""" with self.mutex: key = f"{side}-{code}" return key in self.lock def refresh(self, client: Client) -> None: """从 QMT 刷新进行中和已完成委托,并撤销超时的活动委托。""" orders = client.trade_detail_data("order") current = datetime.now() now_timestamp = current.timestamp() data: list[OrderItem] = [] lock: dict[str, float] = {} for item in orders: # 不处理状态不对的 if item.status not in TRACKED_STATUSES: continue # 清理过期的 if ( item.created_at is not None and item.status in CANCELABLE_STATUSES and current - item.created_at > self.cancel_timeout_sec ): client.cancel_by_id(item.id) continue # 缓存本次有效订单 data.append(item) if item.status in BUSY_STATUSES: key = f"{item.side}-{item.code}" lock[key] = ( item.created_at.timestamp() if item.created_at is not None else now_timestamp ) with self.mutex: self.data = data self.lock = lock def place(self, request: PlaceOrderRequest) -> bool: """按最新价提交委托,并立即写入本地方向锁。""" result = request.client.passorder_latest_tagged( request.op, request.code, request.volume, request.strategy_name, request.order_id, ) if not isinstance(result, dict): return False order_ref = str(result.get("order_ref") or "").strip().lower() if result.get("status") != "success" or order_ref in {"", "unknown", "none"}: return False side = ORDER_SIDE_BY_OFFSET.get(str(request.op), "") pending = OrderItem( id=order_ref, code=request.code, side=side, remark=request.order_id, status="48", created_at=datetime.now(), volume=request.volume, local_order_id=request.order_id, ) with self.mutex: key = f"{side}-{request.code}" self.data[key] = pending self.lock[key] = pending.created_at.timestamp() return True