做市商算法设计:报价更新频率、订单撤销策略、毒药订单与闪电崩盘防御
做市商算法,说白了就是你的交易机器人怎么跟市场打交道。我见过太多人把精力全放在定价模型上,结果算法写得一塌糊涂——报价发不出去,订单被吃干净,遇到闪电崩盘直接爆仓。今天咱们就把这几个核心问题掰开揉碎。
报价更新频率:快不是目的,准才是
很多人觉得做市商就得高频更新报价,恨不得每秒发100次。其实这是个误区。我早期做过一个项目,把更新频率调到50ms一次,结果呢?交易所限流了,订单全被拒掉,反而丢了更多交易机会。
报价更新频率的核心逻辑就一条:在信息变化时更新,而不是在时钟滴答时更新。
关键原则:报价更新频率 = f(市场波动率, 库存风险, 交易所限流阈值)
我个人习惯用事件驱动的方式。什么意思?就是只有以下情况发生时,才触发报价更新:
- 行情变化:最新成交价偏离你的中间价超过某个阈值
- 订单成交:你的某个挂单被吃了,库存变了
- 时间衰减:超过最大报价存活时间(比如500ms),强制刷新
- 风险事件:波动率突然飙升,需要紧急调整
这里给个简单的伪代码思路:
class QuoteUpdater:
def should_update(self, market_state, inventory):
# 检查价格偏移
if abs(market_state.mid_price - self.current_mid) > self.price_tick:
return True
# 检查库存变化
if abs(inventory.net_position - self.last_position) > self.position_threshold:
return True
# 检查超时
if time.now() - self.last_update_time > self.max_quote_age:
return True
# 检查波动率
if market_state.volatility > self.volatility_threshold:
return True
return False
你想想看,如果市场风平浪静,你何必每秒刷几十次报价?白白浪费带宽,还容易被交易所盯上。
订单撤销策略:别让你的订单变成别人的猎物
订单撤销,听起来简单,但坑特别多。我曾经因为撤销策略写得不好,被高频交易者反复"钓鱼"——他们看到我的订单挂在那边,就故意砸盘吃我的止损单,然后迅速拉回,我亏得莫名其妙。
好的撤销策略要考虑三个维度:
| 策略类型 | 适用场景 | 我的经验 |
|---|---|---|
| 时间优先撤销 | 低波动市场 | 设置500ms-2s的存活时间,到期自动撤 |
| 价格偏移撤销 | 趋势行情 | 中间价偏移超过0.5个tick就撤 |
| 库存触发撤销 | 风险控制 | 净持仓超过阈值,立即撤销所有反向订单 |
| 波动率自适应撤销 | 高波动行情 | 波动率翻倍时,撤销频率提高3倍 |
我个人最常用的是混合策略。举个例子:
def cancel_strategy(order, market_state, inventory):
# 1. 时间检查:超过800ms没成交,撤
if time.now() - order.submit_time > 800:
return "CANCEL"
# 2. 价格检查:当前最优价已经越过你的报价
if order.side == "BUY" and market_state.best_ask < order.price:
return "CANCEL"
if order.side == "SELL" and market_state.best_bid > order.price:
return "CANCEL"
# 3. 库存检查:净持仓过大,撤掉同向订单
if abs(inventory.net_position) > inventory.max_position:
if (order.side == "BUY" and inventory.net_position > 0) or \
(order.side == "SELL" and inventory.net_position < 0):
return "CANCEL"
return "HOLD"
避坑指南:我曾经把撤销频率设得太高,结果交易所把我当成了"垃圾订单发送者",直接封了API。后来我加了一个节流器——每秒最多撤销20次,超出部分排队处理。
毒药订单:保护你的报价不被"狙击"
毒药订单(Poison Pill Order)这个名字听起来挺吓人,其实就是一种防御机制。做市商最怕什么?怕别人看到你的报价后,故意用大单来吃你,然后反向操作。
毒药订单的核心思路是:让对手不敢轻易吃你的单。
具体怎么做?我常用的几种方式:
- 隐藏部分数量:公开显示100张,实际挂了500张。对手以为能轻松吃掉,结果发现吃不完,反而把自己套住了。
- 条件触发撤单:当某个大单接近你的报价时,自动撤销。这招叫"冰山下单"的逆向应用。
- 虚假深度:在远离最优价的位置挂一些大单,制造流动性假象。注意,这招在合规上有点灰色地带,我用得很少。
我记得有一次,一个做市商团队被高频交易者盯上了。对方用算法反复试探他们的报价深度,每次吃5张就跑。后来我们给他们的系统加了毒药逻辑——当检测到同一对手方在短时间内连续吃单超过3次,就自动把报价撤掉,换成更宽的价差。那之后,对方的"钓鱼"行为就消失了。
注意:毒药订单不能滥用。有些交易所明确禁止"欺骗性订单",比如挂单后立即撤销。一定要先看清楚交易所的规则,别为了防御把自己搞封号了。
闪电崩盘防御:活下来比赚钱重要
闪电崩盘(Flash Crash)是做市商的噩梦。价格在几秒钟内暴跌几十个百分点,你的止损单根本来不及反应。我亲身经历过一次,账户在3分钟内亏了15%。
从那以后,我给自己定了一条铁律:闪电崩盘时,第一反应不是赚钱,是保命。
防御体系分三层:
- 检测层:监控价格变化速率。如果1秒内价格波动超过正常水平的5倍,立即触发崩盘警报。
- 响应层:警报触发后,做三件事——撤销所有订单、暂停报价、切换到只撤单模式。
- 恢复层:等待市场稳定。我一般等价格波动率回到正常水平的1.5倍以内,再慢慢恢复报价。
代码实现大概是这样的:
class FlashCrashDefender:
def __init__(self):
self.normal_volatility = None
self.is_crash_mode = False
def check_crash(self, market_state):
# 计算当前波动率
current_vol = market_state.volatility_1s
# 如果还没初始化,先记录正常波动率
if self.normal_volatility is None:
self.normal_volatility = current_vol
return False
# 检测:波动率超过正常值5倍
if current_vol > self.normal_volatility * 5:
self.is_crash_mode = True
return True
# 恢复:波动率回到1.5倍以内
if self.is_crash_mode and current_vol < self.normal_volatility * 1.5:
self.is_crash_mode = False
return False
def defend(self, market_state):
if self.check_crash(market_state):
# 紧急操作
self.cancel_all_orders()
self.stop_quoting()
self.enable_cancel_only_mode()
return "CRASH_MODE_ACTIVATED"
return "NORMAL"
核心原则:闪电崩盘时,流动性会瞬间枯竭。你的订单挂在那边,要么被以极差的价格吃掉,要么根本成交不了。所以,先撤单,再观察,最后恢复。顺序不能乱。
嗯,这里还要提一句。有些做市商会在闪电崩盘时反向挂单,试图抄底。我个人不建议这么做。为什么?因为你不知道崩盘会持续多久。2010年的美股闪电崩盘,道琼斯指数几分钟内跌了1000点,抄底的人很多都被埋了。做市商的核心是提供流动性,不是投机。
知识体系总览
下面这张图把今天讲的内容串起来了。你可以看到,报价更新、订单撤销、毒药订单、闪电崩盘防御,这四个模块是相互关联的。它们共同构成了做市商算法的"护城河"。
这四个模块,每一个单独拿出来都能讲一整天。但作为做市商,你得把它们整合成一个有机的系统。报价更新频率决定了你的"反应速度",订单撤销策略决定了你的"生存能力",毒药订单是"防御武器",闪电崩盘防御是"最后一道保险"。
我个人建议,先从闪电崩盘防御开始写。为什么?因为这是保命的。我见过太多团队,算法写得花里胡哨,结果一次黑天鹅事件就全完了。先把防御做好,再谈进攻。
一个小技巧:在实盘上线前,用历史数据做回测,模拟闪电崩盘场景。我每次上线新算法,都会先跑一遍"崩盘测试"——把2010年美股闪电崩盘的数据喂进去,看看算法能不能扛住。扛不住?回去改代码。
好了,今天就聊到这儿。做市商算法设计,说到底就是跟市场博弈。你设计得越精细,市场就越难从你身上占到便宜。但记住一点:没有完美的算法,只有不断进化的系统。