2. 参数优化基础:参数优化的目标、过拟合与欠拟合、优化方法论

好,咱们进入第二章。这一章,说白了就是给策略「调参」打地基。

很多人一上来就写循环、跑网格,结果跑出来一堆漂亮曲线,实盘一跑就崩。为什么?因为没搞懂参数优化的本质。我个人习惯,在动手写代码之前,先花半小时想清楚三个问题:我要优化什么?怎么避免优化过头?用什么方法找最优解?

2.1 参数优化的目标:不只是找最高收益

参数优化,目标到底是什么?很多人第一反应是「让夏普比率最大」或者「让年化收益最高」。嗯,这没错,但太片面了。

我在项目中遇到过这样一个案例:一个价差回归策略,把参数调到极致,回测年化收益80%,夏普3.5。结果实盘第一个月就亏了12%。为什么?因为那个最优参数恰好拟合了历史数据中的某段特殊行情。

所以,参数优化的真正目标应该是:找到一组参数,让策略在「未来未知数据」上表现稳定,而不是在「过去已知数据」上刷出高分。

核心目标三要素:

  • 稳健性:参数微调时,绩效不会剧烈波动
  • 泛化能力:在不同市场环境下都能有效
  • 可解释性:参数值背后有逻辑支撑,不是纯统计巧合

你想想看,如果一组参数只在某一年有效,换一年就失效,那它跟「蒙对」有什么区别?

2.2 过拟合与欠拟合:两个极端都要防

这两个概念,搞机器学习的同学应该很熟。但在量化交易里,它们有更具体的表现。

2.2.1 过拟合(Overfitting)

说白了就是「记性太好」。策略把历史数据里的噪声都记住了,换到新数据就懵了。

典型症状:

  • 回测曲线完美向上,几乎不回撤
  • 参数稍微变一点,绩效就大幅下滑
  • 不同时间段回测结果差异巨大

避坑指南:我曾经犯过一个低级错误——用500个参数组合跑网格搜索,选出了夏普最高的那组。结果发现那个参数组合在样本外测试中直接腰斩。后来我才意识到,参数越多,过拟合的风险就越大。这就是所谓的「维数灾难」。

2.2.2 欠拟合(Underfitting)

欠拟合就是「记性太差」。策略连历史数据里的基本规律都没学到。

典型症状:

  • 回测收益接近零,甚至跑不赢基准
  • 参数怎么调,绩效都差不多
  • 策略逻辑本身可能就有问题

怎么平衡?我个人习惯用「样本内 vs 样本外」测试。把数据分成两段:前70%用来找参数,后30%用来验证。如果样本外表现和样本内差不多,说明参数靠谱。如果样本外差很多,那八成是过拟合了。

2.3 优化方法论:三种主流方法

好,目标明确了,陷阱也知道了。那具体怎么找参数?有三种主流方法:网格搜索、随机搜索、贝叶斯优化。我一个个说。

2.3.1 网格搜索(Grid Search)

这是最基础的方法。说白了就是「穷举」——把所有可能的参数组合都试一遍。

# 网格搜索示例:优化价差回归策略的入场阈值和止损阈值
import numpy as np
import itertools

# 定义参数范围
entry_thresholds = np.arange(1.5, 3.5, 0.5)   # 入场阈值:1.5, 2.0, 2.5, 3.0
stop_loss_thresholds = np.arange(0.5, 2.0, 0.5)  # 止损阈值:0.5, 1.0, 1.5

best_sharpe = -np.inf
best_params = None

for entry, stop in itertools.product(entry_thresholds, stop_loss_thresholds):
    # 假设 run_backtest 是回测函数
    sharpe = run_backtest(entry_threshold=entry, stop_loss=stop)
    if sharpe > best_sharpe:
        best_sharpe = sharpe
        best_params = (entry, stop)

print(f"最优参数:入场阈值={best_params[0]}, 止损阈值={best_params[1]}")
print(f"最优夏普比率:{best_sharpe:.2f}")

优点:简单、直观、一定能找到网格内的最优解。
缺点:计算量随参数数量指数增长。3个参数各10个值,就是1000次回测。5个参数就是10万次。

我的经验:网格搜索适合参数少(2-3个)、范围明确的情况。参数多了,别用,跑死你。

2.3.2 随机搜索(Random Search)

随机搜索不穷举,而是在参数空间里随机采样。听起来不靠谱?其实效果往往比网格搜索好。

# 随机搜索示例
import random

n_iterations = 100  # 采样100次
best_sharpe = -np.inf
best_params = None

for _ in range(n_iterations):
    entry = random.uniform(1.5, 3.5)   # 连续值,随机采样
    stop = random.uniform(0.5, 2.0)
    sharpe = run_backtest(entry_threshold=entry, stop_loss=stop)
    if sharpe > best_sharpe:
        best_sharpe = sharpe
        best_params = (entry, stop)

print(f"随机搜索最优参数:入场阈值={best_params[0]:.2f}, 止损阈值={best_params[1]:.2f}")

为什么随机搜索有时比网格搜索好?因为网格搜索在「不重要的维度」上浪费了大量计算。而随机搜索能更均匀地覆盖整个参数空间。我做过对比:同样是100次回测,随机搜索找到的最优解往往比网格搜索更好。

2.3.3 贝叶斯优化(Bayesian Optimization)

这是目前最先进的方法。它不盲目搜索,而是「边试边学」——根据已有的结果,推测哪里最有可能找到最优参数。

# 贝叶斯优化示例(使用 scikit-optimize 库)
from skopt import gp_minimize
from skopt.space import Real

# 定义目标函数(要最小化的函数,所以取负夏普)
def objective(params):
    entry, stop = params
    sharpe = run_backtest(entry_threshold=entry, stop_loss=stop)
    return -sharpe  # 贝叶斯优化默认求最小值

# 定义参数空间
space = [Real(1.5, 3.5, name='entry_threshold'),
         Real(0.5, 2.0, name='stop_loss')]

# 执行贝叶斯优化
result = gp_minimize(objective, space, n_calls=50, random_state=42)

best_entry = result.x[0]
best_stop = result.x[1]
best_sharpe = -result.fun

print(f"贝叶斯优化最优参数:入场阈值={best_entry:.2f}, 止损阈值={best_stop:.2f}")
print(f"最优夏普比率:{best_sharpe:.2f}")

核心思想:贝叶斯优化用高斯过程(Gaussian Process)来建模「参数 → 绩效」的关系。每次采样后,模型会更新,然后选择「最有潜力」的点去试。说白了,它知道哪里可能有好东西,就重点去挖那里。

三种方法对比:

方法 适用场景 计算量 找到最优解的概率
网格搜索 参数少(≤3个),范围明确 高(指数增长) 高(在网格内)
随机搜索 参数中等(3-6个) 中(线性增长) 中高
贝叶斯优化 参数多(≥5个),计算昂贵 低(智能采样) 高(理论上)

2.4 本章知识体系总览

说了这么多,我画张图帮你理清思路。这张图展示了参数优化的完整逻辑链条:从目标出发,避开过拟合和欠拟合的坑,再根据实际情况选择优化方法。

参数优化知识体系 优化目标 稳健性 泛化能力 可解释性 过拟合(记性太好) 欠拟合(记性太差) 优化方法论 网格搜索 随机搜索 贝叶斯优化

嗯,这张图把本章的核心逻辑串起来了。从上到下看:先明确目标,再警惕陷阱,最后选择合适的方法。每一步都踩实了,参数优化才不会变成「瞎猫碰死耗子」。

实战建议:我个人习惯先用随机搜索跑一轮,找到大概的「好区域」,再用贝叶斯优化精调。这样既省时间,又不容易陷入局部最优。你可以试试看。


公众号:蓝海资料掘金营,微信deep3321