feat(bridge): 补 /orders+/cancel 端点与客户端(实盘查委托/撤单)

bridge.py: POST /cancel + GET /orders; xt_gateway: query_orders/cancel_order(含xtquant状态映射50挂单/56已成/57拒单/54撤单); bridge_client: get_orders/cancel_order. 已在 VPS 生产验证.
This commit is contained in:
2026-07-15 07:12:46 +08:00
parent 0761342baf
commit 8c06ef1e53
3 changed files with 87 additions and 0 deletions
+34
View File
@@ -151,3 +151,37 @@ async def query_positions() -> dict:
logger.error("查持仓异常: %s", e)
return {"ok": False, "error": f"查询异常: {e}"}
return {"ok": True, "positions": positions}
class CancelRequest(BaseModel):
"""撤单请求。"""
order_id: int
@app.post("/cancel", dependencies=[Depends(verify_token)])
async def cancel_order(req: CancelRequest) -> dict:
"""撤单 -> {ok, order_id} 或 {ok:false, error}。撤单请求提交后查 /orders 确认状态。"""
if not gateway.connected:
return {"ok": False, "error": "miniQMT 未连接"}
logger.info("撤单请求 order_id=%s", req.order_id)
try:
ret = gateway.cancel_order(req.order_id)
except (ValueError, RuntimeError) as e:
return {"ok": False, "error": str(e)}
except Exception as e:
logger.error("撤单异常: %s", e)
return {"ok": False, "error": f"撤单异常: {e}"}
return {"ok": True, "order_id": ret}
@app.get("/orders", dependencies=[Depends(verify_token)])
async def query_orders() -> dict:
"""查委托 -> {ok, orders:[{order_id, code, status, status_name, volume, traded, price}]}。"""
if not gateway.connected:
return {"ok": False, "error": "miniQMT 未连接"}
try:
orders = gateway.query_orders()
except Exception as e:
logger.error("查委托异常: %s", e)
return {"ok": False, "error": f"查询异常: {e}"}
return {"ok": True, "orders": orders}
+42
View File
@@ -260,6 +260,48 @@ class XtGateway:
return self._retry_with_reconnect(_do, "place_order")
def query_orders(self) -> list[dict[str, Any]]:
"""查委托列表:[{order_id, code, status, status_name, volume, traded, price}]。"""
def _do() -> list[dict[str, Any]]:
if not self._connected:
raise RuntimeError("xtquant 未连接")
orders = self._xt.query_stock_orders(self._account)
if not orders:
return []
# xtquant order_status 实测映射(50=挂单,56=已成,57=拒单,54=撤单)
_STATUS = {48: "unknown", 50: "pending", 51: "reporting", 52: "reported",
53: "reported", 54: "canceled", 55: "canceled",
56: "filled", 57: "rejected"}
result = []
for o in orders:
try:
st = getattr(o, "order_status", None)
code = getattr(o, "stock_code", None)
result.append({
"order_id": getattr(o, "order_id", None),
"code": to_sanguo_code(code) if code else None,
"status": st,
"status_name": _STATUS.get(st, str(st)),
"status_msg": getattr(o, "status_msg", "") or "",
"volume": int(getattr(o, "order_volume", 0) or 0),
"traded": int(getattr(o, "traded_volume", 0) or 0),
"price": float(getattr(o, "price", 0) or 0),
})
except Exception as e:
logger.warning("query_orders 解析异常: %s", e)
return result
return self._retry_with_reconnect(_do, "query_orders")
def cancel_order(self, order_id: int) -> int:
"""撤单,返回 order_id(>0=撤单请求受理,实际状态查 /orders)。"""
def _do() -> int:
if not self._connected:
raise RuntimeError("xtquant 未连接")
return int(self._xt.cancel_order_stock(self._account, int(order_id)))
return self._retry_with_reconnect(_do, "cancel_order")
# 模块级单例(bridge.py 启动时调 connect
gateway = XtGateway()