1. 项目概述AI驱动的自动化做市商最近在量化交易和DeFi圈子里一个叫“olaxbt/ai-market-maker”的项目引起了我的注意。这名字一看就很有意思它把“AI”和“Market Maker”做市商这两个词直接焊在了一起。简单来说这就是一个试图用人工智能技术来驱动自动化做市策略的开源工具。做市商是金融市场里的“润滑剂”他们通过持续地报出买卖价格为市场提供流动性同时赚取买卖价差。传统上这活儿要么靠经验丰富的人工交易员要么靠基于固定规则的算法。但这个项目想玩点更高级的用AI模型来动态学习市场状态并做出更“聪明”的报价决策。我自己在量化交易和算法开发这块摸爬滚打了十来年从最早的简单均线策略到后来的高频统计套利再到接触机器学习预测价格一路踩坑无数。看到这个项目我第一反应是兴奋因为它戳中了当前算法交易领域的一个核心痛点市场环境瞬息万变基于历史数据回测的静态策略很容易失效。用AI来做动态适应理论上是个非常诱人的方向。但同时我也立刻意识到这里面的水有多深——它绝不是一个简单的“模型训练部署”就能搞定的事情。这个项目适合谁呢我认为有三类人一是对量化交易和做市策略有基本了解想探索AI应用的程序员二是已经有一些传统做市算法经验希望引入机器学习来优化策略的量化研究员三是那些对DeFi去中心化金融和AMM自动化做市商机制感兴趣想构建更智能流动性池的开发者。无论你是哪一类在动手之前都必须对其中涉及的风险、技术栈和核心逻辑有清醒的认识。2. 核心架构与设计思路拆解2.1 传统做市与AI做市的本质区别要理解这个项目我们得先掰扯清楚传统做市商是怎么干的。经典的做市策略比如Avellaneda-Stoikov模型核心是围绕一个“中间价”来动态调整买卖报价。这个中间价可能是标的资产的实际市场价格策略会根据库存风险你手里持有多少该资产、市场波动率、以及你想要达到的预期收益来计算一个最优的买卖价差和报价深度。整个过程是公式驱动的参数如风险厌恶系数、目标库存需要人工设定或根据历史数据校准。而AI做市商其野心在于让机器自己去“悟”。它不再依赖一个预设的、固定的数学模型来描述市场与决策之间的关系。相反它把做市决策在什么价格挂多少单看作一个“状态-动作”的映射问题。AI模型比如深度强化学习模型的输入是“状态”这个状态可能包括当前订单簿的形态买一卖一价格、深度、自身的库存和现金、资产的历史价格序列、市场波动率指标、甚至宏观事件信号等。模型的输出是“动作”即具体的报价指令在买盘挂单的价格和数量在卖盘挂单的价格和数量。为什么说这是本质区别传统模型基于经济学和金融学的理论假设如市场服从某种随机过程而AI模型是数据驱动的它试图从海量的市场交互数据中直接学习到盈利的报价模式甚至可能发现人类未曾总结出的微观结构规律。举个例子传统模型可能无法有效处理“闪电崩盘”或“流动性黑洞”这种极端事件因为其数学模型没有包含这类场景。但一个训练有素的强化学习模型如果在历史数据中“见过”类似模式它可能会学会在特定信号出现时迅速撤单或反向操作从而规避风险。当然这依赖于高质量且丰富的训练数据。2.2 项目可能的技术栈与模块设计虽然项目描述可能比较简略但一个完整的AI做市商系统其架构必然包含以下几个核心模块我们可以据此推断“olaxbt/ai-market-maker”应有的设计数据获取与处理层这是AI的“眼睛”和“耳朵”。它需要实时或准实时地获取市场数据。对于中心化交易所CEX通常通过WebSocket订阅订单簿Order Book和成交Trade数据流。对于去中心化交易所DEX则需要监听区块链事件或者通过节点RPC接口获取池子状态。这一层的关键在于低延迟和高可靠性。数据拿到后需要进行清洗、对齐统一时间戳、并加工成特征Feature比如计算买卖价差、订单簿不平衡度、短期波动率、资金费率如果是永续合约等。这部分代码通常会大量使用asyncioPython异步库和pandas。AI模型层这是系统的“大脑”。最可能采用的模型是深度强化学习DRL特别是基于策略梯度如PPO, SAC或DQN的算法。因为做市是一个连续决策过程强化学习框架状态、动作、奖励与之天然契合。状态State即上述处理后的市场特征向量再加上自身账户状态库存、现金、浮动盈亏。动作Action通常是一个多维连续或离散空间。例如输出两个值相对于中间价的买单价位偏移和卖单价位偏移以及两个挂单数量。更复杂的可能会输出多个档位的报价。奖励Reward这是训练的关键。奖励函数的设计直接决定了AI学习的目标。常见的奖励包括已实现盈亏成交赚取的价差、存货价值变化考虑市价计价、以及惩罚项如库存偏离目标、交易手续费、滑点损失。设计一个稳定、能促进长期盈利的奖励函数是项目最大的挑战之一。策略执行与风控层这是系统的“手”和“刹车”。模型产生动作信号后需要转化为具体的交易所API指令下单、改单、撤单。这一层要处理交易所的限频、订单状态查询、异常报错等。风控模块是生命线必须独立于AI模型运行。它需要实时监控总持仓风险、单边风险敞口、累计亏损、网络延迟、API错误率等。一旦触发风控规则例如一分钟内亏损超过总资金的1%必须能立即暂停策略、平掉所有仓位或切换到保守的备用策略。回测与模拟环境在实盘前所有策略和模型必须在历史数据上进行回测或在模拟交易环境中进行“纸上练兵”。一个高质量的模拟器需要尽可能真实地模拟订单成交机制比如你的限价单何时能成交取决于市场价是否触及你的报价以及你的订单在订单簿中的位置、手续费和滑点。对于强化学习训练通常需要构建一个交互式模拟环境类似OpenAI Gym让AI agent在里面不断地试错学习。注意千万不要以为有了AI模型就万事大吉。在实际开发中数据层和风控层的代码量、复杂度和重要性往往超过模型本身。一个数据延迟或一个API调用异常就可能让再聪明的模型瞬间亏大钱。3. 核心细节解析与实操要点3.1 数据管道的构建低延迟与高可靠数据是AI的粮食粮食不干净、不及时再好的模型也得“生病”。构建数据管道时有几个细节必须死磕连接管理对于WebSocket连接必须实现自动重连和心跳机制。网络抖动是常态你的代码要在连接断开后能无缝重新订阅数据流并且不能重复订阅导致数据错乱。一个常见的做法是使用websockets库配合asyncio在on_error和on_close回调中设置指数退避的重连逻辑。数据对齐与同步如果你同时交易多个相关标的比如BTC/USDT和ETH/USDT或者需要整合不同来源的数据行情数据、链上数据、社交媒体情绪必须保证所有数据的时间戳是同步的。最好使用交易所服务器时间并在本地使用高精度时钟进行校正。处理时建议用一个统一的事件循环event loop来驱动将所有到来的数据打上统一的处理时间戳放入一个线程安全的队列如asyncio.Queue中供下游消费。特征工程的实时代计算一些复杂的特征如用过去1000笔成交计算的波动率如果每次来新数据都从头算开销太大。需要实现滑动窗口的增量计算。例如维护一个固定长度的deque来存储最新价格当新价格到来时弹出旧值加入新值然后快速更新均值和方差从而得到实时波动率。# 一个简单的滑动窗口波动率计算示例非生产代码示意逻辑 import collections import math class RollingVolatility: def __init__(self, window_size): self.window collections.deque(maxlenwindow_size) self.sum 0.0 self.sum_sq 0.0 def update(self, price): if len(self.window) self.window.maxlen: old_price self.window[0] self.sum - old_price self.sum_sq - old_price**2 self.window.append(price) self.sum price self.sum_sq price**2 property def volatility(self): n len(self.window) if n 2: return 0.0 mean self.sum / n variance (self.sum_sq / n) - mean**2 # 年化波动率假设价格是对数收益率 return math.sqrt(variance) * math.sqrt(365*24*60*60) # 假设每秒一个数据点3.2 奖励函数设计引导AI走向盈利奖励函数Reward Function是强化学习模型的“指挥棒”。设计不当AI就会学到一些奇怪甚至有害的行为。在做市场景下奖励通常由以下几部分加权组成即时利润PnL这是最直接的奖励。当挂单成交时计算这笔交易带来的利润卖价-买价* 数量扣除手续费。但只奖励这个AI可能会变得过于激进持有大量单向头寸去“赌”方向违背了做市商提供流动性的初衷。库存风险惩罚为了鼓励AI保持中性持仓需要惩罚其库存的绝对值。例如- inventory_risk_coeff * (current_inventory)^2。平方项意味着库存越大惩罚越重。这个系数需要仔细调校太小了控制不住风险太大了AI会完全不做交易。价差奖励可以鼓励AI在价差大时挂单获取更高利润价差小时谨慎挂单避免被套利。例如将报价价差作为一个正向奖励因子。平滑性惩罚为了防止AI的报价行为过于频繁和跳跃产生大量不必要的撤单和改单消耗手续费和API额度可以对相邻两步动作之间的变化进行惩罚。一个综合的奖励函数可能长这样reward step_pnl - beta * (inventory)^2 alpha * quoted_spread - gamma * |action_t - action_{t-1}|其中beta,alpha,gamma都是需要反复调试的超参数。我的经验是在模拟环境中先用一个简单的奖励函数比如只包含PnL和轻度的库存惩罚让模型跑起来观察其学到的行为模式再逐步引入更复杂的惩罚项来修正其不良行为。这个过程很像驯兽你需要明确地告诉AI什么是你想要的什么是你禁止的。3.3 动作空间与策略网络输出动作空间的设计决定了AI的操作精细度。对于初学者一个简单有效的设计是动作A1买单价位偏移Bid Offset。一个在[-0.001, 0.001]区间连续的值表示买单价 市场中间价 * (1 offset)。动作A2卖单价位偏移Ask Offset。同理卖单价 市场中间价 * (1 offset)。动作A3基础挂单量Base Volume。一个连续值经过转换后成为买卖单的挂单数量。这样策略网络Policy Network的输出层就是3个神经元分别对应这三个动作。在训练时我们通常让网络输出动作的分布参数如高斯分布的均值和方差然后从中采样得到具体动作以鼓励探索。在实盘时则直接取均值作为动作。更复杂的设计可以包括多档报价、动态调整库存目标等但这会急剧增加动作空间的维度和训练难度。建议从最简单的设计开始验证整个pipeline可行后再考虑增加复杂度。4. 实操过程与核心环节实现4.1 搭建本地回测与模拟环境在触碰实盘API之前一个高保真的本地模拟环境是必须的。这里以基于历史订单簿数据通常保存为csv或parquet文件的回测为例说明核心步骤环境初始化模拟环境TradingEnv初始化时加载历史数据重置初始资金和库存为0并设置初始时间点。状态获取step方法在每个step环境根据当前模拟时间从历史数据中切片出一段时间窗口的数据例如过去100条tick数据计算特征向量并拼接上自身的状态现金、库存返回给AI agent作为状态观测。动作执行Agent根据状态给出动作如买偏移-0.0005卖偏移0.0006挂单量0.1 BTC。环境需要模拟这个动作挂单根据当前时刻的市场中间价和偏移量计算出具体的买单价和卖单价生成两张限价单记录放入一个“未成交订单列表”。订单匹配检查下一时刻的市场价格。如果市场价格跌破了你的卖单价即市场买价 你的卖单价则你的卖单成交成交价为你的卖单价。买单同理。成交后更新现金和库存。订单管理通常做市订单是“被动单”如果一段时间比如10秒未成交我们模拟其被撤销。在环境中每个step都需要检查未成交订单的存活时间。计算奖励并进入下一状态根据成交带来的PnL、新的库存情况等按照预设的奖励函数计算奖励。然后模拟时间向前推进一个步长重复步骤2。# 一个极度简化的模拟环境核心逻辑框架 class SimpleMarketMakingEnv: def __init__(self, historical_data, initial_capital10000): self.data historical_data # DataFrame with columns: [timestamp, mid_price, bid_price, ask_price, ...] self.index 0 self.cash initial_capital self.inventory 0.0 # 持有标的资产数量 self.orders [] # 未成交订单列表 def step(self, action): # action: [bid_offset, ask_offset, base_volume] bid_offset, ask_offset, volume action current_mid self.data.iloc[self.index][mid_price] # 1. 取消旧订单模拟 self.orders [] # 2. 挂出新订单 bid_price current_mid * (1 bid_offset) ask_price current_mid * (1 ask_offset) self.orders.append({type: bid, price: bid_price, volume: volume}) self.orders.append({type: ask, price: ask_price, volume: volume}) # 3. 推进到下一个时间点检查成交 self.index 1 next_row self.data.iloc[self.index] next_bid next_row[bid_price] next_ask next_row[ask_price] pnl 0.0 for order in self.orders[:]: # 遍历副本 if order[type] bid and next_ask order[price]: # 买单成交以卖一价成交这里简化用order price self.inventory order[volume] self.cash - order[price] * order[volume] pnl - order[price] * order[volume] # 成本支出 self.orders.remove(order) elif order[type] ask and next_bid order[price]: # 卖单成交 self.inventory - order[volume] self.cash order[price] * order[volume] pnl order[price] * order[volume] # 收入 self.orders.remove(order) # 4. 计算新状态和奖励 # 计算库存市值按当前中间价 inventory_value self.inventory * current_mid total_value self.cash inventory_value # 一个简单的奖励利润减去库存风险 reward pnl - 0.001 * (self.inventory ** 2) # 5. 组装下一个状态特征 # 这里需要从self.data中提取窗口特征例如过去N个mid_price的均值和标准差 window_data self.data.iloc[max(0, self.index-99):self.index1] features self._extract_features(window_data) state np.append(features, [self.cash, self.inventory]) done self.index len(self.data) - 1 info {total_value: total_value, pnl: pnl} return state, reward, done, info def _extract_features(self, window_data): # 实现特征提取例如收益率、波动率、订单簿不平衡度等 prices window_data[mid_price].values returns np.diff(np.log(prices)) vol returns.std() if len(returns) 1 else 0.0 return np.array([vol]) # 简化只返回波动率实操心得在构建模拟环境时成交逻辑的仿真是重中之重也是最容易失真的地方。上述简化逻辑假设只要市场价格触及订单价格就能全部成交这过于理想。更真实的模拟需要考虑订单簿的队列位置你的订单排在同等价格的第几位以及市场冲击。一个改进方法是使用历史订单簿的快照数据你的挂单会“进入”当时的订单簿然后根据后续的成交数据来模拟它是否被吃掉。虽然计算量大但结果可信度会高很多。4.2 模型训练与调参实战假设我们使用Stable-Baselines3这样的强化学习库以PPO算法为例训练流程大致如下环境封装将上面自定义的TradingEnv用gym的接口进行封装使其具有reset()和step(action)方法。策略网络设计通常使用MlpPolicy多层感知机即可。输入维度是状态向量的长度输出是动作的分布。对于连续动作PPO默认使用高斯分布。训练循环import gym from stable_baselines3 import PPO from stable_baselines3.common.env_util import make_vec_env # 创建向量化环境可选用于加速 env make_vec_env(YourTradingEnv, n_envs4) # 定义模型 model PPO( MlpPolicy, env, verbose1, learning_rate3e-4, n_steps2048, # 每次更新前收集多少步数据 batch_size64, n_epochs10, # 每次更新时对数据迭代多少轮 gamma0.99, # 折扣因子接近1表示更看重长期回报 gae_lambda0.95, # 广义优势估计参数 clip_range0.2, # PPO特有的裁剪参数防止更新步长过大 ent_coef0.01, # 熵系数鼓励探索 tensorboard_log./ppo_mm_tensorboard/ ) # 开始训练 model.learn(total_timesteps1_000_000) # 保存模型 model.save(ppo_market_maker)关键超参数解读与调参经验learning_rate学习率太大容易训练不稳定太小则学习慢。可以从3e-4开始尝试这是很多论文的默认值。n_steps和batch_sizen_steps是每次收集多少经验后才进行一次策略更新batch_size是每次更新时从这些经验中采样的小批量大小。n_steps需要足够大以包含一个相对完整的交易情节对于高频做市可能需要几千到几万步。batch_size通常设为32、64或128。gamma折扣因子。在做市任务中因为利润是即时且相对独立的gamma可以设得低一些比如0.9到0.99让模型更关注近期奖励。ent_coef熵系数。这个参数非常重要它控制策略的探索程度。一开始可以设一个较大的值如0.1鼓励AI多尝试不同的报价。随着训练进行可以逐步衰减或手动调小让策略收敛到更确定性的行为。我经常看到新手忽略这个参数导致模型过早陷入局部最优只学会一种固定的、可能无效的报价模式。监控训练使用TensorBoard监控关键指标episode_reward每轮总奖励、policy_loss策略损失、value_loss价值函数损失、entropy策略熵。一个健康的训练曲线应该是奖励总体呈上升趋势并逐渐稳定熵值从高逐渐降低。4.3 实盘部署与系统集成当模型在模拟环境中表现稳定后可以考虑实盘部署。这绝不是简单地把训练好的模型predict一下就行而是一个系统工程。模型服务化将训练好的模型封装成一个独立的服务例如用FastAPI创建一个HTTP服务。主策略程序负责数据获取、风控、执行在需要决策时向这个模型服务发送当前状态并获取动作预测。这样做的好处是解耦方便模型热更新和A/B测试。主策略程序这是一个常驻进程其核心循环如下while True: # 1. 获取最新市场数据 market_data data_feed.get_latest() # 2. 计算特征组装状态向量 state feature_engineer.transform(market_data, account_info) # 3. 调用风控模块检查是否允许交易 if not risk_engine.check(state): cancel_all_orders() continue # 4. 调用模型服务获取动作 action model_client.predict(state) # 5. 将动作转化为具体订单参数 orders action_to_orders(action, market_data) # 6. 执行订单处理撤单、挂单逻辑 exchange_client.execute_orders(orders) # 7. 等待一个周期如100ms time.sleep(0.1)健壮的执行器执行器exchange_client需要处理所有交易所API的复杂性订单类型限价单、市价单、订单生命周期管理部分成交、完全成交、等待中、已撤销、错误重试、频率限制等。一定要为每个订单设置唯一的客户端订单IDclOrdId并在本地维护一个订单状态映射表定期通过查询接口进行对账防止状态不一致。日志与监控所有关键步骤收到数据、状态、预测动作、下单、成交、风控事件都必须有详细的时间戳日志。同时需要有一个仪表盘Dashboard实时展示资金曲线、库存变化、当前挂单、模型预测值、系统延迟等。这是你发现问题和调试系统的眼睛。5. 常见问题与排查技巧实录在实际开发和运行AI做市商的过程中你会遇到无数坑。下面是我总结的一些典型问题及排查思路5.1 模型在模拟环境盈利实盘却亏损这是最常见也最令人沮丧的问题。原因可能有多方面模拟环境失真这是头号嫌疑犯。检查你的模拟器是否忽略了手续费、滑点、网络延迟、订单簿队列位置。解决方案在模拟器中加入这些因素。手续费按交易所标准计算滑点可以建模为成交价在订单价格基础上随机偏移一个小比例网络延迟可以模拟一个随机延迟如10-50ms后再处理订单队列位置需要更精细的订单簿模拟。过拟合模型只是记住了历史数据中的某种巧合模式而非学会了通用的做市逻辑。解决方案1) 使用更长时间段、更多市场状态牛市、熊市、震荡市的数据训练。2) 在训练中引入数据增强比如对价格序列加入微小的随机噪声。3) 使用更简单的模型结构降低过拟合风险。4) 进行样本外测试Out-of-Sample Test用训练时段之后的数据验证。市场状态迁移训练数据的市场微观结构如平均价差、波动率、交易量与实盘时期发生了显著变化。解决方案定期用近期数据重新训练或微调模型。可以设计一个在线学习框架用实盘产生的少量新数据持续更新模型需极其谨慎防止灾难性遗忘。5.2 模型行为极端要么不交易要么单边豪赌奖励函数设计不当如果奖励函数中库存风险惩罚系数beta设置得过大模型会发现什么都不做库存为0的惩罚最小从而选择不挂单。如果beta太小而即时利润奖励又很高模型就会倾向于积累单向头寸去博取方向性收益。解决方案仔细平衡奖励函数的各个部分。可以通过观察训练过程中模型的平均持仓、成交频率等指标来调整。一个技巧是使用课程学习Curriculum Learning先在一个简单的环境如低波动、无风险惩罚中让模型学会基本的挂单行为再逐步增加环境难度提高波动率、加入风险惩罚。探索不足策略熵太低模型过早收敛到一个次优策略。解决方案提高ent_coef或者使用像SAC这类自带熵最大化的算法。也可以在动作输出层加入探索噪声。5.3 系统运行时出现延迟或数据不同步数据管道阻塞如果数据处理特别是特征计算太慢会导致状态信息滞后模型基于过时信息做决策。解决方案性能剖析Profiling。使用cProfile或line_profiler找到代码热点。将耗时的特征计算尽可能向量化或移到更快的语言如Cython中实现。确保异步IO使用得当避免阻塞事件循环。网络或API延迟从交易所获取数据或发送订单存在不可控延迟。解决方案1) 选择低延迟的交易所API连接点通常有多个地域端点。2) 使用UDP或专线如果交易所支持且成本允许。3) 在策略逻辑中考虑延迟补偿例如使用预测的下一个时刻状态而不是当前状态。4) 实施超时和重试机制但要有熔断策略避免在异常情况下疯狂重试。5.4 风控模块如何有效设计风控不能只靠AI模型的“自觉”必须有硬性规则。多层次风控仓位风控设置单品种最大净头寸、总仓位上限。亏损风控设置单日最大亏损、连续亏损次数、回撤幅度。一旦触发立即停止策略。性能风控监控策略的夏普比率、成交成功率等指标如果持续低于阈值可能说明市场环境已变或模型失效。技术风控监控心跳包延迟、API错误率、订单成交率。如果订单长时间不成交或大量被拒可能网络或账户有问题。独立进程风控模块最好运行在一个独立的进程或线程中定期如每秒检查风险指标。它应该有权限直接调用交易所API进行平仓或撤单而不受主策略进程是否卡死的影响。定期压力测试用历史极端行情如暴跌、暴涨、流动性枯竭数据回测看你的风控规则能否在关键时刻生效。开发AI做市商是一个充满挑战但也极具成就感的工程。它要求你不仅是机器学习专家还得是系统架构师、低延迟程序员和风险管理师。这个项目olaxbt/ai-market-maker提供了一个绝佳的起点和框架思路但真正的价值在于你如何根据自己对市场的理解去填充每一个细节打磨每一个模块。记住在实盘中生存永远比盈利优先级更高。从小资金、低频率开始充分测试逐步迭代才是通往稳健之路的不二法门。