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%的搜索空间都是浪费的。

那怎么定范围? 我个人习惯三步走:

  1. 基于逻辑:比如均线周期,太短(<5)噪声大,太长(>200)反应慢。先根据交易逻辑砍掉明显不合理的区间。
  2. 基于经验:同类策略的历史最优参数,一般落在什么范围?可以参考。
  3. 基于预搜索:用大步长快速扫一遍,找到“热点区域”,再缩小范围。

小技巧:参数范围最好用“对数尺度”来设定。比如周期参数,从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夜,结果发现最优参数组合的回测曲线是过拟合的。白白浪费了时间。

为什么会这样?因为高维空间里,数据点变得极其稀疏。你搜到的“最优解”,很可能只是恰好在这个稀疏空间里表现好,换个时间段就崩了。

下面这张图,展示了维度增加时搜索空间的变化:

维度灾难:搜索空间随参数数量指数增长 参数数量(维度) 搜索空间大小(对数尺度) 1 2 3 4 5 6 7 10⁰ 10¹ 10² 10³ 10⁴ 10⁵ 指数增长 可行区域(≤3个参数) 灾难区域(≥5个参数)

2.4 怎么对抗维度灾难?

既然维度高了搜不动,那就想办法降维。我常用的方法有几种:

  1. 参数敏感性分析:先固定其他参数,只调一个,看看它对收益的影响有多大。影响小的参数,直接固定,不参与搜索。
  2. 相关性分析:两个参数高度相关?比如快线周期和慢线周期,其实可以合并成一个“周期比”参数。
  3. 随机搜索代替网格搜索:网格搜索在低维空间还行,高维空间里,随机搜索效率高得多。因为网格搜索的采样点分布均匀,但高维空间里大部分区域都是“无效区域”。随机搜索反而更容易找到好点。
  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个,就别穷举了。用随机搜索、先粗后精、敏感性分析来降维。

嗯,这一节就到这里。下一节我们聊聊具体的搜索算法——网格搜索、随机搜索、贝叶斯优化,到底该怎么选。


专注资料整理