8、参数优化方法:网格搜索、随机搜索、贝叶斯优化
做市策略写好了,参数怎么调?
这是每个做市商都会头疼的问题。我见过不少团队,策略逻辑没问题,就是参数没调好,回测结果差一大截。说白了,参数优化就是给策略找最合适的「旋钮位置」。
今天咱们聊聊三种主流方法:网格搜索、随机搜索、贝叶斯优化。每种都有它的脾气,用对了事半功倍。
8.1 网格搜索:简单粗暴,但别滥用
网格搜索是最直观的方法。你把每个参数的可能取值列出来,然后穷举所有组合。
举个例子,假设我们要优化两个参数:
- 订单簿深度:取 5、10、15 层
- 报价更新频率:取 100ms、200ms、500ms
网格搜索会跑 3×3 = 9 次回测,找出最优组合。
优点:实现简单,结果可复现,适合参数少的情况。
缺点:参数一多就爆炸。5 个参数,每个取 10 个值,就是 10 万次回测。
# 网格搜索示例
import itertools
param_grid = {
'depth': [5, 10, 15],
'update_ms': [100, 200, 500]
}
best_score = -float('inf')
best_params = None
for depth, update_ms in itertools.product(param_grid['depth'], param_grid['update_ms']):
score = run_backtest(depth, update_ms)
if score > best_score:
best_score = score
best_params = {'depth': depth, 'update_ms': update_ms}
print(f"最优参数: {best_params}, 得分: {best_score}")
我的经验:网格搜索适合做「粗调」。先用大步长扫一遍,找到大致区域,再缩小范围细调。别一上来就搞精细网格,那是在浪费算力。
8.2 随机搜索:更聪明的穷举
随机搜索的思路很简单:既然穷举太慢,那就随机采样。
你想想看,网格搜索在参数空间里均匀打点,但很多点其实没必要跑。随机搜索在指定范围内随机选参数组合,跑 N 次后取最优。
为什么随机搜索往往比网格搜索好?
- 网格搜索的采样点分布均匀,但维度高时「稀疏」得厉害
- 随机搜索能覆盖更多「有希望」的区域
- 同样的计算次数,随机搜索找到好参数的概率更高
# 随机搜索示例
import random
param_ranges = {
'depth': (5, 20),
'update_ms': (50, 1000)
}
n_iterations = 100
best_score = -float('inf')
best_params = None
for _ in range(n_iterations):
depth = random.randint(*param_ranges['depth'])
update_ms = random.randint(*param_ranges['update_ms'])
score = run_backtest(depth, update_ms)
if score > best_score:
best_score = score
best_params = {'depth': depth, 'update_ms': update_ms}
print(f"最优参数: {best_params}, 得分: {best_score}")
注意:随机搜索的结果不稳定。同样的代码跑两次,可能得到不同结果。我建议多跑几轮,取平均或取最优中的稳定值。
8.3 贝叶斯优化:用历史指导未来
网格搜索和随机搜索都是「盲人摸象」。每次跑完回测,结果就扔了,下次从头再来。
贝叶斯优化不一样。它会记住每次回测的结果,用这些信息指导下一次采样。说白了,它知道哪些区域「有希望」,就多去那里看看。
核心思想:
- 用高斯过程(GP)拟合参数与得分的关系
- 根据拟合结果,计算「采集函数」
- 在采集函数最大的地方采样
- 重复直到收敛
# 贝叶斯优化示例(使用 scikit-optimize)
from skopt import gp_minimize
from skopt.space import Integer
def objective(params):
depth, update_ms = params
return -run_backtest(depth, update_ms) # 最小化负得分
space = [
Integer(5, 20, name='depth'),
Integer(50, 1000, name='update_ms')
]
result = gp_minimize(
objective,
space,
n_calls=50, # 跑 50 次
random_state=42
)
print(f"最优参数: depth={result.x[0]}, update_ms={result.x[1]}")
print(f"最优得分: {-result.fun}")
贝叶斯优化的优势:
- 收敛快,通常 30-50 次就能找到不错的参数
- 适合高维参数空间
- 能处理噪声(回测结果有随机性)
8.4 三种方法对比
| 方法 | 适用场景 | 计算量 | 结果稳定性 | 实现难度 |
|---|---|---|---|---|
| 网格搜索 | 参数少(≤3个),范围明确 | 高(指数增长) | 高 | 低 |
| 随机搜索 | 参数中等(3-6个) | 中(线性增长) | 中 | 低 |
| 贝叶斯优化 | 参数多(≥5个),计算昂贵 | 低(智能采样) | 中高 | 高 |
我的建议:
- 刚开始做回测,先用网格搜索找感觉
- 参数多了,换随机搜索
- 如果一次回测要跑几分钟甚至几小时,果断上贝叶斯优化
8.5 避坑指南
我在项目中遇到过不少坑,分享几个典型的:
- 过拟合:优化出来的参数在回测里表现很好,实盘一塌糊涂。解决办法:用交叉验证,或者留一段数据做验证集。
- 参数相关性:有些参数是相关的,比如订单簿深度和报价频率。单独优化一个,另一个跟着变。建议用贝叶斯优化,它能自动处理相关性。
- 计算资源浪费:我曾经用网格搜索优化 8 个参数,跑了整整一周。后来发现,前 3 个参数就决定了 90% 的性能。先做敏感性分析,找到关键参数再优化。
重要提醒:参数优化不是一劳永逸的。市场环境变了,最优参数也会变。我建议每季度重新优化一次,或者用自适应参数。
8.6 知识体系图
下面这张图展示了三种优化方法的核心逻辑和适用场景:
嗯,三种方法各有千秋。我个人习惯是:先用网格搜索做粗调,再用贝叶斯优化做精调。这样既快又稳。
记住,参数优化不是终点。优化出来的参数要在不同市场环境下验证,才能放心实盘。
交易系统化学习资料 微信Strategy888888