第24章 做市商策略优化:参数调优方法
做市商策略写好了,参数怎么设?
说实话,这是我在实战中踩坑最多的地方。策略逻辑再漂亮,参数不对,照样亏钱。今天我就把几种主流的参数调优方法掰开揉碎讲清楚。
24.1 参数调优的核心思路
做市商策略的参数,说白了就是一堆数字。比如报价深度、价差宽度、库存上限、调整频率等等。这些数字设大了不行,设小了也不行。
我个人的习惯是:先理解每个参数的业务含义,再谈优化。你想想看,如果你连参数是干什么的都不知道,调优就是瞎蒙。
核心原则:参数调优不是找最优解,而是找稳健解。市场变了,参数也得跟着变。
24.2 网格搜索与随机搜索
最朴素的方法,就是网格搜索。把所有参数的可能取值列出来,挨个试一遍。
# 网格搜索示例
param_grid = {
'spread_bps': [5, 10, 15, 20],
'depth_lots': [10, 20, 50, 100],
'max_inventory': [100, 200, 500]
}
best_params = None
best_sharpe = -np.inf
for spread in param_grid['spread_bps']:
for depth in param_grid['depth_lots']:
for inv in param_grid['max_inventory']:
sharpe = backtest(spread, depth, inv)
if sharpe > best_sharpe:
best_sharpe = sharpe
best_params = (spread, depth, inv)
但问题来了——参数一多,组合数爆炸。比如上面只有3个参数,每个参数4个值,就有64种组合。如果参数增加到10个,你算算看?
所以我后来改用随机搜索。随机采样,效率高很多。我记得有一次做ETH做市商策略,网格搜索跑了三天,随机搜索半天就找到了差不多的结果。
我的经验:随机搜索在参数空间大时,比网格搜索快10倍以上。而且结果往往不差。
24.3 贝叶斯优化
贝叶斯优化,说白了就是「聪明地试」。它不会盲目搜索,而是根据之前的结果,推测哪里可能更好。
我刚开始接触贝叶斯优化时,觉得这东西太玄乎。后来用了一次,真香。
from skopt import gp_minimize
def objective(params):
spread, depth, inv = params
return -backtest(spread, depth, inv) # 最小化负夏普
res = gp_minimize(
objective,
dimensions=[(5, 20), (10, 100), (100, 500)],
n_calls=50,
random_state=42
)
print(f"最优参数: {res.x}")
print(f"最优夏普: {-res.fun}")
贝叶斯优化的核心是高斯过程。它会建立一个「猜测模型」,告诉你每个参数组合大概能得多少分,以及这个猜测有多不确定。然后它会在「可能高分」和「不确定区域」之间做平衡。
嗯,这里要注意:贝叶斯优化对连续参数效果好,离散参数效果一般。我踩过这个坑。
避坑指南:我曾经用贝叶斯优化调一个离散参数(比如订单簿档位),结果收敛到局部最优。后来改成连续参数+取整,效果才好起来。
24.4 遗传算法参数搜索
遗传算法,模拟生物进化。把参数组合看成「基因」,通过交叉、变异、选择,一代代进化出更好的参数。
我个人觉得,遗传算法适合参数多、关系复杂的场景。比如同时优化10个参数,贝叶斯优化可能吃力,但遗传算法还能打。
# 遗传算法伪代码
population = init_population(100) # 100组参数
for generation in range(50):
fitness = [evaluate(ind) for ind in population]
# 选择:保留前20%
parents = select_top(population, fitness, 0.2)
# 交叉:随机配对
offspring = crossover(parents, rate=0.7)
# 变异:随机扰动
offspring = mutate(offspring, rate=0.1)
# 新种群
population = parents + offspring
我记得有一次做跨交易所套利策略,参数有15个。用网格搜索根本不可能,贝叶斯优化也跑不动。最后用遗传算法,跑了200代,找到了不错的参数组合。
关键点:遗传算法的交叉率和变异率很敏感。我一般设交叉率0.7-0.8,变异率0.05-0.1。太高了容易震荡,太低了容易早熟。
24.5 在线学习与自适应策略
前面几种方法都是离线调优——用历史数据跑一遍,找到好参数,然后固定下来。
但市场是变化的。去年好用的参数,今年可能就废了。所以我们需要在线学习,让策略自己适应市场。
说白了,就是让策略边交易边学习。我常用的方法有两种:
- 滑动窗口重优化:每N笔交易后,用最近的数据重新跑一次贝叶斯优化
- 在线梯度下降:把参数看成可学习的权重,用梯度更新
# 滑动窗口重优化示例
class AdaptiveMarketMaker:
def __init__(self, window_size=1000):
self.window_size = window_size
self.trades = []
self.params = [10, 50, 200] # 初始参数
def on_trade(self, trade):
self.trades.append(trade)
if len(self.trades) >= self.window_size:
# 用最近的数据重新优化
recent_data = self.trades[-self.window_size:]
self.params = bayesian_optimize(recent_data)
print(f"参数已更新: {self.params}")
自适应策略的好处是能跟上市场节奏。但坏处是——如果市场突然剧烈波动,策略可能过度反应。
我曾经遇到过一次:2020年3月12日,比特币暴跌。我的自适应策略看到亏损,疯狂调整参数,结果越调越亏。后来我加了「冷却期」——参数更新后,至少等100笔交易再更新。
血的教训:在线学习一定要加约束。比如参数变化不能超过20%,或者更新频率不能太高。否则策略会「学歪」。
24.6 知识体系总览
下面这张图,是我自己整理的做市商策略参数调优知识体系。你可以把它当成地图,随时回来看看。
24.7 实战建议
说了这么多,到底怎么选?我给出一个简单的决策流程:
- 先用手动调优:理解每个参数的影响,找到大致范围
- 再用贝叶斯优化:在范围内精细搜索,找到候选参数
- 最后加在线学习:让策略自适应,但一定要加约束
我自己的做市商策略,现在就是这套流程。每周跑一次贝叶斯优化,每天用滑动窗口微调。效果嘛,夏普从1.2提到了1.8。
一个小技巧:参数调优时,别只看夏普比率。还要看最大回撤、交易次数、库存周转率。有时候夏普高但回撤大,实盘根本扛不住。
好了,参数调优就讲到这里。记住一句话:没有万能参数,只有不断适应的策略。市场在变,你的参数也得跟着变。