158 lines
10 KiB
Markdown
158 lines
10 KiB
Markdown
# 模拟盘影子交易柜台设计 v2(验收第四轮 #4)
|
||
|
||
> 状态:设计稿 v2(2026-08-14,按用户反馈重写驱动方式)。**P0 对账已通过(2026-08-14);
|
||
> P1 影子柜台已上线(2026-08-14 晚,shadow#39 实跑)**;P3 前半(双轨对账报表)已实现(534a06a)。
|
||
> v1 的「schtask 每 15 分钟定时拉数据」已废弃——用户明确:数据要与实盘完全一样
|
||
> v1 的「schtask 每 15 分钟定时拉数据」已废弃——用户明确:数据要与实盘完全一样
|
||
> 由 miniQMT 实时提供,步长由策略自己的周期决定(tick 到月线),15 分钟只是举例。
|
||
|
||
## 0. 一句话版(业务视角)
|
||
|
||
在 VPS 上放一个「模拟券商」:
|
||
- **行情**:跟实盘完全一样,从 miniQMT 实时订阅(tick / 各周期 bar);
|
||
- **账户**:每个策略实例一个独立虚拟账户(资金、持仓、净值曲线各自独立,互不干扰);
|
||
- **交易**:策略下单不发给券商,由模拟券商在**下单那一刻的实时价 + 滑点**本地成交;
|
||
- **周期**:每个策略声明自己的周期(tick/1m/5m/15m/30m/d/月线合成),柜台按各自的周期喂数据。
|
||
|
||
跟实盘唯一的区别就是订单不出门。
|
||
|
||
## 1. 目标 / 非目标
|
||
|
||
**目标**
|
||
- 行情驱动 = 实盘同款:miniQMT(xtdata)实时推送,事件驱动,非定时轮询。
|
||
- 步长 = 策略自身周期(tick 到日线/月线合成),柜台不预设统一步长。
|
||
- 本地模拟撮合:实时价 + 滑点 + 真实费率(佣金/印花税/最低佣金)。
|
||
- 多策略多账户完全隔离:每实例独立虚拟账户,独立盈亏曲线。
|
||
- 状态可恢复:进程重启后从断点续跑(checkpoint),不丢已结算数据。
|
||
|
||
**非目标(MVP 不做)**
|
||
- 订单真发券商(这是后续真实现盘;本设计正是它的前置演练场)。
|
||
- 撮合排队/部分成交等微观结构(实时价全额成交,先验证策略行为)。
|
||
- CTA 个股策略接入(先组合策略;CTA 的 vnpy 线保持现状)。
|
||
|
||
## 2. 架构
|
||
|
||
```
|
||
VPS(miniQMT 客户端保持登录,唯一有实时行情的机器)
|
||
┌───────────────────────────────────────────────────────┐
|
||
│ sanguo-shadow-desk 常驻进程(交易时段运行,开机自启) │
|
||
│ ├─ 行情层:xtdata 订阅(tick 推送 + 各周期 bar 推送) │
|
||
│ │ 按每个策略实例声明的周期分发(月线=日线本地合成) │
|
||
│ ├─ 账户层:每实例一个虚拟账户(现金/持仓/净值独立) │
|
||
│ ├─ 撮合层:本地成交 = 下单时刻实时价 + 滑点 + 费率 │
|
||
│ │ (复用 sanguo_trader 费率/风控件,涨跌停/停牌拦截) │
|
||
│ ├─ 落库:成交/持仓/净值 → paper 库(VPS 本地) │
|
||
│ └─ checkpoint:每成交/每 bar 结算后记断点(重启恢复用) │
|
||
└───────────────────────────────────────────────────────┘
|
||
↓ 公网 web(vnpy.mysanguo.online → VPS API)
|
||
前端模拟盘页(现有,读库即得盘中实时刷新)
|
||
|
||
兜底:收盘后日终对账(VPS 晚间跑一次,发现盘中断档则用日终全量重放补齐
|
||
——沿用现有 portfolio_paper 能力;另保留 NAS 20:30 日终重放作交叉对照)
|
||
```
|
||
|
||
## 3. 关键设计决策
|
||
|
||
### 3.1 行情:订阅推送,不轮询
|
||
- xtdata `subscribe_quote`(单标的周期 bar)/ `subscribe_whole_quote`(全市场 tick 快照)
|
||
都是**推送**语义,与实盘网关行为一致;断线自动重连。
|
||
- 策略周期映射:tick → tick 推送;1m/5m/15m/30m/d → 对应周期 bar 推送;
|
||
月线/周线 → 日线本地合成(与 vnpy BarGenerator 同思路)。
|
||
- 需要历史因子(动量等):直查 VPS 本地库(unified provider),与实时推送互补。
|
||
|
||
### 3.2 撮合:实时价成交(与实盘市价单最接近)
|
||
- 订单到达即以下单时刻实时价 + 滑点成交(替代回放模式的 NEXT_OPEN/CURRENT_CLOSE
|
||
——那是没有实时价时的近似;现在有实时价,直接用)。
|
||
- 涨跌停/停牌/未成交约束复用 `sanguo_trader/limit.py` 既有逻辑。
|
||
- 费率按账户行配置(佣金/印花税/最低佣金/滑点),与回测同口径。
|
||
|
||
### 3.3 恢复:checkpoint 增量(保留 v1 验证结论)
|
||
进程重启/断电后不从头跑:引擎原生支持初始持仓
|
||
(`initial_positions=[{security, amount, avg_cost}]`)+ 初始资金恢复,
|
||
策略因子历史直查本地库不受引擎窗口限制 → 从断点下一根 bar 续跑,
|
||
无双重成交。对账用例(P0)显式覆盖跨除权日的复权正确性。
|
||
|
||
### 3.4 策略状态约定
|
||
- 需要跨 bar 记忆的状态一律放持仓/账户(天然随 checkpoint 恢复);
|
||
- 策略内临时变量只放可重建的缓存(重启丢失不影响正确性)。
|
||
|
||
## 4. 存储变更(paper 库,向后兼容)
|
||
|
||
- `paper_accounts` + `engine` 列('eod_replay'(NAS 现状)/ 'shadow'(VPS 新),新账户默认 shadow)。
|
||
- checkpoint 扩展为 `checkpoint_dt`(最后已结算 bar 时间戳,tick/分钟精度)
|
||
+ `cash` + `positions_json` + `avg_cost`。旧日粒度行迁移:视作当日收盘。
|
||
- 成交/净值/持仓表结构不变(datetime 字段天然容纳 tick/分钟精度)。
|
||
|
||
## 5. 运行方式
|
||
|
||
- VPS schtask `sanguo-shadow-desk`:交易时段前启动常驻进程(BootTrigger +
|
||
交易日 09:15 拉起,15:30 后自动退出/空闲驻留皆可,首版选驻留+收盘结算)。
|
||
- 单实例文件锁(防双开重复撮合)。
|
||
- CLI 手工入口:`python -m sanguo_trader.shadow_desk [--account N]`(补跑/调试)。
|
||
- miniQMT 登录依赖:现有数据采集 schtask 同样依赖它,无新增要求,
|
||
但影子柜台进程在交易时段**不能容忍长时间掉线**——掉线期间不撮合,
|
||
重连后由日终对账补(见 §2 兜底)。
|
||
|
||
## 6. 分期
|
||
|
||
| 期 | 内容 | 验收 |
|
||
|----|------|------|
|
||
| P0 | 对账 spike:checkpoint 续跑 vs 全量重放,末持仓/净值一致(含跨除权用例) | ✅ **已通过(2026-08-14,VPS 真数据)**:all_weather 2024 全年(242 交易日)全量重放 vs 3 段续跑(5月初/9月初分段,跨多次除权),期末 9 只持仓逐只一致、现金 2,256.29/总值 1,335,875.29 相对偏差 <1e-6、净值曲线 <1e-6。脚本 `scripts/shadow_desk/spike_p0_checkpoint_replay.py`,runner 通道 `--initial-positions`+`final_portfolio`(1e7d696) |
|
||
| P1 | 影子柜台常驻进程:bar 级订阅(分钟/日线)+ 实时价撮合 + 虚拟账户 + 落库 | ✅ **已上线(2026-08-14 晚)**:supervisor schtask 拉起 shadow 进程,shadow#39(all_weather 影子)实跑;ShadowBroker 5 协议方法补齐、bullet_trade 两层实例锁(b27c3b4/e1cafac),四实例齐跑 live3/4/5+shadow39。涨跌停/停牌拒单 61cc01e |
|
||
| P2 | tick 推送接入(tick 级策略)+ 月线/周线合成 | tick 策略可跑(挂起:等双轨验证稳) |
|
||
| P3 | 日终对账自动化 + NAS 交叉对照;影子单同步 QMT(真实现盘前置) | 前半 ✅ **双轨对账报表(534a06a,2026-08-15)**:reconcile 四指标+自动配对 live5↔shadow39+API/CLI/supervisor 日终兜底;后半(影子单同步 QMT)挂起 |
|
||
|
||
工程分期不影响使用语义:策略侧从第一天就按「声明周期」配置,
|
||
tick 级在 P2 放开。
|
||
|
||
## 7. 风险
|
||
|
||
- **盘中掉线/miniQMT 掉登录**:断档期间不撮合,日终对账补齐;连续多日异常告警。
|
||
- **跨除权 checkpoint**:P0 对账显式覆盖。
|
||
- **tick 级数据量**:tick 只在内存消费,落库仍为成交/净值/bar 级快照,库不膨胀;
|
||
但全市场 tick 订阅的内存/CPU 峰值需 P2 实测(先单标的/小池子放开)。
|
||
- **多账户规模**:虚拟账户互相隔离,几十个实例同进程无压力;
|
||
策略数上百时再谈多进程分片。
|
||
|
||
## 8. 双轨一致性验证(影子柜台 vs 实盘模拟,用户需求 2026-08-14)
|
||
|
||
用户将同策略/实例同时跑在实盘模拟(miniQMT 真实下单)与影子柜台,
|
||
要求设计验证方案使两边最终盈亏尽量一致。
|
||
|
||
**预期管理**:完全一致不可能(实盘排队/部分成交/行情抖动),目标是
|
||
**差异可解释且小于阈值**——该差异即策略从纸面到真实的真实滑点成本,
|
||
本身是影子柜台的核心产出之一。
|
||
|
||
### 8.1 一致性参数(影子侧照抄实盘)
|
||
|
||
| 参数 | 设定 | 作用 |
|
||
|------|------|------|
|
||
| 行情源 | 同一 miniQMT | 同 bar 同时刻触发 → 信号相同 |
|
||
| 佣金/印花税/最低佣金 | 券商真实费率 | 消除费用差 |
|
||
| 滑点 | 初始 0.001,实测标定(价差均值重标) | 弥补实时价 vs 排队成交价差 |
|
||
| 涨跌停/停牌 | 同规则拦截 | 影子不成交实盘成不了的单 |
|
||
| T+1 / 100 股整手 | 影子同样强制 | A 股真实约束对齐 |
|
||
|
||
### 8.2 日终对账报表(自动)
|
||
|
||
| 对比项 | 一致标准 | 不一致归因动作 |
|
||
|--------|---------|---------------|
|
||
| 成交笔数 | 完全相同 | 实盘漏单 → 影子补拦截规则 |
|
||
| 每笔成交价差 | 平均 <10bps | 稳定偏一侧 → 用差值均值重标滑点 |
|
||
| 收盘持仓 | 逐只股票+数量相同 | 分歧报警,逐笔追查 |
|
||
| 净值偏差 | 月累计 <0.5% | 超限 → 滑点/费用重新标定 |
|
||
|
||
### 8.3 操作流程
|
||
|
||
同一策略同参数双轨并跑 ≥20 交易日(覆盖 2~3 个调仓日);
|
||
前几天价差数据用于滑点标定,之后两边贴合度应收敛到阈值内。
|
||
对账报表落在 VPS,前端模拟盘页可查(后续可加独立"双轨对账"页)。
|
||
|
||
## 9. 关联
|
||
|
||
- memory: `project-live-arch-shadow-mode`(实盘=影子模式既定方向,本设计是其前置)、
|
||
`xtdata-as-live-source`(xtdata 行情能力与坑 volume÷100)、
|
||
`paper-matching-mechanism`(旧回放撮合语义,本设计取代其实走部分)
|
||
- 现状代码:`sanguo_trader/portfolio_paper.py`(EOD 全量重放→降级为对账兜底)、
|
||
`sanguo_trader/scheduler.py`(NAS 20:30 job)、`sanguo_trader/matcher.py`、`sanguo_trader/limit.py`
|