AI策略参数寻优方法:网格搜索和并行模块M.tune
由bqtnziby创建,最终由bqtnziby 被浏览 6 用户
在量化策略当中,策略逻辑当然是排在第一位的。但是,在一个策略已经表现较好时,对参数进行调整,往往能收获更多的超额收益。本文以模板策略中的可视化AI策略为例,进行参数寻优过程,看能为绩效结果提高带来什么效果。
一、策略核心逻辑回顾
模板中的可视化AI策略的核心逻辑如下:
- 因子筛选层:从全市场股票中提取价值类(PE_TTM、PB、PS_TTM)、动量类(momentum_5、reversal_5)、波动类(volatility_5)、换手类(turn)等12个核心因子,同时通过st_status=0(剔除ST股)和list_days>260(上市满1年)进行基础过滤,确保股票池的有效性。
- 标签标注层:以“未来5日涨跌幅”为预测目标,通过m_lead(close,5)/m_lead(open,1)计算_future_return,并采用分位数裁剪(去极值)、等频分箱(c_cbins)生成排序标签,为模型训练提供监督信号。
- 模型排序层:使用StockRanker树模型(排序算法),以2021-2024年数据为训练集、2025年数据为预测集,输出股票的“预期收益排序分数”,分数越高代表未来收益潜力越强。
- 交易执行层:按排序分数选取前N只股票,每M个交易日进行一次调仓,采用等权仓位分配方式,通过BigTrader交易引擎完成买卖执行与绩效跟踪。
训练期选择的是2021年1月1日-2024年12月31日,在这里经过StockRanker之后,我们可以得到以下的绩效图:
经过多次参数的调整之后,我们发现绩效结果变化不大,说明策略大概率是有效的。那么,我们现在进行参数寻优,以求更好的绩效结果。
二、参数寻优
参数寻优并非盲目试错,而是“策略模块-可调参数-绩效目标”的精准匹配。以下从四个核心环节拆解体系化寻优逻辑:
1.参数空间选择
参数空间的设计需遵循“抓主要矛盾”原则,优先选择对策略绩效影响最直接的变量。结合上述策略框架,参数空间与策略模块的映射关系如下表所示:
| 策略模块 | 优化参数 | 参数含义与意义 |
|---|---|---|
| 模型排序 | number_of_trees | StockRanker树数量,控制模型拟合能力 |
| 模型排序 | number_of_leaves | 单棵树叶子节点数,平衡模型复杂度与泛化性 |
| 持仓管理 | hold_count | 单次持仓股票数量,影响分散度 |
| 交易调仓 | rebalance_days | 调仓周期(交易日),平衡交易成本与机会成本 |
上面四个参数即为我们此次参数寻优调整的对象,以下我们用持股数量和调仓周期两个参数进行演示:
2.参数注入
我们将需要调整的参数注入原策略逻辑中,我们使用run_backtest_with_params函数来连接参数和策略,其核心作用是将参数动态注入对应模块,驱动策略完整运行。
代码中用例如下:
2.1模型层面
将number_of_trees和number_of_leaves传入M.stockranker.v9,控制树模型的复杂度:
m5 = M.stockranker.v9(
train_data=m3.data,
predict_data=m4.data,
learning_algorithm="排序",
number_of_leaves=params['number_of_leaves'],
min_docs_per_leaf=1000,
number_of_trees=params['number_of_trees'],
learning_rate=0.1,
max_bins=1023,
feature_fraction=1,
data_row_fraction=1,
sort_by="date,instrument",
plot_charts=False,
ndcg_discount_base=1
)
2.2持仓层面
hold_count决定排序后选取的股票数量,直接影响持仓分散度:
m6 = M.score_to_position.v4(
input_1=m5.predictions,
score_field="score DESC",
hold_count=params['hold_count'],
position_expr="1 AS position",
total_position=1,
extract_data=True
)
2.3交易层面
rebalance_days控制调仓频率,需与策略的因子有效性周期匹配:
m7 = M.bigtrader.v47(
data=m6.data,
start_date="",
end_date="",
.................
handle_order=m7_handle_order_bigquant_run,
after_trading=m7_after_trading_bigquant_run,
capital_base=1000000,
frequency="daily",
product_type="股票",
rebalance_period_type="交易日",
rebalance_period_days=str(params['rebalance_days']),#此处注入rebalance_day
............
3.网格搜索执行
网格搜索算法优势在于“穷举性”——确保在给定参数空间内不遗漏最优组合。parameter_optimization函数的核心执行逻辑如下:
- 组合生成:通过itertools.product对参数空间进行笛卡尔积运算,生成所有可能的参数组合。例如本文参数空间共生成1×1×3×2=6组组合。
- 结果缓存:加载历史结果文件(optimization_results.csv),跳过已测试的参数组合,避免重复计算,尤其适用于回测耗时较长的场景。
- 遍历测试:对每组参数调用run_backtest_with_params,实时将参数与绩效结果合并保存至CSV文件,防止程序中断导致数据丢失。
def parameter_optimization():
"""参数平原寻优主函数"""
# 生成所有参数组合
param_names = list(param_grid.keys())
param_values = list(param_grid.values())
all_combinations = list(product(*param_values))
print(f"总共需要测试 {len(all_combinations)} 组参数组合")
print("=" * 80)
results = []
results_file = 'optimization_results.csv'
# 加载已有结果
if os.path.exists(results_file):
existing_results = pd.read_csv(results_file)
results = existing_results.to_dict('records')
print(f"加载了 {len(results)} 组已有结果")
for i, combination in enumerate(all_combinations):
params = dict(zip(param_names, combination))
# 检查是否已测试过
already_tested = any(
all(result.get(k) == v for k, v in params.items())
for result in results
)
..........
4.如何衡量参数调整结果:多维度评判
单一绩效指标无法全面衡量策略优劣,所以我们构建“多维度加权评分体系”,结合策略目标设计评估逻辑:
# 最终分析
results_df = pd.DataFrame(results)
print("\n" + "=" * 80)
print("寻优完成!结果已保存到 optimization_results.csv")
# 按夏普比率排序
print("\n按夏普比率排序的 Top 5:")
print("-" * 80)
top_5 = results_df.nlargest(5, 'sharpe_ratio')
print(top_5.to_string(index=False))
# 综合评分
results_df['score'] = (results_df['sharpe_ratio'] * 0.5 +
results_df['annual_return'] * 0.3 -
results_df['max_drawdown'].abs() * 0.2)
- 单指标排序:先按夏普比率(风险调整后收益的核心指标)排序,筛选Top5组合,初步锁定风险收益比优异的参数。
- 综合评分公式:考虑到策略的“稳健性优先”目标,设计如下评分公式: results_df['score'] = (results_df['sharpe_ratio'] * 0.5 + results_df['annual_return'] * 0.3 - results_df['max_drawdown'].abs() * 0.2) 权重逻辑:夏普比率(50%)> 年化收益率(30%)> 最大回撤(20%),既重视收益,也强调风险控制。
- 最优参数确定:选取综合评分最高的参数组合,确保策略在收益、风险、风险调整效率之间达到最佳平衡。
5.调整参数后绩效展示
我们为了演示方便,本文只对其中两个参数进行了寻优,一共2*3=6个寻优网格:
param_grid = {
'return_period_long': [90],
'return_period_short': [30],
'rank_threshold_long': [0.1],
'rank_threshold_short': [0.1],
'hold_count': [5, 10, 15],#进行寻优
'rebalance_days': [5, 10],#进行寻优
'number_of_trees': [20],
'number_of_leaves': [30],
}
想要改变其它参数,可以在[ · , · ]中加入不同数值进行寻优,经过上述寻优之后绩效结果如下:
可以看到收益率提升50%左右,并且阿尔法和夏普比率都得到了提高。风险方面,虽然贝塔、收益波动率和最大回撤相应上涨,但是总体表现与原策略差异不大。
三、优化方向
1.优化算法升级:当参数数量增多或候选值范围扩大时,网格搜索的计算成本呈指数级增长,可改用贝叶斯优化(Bayesian Optimization)。其核心优势是基于历史测试结果构建概率模型,智能选择下一个最可能提升绩效的参数组合,大幅减少迭代次数。(贝叶斯优化在知识库精华帖子中有介绍文档)
2.过拟合防控:滚动窗口验证:采用“滚动训练-验证”框架,例如每半年用过去3年数据进行参数优化,再用接下来半年数据验证绩效。若参数在多个滚动窗口中均表现稳定,则说明其泛化能力强,不易受历史数据过拟合影响。(如何用WFA进行滚动参数寻优在知识库中有帖子介绍)
参数寻优代码:
https://bigquant.com/codesharev3/9037a6fd-05ed-47e6-93ad-70866e5787e9
四、使用并行模块tune
正如前文所说,我们进行网格搜索是一个从前往后遍历的过程,一旦参数组合增多,需要遍历的组合会急剧增加。比如我们前文所演示的,仅仅是2*3=6组参数的寻优,但是对于机器学习的内部参数,我们往往需要更多的调试,比如我们如果要调节叶节点数量和学习率这两个参数,叶节点数量从10取到35(步长为5),学习率从0.1取到2(布长为0.1),则就是6*20=120组参数,再加上前面的6组参数组合,一共就是120*6=720组参数。如果此时仍然使用网格搜索的方法,运行的时长是难以想象的。
BIgQuant中有并行模块tune的方法,使用并行计算的方法有效地缩短参数寻优运行的时间,具体参考知识库文章,链接:161-alpha挖掘大杀器——并行模块tune
以下的代码块是这种方法调整所需验证的参数的核心部分,我们在这个演示中设置的参数寻优为:
(1)叶节点数量:从10取到40,步长为5(注意,这里是10,15,20,25,30,35,不包含40!),一共6个候选参数
(2)学习率:从0.1取到2.0.步长为0.1,一共20个候选参数
(3)持股数量:选择了3,5,10,一共3个候选参数
(4)调仓周期:选择1,3,5,,10,一共4个候选参数
一共有6*20*3*4=1440组参数组合
为了演示方便,此处我们一共选择了180组参数进行寻优,用时14min,远远快于网格搜索。(此处有缓存的因素,但从参数组合的数量和用时来讲,效率增加地很明显)
注意:本参数寻优中判断“优”的标准为累积收益率,请务必确认寻优的过程中的训练集、预测集和所需回测过程的训练集、预测集时间相同!
from bigmodule import M
outputs = ["m4", "m6", "m8"]
parameter_tasks = [
{"m5.number_of_leaves": j,
"m5.learning_rate": w/10,
"m8.options": {"stock_count": sc, "hold_days": hd},
"__outputs__": outputs}
for j in range(10, 40, 5)
for w in range(1, 21, 1)
for sc in [3, 5, 10]
for hd in [1, 3, 5, 10]
]
print('并行数量:', len(parameter_tasks))
result = M.tune.run(
name = "hyper_run",
parameters = parameter_tasks,
silent_log=True,
max_retries=2,
workers=5,
remote_run=True)
运行后可在末尾处看到寻优结果(这里的结果不可直接用在不同回测期,不同的回测期都需要重新进行参数寻优确定最优参数):
M.tune模块参数寻优代码如下:
https://bigquant.com/codesharev3/d948d783-ea65-43ad-b4b0-70bf13393434
\