← 返回目录
p8.8动手⏱ 约 16 分钟

纸上交易全流程:它当场抓到了一个回测掩盖的缺陷

第一版 209 笔委托里 112 笔被拒,全部是"资金不足"——下单数量按开盘价算,却按含滑点与佣金的价格成交

🔎 最后验证 2026-08📚 来源:纸上交易闭环实测与缺陷修复前后对照,2026-08-01 于 ECS 实测,输出见 code/outputs/stdout.txt🧰 Python 3.12.13、qsys(本课自研)
为什么学这个

把系统跑成闭环的第一次,它就抓到了一个缺陷:

第一版:委托 209   成交 97   被拒 112
        被拒原因:资金不足 112 笔(100.0%)

53.6% 的委托因为"资金不足"被拒——而账户里明明还有钱。

根因很简单,也很典型:

下单数量按开盘价算,却按「开盘价 × (1+滑点) + 佣金」成交。

每次想满仓,都会差那么一点点。

修复后:

修复后:委托 206   成交 98   被拒 108
        被拒原因:不足一手 108 笔(100.0%)

笔数几乎没变,但原因从"一个 bug"变成了"一个真实约束"。

而这个缺陷,在 P4 的回测里跑了整整一章都没被发现。

💡 打个比方

你按标价算好了要买 10 件,到收银台才发现还有税。

结账时刷不过——不是你没钱,是你算的时候没把税算进去。

而如果你只是在 Excel 里模拟购物,永远不会发现这件事。

一、一天的处理顺序(顺序即纪律)

① 新的一天 → 解锁昨日买入(T+1)
② 推入今日 Bar → 更新行情
③ 用「昨日及之前」的数据算信号 —— 绝不用今日
④ 按信号生成委托 → 走 Gateway → 模拟撮合
⑤ 收盘后记账并落盘

与 P4 事件引擎完全一致。 而它与回测的唯一区别是:

数据是一天一天喂进来的,而不是一次性给全。

这个区别正是纸上交易的价值——它能暴露"我以为我没用未来数据"的那些地方,也能暴露这一节这种下单数量的计算缺陷

二、缺陷是怎么被抓到的

⚠️ 避坑

回测掩盖它,纸上交易暴露它——原因在"谁来决定下单数量"。

谁算数量结果
P4 回测引擎内部按差额算,并自带"买不起就记被拒"的保护(p4.1)缺陷被引擎兜住了
纸上交易调用方(策略循环)算好数量再发给撮合器缺陷暴露在调用方

这不是说 P4 的引擎更好,而是说:一旦把"算数量"这件事移到系统边界外,它就成了一个独立的、需要被测试的东西。

修复:

# ⚠️ 下单数量必须按**含滑点与佣金的价格**算,并预留缓冲。
eff = bar.open * (1 + gw.match.slippage) * (1 + gw.match.commission)
if side == "BUY":
    affordable = gw.match.cash / eff
    qty = int(min(abs(diff) / eff, affordable))

一行 bar.open 换成 eff,112 笔"资金不足"归零

🔧 动手做:重现纸上交易当场抓到的那个 bug(6 分钟)

跑纸上交易闭环的第一版(带 bug 的那版),看被拒统计。

你会看到(实测):209 笔委托里 112 笔被拒,原因 100% 是"资金不足"。而这不是策略没钱——是 bug:下单数量按开盘价算,却按含滑点与佣金的价格成交,于是每次都差那么一点点钱,系统性地"资金不足"。

想明白:这个 bug 在回测里根本不会暴露(回测不模拟"下单时和成交时价格不一致"这件事)——是纸上交易全流程跑起来才当场抓到的。这就是为什么止于回测不够,要有纸上交易:把下单、撮合、账户串成真实闭环,才会撞出回测口径掩盖的缺陷。修复之后剩下的 108 笔被拒,才是真实约束(涨跌停/停牌),不是 bug。

三、修复之后剩下的 108 笔,不是 bug

不足一手  108 笔(100.0%)

这是真实约束:仓位接近满仓后,策略仍在要求加仓,而剩余现金买不起一手。

它恰恰是 p4.1 要求必须记录、不许静默跳过的那种情形。

同样是 100+ 笔被拒,修复前是"我的代码错了",修复后是"市场就是这样"。

而分辨这两者的唯一办法,是看被拒原因的分布——不是看被拒数量。

四、纸上交易该记什么

记录用途
equity.jsonl(每日权益与现金)净值曲线、回撤、水下期
fills.jsonl(每笔成交)成交点标注、换手统计、成本核对
rejects.jsonl(每笔被拒)诊断报告——本节的缺陷就是从这里发现的
策略指纹 + 数据指纹追溯(p8.4 / p8.5)

第三行是最容易被省掉、也是最有价值的一份。

五、纸上交易跑多久才算数

这是一个没有标准答案、但可以定量讨论的问题。

接 p5.8 的结论:在信息比率约 0.22 的量级下,要在统计上确认策略衰减需要 82 年数据

所以纸上交易的目的从来不是"验证策略能赚钱"——那需要的样本量你等不起。

它验证的是三件几十笔交易内就能确认的事:

  1. 成交价与假设是否吻合(实际滑点 vs 假设滑点)
  2. 委托被拒的比例与原因(本节)
  3. 信号频率是否与回测一致(不一致 = 有一方用错了数据)

这三件事,正是 p5.8 给出的"更诚实的替代方案"——监控假设是否被推翻,而不是监控净值。

❓ 测验
纸上交易跑了 30 天,净值比回测低了 3 个百分点。最该先查什么?
✏️ 填空
第一版的缺陷是:下单数量按开盘价算,却按「开盘价 × (1+滑点) + ___」成交。

📌 免责:本课为技术教学,纸上交易为模拟撮合,不接真实券商通道;不构成投资建议。

✅ 小结

四件事:

  1. 闭环第一次跑就抓到一个缺陷:209 笔委托里 112 笔"资金不足"(53.6%),而账户里明明还有钱。根因是下单数量按开盘价算,却按含滑点与佣金的价格成交。修复后 112 笔归零
  2. 回测掩盖它、纸上交易暴露它,原因在"谁算数量":P4 回测由引擎内部算并自带 p4.1 的保护,而纸上交易由调用方算——一旦把"算数量"移到系统边界外,它就成了一个独立的、需要被测试的东西
  3. 修复后剩下的 108 笔"不足一手"不是 bug,是真实约束同样是 100+ 笔被拒,修复前是"我的代码错了",修复后是"市场就是这样"——而分辨这两者的唯一办法,是看被拒原因的分布,不是看被拒数量
  4. 纸上交易的目的不是"验证策略能赚钱"(p5.8:那需要 82 年数据),而是验证三件几十笔内就能确认的事:成交价与假设是否吻合 · 被拒比例与原因 · 信号频率是否与回测一致

它与回测的唯一区别是数据一天一天喂进来——而这个区别正是它的全部价值

必记的四份记录里,rejects.jsonl 是最容易被省掉、也最有价值的一份:本节的缺陷就是从它发现的。

下一节讲怎么让这套系统活下去:进程守护、日志、告警、定时任务,以及故障演练

下一节 → 部署与运维
🔎 来源与核验· 2 条,点开核对
本节每个关键论断都对应一个可追溯的来源 —— 这是本课程"靠谱、不过时"的底线。
「纸上交易闭环第一版实测(sh.600000,2024-01~2026-07,622 个交易日):委托 209、成交 97、被拒 112,被拒原因 100% 为「资金不足」;根因为下单数量按 bar.open 计算而成交价含滑点与佣金」
📚 本节 code/p8_demo.py 与 _shared/qsys/paper.py,2026-08-01 于 ECS 实测过程记录✓ 已核验 2026-08
「修复(按含滑点与佣金的有效价格计算并按可用现金封顶)后:委托 206、成交 98、被拒 108,被拒原因 100% 转为「不足一手」」
📚 同上实测,输出见 code/outputs/stdout.txt✓ 已核验 2026-08
纸上交易全程为模拟撮合,不接任何真实券商通道;策略与参数仅为演示口径。
智图软件的赞赏码
都看到这了,打个赏呗!
接下来 · p8.9
部署与运维:主动把系统弄坏,看它怎么坏
继续读下一节 →