在学习交易系统时,撮合模块通常是最早接触到的内容之一。
规则也不复杂:价格优先、时间优先,买卖盘一对就成交。
但在真正做策略回放和撮合仿真的过程中,我慢慢发现一个问题:
同样的策略,在不同回放条件下,成交结果会不一样。
一开始我以为是参数没调好,后来才意识到,问题出在撮合逻辑本身——
并不是所有“能成交”的实现,都符合滚动撮合交易的真实节奏。
撮合在系统里到底在做什么
从学习和实现的角度看,撮合并不是一次性的计算,而是一个持续运行的过程。
它会不断接收行情和订单变化,并在每一步判断是否产生新的成交:
行情更新 → 订单簿变化 → 撮合判断 → 成交生成 → 继续等待下一次变化
当系统使用 tick 级行情时,这个流程就会被不断触发,而不是等一批数据处理完再统一撮合。
这正是“滚动撮合”的含义:
撮合会随着行情一点点向前推进,而不是集中执行。
滚动撮合为什么“滚”
从实现角度看,滚动撮合可以拆解成一个很基础的循环:
新订单进入系统
与当前最优对手盘比较
条件满足就成交
剩余订单继续留在订单簿中
真正影响结果的,其实是订单簿的组织方式,而不是表面的撮合规则。
常见的结构是:
买盘按价格从高到低排序
卖盘按价格从低到高排序
同一价位内按进入顺序处理
这也是价格优先、时间优先在代码层面的直接体现。
一个简化的滚动撮合核心示例
下面这段代码只关注撮合本身,结构很简单,但已经具备滚动撮合的基本特征:
from collections import deque class Order: def __init__(self, price, volume): self.price = price self.volume = volume def rolling_match(buy_book, sell_book): trades = [] while buy_book and sell_book: buy = buy_book[0] sell = sell_book[0] if buy.price < sell.price: break volume = min(buy.volume, sell.volume) trades.append((sell.price, volume)) buy.volume -= volume sell.volume -= volume if buy.volume == 0: buy_book.popleft() if sell.volume == 0: sell_book.popleft() return trades
从学习角度看,这段逻辑已经包含了几个关键点:
撮合是逐笔推进的
支持部分成交
成交价格来自对手盘
未成交的订单会自然进入下一轮撮合
在真实系统中,复杂度更多来自并发、时间顺序和回报处理,而不是这段核心逻辑。
把撮合结果“画出来”更好理解
在调试撮合或做策略回放时,仅靠打印日志其实不太直观。
我更习惯把成交价格画成简单的曲线,帮助自己理解撮合过程。
下面是一个最小的可视化示例:
import matplotlib.pyplot as plt
prices = [101, 101, 100.5, 100.5, 100]
times = range(len(prices))
plt.plot(times, prices)
plt.xlabel("Match Sequence")
plt.ylabel("Trade Price")
plt.title("Rolling Matching Trade Price")
plt.show()这种图在学习阶段非常有用:
能快速发现成交价格是否异常
可以对比不同撮合逻辑的差异
对理解滑点来源也很直观
为什么学习滚动撮合要用 tick 行情
在撮合层面,时间顺序往往比价格本身更重要。
如果使用的是聚合后的行情数据:
多笔真实成交会被合并
撮合触发点被提前或延后
回放结果和真实市场存在偏差
因此,在学习和仿真阶段,更合理的方式是:
用逐笔 tick 行情驱动撮合逻辑
让撮合过程跟随市场节奏前进
在一些练习项目中,我会直接使用多市场的 tick 行情流作为输入,比如接入 AllTick API 提供的实时 tick 数据。统一的数据结构可以减少很多适配工作,也更方便还原滚动撮合的过程。
学到的一点体会
撮合模块在最初看起来总是“已经写完了”,
但当你开始做高频回放、策略并行或者跨市场实验时,它往往是最先暴露问题的地方。
是否真正理解滚动撮合交易,直接影响:
回测结果是否可信
策略行为是否稳定
学习过程中对市场机制的理解深度
从学习角度来看,把撮合逻辑拆开、多跑几次、多画几张图,比单纯看规则要有效得多。
如果你正在学习交易系统或量化相关内容,这一部分非常值得花时间认真理解。
共同学习,写下你的评论
评论加载中...
作者其他优质文章
