← 返回目录
p4.4动手⏱ 约 16 分钟

撮合层:把约束推到会绑定的场景

7/7 单元测试通过,而滑点从 0 到 50bp 让收益从 +35% 变成 -60%

🔎 最后验证 2026-07📚 来源:撮合层单元测试、极端行情段与滑点敏感性于 2026-07-31 在 ECS 实测,输出见 code/outputs/stdout.txt🧰 Python 3.12.13、qlab.engine.matching(本课自研)
为什么学这个

p4.1 留下一个问题:三条交易约束在大盘股 + 低频策略下,一次都没绑定

那撮合层白写了吗?这一节回答它,分三步:

  1. 单元测试:七条规则逐条验证,不依赖任何行情数据、毫秒级跑完——7/7 通过
  2. 极端行情段:2015 年 6–9 月,一字板密度是全区间的 3 倍
  3. 滑点敏感性:从 0 到 50bp,同一条策略的累计收益从 +35.32% 变成 -59.58%

最后那个数字才是撮合层真正的重点:约束是否绑定要看场景,但成交价假设永远在起作用。

💡 打个比方

消防栓一年到头没人用。

你不会因此拆掉它——但你必须定期检查它能不能出水

撮合层的约束就是消防栓:平时不绑定,而检查它"能不能出水"的办法,就是单元测试。

一、单元测试:七条规则逐条验证

撮合层最怕的不是"没实现",是"实现了但从来没被触发过,所以没人知道它对不对"。

解法是 p0.3 那条纪律的直接应用:用构造数据,让每条规则各响一次

✓ 一字涨停买入                    涨停封板买不进
✓ 一字跌停卖出                    跌停封板卖不出
✓ 停牌日下单                      停牌不可交易
✓ T+1 当日买入卖出                 T+1 当日买入不可卖
✓ 资金不足(买 10 万股只有 5000 元)   成交 400 股
✓ 整手取整(下单 150 股)            成交 100 股
✓ 限价买 9.0(当日最低 9.8)         限价买单未触及

7/7 条规则按预期工作

注意后两条不是"拒绝",而是部分成交与取整——撮合层不是只会说"不",它要给出在约束下能做到的最好结果:

  • 资金只有 5000 元却想买 10 万股 → 成交 400 股(而不是全部拒绝)
  • 下单 150 股 → 成交 100 股(向下取整到一手)

这七条测试不依赖行情数据、毫秒级跑完,应该挂在每次提交上。

二、极端行情段:约束在什么时候绑定

p4.1 实测:8 只大盘股全区间,一字板占 0.038%。那极端行情呢?

2015-06-01 ~ 2015-09-30,20 只标的 1572 个交易日:
  一字涨停 1 天、一字跌停 1 天(合计占 0.13%)
 20 只标的全区间 76806 个交易日:一字板合计占 0.039%

 极端行情段的一字板密度是全区间的 3 
⚠️ 避坑

3 倍——但绝对数量仍然很小(1572 天里只有 2 天)。这个结果需要诚实说明。

原因是样本选择:我们的标的池是沪深300 成分股,也就是市值最大、流动性最好的那批。2015 年那轮"千股跌停",跌停的主力是中小市值股票,而它们不在这个池子里。

所以这里的正确结论不是"极端行情也不太绑定",而是:

在本样本(大盘蓝筹)上,即使极端行情段,一字板也很少。要真正压出撮合层,需要把标的池换成中小市值——那是另一份数据。

我保留这个"不够震撼"的结果,因为改样本去凑一个更好看的数字,正是这门课一直在批评的做法。

三、滑点:那个永远在起作用的东西

约束是否绑定要看场景。但成交价假设,每一笔交易都在起作用:

滑点设定平均累计%平均成交笔数
0 bp+35.32%434.7
5 bp+19.87%416.8
10 bp+6.23%402.5
20 bp-16.52%402.0
50 bp-59.58%381.3

从 +35.32% 到 -59.58%,95 个百分点。

这就是为什么撮合层不能省:它不只是"处理涨跌停"那几行代码,它决定了你每一笔交易的成交价——而成交价是回测里最基础、也最容易被默认掉的假设。

🔧 动手做:找出这条策略的"死亡线"(8 分钟)

0 成本下它 +35%,50bp 下 -60%。那它到底在多少滑点由赚转亏?别看答案,自己找。

把第 134 行的滑点档位改密,逼近那个拐点:

for slip in (0.001, 0.0012, 0.0014, 0.0016, 0.0018, 0.002):   # 10~20bp

重跑 python matching_stress.py,你会看到(实测):12bp 还 +1.09%,14bp 就 -3.65%——盈亏平衡线在 ≈13bp

想明白:13bp 是多小的数?这条策略跑在沪深300 大盘股上,平时滑点也许才几个 bp、还撑得住;可波动日、下大单、或换到流动性差的小盘股,单边十几 bp 分分钟。这条零成本下 +35% 的"好策略",利润薄到只剩一层滑点的厚度——滑点稍一放大就归零。 所以看到任何零成本下好看的策略,先算它的死亡线在哪、你实盘会不会碰到。

市价单 vs 限价单

市价单限价单
成交一定成交可能不成交
价格吃滑点省滑点
危险极端行情下成交价可能很差没成交的往往正是行情跑得最快的时候

限价单看起来更省,但它的代价是隐性的:你想追的时候追不上,而追不上的那几次,恰恰是决定收益的那几次。

真实项目里,限价单的"未成交率"必须单独统计。只报成交的那些,等于只统计了你运气好的时候。

四、撮合层的完整规则表

本引擎实现的 A 股规则(limit_pct 等均可配置):

规则实现来源
涨跌停一字板不成交;成交价被限价钳住交易所公开规则(主板 ±10%)
T+1当日买入锁定,次日解锁交易所规则
整手买入按 100 股向下取整;卖出可不足一手交易所规则
停牌tradable=False 直接拒单p1.3 实测 68 天
资金约束逐手递减直到买得起现实约束
佣金双边,含最低 5 元券商常见档位
印花税仅卖出收取现行规则
滑点买入向上、卖出向下,并被涨跌停钳住建模假设

⚠️ ST 类、创业板与科创板的涨跌停档位不同(且规则会调整)。本引擎把它做成参数而不是写死,正是因为规则会变,而写死的代码不会告诉你它过时了

❓ 测验
你的撮合层代码里有涨跌停判断,但回测跑了三年从没触发过。最该做的是?
✏️ 填空
约束是否绑定取决于场景,但 ___ 假设每一笔交易都在起作用——本节实测它带来 95 个百分点的差距。

📌 免责:本课为技术教学,标的为沪深300 成分股按代码序取样(非精选),所有回测为历史模拟,不构成投资建议。交易规则以交易所现行公告为准。

✅ 小结

三件事:

  1. 单元测试 7/7 通过——七条规则用构造数据各响一次,不依赖行情、毫秒级跑完。撮合层不只会说"不":资金不足时成交 400 股、下单 150 股时成交 100 股,它给出的是约束下能做到的最好结果
  2. 极端行情段一字板密度是全区间的 3 倍,但绝对数量仍很小(1572 天里 2 天)——因为样本是大盘蓝筹,而 2015 年跌停的主力是中小市值股。我保留这个不够震撼的结果,因为改样本去凑好看数字正是本课一直在批评的做法
  3. 滑点从 0 到 50bp:+35.32% → -59.58%,95 个百分点——约束是否绑定看场景,成交价假设永远在起作用

下一节讲账户层:持仓、可用资金、T+1 锁定、分红除权。其中有一个必须写在明处的约定——用后复权价就不能再单独入账现金分红,否则同一笔钱会被算两次。

下一节 → 账户层:持仓、可用资金与分红除权
🔎 来源与核验· 3 条,点开核对
本节每个关键论断都对应一个可追溯的来源 —— 这是本课程"靠谱、不过时"的底线。
「撮合层 7 条单元测试(一字涨停买入/一字跌停卖出/停牌/T+1/资金不足/整手取整/限价未触及)全部按预期工作;资金 5000 元买 10 万股实际成交 400 股,下单 150 股实际成交 100 股」
📚 本节 code/matching_stress.py,2026-07-31 于 ECS 实测,输出见 code/outputs/stdout.txt✓ 已核验 2026-07
「2015-06-01~2015-09-30,20 只标的 1572 个交易日中一字涨停 1 天、一字跌停 1 天(0.13%);同 20 只标的全区间 76806 个交易日一字板占 0.039%,极端段密度为全区间的 3 倍」
📚 同上实测✓ 已核验 2026-07
「滑点敏感性(6 只标的平均累计收益):0bp +35.32%、5bp +19.87%、10bp +6.23%、20bp -16.52%、50bp -59.58%」
📚 同上实测✓ 已核验 2026-07
样本为沪深300 成分股(大盘蓝筹),2015 年跌停主力为中小市值股票,不在本样本内;结论不可外推至全市场。交易规则以交易所现行公告为准。
智图软件的赞赏码
都看到这了,打个赏呗!
接下来 · p4.5
账户层:持仓、可用资金与那个必须写在明处的分红约定
继续读下一节 →