[sanguo_vnpy_v2] 实盘模拟(miniQMT跑策略)前后端实现 #5
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
背景
当前模拟盘用 parquet 收盘数据慢速回放(≈回测)。要做"实盘模拟":用 miniQMT 测试账户的真实行情驱动 vnpy 策略,真实下单跑 15min 策略。已确认:miniQMT 直连通、vnpy_qmt 网关代码全、策略是标准 CtaTemplate。
范围
前后端都要。风控不做(加仓限额/止损/熔断等跳过)。
架构
新建 sanguo_live/ 独立模块(不动现有模拟盘 sanguo_trader)。
MainEngine+QmtGateway+CtaEngine+subscribe+BarGenerator(15min)→ 策略 on_bar → 真实下单测试账户(66639661)。工作清单
连通性6步(验收标准)
状态表
完成标记约定
@main-agent ✅ EXECUTE_DONE@main-agent ✅ REVIEW_DONE verdict=approved@main-agent ✅ VERIFICATION_PASSED@main-agent ✅ EXECUTE_DONE
任务 #3 实盘模拟后端核心引擎 — 完成
新建文件清单(8 个文件,全部 < 300 行)
sanguo_live/__init__.pysanguo_live/__main__.pypython -m sanguo_live [config]sanguo_live/base_template.pysanguo_live/strategies.pysanguo_live/engine.pysanguo_live/runner.pyconfig/live.yamltests/test_live_engine.py关键设计决策
BarGenerator(self.on_bar)无 window 参数(仅 1min),且parameters=["fast_window","slow_window"]不暴露周期,无法通过 setting 切 15min。改写AShareDoubleMaStrategy用BarGenerator(self._on_1min_bar, window=15, on_window_bar=self.on_bar),on_bar 直接收 15min bar。short()返回 [] 拦截;AShareDoubleMaStrategy.on_bar的死叉分支删除原 DoubleMa 的pos==0 → short和pos<0 → cover+buy分支,只保留pos>0 → sell。buy(1)会下 1 股被拒。AShareCtaTemplate 在 buy/cover/sell 内部×= self.size(默认100)。单测结果
venvPython 3.14)C:\Python310, vnpy 4.4.0 + vnpy_ctastrategy 1.4.1 + vnpy_qmt 0.3.3 vendor)test_strategy_registry.py(4) +test_cta_adapter.py(5)VPS 环境核实
vnpy_ctastrategy 1.4.1装在C:\Python310\lib\site-packages\(pip 安装) ✅vnpy 4.4.0同上 ✅vnpy_qmt 0.3.3vendor 在C:\sanguo_vnpy_v2\vnpy_qmt_v0.3.3\(非 pip,通过sys.path.insert引用,与run_web.py一致) ✅vnpy_v4.4.0;vnpy_qmt_v0.3.3;sanguo_live_test(生产由 run_web 风格的 bootstrap 注入,已在 runner.py 文档说明)连通性状态
python -m sanguo_live即可端到端验证 connect → subscribe tick → 15min bar → 策略 → 真实下单)约束遵循
@main-agent ✅ REVIEW_DONE verdict=approved (task #3 引擎核心)
Review 通过。定寸/禁做空/15min BarGenerator/引擎组装/runner流程全正确。单测 Mac 7pass+VPS 15pass+回归9pass。2个小点(OMS engine名/固定sleep)不影响MVP,盘中连通性验收时优化。连通性6步归 #6 盘中统一验。
推进 #4(后端API+持久化)。
@main-agent ✅ EXECUTE_DONE
task #4 实盘模拟 API + 持久化 完成
文件清单(7 文件)
sanguo_live/persistence.pysanguo_api/routes_live.pysanguo_api/app.pysanguo_live/runner.pyrun_supervisor()+ 事件/快照辅助(run()原签名不动)sanguo_live/__main__.py--supervisor [db]模式config/data_platform.yamllive_trading:段(enabled/db_path/poll/snapshot)tests/test_live_api.pyAPI 路由清单(prefix
/api/v1)POST /live/create— 建 live_account(status=stopped)GET /live— 列表(带 latest_equity/total_return/position_count 摘要)GET /live/{aid}— 详情POST /live/{aid}/start— status=running(account 空 → 400)POST /live/{aid}/stop— status=stoppedGET /live/{aid}/positions— 持仓快照GET /live/{aid}/trades— 成交明细GET /live/{aid}/account— 账户最新快照GET /live/{aid}/status— 运行状态supervisor 设计(API↔runner DB 解耦)
python -m sanguo_live --supervisor [db]起 supervisor 常驻进程live_accounts.status(默认 5s):running → 起 LiveTradingEngine + 注册 EVENT_TRADE 回调(成交落 live_trades);stopped → stop+close engineget_positions()/get_all_accounts()覆盖式落 live_positions/live_balancerun()(config 驱动单实例)签名不动,#3 引擎代码 0 改动单测结果
约束达成
未 commit / 未建 PR(等 Main Agent Review)。
@main-agent ✅ REVIEW_DONE verdict=approved (task #4 API+持久化)
Review 通过。9路由全(auth照搬/查询全走DB不碰engine,解耦干净);supervisor 轮询status起停engine+EVENT_TRADE落库+30s快照,run()原签名保留(#3零改动);16单测+回归0。小点(:memory:边界/AccountData字段近似/trade strategy_name暂空)MVP可接受。
推进 #5 前端UI。
@main-agent ✅ EXECUTE_DONE
实盘模拟前端 UI 实现完成
文件清单
新增 4 个文件:
frontend/src/api/live.ts— API 客户端(9 个接口:createLive/listLives/getLive/startLive/stopLive/getLivePositions/getLiveTrades/getLiveAccountBalance/getLiveStatus)frontend/src/views/live/New.vue— 新建实盘(表单:实例名/交易账号/标的/策略类下拉/策略实例名/setting 参数 fast_window/slow_window/window/size/forbid_short/interval/initial_capital/connect_wait_sec/init_wait_sec/mini_path)frontend/src/views/live/List.vue— 列表(统计卡总数/运行中/已停止、表格列含收益率/最新净值/持仓数/状态、启动/停止/监控操作、15s 自动刷新)frontend/src/views/live/Monitor.vue— 实时监控(6 状态卡 + 净值三件套 cash/market_value/total + 持仓表 + 今日成交 + 全部成交 + 策略参数,10s 自动刷新,启动/停止按钮)修改 2 个文件:
frontend/src/router/index.ts— 加 3 个路由(live-new / live-list / live-monitor)frontend/src/views/Layout.vue— 侧边栏原 disabled "实盘" 改为启用 "实盘模拟" 子菜单(列表/新建),breadcrumb 加 3 条匹配实现要点
构建验证
TypeScript 类型检查通过(
vue-tsc -b是 build 脚本的前置步骤,失败会中断)。仅有第三方 @vueuse/core 的 PURE annotation 警告(node_modules 内,非本次代码)和 echarts chunk size 警告(项目既有)。未做
@main-agent ✅ REVIEW_DONE verdict=approved (task #5 前端UI)
Review 通过。api/live.ts 9接口路径/方法/TS类型全匹配后端契约;List.vue 启停带确认+15s刷新+错误处理;vue-tsc+vite build 0错误;风格复用paper。前后端代码全部交付(#3/#4/#5 review全过)。
进 #6 集成验收。当前盘后(19:23),盘中真实下单6步要明天盘中。
Task #6a — VPS 部署 + 非盘中冒烟 报告
@main-agent ✅ TEST_DONE result=smoke_passed (7/7 非盘中项;盘中 6 步待明天盘中补)
冒烟清单 7 项结果
{accounts:[]}{account_id:1, status:"stopped"}[supervisor] 启动 (db=...backtest_results.db poll=5.0s snapshot=30.0s)[td] 连接成功 / 订阅账户成功:0 / engine 已启动 (account=66639661 strategy=smoke_test)/live/1/accounttotal=9999929.51;/live/1/statusrunning;/live/1/positions2 只(000001.SZSE 100 股 + 600000.SSE 200 股)/live见 smoke_test #1, 9,999,930 净值, 2 持仓, 运行中;/live/monitor/1完整{status:"stopped"}+ log:[supervisor] 停止 engine (account=1) / LiveTradingEngine 已关闭Item 4 修过的一次坑(冒烟定位+修复)
首次 start 时
[td] 连接失败:-1 / 订阅账户【失败】:-1。根因: smoke_test 创建时mini_path留空(默认),td.py不做 auto-scan(只有md.py做),XtQuantTrader(path="", ...)连不上 miniQMT IPC。直接 sqlite3 把live_accounts.mini_path改为C:\国金QMT交易端模拟\userdata_mini,stop+start 后 connect 一次成功。VPS 部署方式摘要
C:\sanguo_vnpy_v2\sanguo_live\,sanguo_api\app.py,sanguo_api\routes_live.py,config\live.yamlsanguo-apischtask(schtasks /Run)run_api.ps1→run_web.py→ uvicorn:8000npm run buildfrontend/dist/C:\sanguo_vnpy_v2\frontend\dist\(FastAPI StaticFiles 自动 mount/,无需重启)config/backtest.yaml)Supervisor 常驻配置状态
Start-Process powershell -File run_supervisor.ps1 -WindowStyle Hidden,PID 6272,19:42:54 起,已稳定运行 25min+(stop/start API 循环 3 次都正常响应)C:\sanguo_vnpy_v2\run_supervisor.ps1(显式传 DB 路径C:\sanguo_vnpy_v2\data\backtest_results.db,因为config/data_platform.yaml默认指 NAS 路径,在 VPS 上会错)C:\Users\Administrator\supervisor.logschtasks /Create /tn sanguo-supervisor /sc onstart /tr "powershell -File C:\sanguo_vnpy_v2\run_supervisor.ps1"(仿 sanguo-api 模式),delayed start 30s 等 miniQMT 起来。盘中真实下单 6 步(明天盘中补)
/live/1/start后 log[td] 连接成功)order_stock_async是否成交(测试账户 66639661 有 9.99M 可用,200 股 600000@9.27 约 1854 元,够)live_trades表落库(⚠️ 必须先修上述遗留 #3 的vnpy.eventimport 问题,否则成交不落库)约束遵守
-X utf8@main-agent ✅ EXECUTE_DONE fixes_local
3 点代码修复完成(仅 Mac 本地,未动 VPS/未重启/未杀进程)
1. mini_path 默认空 → connect=-1
sanguo_api/routes_live.pycreate_live:os.environ.get("SANGUO_QMT_PATH")→ 内置默认C:\国金QMT交易端模拟\userdata_mini(三段兜底)_DEFAULT_MINI_PATHfrontend/src/views/live/New.vue:form.mini_path默认值改为内置路径(原本是空字符串)2. EVENT_TRADE import 失败 → 成交不落 live_trades
只读排查(VPS ssh 验证,无写入/无重启/无杀进程):
C:\Python310\lib\site-packages\vnpy\event\__init__.py只导出Event / EventEngine / EVENT_TIMER,不含 EVENT_TRADEfrom vnpy.event import EVENT_TRADE→ ImportError(实证,runner.py:252 旧代码 bug)from vnpy.trader.event import EVENT_TRADE→ ok,返回"eTrade."vnpy/trader/event.py:8修复
sanguo_live/runner.py_register_trade_handler:from vnpy.event import EVENT_TRADE→from vnpy.trader.event import EVENT_TRADE3. 收益率显示失真(initial_capital 兜底)
sanguo_live/persistence.py: 新增get_first_balance(db, account_id)(ORDER BY id ASC LIMIT 1),加入__all__sanguo_api/routes_live.pylist_lives:live_balance.total(该 account 最早一条)(last_total - first_total) / first_totaltotal_return=None(不再兜底initial_capital)单测验证(Mac 本地)
新增 3 个用例:
test_create_live_mini_path_default_when_empty— 不传 mini_path → 落库值含userdata_minitest_create_live_mini_path_env_fallback— envSANGUO_QMT_PATH兜底test_list_lives_total_return_uses_first_snapshot_baseline— initial_capital=6e5 + 首快照 5e5 + 末快照 5.5e5 → 收益率 0.1(按首快照,非 -0.0833 按 initial)既有
test_routes_reflect_db_writes单条快照场景: first==last → 0.0,仍通过(0 回归)。前端构建
约束遵守