"""全天候策略实盘入口(VPS Windows / miniQMT 直连)。 **实盘就绪,但需在交易日+miniQMT 连接下首次跑**。 用法: set DEFAULT_DATA_PROVIDER=miniqmt set MINIQMT_MARKET=SH python -m sanguo_portfolio.runner_live 不在 Mac 跑(Mac 无 xtquant/miniQMT 客户端)。 """ from __future__ import annotations # ENV GUARD 必须早于任何 bullet_trade import import os os.environ.setdefault("DEFAULT_DATA_PROVIDER", "miniqmt") import logging from typing import Any, Dict logger = logging.getLogger(__name__) def build_provider(provider_config: Dict[str, Any] | None = None) -> Any: """构造 live 模式的 SanguoMiniQmtProvider。""" from .providers import SanguoMiniQmtProvider cfg = dict(provider_config or {}) cfg.setdefault("mode", "live") cfg.setdefault("auto_download", True) return SanguoMiniQmtProvider(cfg) def build_broker_facade() -> Any: """实盘 BrokerFacade:委托 bullet_trade 顶层聚宽风格 API + QmtBroker。""" from .strategies.all_weather import BrokerFacade from bullet_trade.core.api import ( # type: ignore order_target_value as bt_otv, order_value as bt_ov, set_benchmark, set_option, set_slippage, set_order_cost, run_daily, run_monthly, ) return BrokerFacade( order_target_value=lambda c, v: bt_otv(c, v), order_value=lambda c, v: bt_ov(c, v), set_benchmark=set_benchmark, set_option=set_option, set_slippage=set_slippage, set_order_cost=set_order_cost, run_daily=run_daily, run_monthly=run_monthly, ) def run_live(provider_config: Dict[str, Any] | None = None) -> None: """启动 LiveEngine + AllWeatherStrategy。 LiveEngine 负责驱动 scheduler(context.current_dt 推进)和下单路由。 我们负责把 provider/broker 注入 AllWeatherStrategy。 """ from bullet_trade.core.live_engine import LiveEngine # type: ignore from bullet_trade.data.api import set_data_provider # type: ignore from bullet_trade.broker.qmt import QmtBroker # type: ignore from .strategies import AllWeatherConfig, AllWeatherStrategy provider = build_provider(provider_config) set_data_provider(provider) broker_facade = build_broker_facade() strategy = AllWeatherStrategy( provider=provider, broker=broker_facade, config=AllWeatherConfig(), ) def initialize(context): strategy.initialize(context) # QmtBroker 需要 account_id + data_path(miniQMT userdata_mini)。 # 从 env 读(与 sanguo_live 约定一致),缺 account 拒绝启动避免误下单。 account = os.environ.get("SANGUO_QMT_ACCOUNT", "") mini_path = (os.environ.get("SANGUO_QMT_PATH") or r"C:\国金QMT交易端模拟\userdata_mini") if not account: raise RuntimeError( "缺 SANGUO_QMT_ACCOUNT(miniQMT 交易账号),实盘无法启动。" "设 set SANGUO_QMT_ACCOUNT=66639661 后重试。" ) broker = QmtBroker(account_id=account, data_path=mini_path) logger.info("QmtBroker 装配 account=%s data_path=%s", account, mini_path) # 小资金 1e6 起步,等交易日观察(9:05 prepare / 月初 9:30 monthly / 14:00 stop_loss) engine = LiveEngine( initialize=initialize, initial_cash=1_000_000.0, broker=broker, ) logger.info("AllWeather live engine 启动,等交易日触发 monthly_adjustment") engine.run() def main() -> None: logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s") run_live() if __name__ == "__main__": main()