第19章:Optuna实战:安装配置、目标函数与搜索空间
说实话,做量化交易这几年,我调参调得最多的就是基差策略。以前用网格搜索,那叫一个痛苦——参数一多,组合爆炸,跑一宿都出不来结果。后来接触到Optuna,嗯,这玩意儿确实香。今天我就把Optuna这套东西掰开揉碎了讲给你听。
19.1 安装与配置:别踩这些坑
安装Optuna其实很简单,一行命令搞定:
pip install optuna
但我建议你用conda环境,尤其是做回测的时候。我曾经在系统Python里直接装,结果跟numpy版本冲突,折腾了俩小时。后来学乖了,专门建个虚拟环境:
conda create -n optuna_env python=3.9
conda activate optuna_env
pip install optuna pandas numpy matplotlib
这里有个小细节——如果你要用TPE算法(后面会讲),建议装个额外的依赖:
pip install optuna[plotly]
这样可视化效果会好很多。我个人习惯把Optuna跟Jupyter Notebook配合使用,边调边看结果,效率高。
19.2 定义目标函数:核心中的核心
目标函数是什么?说白了,就是你要优化的那个东西。在基差交易里,我们通常优化夏普比率或年化收益率。
来看个实际例子。假设我们有个简单的基差策略,参数就两个:zscore_threshold(开仓阈值)和lookback_window(回看窗口)。目标函数长这样:
import optuna
import numpy as np
def objective(trial):
# 定义参数
zscore_threshold = trial.suggest_float('zscore_threshold', 1.0, 3.0)
lookback_window = trial.suggest_int('lookback_window', 10, 60)
# 模拟回测(这里简化了)
sharpe = backtest_strategy(zscore_threshold, lookback_window)
return sharpe # Optuna默认最大化
你可能会问:为什么返回值是夏普比率而不是收益率?嗯,我在项目里吃过亏——单纯优化收益率,容易过拟合出极端参数。夏普比率考虑了风险调整,结果更稳健。
return -max_drawdown
19.3 定义搜索空间:别太贪心
搜索空间就是参数的范围。Optuna支持好几种类型:
| 参数类型 | 函数 | 适用场景 |
|---|---|---|
| 连续型 | suggest_float | 阈值、比例等 |
| 离散型 | suggest_int | 窗口长度、次数等 |
| 类别型 | suggest_categorical | 策略模式、合约选择等 |
举个例子,我有个策略需要选择是近月还是远月合约:
contract_type = trial.suggest_categorical('contract_type', ['near', 'far'])
这里有个经验之谈——搜索空间别设太大。我曾经把lookback_window设成1到200,结果Optuna花了大量时间在无效区域探索。后来我根据经验缩到10到60,效果反而更好。你想想看,参数范围越合理,找到最优解的速度就越快。
19.4 优化器选择:TPE vs CMA-ES
Optuna内置了好几种优化算法,最常用的就两个:TPE和CMA-ES。
TPE(Tree-structured Parzen Estimator):这是Optuna的默认算法。它的思路是——先随机采样一批参数,然后根据结果好坏,把参数空间分成"好区域"和"差区域",接下来重点在好区域里采样。
我个人特别喜欢TPE,原因很简单:它处理离散参数和条件参数特别顺手。比如你的策略有时候用均线,有时候用布林带,TPE能自动处理这种"如果A则B"的逻辑。
CMA-ES(Covariance Matrix Adaptation Evolution Strategy):这个算法更"暴力"一些。它通过不断调整参数分布的协方差矩阵来逼近最优解。适合连续型参数多、参数之间关联性强的情况。
怎么选?我一般这样判断:
- 参数少于10个,且包含离散参数 → 用TPE
- 参数全是连续型,且超过10个 → 用CMA-ES
- 不确定?先用TPE跑100次看看结果
代码上切换很简单:
# 默认TPE
study = optuna.create_study(direction='maximize')
# 指定CMA-ES
study = optuna.create_study(sampler=optuna.samplers.CmaEsSampler(), direction='maximize')
19.5 可视化结果:一眼看出好坏
跑完优化,光看数字可不行。Optuna的可视化功能很强大,我常用的就这几个:
1. 优化历史图:看看每次迭代的目标值变化
optuna.visualization.plot_optimization_history(study)
2. 参数重要性图:哪个参数对结果影响最大?一目了然
optuna.visualization.plot_param_importances(study)
3. 平行坐标图:看参数组合与目标值的关系
optuna.visualization.plot_parallel_coordinate(study)
我一般先看参数重要性图。如果某个参数重要性接近0,说明这个参数设成啥都差不多,下次优化可以直接固定住,减少搜索维度。
下面这张图展示了Optuna调参的核心流程,我画了个简图帮你理解:
跑完优化后,记得用study.best_params查看最优参数组合:
print("最佳参数:", study.best_params)
print("最佳夏普:", study.best_value)
study对象保存下来,方便以后复盘:joblib.dump(study, 'optuna_study.pkl')
好了,Optuna这套东西其实不复杂。核心就三步:定义目标函数、划好搜索空间、选对优化器。剩下的交给算法去跑就行。你只要记住——参数范围别太贪心,可视化结果多看看,基本不会出大问题。
公众号:蓝海资料掘金营,微信deep3321