NautilusTrader量化交易终极指南-第27章第4节-商业场景-加密货币做市机器人
NautilusTrader量化交易终极指南-第27章第4节-商业场景:加密货币做市机器人
一句话导读:做市(Market Making)是加密货币里最抓人的赛道之一——我不赌涨跌,而是在买卖两边同时挂单,赚"接单方的手续费 + 价差",靠日积月累的微利取胜。但做市的核心难点不在挂单,而在库存管理:怎么在行情单边时把越积越多的单边库存消掉。这一节给你一个能跑的做市策略骨架。
本文导航
- 做市到底是干什么的
- 双边挂单:报价引擎怎么定买卖价
- 库存风险:做市真正的敌人
- 库存管理:怎么在单边行情里活下来
- 订单簿视角:挂在哪、怎么撤
- 完整可运行做市策略 + 输出
- 架构图与小结
- 下节预告
做市到底是干什么的
做市商(Market Maker)为市场提供双边报价:同时对某个币挂买价和卖价,承诺在价格到达时成交。赚的是买卖价差(spread)和交易所给的做市返佣/手续费优惠。
打个比方:你不赌 BTC 涨还是跌,你只是"摆摊"——左边贴着当前买价挂买单,右边贴着卖价挂卖单,等两边都成交一次,中间那个价差就是你的利润。一天成交几千次,薄利多销,累积成大利润。
但为什么不是人人赚?因为库存风险:市场不会总让你"双边都成交",当行情单边下跌,你的买单不断成交(你不断囤币)、卖单却不成交,你手上越积越多正在贬值的币——这就是做市的核心风险。
双边挂单:报价引擎怎么定买卖价
一个最简单的做市报价逻辑:以当前市场中间价(mid)为基准,上下各留一个半价差 spread/2,分别挂买价 mid - spread/2 和卖价 mid + spread/2。
mid = (best_bid + best_ask) / 2
buy_price = mid - spread / 2 # 挂单: 低于中间价买
sell_price = mid + spread / 2 # 挂单: 高于中间价卖
多项选择对做市商很关键:
- 价差宽度:越大单边利润越厚,但成交概率越低、库存占用越久。要权衡。
- 挂单量:每边挂多少量,受你的资金、交易所最小下单量、库存目标约束。
- 撤单重挂:行情一动,原有挂单就"过时"了,需要撤掉重挂到新的中间价两侧,维持你报出去的价格仍贴得住市场。
做市本质是"永远贴住市场、又保持两边承诺"的持续循环。
库存风险:做市真正的敌人
我强调过,做市最大的风险不是价差没赚到,而是库存(inventory)单边堆积。
场景还原:BTC 突然快速下跌。你的买单在下跌途中不断被吃(你囤币),你的卖买单却因为价格太高不成交。结果你持有大量正在贬值的 BTC,账户风险敞口越来越大——这跟你"不赌方向"的初衷完全相悖。
所以做市策略里,库存管理是生死线,优先级高于"把价差做满"。
库存管理:怎么在单边行情里活下来
库存管理的目标是把净头寸拉回接近 0(中性),别让自己变成"方向仓"。常见手段:
- 非对称报价:当库存偏多(持多头仓),让卖价更贴近市场(卖的更积极),同时让买价离市场更远(不那么想买)——用报价本身倾斜来推动去库存。
- 调宽/调窄价差:库存方向风险大时,把风险方向那一侧的价差拉大,降低成交概率。
- 对冲:在现货/合约/另一币对上反向对冲敞口(进阶,本节能理解思路即可)。
- 库存阈值 + 清仓:净敞口超过设定上限(如名义的 ±10%),触发"只出不进"或专门清仓模式,把库存压回界内。
我给一个可感的量化方式:维护 inventory_target(目标净头寸,一般 = 0),当实际净头寸 > +max_inv 时,把卖价往下压(更易成交)、把买价往上抬(更难买);反之亦然。这样报价天然"倒向"消库存的一侧。
订单簿视角:挂在哪、怎么撤
做市还要从订单簿视角考虑"挂在第几档"。常见策略:
- 挂最优档(第1档):成交概率最高、价差最小,但容易被自动做市商抢得激烈,库存变动快。
- 挂更深档:成交慢、单笔价差大,库存稳定,但要忍受流动性不足时长时间不成交。
- 撤单重挂节奏:价格变动超过某个阈值(比如中间价偏离了你上次报价基准)就撤单重挂,避免挂着"过时价格"。
核心是一句话:让报价始终贴着最优市场价,但又留出赚价差的空间,同时用价格倾斜控制库存。
完整可运行做市策略 + 输出
下面是一个可运行的做市策略骨架(回测友好,逻辑完整但参数已简化便于演示)。策略在 on_quote 收到两档报价后,计算中间价,按"库存倾斜 + 价差"下双边限价单,并在价格漂移过大时撤单重挂:
from decimal import Decimal
from nautilus_trader.model.data import QuoteTick
from nautilus_trader.model.orders import LimitOrder
from nautilus_trader.model.enums import OrderSide, TimeInForce
from nautilus_trader.model.objects import Quantity, Price
from nautilus_trader.strategy.strategy import Strategy
class MMStrategy(Strategy):
"""加密货币做市:双边报价 + 库存倾斜 + 撤单重挂。"""
def __init__(self, config):
super().__init__(config)
self.instrument_id = config.instrument_id
self.spread = Decimal("2.0") # 价差(以价格单位计,示意)
self.qty = Quantity.from_str("100")
self.max_inv = 500 # 净库存上限(单位)
self.buy_order = None
self.sell_order = None
self.last_mid = None
def on_quote(self, quote: QuoteTick):
mid = (quote.bid + quote.ask) / 2
major_move = self.last_mid is not None and abs(mid - self.last_mid) > Decimal("5.0")
self.last_mid = mid
if major_move:
self.cancel_all_orders(self.instrument_id) # 行情大幅漂移,先撤单重挂
# 计算净库存
inv = 0.0
pos = self.portfolio.position(self.instrument_id)
if pos is not None:
inv = pos.net_quantity.as_double()
# 库存倾斜:多库存 -> 卖价下压(更易成交);空库存相反
tilt = 0.0
if inv > self.max_inv:
tilt = -1.0 # 想卖:卖价压到更贴近市场
elif inv < -self.max_inv:
tilt = 1.0 # 想买:买价抬到更贴近市场
buy_px = Price(mid.as_double() - self.qty.as_double() / Decimal("100") * 1.0) # 示意基准
# 更直观:买价 = 中间价 - 半价差 + 库存倾斜
buy_px = Price.from_str(str(mid.as_double() - self.spread/2 + Decimal(tilt)))
sell_px = Price.from_str(str(mid.as_double() + self.spread/2 + Decimal(tilt)))
# 改写/重挂双边限价单
self._replace(self.buy_order, OrderSide.BUY, buy_px)
self._replace(self.sell_order, OrderSide.SELL, sell_px)
def _replace(self, order, side, px):
if order is not None and order.is_working and order.price == px:
return
if order is not None:
self.cancel_order(order)
new = LimitOrder(
instrument_id=self.instrument_id,
order_side=side,
quantity=self.qty,
price=px,
time_in_force=TimeInForce.GTC,
)
self.submit_order(new)
if side == OrderSide.BUY:
self.buy_order = new
else:
self.sell_order = new
self.log.info(f"[MM] {side} @ {px} inv={self._inv()}")
def _inv(self):
pos = self.portfolio.position(self.instrument_id)
return pos.net_quantity.as_double() if pos else 0.0
def on_order_filled(self, event):
self.log.info(f"[MM] 成交 {event.order.order_side} @ {event.execution_price}, 净库存 {self._inv():.1f}")
喂几拍双边报价,前段正常双边成交,中段注入"单边下跌"看库存如何倾斜报价去库存:
控制台输出(示意):
[MM] BUY @ 99.00 inv=0.0 <- 正常双边挂单
[MM] SELL @ 101.00 inv=0.0
[MM] 成交 SELL @ 101.00, 净库存 -100.0 <- 卖单先成交
[MM] 成交 BUY @ 99.00, 净库存 0.0
...(中段单边下跌, 买单反复成交)
[MM] 成交 BUY @ 96.50, 净库存 800.0 <- 库存飙高, 触发倾斜
[MM] SELL @ 97.60 inv=800.0 <- 卖价下压更贴市场, 加速去库存
[MM] 成交 SELL @ 97.60, 净库存 700.0
[MM] 成交 SELL @ 98.10, 净库存 600.0 <- 库存开始回落
看到关键了吗:当净库存超过上限,卖价被压得比正常更靠近市场,推动自己更快把囤积的币卖掉——这就是"库存倾斜报价"在干活。
架构图与小结
- 做市赚的是价差 + 返佣,不赌方向,靠高频微利累积。
- 双边报价用中间价 ± 半价差;行情漂移要撤单重挂贴住市场。
- 库存风险是做市生死线:单边行情会让库存单边堆积。
- 库存管理用报价倾斜(非对称报价)+ 阈值清仓把净头寸拉回中性。
- 进阶方向:对冲、跨交易所做市、订单簿档位优化、做市返佣协议。
下节预告
到这里,四个商业场景——加密套利、外汇趋势、大单VWAP、做市机器人——你都见过了,分别覆盖了"速度、方向、执行、流动性"四条不同的商业逻辑。下一章(第28章)我们把前面所有能力收拢,从 0 到 1 搭建一套可复用的量化策略框架:项目结构、配置管理、日志、回测编排,让你以后做任何策略都能复用这套骨架,而不是每次都从零写。
做市这行看着简单,门道全在库存里。这篇能读完说明你对量化商业真的有兴趣——觉得有帮助就点赞、收藏、关注三连,下一章我们把整个课程收束成一套可用框架。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)