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:
@@ -151,3 +151,37 @@ async def query_positions() -> dict:
|
|||||||
logger.error("查持仓异常: %s", e)
|
logger.error("查持仓异常: %s", e)
|
||||||
return {"ok": False, "error": f"查询异常: {e}"}
|
return {"ok": False, "error": f"查询异常: {e}"}
|
||||||
return {"ok": True, "positions": positions}
|
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}
|
||||||
|
|||||||
@@ -260,6 +260,48 @@ class XtGateway:
|
|||||||
|
|
||||||
return self._retry_with_reconnect(_do, "place_order")
|
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)
|
# 模块级单例(bridge.py 启动时调 connect)
|
||||||
gateway = XtGateway()
|
gateway = XtGateway()
|
||||||
|
|||||||
@@ -110,3 +110,14 @@ class BridgeClient:
|
|||||||
if resp is None:
|
if resp is None:
|
||||||
return None
|
return None
|
||||||
return resp.get("positions")
|
return resp.get("positions")
|
||||||
|
|
||||||
|
def get_orders(self) -> list | None:
|
||||||
|
"""GET /orders → 委托列表;失败返回 None。"""
|
||||||
|
resp = self._get("/orders")
|
||||||
|
if resp is None:
|
||||||
|
return None
|
||||||
|
return resp.get("orders")
|
||||||
|
|
||||||
|
def cancel_order(self, order_id: int) -> dict | None:
|
||||||
|
"""POST /cancel → {ok, order_id} 或 {ok:false, error};失败返回 None。"""
|
||||||
|
return self._post("/cancel", {"order_id": int(order_id)})
|
||||||
|
|||||||
Reference in New Issue
Block a user