feat order.py
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -73,15 +73,15 @@ def StartTrend() -> None:
|
||||
)
|
||||
assets = client.assets()
|
||||
_, positions = client.positions()
|
||||
order_book = OrderBook()
|
||||
order_book.refresh()
|
||||
|
||||
storeState = State.for_strategy(
|
||||
config.global_config.qmt_data_dir,
|
||||
config.account_config.strategy,
|
||||
config.account_config.account_id,
|
||||
)
|
||||
orders = client.trade_detail_data("order")
|
||||
deals = client.deals()
|
||||
storeState.reconcile(positions, orders, deals)
|
||||
storeState.reconcile(positions, order_book.data)
|
||||
|
||||
# 获取本策略的信号开仓数据
|
||||
signals = init_signals(
|
||||
@@ -93,7 +93,7 @@ def StartTrend() -> None:
|
||||
global_cfg=config.global_config,
|
||||
account_cfg=config.account_config,
|
||||
state=storeState,
|
||||
orders=OrderBook(),
|
||||
orders=order_book,
|
||||
open_watch=DipWatch(),
|
||||
add_watch=DipWatch(),
|
||||
profit_tracker=GridTrailingTracker(config.account_config.grid_step_pct),
|
||||
@@ -120,7 +120,7 @@ def RunOnce(run: Runtime, signals) -> None:
|
||||
|
||||
# 1. 取消超过有效期仍未完成的委托订单。
|
||||
try:
|
||||
run.orders.cancel_expired(run.client)
|
||||
run.orders.refresh(run.client)
|
||||
except Exception:
|
||||
logging.exception("取消过期订单失败")
|
||||
|
||||
|
||||
@@ -12,6 +12,9 @@ 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)
|
||||
@@ -32,7 +35,7 @@ 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: dict[str, OrderItem] = {}
|
||||
self.data: list[OrderItem] = []
|
||||
self.lock: dict[str, float] = {}
|
||||
self.mutex = Lock()
|
||||
|
||||
@@ -44,42 +47,45 @@ class OrderBook:
|
||||
def busy(self, code: str, side: str) -> bool:
|
||||
"""判断证券是否存在仍在处理中的同方向委托。"""
|
||||
with self.mutex:
|
||||
self._clear_expired_locks(datetime.now().timestamp())
|
||||
key = f"{side}-{code}"
|
||||
return key in self.lock
|
||||
|
||||
def refresh(self, client: Client) -> None:
|
||||
"""从 QMT 刷新当前委托明细和方向索引。"""
|
||||
"""从 QMT 刷新进行中和已完成委托,并撤销超时的活动委托。"""
|
||||
orders = client.trade_detail_data("order")
|
||||
parsed_orders = [(f"{item.side}-{item.code}", item) for item in orders]
|
||||
now_timestamp = datetime.now().timestamp()
|
||||
with self.mutex:
|
||||
self.data = {key: item for key, item in parsed_orders}
|
||||
self.lock = {
|
||||
key: (
|
||||
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
|
||||
)
|
||||
for key, item in parsed_orders
|
||||
if item.status in BUSY_STATUSES
|
||||
}
|
||||
self._clear_expired_locks(now_timestamp)
|
||||
|
||||
|
||||
def cancel_expired(self, client: Any, now: datetime | None = None) -> None:
|
||||
"""尝试撤销超过有效期且具有委托编号的订单。"""
|
||||
self.refresh(client)
|
||||
current = now or datetime.now()
|
||||
|
||||
# 使用快照遍历,避免网络调用期间长期持有互斥锁。
|
||||
for order in list(self.data.values()):
|
||||
if (
|
||||
order.created_at is not None
|
||||
and order.status in {"49", "50", "51", "52"}
|
||||
and current - order.created_at > self.cancel_timeout_sec
|
||||
and order.id
|
||||
):
|
||||
client.cancel_by_id(order.id)
|
||||
with self.mutex:
|
||||
self.data = data
|
||||
self.lock = lock
|
||||
|
||||
def place(self, request: PlaceOrderRequest) -> bool:
|
||||
"""按最新价提交委托,并立即写入本地方向锁。"""
|
||||
@@ -112,13 +118,3 @@ class OrderBook:
|
||||
self.data[key] = pending
|
||||
self.lock[key] = pending.created_at.timestamp()
|
||||
return True
|
||||
|
||||
def _clear_expired_locks(self, now_timestamp: float) -> None:
|
||||
"""清理过期方向锁;调用方必须已持有 ``mutex``。"""
|
||||
expired = [
|
||||
key
|
||||
for key, created_at in self.lock.items()
|
||||
if now_timestamp - created_at >= self.lock_timeout_sec
|
||||
]
|
||||
for key in expired:
|
||||
self.lock.pop(key, None)
|
||||
|
||||
@@ -136,7 +136,6 @@ class State:
|
||||
self,
|
||||
positions: Iterable[PositionItem],
|
||||
orders: list[OrderItem],
|
||||
deals: list[dict[str, str]],
|
||||
) -> None:
|
||||
"""用真实持仓、委托和成交恢复本地状态,不增加持久化字段。"""
|
||||
position_list = list(positions)
|
||||
|
||||
Reference in New Issue
Block a user