2. 参数空间与搜索维度:参数类型、范围设定与维度灾难
做量化策略,说白了就是在找一组“好参数”。
但参数不是乱找的。你得先搞清楚:你的参数长什么样? 是连续变化的数值,还是只能选几个固定值?范围设多大?参数一多,会不会把电脑跑冒烟?
这一节,我们就来聊聊参数空间的底层逻辑。我个人习惯,在动手写代码之前,先把这些想清楚。不然跑了一周发现方向错了,那才叫崩溃。
2.1 参数类型:三种不同的“变量”
参数类型,我一般分成三类。每种类型,搜索方式完全不同。
| 参数类型 | 特点 | 典型例子 | 搜索方式 |
|---|---|---|---|
| 连续参数 | 在区间内可取任意值 | 移动平均周期(5.0 ~ 50.0) | 网格搜索、随机搜索、贝叶斯优化 |
| 离散参数 | 只能取有限个整数值 | RSI阈值(30, 40, 50, 60, 70) | 枚举、组合搜索 |
| 类别参数 | 无顺序关系的类别 | 出场方式(止损/移动止盈/时间出场) | 穷举、随机组合 |
嗯,这里要注意:连续参数最容易被忽视。很多人习惯用整数步长去搜,比如从5到50,步长5。但真实世界里的最优值,可能落在27.3这种小数上。你想想看,步长设成5,直接跳过了最优解。
核心观点:连续参数不要只搜整数点。先用大步长粗搜,找到“好区域”,再用小步长或随机搜索精搜。
2.2 参数范围设定:宽了还是窄了?
参数范围怎么定?我见过两种极端:
- 范围太小:最优解可能就在边界外,你永远找不到。
- 范围太大:搜索空间爆炸,而且容易过拟合到噪声上。
我在项目中遇到过一件事:一个简单的双均线策略,我把快线周期设成1到200,慢线周期设成5到250。结果跑了一整夜,最后发现最优参数是快线3、慢线8。你想想看,90%的搜索空间都是浪费的。
那怎么定范围? 我个人习惯三步走:
- 基于逻辑:比如均线周期,太短(<5)噪声大,太长(>200)反应慢。先根据交易逻辑砍掉明显不合理的区间。
- 基于经验:同类策略的历史最优参数,一般落在什么范围?可以参考。
- 基于预搜索:用大步长快速扫一遍,找到“热点区域”,再缩小范围。
小技巧:参数范围最好用“对数尺度”来设定。比如周期参数,从10到200,步长用对数增长(10, 20, 40, 80, 160),比线性步长更合理。因为周期从10变到20,和从100变到110,对策略的影响是完全不同的。
2.3 维度灾难:参数一多,世界就变了
这是最要命的问题。什么叫维度灾难?
简单说:参数每增加一个,搜索空间呈指数级增长。
举个例子:
- 1个参数,10个取值 → 10种组合
- 2个参数,10个取值 → 100种组合
- 5个参数,10个取值 → 10万种组合
- 10个参数,10个取值 → 100亿种组合
你想想看,10个参数,每个只取10个值,组合数就已经是100亿了。一台普通电脑,每秒算1000组参数,也得算115天。这还不算回测本身的时间。
警告:参数超过5个,就别想着穷举了。我曾经犯过这个错——一个策略有8个参数,我设了网格搜索,跑了3天3夜,结果发现最优参数组合的回测曲线是过拟合的。白白浪费了时间。
为什么会这样?因为高维空间里,数据点变得极其稀疏。你搜到的“最优解”,很可能只是恰好在这个稀疏空间里表现好,换个时间段就崩了。
下面这张图,展示了维度增加时搜索空间的变化:
2.4 怎么对抗维度灾难?
既然维度高了搜不动,那就想办法降维。我常用的方法有几种:
- 参数敏感性分析:先固定其他参数,只调一个,看看它对收益的影响有多大。影响小的参数,直接固定,不参与搜索。
- 相关性分析:两个参数高度相关?比如快线周期和慢线周期,其实可以合并成一个“周期比”参数。
- 随机搜索代替网格搜索:网格搜索在低维空间还行,高维空间里,随机搜索效率高得多。因为网格搜索的采样点分布均匀,但高维空间里大部分区域都是“无效区域”。随机搜索反而更容易找到好点。
- 先粗后精:先用大步长粗搜,锁定“好区域”,再在局部用小步长精搜。这就像找宝藏,先确定大概位置,再仔细挖。
我的经验:一个策略的参数,最好控制在3-5个以内。超过5个,你就要认真考虑:这些参数真的都有必要吗?还是说,有些参数只是“看起来有用”?
2.5 实战:一个参数空间的代码示例
说了这么多,来点实际的。下面是我常用的参数空间定义方式:
# 参数空间定义示例
param_space = {
'fast_ma': {
'type': 'continuous',
'range': (5, 50),
'step': 1.0,
'description': '快线周期'
},
'slow_ma': {
'type': 'continuous',
'range': (20, 200),
'step': 1.0,
'description': '慢线周期'
},
'rsi_threshold': {
'type': 'discrete',
'values': [30, 40, 50, 60, 70],
'description': 'RSI超买超卖阈值'
},
'exit_strategy': {
'type': 'categorical',
'values': ['stop_loss', 'trailing_stop', 'time_exit'],
'description': '出场方式'
}
}
# 生成随机搜索样本
import random
def generate_random_samples(param_space, n_samples=1000):
samples = []
for _ in range(n_samples):
sample = {}
for name, config in param_space.items():
if config['type'] == 'continuous':
low, high = config['range']
step = config.get('step', 0.1)
# 随机生成连续值,并四舍五入到步长
value = round(random.uniform(low, high) / step) * step
value = max(low, min(high, value))
sample[name] = value
elif config['type'] == 'discrete':
sample[name] = random.choice(config['values'])
elif config['type'] == 'categorical':
sample[name] = random.choice(config['values'])
samples.append(sample)
return samples
# 生成1000组随机参数
random_params = generate_random_samples(param_space, 1000)
print(f"生成了 {len(random_params)} 组随机参数组合")
print("前3组示例:")
for i in range(3):
print(f" 样本{i+1}: {random_params[i]}")
这段代码,我几乎每个项目都会用到。它把参数类型、范围、步长都定义清楚了。随机搜索比网格搜索快得多,尤其是在高维空间里。
一个小建议:连续参数不要用固定的线性步长。比如周期参数,用对数步长更合理。你可以把范围设成(5, 200),步长用1.2倍增长(5, 6, 7.2, 8.6, ...)。这样在低周期区域搜索更密,高周期区域搜索更疏,符合交易逻辑。
2.6 总结一下
参数空间设计,是量化策略优化的第一步,也是最容易被忽视的一步。我见过太多人,上来就写个三层循环开始穷举,结果跑了一周发现方向错了。
记住三点:
- 参数类型要分清:连续、离散、类别,搜索方式完全不同。
- 范围设定要合理:别太大也别太小,基于逻辑和经验来定。
- 维度灾难要警惕:参数超过5个,就别穷举了。用随机搜索、先粗后精、敏感性分析来降维。
嗯,这一节就到这里。下一节我们聊聊具体的搜索算法——网格搜索、随机搜索、贝叶斯优化,到底该怎么选。