第八章:做市报价引擎设计

报价引擎,说白了就是做市商的「心脏」。你策略再牛,模型再准,最后都得靠它把价格报出去。我见过太多团队,策略逻辑写得漂漂亮亮,结果报价引擎一上线就崩——要么报价频率太高被交易所限流,要么买卖价差设得太死被套利者反复收割。

今天咱们就把报价引擎拆开揉碎,从四个维度来讲:报价频率怎么定、价差怎么调、订单簿怎么管、库存怎么控。嗯,这些都是我踩过坑之后才真正搞明白的东西。

核心观点:报价引擎不是越快越好,也不是越慢越好。它是在「流动性提供」和「风险暴露」之间找平衡。

8.1 报价频率优化

先问个问题:你每秒报多少次价合适?

我刚开始做结构化产品做市时,总觉得报价越快越好。你想啊,报价快意味着流动性好,能吸引更多订单。结果呢?被交易所警告了三次,说我们「过度报价」。后来我才明白,报价频率不是拍脑袋定的,它受三个因素约束:

  • 交易所规则:很多交易所对报价更新频率有硬性限制,比如每秒最多10次
  • 市场波动率:波动大的时候需要高频更新,波动小的时候可以降低频率
  • 计算延迟:你的定价模型跑一次要多久?如果模型跑一次要50ms,那每秒最多报20次

我个人习惯的做法是:先做压力测试,摸清系统的极限。比如,我曾在某次做市项目中,把报价频率从每秒5次逐步提升到每秒20次,观察成交率和撤单率的变化。结果发现,超过每秒12次后,成交率不再提升,反而因为频繁撤单增加了成本。

实战技巧:用「自适应报价频率」策略。市场波动率低时,每秒报3-5次;波动率高时,提升到每秒10-15次。这样既节省系统资源,又能保证流动性。

这里给一段简单的频率控制代码,我用的是Python伪代码,实际生产环境会用C++或Rust:

class QuoteFrequencyManager:
    def __init__(self, max_freq=10):
        self.max_freq = max_freq  # 每秒最大报价次数
        self.last_quote_time = 0
        self.quote_count = 0
        
    def should_quote(self, current_time, volatility):
        # 根据波动率动态调整频率
        if volatility > 0.05:  # 高波动
            target_freq = min(self.max_freq, 15)
        elif volatility > 0.02:  # 中波动
            target_freq = min(self.max_freq, 10)
        else:  # 低波动
            target_freq = min(self.max_freq, 5)
            
        # 检查是否达到频率上限
        if current_time - self.last_quote_time < 1.0 / target_freq:
            return False
        return True

8.2 买卖价差动态调整

价差怎么定?这是做市商的核心利润来源。价差太宽,没人来交易;价差太窄,赚不到钱还容易亏。

我记得有一次,我们给一个雪球产品做市,初始价差设了0.5%。结果市场一波动,价差瞬间被套利者吃掉,一晚上亏了十几万。后来我学乖了,价差必须动态调整。

动态调整的核心逻辑其实就三个维度:

  1. 市场波动率:波动越大,价差越宽。因为你要覆盖更大的风险
  2. 库存水平:库存偏离目标越远,价差越宽。这是为了吸引反向订单
  3. 订单簿深度:深度越浅,价差越宽。流动性差的时候要保护自己

我常用的一个公式是这样的:

spread = base_spread * (1 + alpha * volatility) * (1 + beta * inventory_deviation) * (1 + gamma / depth)

其中alpha、beta、gamma是调参系数。我在项目中一般先用历史数据回测,找到最优参数组合,再上线实盘。

避坑指南:我曾经把gamma设得太大,导致深度浅的时候价差变得极宽,完全失去了竞争力。后来发现,gamma控制在0.1-0.3之间比较合理。

下面是一个动态价差调整的示例表:

市场状态 波动率 库存偏离 订单簿深度 建议价差
平静 低 (0.01) 低 (0.1) 深 (1000) 0.3%
波动 中 (0.03) 中 (0.3) 中 (500) 0.6%
危机 高 (0.08) 高 (0.6) 浅 (100) 1.5%

8.3 订单簿管理

订单簿管理,说白了就是你怎么在盘口上摆单子。是只摆一档?还是摆多档?是挂限价单还是用市价单?

我个人的经验是:结构化产品做市,最好用「分层报价」策略。什么意思呢?就是在买一、买二、买三,以及卖一、卖二、卖三都挂上单子。这样既能提供流动性,又能捕捉到不同价格区间的订单。

举个例子,假设某结构化产品的理论价格是100元:

  • 卖三:100.6元,挂5手
  • 卖二:100.4元,挂10手
  • 卖一:100.2元,挂15手
  • 买一:99.8元,挂15手
  • 买二:99.6元,挂10手
  • 买三:99.4元,挂5手

这样做的目的是:如果市场突然上涨,你的卖一被吃掉,卖二、卖三还能继续提供流动性,同时给你时间重新调整报价。我曾经见过只用一档报价的团队,市场一波动,单子瞬间被吃光,然后就没有然后了。

小技巧:订单簿的层数和每层的手数,可以根据你的风险偏好来调。保守型就多摆几层,每层少一点;激进型就少摆几层,每层多一点。

订单簿管理还有一个关键点:撤单和重报。你报出去的单子,如果市场变了,必须及时撤掉重新报。我一般设置一个「最大驻留时间」,比如500ms。超过这个时间,不管单子有没有成交,都撤掉重报。这样能避免「过时报价」被套利者盯上。

8.4 做市商库存控制

库存控制,这是做市商的风控命门。你库存太多,万一市场暴跌,直接爆仓;库存太少,又赚不到钱。

我记得有一次,我们给一个挂钩中证500的雪球产品做市,那天市场突然跳水,我们的库存从目标值的+5%一下子飙到了+25%。为什么?因为我们的买单一直在成交,卖单却没人接。要不是及时启动了「库存紧急平仓」机制,那天可能就交代了。

库存控制的核心思路就一句话:让库存围绕目标值波动,偏离越大,纠正力度越强

具体怎么做?我一般用三个工具:

  1. 价差偏移:库存偏多时,降低买价、提高卖价,吸引卖出订单
  2. 报价量调整:库存偏多时,减少买单量、增加卖单量
  3. 对冲交易:如果库存偏离太大,直接用期货或期权对冲

下面是一个库存控制的流程图,我用SVG画出来,方便你理解:

库存控制流程图 监控当前库存 库存偏离目标? 继续监控 偏离方向? 偏多 降低买价,提高卖价 偏空 提高买价,降低卖价 更新报价并监控

库存控制还有一个重要概念:库存目标值。这个目标值不是固定的,它取决于你的风险承受能力和市场预期。比如,如果你看好后市,可以把目标库存设为正(偏多);如果你看空,就设为负(偏空)。

重要提醒:库存控制不是越精确越好。我见过有人把库存目标设得非常精确,比如「必须控制在±2%以内」,结果频繁调整报价,反而增加了交易成本。一般来说,±5%到±10%的容忍区间是比较合理的。

最后,说一个我自己的经验:库存控制要和价差调整联动。你不能只调价差不管库存,也不能只调库存不管价差。我一般用一个「综合评分」来决策:

def adjust_quote(inventory_deviation, volatility, depth):
    # 计算价差调整系数
    spread_factor = 1 + 0.5 * abs(inventory_deviation) + 0.3 * volatility
    
    # 计算报价量调整系数
    volume_factor = 1 - 0.8 * inventory_deviation  # 偏多时减少买单
    
    # 综合决策
    if inventory_deviation > 0.1:  # 库存偏多超过10%
        # 降低买价,减少买单量
        bid_price = fair_price * (1 - 0.001 * spread_factor)
        bid_volume = base_volume * max(0, volume_factor)
        # 提高卖价,增加卖单量
        ask_price = fair_price * (1 + 0.001 * spread_factor)
        ask_volume = base_volume * (2 - volume_factor)
    elif inventory_deviation < -0.1:  # 库存偏空超过10%
        # 反向操作
        bid_price = fair_price * (1 + 0.001 * spread_factor)
        bid_volume = base_volume * (2 - volume_factor)
        ask_price = fair_price * (1 - 0.001 * spread_factor)
        ask_volume = base_volume * max(0, volume_factor)
    else:
        # 正常报价
        bid_price = fair_price * (1 - 0.001)
        ask_price = fair_price * (1 + 0.001)
        bid_volume = base_volume
        ask_volume = base_volume
    
    return bid_price, ask_price, bid_volume, ask_volume

嗯,这套逻辑我用了好几年,虽然中间也翻过车,但整体上还是比较稳健的。你拿去用的时候,记得根据你的产品特性和市场环境调参。没有一套参数能通吃所有市场,这是做市商的宿命,也是乐趣所在。


交易系统化学习资料 微信Strategy888888