"""LiveTradingEngine:vnpy 原生实盘链路封装。 参考 ``vnpy_v4.4.0/examples/no_ui/run.py`` 和 ``examples/no_ui/run.py``, 按 A 股 + QMT 场景精简: - EventEngine + MainEngine - add_gateway(QmtGateway) 连 miniQMT - add_app(CtaStrategyApp) 挂 CTA 引擎 - 透出 connect / add_strategy / subscribe / init_all / start_all / stop_all / 查询 本机未装 vnpy_ctastrategy / vnpy_qmt 时 import 容错,实例化才报错。 """ from __future__ import annotations import logging from typing import Any logger = logging.getLogger(__name__) try: from vnpy.event import EventEngine # type: ignore from vnpy.trader.engine import MainEngine # type: ignore from vnpy_qmt import QmtGateway # type: ignore from vnpy_ctastrategy import CtaStrategyApp, CtaEngine # type: ignore _DEPS_OK = True _IMPORT_ERROR: Exception | None = None except ImportError as _e: EventEngine = None # type: ignore MainEngine = None # type: ignore QmtGateway = None # type: ignore CtaStrategyApp = None # type: ignore CtaEngine = None # type: ignore _DEPS_OK = False _IMPORT_ERROR = _e class LiveTradingEngine: """vnpy 原生实盘引擎封装。单实例持有 MainEngine 生命周期。 使用: eng = LiveTradingEngine() eng.connect({"交易账号": "66639661", "mini路径": "C:\\\\..."}) eng.add_strategy(AShareDoubleMaStrategy, "dm1", "600000.SSE", {...}) eng.subscribe(["600000.SSE"]) eng.init_all(); eng.start_all() # ... 常驻 ... eng.stop_all(); eng.close() """ def __init__(self) -> None: if not _DEPS_OK: raise RuntimeError( f"vnpy 依赖缺失(vnpy/vnpy_qmt/vnpy_ctastrategy)," f"无法初始化 LiveTradingEngine: {_IMPORT_ERROR}" ) self.event_engine: EventEngine = EventEngine() self.main_engine: MainEngine = MainEngine(self.event_engine) self.main_engine.add_gateway(QmtGateway) self.cta_engine: CtaEngine = self.main_engine.add_app(CtaStrategyApp) logger.info("LiveTradingEngine 初始化完成(MainEngine + QMT + CTA)") # ---------------------- 连接 / 策略 / 行情 ---------------------- def connect(self, setting: dict[str, str]) -> None: """连接 miniQMT。``setting`` = ``{"交易账号":..., "mini路径":...}``。""" self.main_engine.connect(setting, "QMT") logger.info("已请求连接 QMT: 账号=%s", setting.get("交易账号")) def add_strategy( self, strategy_class: type, strategy_name: str, vt_symbol: str, setting: dict[str, Any], ) -> None: """注册策略实例到 CTA 引擎。""" self.cta_engine.add_strategy( strategy_class, strategy_name, vt_symbol, setting ) logger.info("已添加策略 %s @ %s (class=%s)", strategy_name, vt_symbol, strategy_class.__name__) def subscribe(self, vt_symbols: list[str]) -> None: """订阅 ``vt_symbol`` 列表(格式 ``SYMBOL.EXCHANGE``,如 ``600000.SSE``)。 订阅是行情驱动策略的前提:QmtGateway.on_tick → EVENT_TICK → CtaEngine → strategy.on_tick → BarGenerator 合成 15min bar。 """ from vnpy.trader.object import SubscribeRequest # type: ignore from vnpy.trader.constant import Exchange # type: ignore ok = 0 for vt_symbol in vt_symbols: try: symbol, exch_code = vt_symbol.split(".", 1) exchange = Exchange(exch_code) except (ValueError, KeyError): logger.warning("vt_symbol 无法解析,跳过: %r", vt_symbol) continue req = SubscribeRequest(symbol=symbol, exchange=exchange) self.main_engine.subscribe(req, "QMT") ok += 1 logger.info("已请求订阅 %d / %d 个标的", ok, len(vt_symbols)) # ---------------------- 生命周期 ---------------------- def init_all(self) -> None: self.cta_engine.init_all_strategies() logger.info("所有策略初始化完成") def start_all(self) -> None: self.cta_engine.start_all_strategies() logger.info("所有策略已启动") def stop_all(self) -> None: try: self.cta_engine.stop_all_strategies() logger.info("所有策略已停止") except Exception as e: # noqa: BLE001 logger.warning("stop_all_strategies 异常: %s", e) def close(self) -> None: try: self.main_engine.close() finally: logger.info("LiveTradingEngine 已关闭") # ---------------------- 查询(OMS 缓存) ---------------------- def _oms(self) -> Any: return self.main_engine.get_engine("oms") def get_all_accounts(self) -> list: """返回 QMT 网关所有账户(AccountData 列表)。连接前可能为空。""" oms = self._oms() if oms is None: return [] return [a for a in oms.get_all_accounts() if a.gateway_name == "QMT"] def get_positions(self) -> list: """返回 QMT 持仓(PositionData 列表)。""" oms = self._oms() if oms is None: return [] return [p for p in oms.get_all_positions() if p.gateway_name == "QMT"] def get_orders(self) -> list: """返回 QMT 当日委托(OrderData 列表)。""" oms = self._oms() if oms is None: return [] return [o for o in oms.get_all_orders() if o.gateway_name == "QMT"] __all__ = ["LiveTradingEngine"]