news 2026/9/19 10:29:23

高频交易算法核心解析:订单簿、做市与回测实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
高频交易算法核心解析:订单簿、做市与回测实战

简介:面向量化交易初学者与对高频交易感兴趣的人群,这份PDF以简洁篇幅梳理高频交易领域的经典算法与技术细节,重点讲解冰山订单(Iceberg Order)的运作逻辑、订单簿(Order Book)的信息结构,以及如何通过盘口扰动探测隐藏订单、基于回归模型与机器学习识别冰山订单概率等方法,内容贴近实战问答,便于快速建立认知框架。压缩包仅含1个PDF文件,大小约769KB,无需安装额外环境,打开即可阅读。目前已有406人学习,适合在通勤或碎片时间阅读入门,也可作为理解高频交易策略背后微观结构知识的参考材料。文档从交易所公开数据聊起,逐步延伸到冰山订单的优缺点、探测手段与高级建模思路,能帮助读者理解高频交易并非单纯追求速度,而更多是对市场信息不对称的利用与博弈。

1. 高频交易算法不止是“预测行情”,更是订单簿上的资源竞争

高频交易算法常被外行理解成“用机器预测涨跌的炒股程序”,实际在交易所内部,它处理的不是明天几点几分发生什么,而是在毫秒甚至微秒尺度上回答三个问题:该不该在这个价位挂单、挂了之后会不会暴露意图、对手方真正出现时能不能以最小滑点成交。做市、统计套利、冰山和狙击这些著名算法,输出并不是价格判断,而是一连串“报单、撤单、改单”的确定性指令。它们的共同输入是限价订单簿,共同约束是“先到先得”的排队规则。下面按工程顺序拆开讲,适合已经能写代码、想自己搭一套 HFT 原型的量化开发者。

2. 订单簿与市场微结构:高频交易算法的主要输入

2.1 用价格档位重建市场

高频交易算法的数据主餐是订单簿增量,不是传统 K 线。订单簿把买盘和卖盘按价格分层,每一层包含当前可成交数量和在场委托数量。行情服务商通常每秒推几十到几百个快照,核心算法关心的是增量,因为全量重建订单簿在延迟上不划算。

先看一个最简订单簿对象:

# order_book.py class Level: def __init__(self, price, size, order_count): self.price = price # 价格,按最小变动价位取整 self.size = size # 该价位可执行数量 self.order_count = order_count # 该价位委托笔数,用于估计离散度 class OrderBook: def __init__(self, max_levels=10): self.bids = [] # 买盘,按价格降序 self.asks = [] # 卖盘,按价格升序 self.max_levels = max_levels def apply_delta(self, side, price, size, order_count): levels = self.bids if side == 'bid' else self.asks if size == 0: # size 为 0 表示该价位全部撤单,必须整层移除 levels[:] = [lv for lv in levels if lv.price != price] return for lv in levels: if lv.price == price: lv.size = size lv.order_count = order_count break else: levels.append(Level(price, size, order_count)) # 每次变动后排序,价格优先是撮合的前提 levels.sort(key=lambda lv: lv.price, reverse=(side == 'bid')) del levels[self.max_levels:]

apply_delta里的三个参数决定算法输入质量:side区分买卖方向,price必须用整数倍表示,避免浮点误差影响价格排序;size表示数量变化,0在协议里通常代表撤单或成交后余量为空。max_levels控制研究内存占用,10 到 20 档已经能覆盖绝大多数 HFT 策略的视野范围。生产环境很少用 Python 处理原始行情包,因为垃圾回收会造成抖动,但用 Python 做数据结构验证,逻辑上完全够。

2.2 价格-时间优先与排队经济学

订单簿之所以有价值,是因为撮合规则是价格优先、时间优先。价格优先指的是买单挂在更高价位就先成交;时间优先是指同一个价位上,谁先挂谁先成交。高频交易算法所有“抢”的技巧,都是在时间优先级上做文章。

数据层级包含内容高频交易算法关注点
L1 快照买一价、卖一价及对应数量价差是否收窄、最佳价位是否可成交
L2 快照多个档位的价格和数量档位厚薄、订单不平衡、支撑和阻力
L3 委托簿每个价格档位的单笔委托及到达顺序排队位置、单笔剩余量、撤单概率

L1 和 L2 是行情服务商最常提供的级别,L3 则只从特定柜台或自建接入拿到。队列位置敏感的算法需要 L3,比如做市商在同一价位排队,若前面有数百手大单,自己的订单虽然价格占优,实际上要等很久才能成交。这里的“排序算法”不只是软件教科书里的快排,而是对价格队列结构的利用:算法要维护自己的排队位次,并根据队前数量和撤单速度决定是否挪单。

2.3 高频交易的两个主要噪声源

订单簿最不可信的字段是静态数量。买单挂得厚,不代表买方真的想买,可能是大单在制造氛围。高频交易算法因此常用“微价”做信号过滤,而不是直接看买一卖一中间价:

# micro_price.py def micro_price(bid_px, bid_sz, ask_px, ask_sz): """ 微价:按买卖盘数量加权后的价格,它更接近下一笔成交的期望位置。 """ if bid_sz + ask_sz == 0: return (bid_px + ask_px) / 2 return (ask_px * bid_sz + bid_px * ask_sz) / (bid_sz + ask_sz)

当买单数量明显大于卖单数量时,微价偏向 ask 一侧,说明成交推动力偏向上行。这只是信号,不是保证。另一个噪声是撤单:大量限价单在到达队列头部之前被撤销,让算法误以为深度充足。处理撤单噪声没有捷径,只能统计每个档位的撤单率和平均存续时长,再用阈值过滤掉“看起来厚但活不过几十毫秒”的档位。

3. 高频交易著名算法家族:做市、统计套利、狙击与拆单

3.1 做市算法:报价换价差,库存是控制变量

做市算法是高频交易中最经典、也最难做的家族,代表做法是同时在买一和卖一挂单,成交后在两边都留差价,赚的不是预测收益,而是“提供流动性”的价差。真实工程难点在库存管理,因为对手方可能只吃掉你买一上的单量,导致库存多出一大截。

常见库存解法是买卖报价不对称偏移:

# mm_quote.py class MarketMakingPolicy: def __init__(self, fair, half_spread=0.01, max_inventory=200, skew_step=0.005): self.fair = fair # 外部信号给出的理论公允价 self.half_spread = half_spread # 当前盘口半价差 self.max_inventory = max_inventory self.skew_step = skew_step # 库存每变动一单位时价格偏移量 def make_quotes(self, inventory): scale = inventory / self.max_inventory bid = self.fair - self.half_spread - scale * self.skew_step ask = self.fair + self.half_spread - scale * self.skew_step return round(bid, 2), round(ask, 2)

库存为正说明手里净多头,此时系统降价卖、降价买,让买方更愿意主动成交,降低库存。库存为负则反向。half_spread必须比真实盘口价差略小,否则两边报价都无法成对成交;skew_step不能太激进,否则报价距离公允价过远,长时间不会成交。做市算法的显著特征是撤单率高,因为需要不断调整报价抢排队位置,所以很多交易所对高频撤单有“订单到成交比”约束。

3.2 统计套利算法:在价差回归中赚取确定性差异

统计套利对应的是市场中性和均值回归。HFT 场景下,统计套利不是对某只股票的绝对价格做预测,而是观察两个高度相关合约之间价差的偏离,比如同一指数在不同市场上市的期货,或者现货与 ETF。当价差超过历史分布的一个标准差,算法买入相对便宜的一端、卖出相对贵的一端,等待价差收敛后同时平仓。

统计套利中最少不了的参数是进入阈值和退出阈值。进入阈值太窄会频繁交易,收益被手续费吃掉;太宽则出场等待时间过长。高频版本还会加入“最小持有时间”,防止订单刚成交就被反向波动打掉。对冲比例也不是固定 1 比 1,一般通过盘口买卖数量之比实时更新,让两边的名义金额尽量匹配。

3.3 狙击算法与冰山算法:把大单藏起来

狙击算法的特征是主动吃单:当监测到大额买单在多个档位连续出现,算法快速跟随买入,抢在后续行情发酵前建立头寸。它更像一个有序的行情反应器,而不是价格预测器。高频版本通常会对每个价格档位设置存活时间,超过两个快照还没触发就放弃,这就是决策里的“剪枝”:把不成立的子路径提前砍掉,避免信号过期后仍然发单。

冰山算法则是另一种思路,它属于大单执行算法。一个大单直接挂在订单簿上会引起狙击算法注意,所以冰山只暴露一小部分数量,成交后再补量。参数上要关注三个值:

参数典型设置作用
可见量父单总量的 2%-5%控制暴露风险,量太小撤单率高
补单阈值可见量剩余 30% 时补单维持队列位置,降低排队磨损
价格偏离上限父单限价 ±1 个最小变动价位防止追价过快,锁住执行成本

冰山订单和做市算法经常被放在一起比较:做市是为了赚价差,冰山是为了减少大单成交对市场价格的冲击。两者都用“小量多批次”的思想,但做市的盈利目标是收益,冰山的盈利目标是不输给市场均价。

3.4 各类算法的出场条件差异

算法族订单类型出场条件主要故障
做市双向限价库存超过容量或信号衰减单边成交后库存失衡
统计套利价差进入价差回归目标位价差不收敛
狙击主动吃单行情确认或持仓超时滑点大于预期
冰山限价拆分父单全部完成只能通过排队等待成交

这里有一个高频交易中常犯的错误:用做市算法的参数去跑狙击算法。做市强调订单在簿时间越长越好,狙击则相反,所以两类算法必须独立设置超时、撤单和最大持仓,不能共享风控参数。

4. 高频交易算法的回测参数:订单簿回放与撮合假设

4.1 用订单簿回放器验证算法

高频交易算法最怕的是 K 线级回测,因为 K 线把买卖方向、排队情况和订单簿深度全丢掉了。常见做法是把历史订单簿增量按事件流回放,边回放边维护本地订单簿,同时模拟自己的限价单和吃单。

# replay_book.py import json def replay(path, book, signal): with open(path, 'r', encoding='utf-8') as fp: for line in fp: ev = json.loads(line) book.apply_delta(ev['side'], ev['price'], ev['size'], ev['order_count']) if ev['kind'] != 'TRADE': continue if not book.bids or not book.asks: continue bid = book.bids[0] ask = book.asks[0] px = micro_price((bid.price, bid.size), (ask.price, ask.size)) if signal.is_triggered(px) and ev['price'] >= ask.price: # 触发后按 ask 价格吃单,只记录数量与耗时 send_child_order('BUY', ask.price, signal.qty)

回放器关键不是撮合逻辑多复杂,而是事件顺序必须严格按行情时间戳排列。TRADE事件表示该价格已经成交过一笔,说明盘口更新有效。真正的回测还要把“自己订单被部分成交”的状态加进来,也就是行情中出现了比自己挂单价更优或等于自己挂单价的成交,就认为系统吃到了一部分。

4.2 决定回测质量的六个参数

高频交易算法的回测结果与实盘差距,通常差在这些参数上:

参数建议设置设置不当的后果
快照年龄不超过 5ms信号延迟导致成交价偏移
最小挂单寿命300ms 起寿命太短造成高撤单率
最大库存日成交量的 1% 以内库存单边无法对冲
止损距离一个盘口价差加滑点止损太小被噪声反复打穿
信号衰减阈值1 个 tick信号衰减不撤单会追高
序列号间隙必须为 0行情缺口会被误认为价格跳空

其中“快照年龄”最容易忽略:行情中心推送快照时的系统时间与本地接收时间之间的差,会直接影响微价计算。回测时如果直接把文件里的价格当成即时价格,等于假设自己的行情线零延迟,这是最典型的自欺回测。

4.3 回测必须模拟的高频成本

高频交易成本不是佣金那么简单。实际吃单时的成本是盘口价差和排队失败的综合结果。做市算法在实盘中经常被“反选”:只会让你在不利方向成交、有利方向跑掉的订单,这种选择效应在回测中几乎无法完全模拟。参数纪律上,一般会把回测成交价再加一个 tick 作为额外惩罚,然后观察算法是否仍然盈利。吃掉一个 tick 听起来可小,但在高频交易里的胜率优势通常也是几分之一 tick,所以成本模型错了,结论整个会反过来。

5. 高频交易算法上线前的验证:撤单率、延迟与排队位置

算法完成回测后,第一件事不是实盘,而是用一天的高频行情做“影子模式”验证。影子模式只计算信号、只发送日志,不实际报单到交易所,但它能告诉你三个最关键的运营指标:信号频率、信号存活时间和理论成交率。

撤单率是第一个要卡死的指标。交易所会统计每个账户的订单到成交比,指标过高会被收取额外费用,甚至限制接入。高频做市算法的撤单率天然就高,50% 以上的撤单不算异常,但每秒撤单次数必须设上限。我通常会在风控模块里加一个滑动窗口计数器,一秒内撤单超过 20 笔就暂停当前算法 30 秒,防止某个行情异常把当天额度扫光。

延迟是第二个指标。回测系统里记录的信号时间与本地行情接收时间的差,本质上是两条不同延迟的合成:一条是交易所到本地行情客户端的网络延迟,另一条是决策程序到柜台系统的发送延迟。测试时可以直接在日志里打上统一时钟戳,比较下单请求的发送时刻与柜台确认时刻。差值是固定损耗还是抖动损耗,比绝对数值更有价值:固定损耗可以通过前置队列优化,抖动损耗说明进程内有 GC 或锁竞争。

排队位置是第三个被高估的指标。L2 行情显示某价位有 100 手,不代表你的订单前面只有 100 手;真实的 L3 数据里,同一价格可能只有少数几条大单,也可能挤满了几百条小单。验证方法很简单:用同一笔历史行情跑两次回放,第一次假设自己的单排在最前,第二次假设排在 50% 位置,对比成交率和成交均价。两次结果差距过大,说明算法过于依赖运气,而不是优势。

这套验证流程落到最后,真正值得长期监控的是“信号发出后 10ms 内是否被执行”这条比例线。高撤单率和低成交概率很多时候是同一件事的两面:信号生命周期太短,导致订单都还没来得及进入最佳排队位置就被撤掉。把这些数字固定成报警阈值,比翻看回测净值曲线更值得做。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 10:28:02

WSL2性能调优实战:内存、磁盘、网络与GPU加速全指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 10:27:55

VLSI复习核心:CMOS电路与时序约束高频考点解析

简介:VLSI复习题答案(潘传洲)围绕扫描测试、后端布局布线、硬件仿真、特定应用数字系统设计、自顶向下设计方法以及时序收敛等VLSI核心考点展开,适合集成电路相关专业学生、初入行数字IC工程师以及正在备考相关课程或面试的读者&a…

作者头像 李华
网站建设 2026/9/19 10:21:49

Hugo 短代码 .Inner:在开闭标签之间提取与渲染内容

Hugo 短代码 .Inner:在开闭标签之间提取与渲染内容 【免费下载链接】hugo The world’s fastest framework for building websites. 项目地址: https://gitcode.com/gh_mirrors/hu/hugo 导读 .Inner 是 Hugo 短代码(shortcode)模板中…

作者头像 李华