精华帖子

基于随机森林的横截面量化选股策略及 Optuna-TPE 超参数优化研究

由bq5973r5创建,最终由bq5973r5 被浏览 4 用户

一、研究背景与问题提出

传统量化选股策略通常建立在人工构造因子和线性打分模型基础上,例如将价值、成长、质量、动量等因子进行加权求和,再依据得分进行选股。这类方法优点在于逻辑清晰、可解释性强,但也存在明显局限:一方面,不同因子与未来收益之间的关系未必是线性的;另一方面,不同因子之间可能存在复杂交互关系,而线性模型难以有效刻画这类结构。

基于此,本文尝试将横截面选股问题转化为一个监督学习问题,使用随机森林模型,从股票的多维因子特征中学习其未来收益排序规律,并构建一个滚动训练、滚动预测、定期调仓的量化选股策略。在此基础上,进一步引入 Optuna 框架中的 TPE 采样器,对随机森林的关键超参数进行自动搜索,以比较默认参数策略与优化参数策略的表现差异。

本文主要研究三个问题:

第一,如何将股票横截面选股问题形式化为一个机器学习问题; 第二,为什么选择随机森林模型来完成收益排序预测; 第三,Optuna-TPE 的数学原理是什么,以及它如何作用于本策略的超参数优化。

二、策略整体框架

本策略是一个典型的横截面机器学习选股策略。其基本思想是:在每个调仓日,对股票池中所有股票计算一组因子特征,利用历史样本训练一个随机森林模型,预测这些股票未来 20 个交易日的收益排序,然后选出预测得分最高的前 30 只股票进行等权配置,并每 20 个交易日调仓一次。

从数学形式上看,设在交易日 $t$ 的股票池为:

$ \mathcal{U}t={i_1,i_2,\dots,i{N_t}}$

对于每只股票 $i$,构造其在时点 $t$ 的特征向量:

$ \mathbf{x}_{i,t}\in\mathbb{R}^p$

模型学习一个评分函数:

$ \hat{y}{i,t}=f(\mathbf{x}{i,t};\lambda)$

其中 $\lambda$ 表示模型超参数。之后,在时点 $t$ 的横截面上,将所有股票按 $\hat{y}_{i,t}$ 从高到低排序,选取前 $K$ 只股票构成组合:

$ \mathcal{S}t=\operatorname{TopK}\bigl(\hat{y}{i,t}\bigr)$

若 $K=30$,则组合权重采用等权分配:

$ w_{i,t}= \begin{cases} 1/30,& i\in\mathcal{S}_t
0,& \text{otherwise} \end{cases}$

这套结构对应代码中的核心预测与选股逻辑:

scores = model.predict(X_predict)

rank_df = pd.DataFrame({
    'instrument': instruments,
    'score': scores
})

selected = set(rank_df.nlargest(context.stock_num, 'score')['instrument'])
weight = 1.0 / context.stock_num

这里 model.predict(X_predict) 对应数学中的 $\hat{y}_{i,t}$, nlargest(...) 对应横截面排序并选取 TopK。

三、股票池设计

为了减少小市值、低流动性、财务信息缺失严重股票带来的噪音,策略并未采用全市场股票,而是限定在核心宽基成分股范围内,包括:

  • HS300
  • ZZ500
  • ZZ1000

同时施加如下约束:

  • 非 ST 股票
  • 非停牌股票
  • 上市满 365 天以上
  • 估值和价格数据为正且有效

代码中对应的 SQL 条件如下:

AND (is_hs300 = 1 OR is_zz500 = 1 OR is_zz1000 = 1)
AND st_status  = 0
AND suspended  = 0
AND list_days  > 365
AND pe_ttm > 0 AND pb > 0 AND ps_ttm > 0
AND close  > 0 AND amount > 0

这种股票池设计的目的,是在保持样本覆盖广度的同时,提高数据质量和交易可执行性,使模型训练结果更稳定,也更贴近实盘环境。

四、特征工程与因子构造

本策略使用的输入特征覆盖价值、成长、质量、动量、波动与交易活跃度等多个维度,力图从不同视角刻画股票的横截面特征。

设第 $i$ 只股票在时点 $t$ 的特征向量为:

$ \mathbf{x}{i,t}=\bigl(x{i,t}^{(1)},x_{i,t}^{(2)},\dots,x_{i,t}^{(p)}\bigr)$

1. 价值类因子

例如:

$ EP_{i,t}=\frac{1}{PE_{i,t}},\quad BP_{i,t}=\frac{1}{PB_{i,t}},\quad SP_{i,t}=\frac{1}{PS_{i,t}}$

这些因子反映的是股票相对于盈利、净资产和销售收入的估值水平,倒数越高通常意味着估值越便宜。

2. 成长类因子

包括收入和利润的同比增长:

$ RevenueGrowth_{i,t},\quad ProfitGrowth_{i,t}$

这些变量用来刻画企业的扩张能力。

3. 质量类因子

例如:

$ ROE_{i,t},\quad ROA_{i,t},\quad GrossMargin_{i,t},\quad ROIC_{i,t}$

这些因子用于衡量企业盈利能力、资本使用效率与经营质量。

4. 动量类因子

例如 20 日动量定义为:

$ \mathrm{MOM20}{i,t}=\frac{P{i,t}-P_{i,t-20}}{P_{i,t-20}}$

类似地可以定义 60 日和 120 日动量。它们用来描述价格趋势。

5. 波动与交易活跃度因子

例如:

$ \mathrm{Volatility}{i,t}=\frac{High{i,t}-Low_{i,t}}{Close_{i,t}}$

$ \mathrm{AmountRatio20}{i,t}=\frac{Amount{i,t}}{Amount_{i,t-20}}$

$ \mathrm{TurnoverRate}{i,t}=\frac{Amount{i,t}}{FloatMarketCap_{i,t}}$

这些因子反映价格波动、成交额变化和换手活跃程度。

6. 组合衍生因子

例如:

$ ROE_to_PB_{i,t}=\frac{ROE_{i,t}}{PB_{i,t}+0.01}$

$ ROA_to_PE_{i,t}=\frac{ROA_{i,t}}{PE_{i,t}+0.01}$

这类因子本质上是把“盈利质量”和“估值水平”联系起来,增强对优质低估股票的识别能力。

代码中的对应实现如下:

df['momentum_20d']  = (df['close'] - df['close_20d_ago']) / (df['close_20d_ago'] + 1e-10)
df['momentum_60d']  = (df['close'] - df['close_60d_ago']) / (df['close_60d_ago'] + 1e-10)
df['momentum_120d'] = (df['close'] - df['close_120d_ago']) / (df['close_120d_ago'] + 1e-10)
df['volatility_price'] = (df['high'] - df['low']) / (df['close'] + 1e-10)
df['amount_ratio_20d'] = df['amount'] / (df['amount_20d_ago'] + 1)
df['turnover_rate']    = df['amount'] / (df['float_market_cap'] + 1)
df['roe_to_pb']        = df['roe'] / (df['pb'] + 0.01)
df['roa_to_pe']        = df['roa'] / (df['pe_ttm'] + 0.01)
df['log_market_cap']   = np.log(df['float_market_cap'] + 1)

这部分代码对应的是从原始财务与行情字段构造机器学习模型的输入特征。

五、标签设计:为什么预测未来收益排名而不是绝对收益

本策略并没有直接预测未来 20 日收益率本身,而是构造了一个未来收益的横截面排名标签

首先定义未来 20 日收益率:

$ r_{i,t+20}=\frac{P_{i,t+20}-P_{i,t}}{P_{i,t}}$

然后在同一天的股票横截面上做分位排序,得到标签:

$ y_{i,t}=\operatorname{RankPct}\bigl(r_{i,t+20}\bigr)$

也就是说,标签并不是“未来涨几个点”,而是“在同一天所有股票中,这只股票未来表现排在什么位置”。

这样做的原因在于,本策略最终的交易目标并不是预测精确收益率,而是从横截面中选出未来相对更强的股票。因此,用收益排名作为标签,比直接预测绝对收益更贴近策略的最终用途。

代码实现如下:

df_std['future_return'] = (
    (df_std['future_close'] - df_std['close']) / (df_std['close'] + 1e-10)
)
df_std['future_return_rank'] = (
    df_std.groupby('date')['future_return'].rank(pct=True)
)

六、数据预处理:标准化、缺失值处理与极值裁剪

1. 截面标准化

虽然树模型对特征量纲不像线性模型那样敏感,但在量化研究中,对横截面特征做标准化仍然有助于减少不同日期间分布漂移的影响。

对于每个交易日 $t$ 和每个因子 $j$,做如下标准化:

$ z_{i,t}^{(j)}=\frac{x_{i,t}^{(j)}-\mu_t^{(j)}}{\sigma_t^{(j)}+\epsilon}$

其中:

$ \mu_t^{(j)}=\frac{1}{N_t}\sum_{i=1}^{N_t}x_{i,t}^{(j)}$

$ \sigma_t^{(j)}=\sqrt{\frac{1}{N_t}\sum_{i=1}^{N_t}\left(x_{i,t}^{(j)}-\mu_t^{(j)}\right)^2}$

代码对应如下:

for col in context.factor_cols:
    df_std[col] = df_std.groupby('date')[col].transform(
        lambda x: (x - x.mean()) / (x.std(ddof=0) + 1e-10)
    )

2. 缺失值过滤

由于机器学习模型无法直接处理缺失的输入特征,因此策略仅保留因子齐全的样本。

3. 极值裁剪

为了防止异常值影响模型训练,在每个调仓日,对训练集因子使用 1% 和 99% 分位数进行裁剪:

$ x^{\mathrm{clip}}=\min\bigl(\max(x,q_{0.01}),q_{0.99}\bigr)$

代码实现如下:

lo = X_train_df[col].quantile(0.01)
hi = X_train_df[col].quantile(0.99)
X_train_df[col] = X_train_df[col].clip(lo, hi)
X_pred_df[col] = X_pred_df[col].clip(lo, hi)

需要强调的是,这里使用的是训练集分位数去裁剪预测集,而不是反过来,这样做可以避免前视偏差。


七、随机森林模型原理

随机森林是一种集成学习方法,其核心思想是利用大量带随机性的决策树进行回归预测,再将各棵树的结果进行平均,从而提高模型稳健性并降低过拟合风险。

1. 单棵回归树的基本形式

设训练样本为:

$ \mathcal{D}={(\mathbf{x}i,y_i)}{i=1}^{n}$

回归树在每个节点会寻找一个特征 $j$ 和一个切分点 $s$,将样本划分为:

$ R_1(j,s)={\mathbf{x}\mid x^{(j)}\le s}$

$ R_2(j,s)={\mathbf{x}\mid x^{(j)}>s}$

并最小化节点内平方误差:

$ \min_{j,s}\left[\sum_{\mathbf{x}i\in R_1(j,s)}(y_i-c_1)^2+\sum{\mathbf{x}_i\in R_2(j,s)}(y_i-c_2)^2\right]$

其中 c1 与 c2 分别是两个区域内标签的均值。

2. 随机森林的集成形式

如果一共训练 B 棵树,则随机森林的预测结果为:

$ \hat{f}(\mathbf{x})=\frac{1}{B}\sum_{b=1}^{B}T_b(\mathbf{x})$

其中 Tb(x) 表示第 b 棵树的输出。

随机森林通过两种随机性提高泛化能力:

  • 对样本进行 bootstrap 抽样
  • 对每次节点分裂只考虑部分特征

因此,它能够自动学习非线性关系和因子之间的交互结构。

3. 随机森林为何适合本策略

在本策略中,因子和未来收益排名之间可能存在非线性映射。例如,某些因子单独看效果一般,但组合起来可能具有较强预测力。随机森林不要求提前人工设定这种交互形式,能够通过树结构自动发现局部规律,因此非常适合处理多因子横截面排序问题。

代码中对应的模型训练如下:

model = RandomForestRegressor(
    n_estimators=n_estimators,
    max_depth=max_depth,
    min_samples_leaf=min_samples_leaf,
    random_state=42,
    n_jobs=-1,
)
model.fit(X_train, y_train)
scores = model.predict(X_predict)

这里的 RandomForestRegressor(...) 对应数学中的评分函数 $f(\mathbf{x};\lambda)$,其中 $\lambda$ 即随机森林的超参数集合。

八、滚动训练与调仓逻辑

本策略采用滚动窗口训练,即在每个调仓日只使用过去一段时间内的历史样本作为训练集。设训练窗口长度为 240 个交易日,则在调仓日 $t$ 时,训练集对应的日期集合为:

$ \mathcal{T}_{\mathrm{train}}(t)={t-240,t-239,\dots,t-1}$

模型训练完成后,在当前可用日期的股票横截面上进行打分,选出 Top30 股票构成组合,并在未来 20 个交易日持有,之后再重复这一过程。

这种方法的优点在于:

  • 避免使用未来数据
  • 让模型不断适应最新市场结构
  • 符合真实量化研究中的滚动训练模式

代码中相关逻辑如下:

hist_dates = [d for d in context.all_dates if d < current_dt]
train_dates = hist_dates[-context.train_window:]

train_df = context.df_all[
    context.df_all['date'].isin(train_dates) &
    context.df_all['future_return_rank'].notna()
].copy()

以及调仓周期设置:

context.rebalance_period = bigtrader.TradingDaysRebalance(20, context=context)

九、为什么需要超参数优化

随机森林虽然模型结构相对简单,但其预测能力仍高度依赖超参数设置。例如:

  • n_estimators 决定森林中树的数量
  • max_depth 控制单棵树的复杂度
  • min_samples_leaf 影响叶节点最少样本数
  • min_samples_split 影响内部节点继续分裂的条件
  • max_features 控制每次分裂时考虑的特征数

设超参数向量为:

$ x=(n_estimators,\ max_depth,\ min_samples_leaf,\dots)$

则模型的实际表现依赖于 x。我们的优化目标可以写为:

$ x^*=\arg\max_{x\in\mathcal{X}}f(x)$

其中 $f(x)$ 表示给定一组参数后,在验证集上的策略相关指标。在本研究中,$f(x)$ 定义为验证集上的 RankIC

也就是说,我们希望找到一组随机森林参数,使得模型在验证集上的收益排序预测能力最强。

十、RankIC 的定义及其作为优化目标的原因

RankIC 定义为两者秩的 Spearman 相关系数:

$ \operatorname{RankIC}=\rho_s\bigl(\operatorname{rank}(y),\operatorname{rank}(\hat{y})\bigr)$

其经典公式为:

$ \rho_s=1-\frac{6\sum_{i=1}^{n}d_i^2}{n(n^2-1)}$

其中 di表示第 i个样本在真实值和预测值中的秩差。

之所以使用 RankIC,而不是普通回归中的均方误差 MSE,原因在于本策略最终关注的是股票排序能力,而不是预测值与真实收益的数值误差。只要模型能够更准确地把未来表现好的股票排在前面,就能更好地服务于 TopN 选股。

代码对应如下:

def calc_rank_ic(y_true, y_pred):
    ic, _ = spearmanr(y_true, y_pred)
    return ic

十一、Optuna 与 TPE:方法概述

Optuna 是一个超参数优化框架,它并不是单一算法,而是一个可以支持多种采样策略的工具。在本文中,Optuna 使用的是 TPE(Tree-structured Parzen Estimator) 采样器。

严格来说:

  • Optuna 是优化框架
  • TPE 是其中一种采样与搜索方法
  • TPE 属于一种贝叶斯优化式超参数搜索方法

因此,当我们说“使用 Optuna-TPE 做贝叶斯优化”时,更准确的含义是:

使用 Optuna 框架,并采用其中的 TPE 采样器,对模型超参数进行贝叶斯优化式搜索。

十二、Optuna-TPE 的数学原理

这一部分是全文的理论核心。

1. 超参数优化问题的数学形式

设超参数空间为 $\mathcal{X}$,每一组参数记为 $x\in\mathcal{X}$,目标函数为:

$ f(x)$

本文中,$f(x)$ 就是验证集上的 RankIC。因此我们要解决的问题是:

$ x^*=\arg\max_{x\in\mathcal{X}}f(x)$

由于 f(x)无法写成解析表达式,而且每计算一次 f(x) 都需要重新训练一个随机森林模型,因此这是一个高成本黑箱优化问题。

2. 普通贝叶斯优化的思路

在传统贝叶斯优化中,设已经试验过的参数和结果为:

$ \mathcal{D}_t={(x_1,y_1),(x_2,y_2),\dots,(x_t,y_t)}$

其中 yi = f(xi)

传统方法通常尝试直接建模:

$ p(y\mid x)$

也就是在给定参数 x 时,目标值y 的分布。然后再通过 Expected Improvement 等采集函数,决定下一组参数该选什么。

3. TPE 与普通贝叶斯优化的区别

TPE 的关键创新在于:它不直接建模 $p(y\mid x)$,而是转而建模:

$ p(x\mid y)$

也就是说,它不是在问“这组参数会产生怎样的结果”,而是在问“好的结果通常对应怎样的参数”。

这是 TPE 与经典高斯过程贝叶斯优化的最大区别。

4. 好样本分布与坏样本分布

假设已经完成若干轮试验,得到一批历史样本:

$ \mathcal{D}={(x_i,y_i)}_{i=1}^{n}$

接下来,设定一个目标值阈值 $y^*$,把历史试验分成两类:

  • 表现较好的样本
  • 表现一般或较差的样本

于是可以分别定义两个条件密度:

$ l(x)=p(x\mid y<y^*)$

$ g(x)=p(x\mid y\ge y^*)$

在最大化问题中,也可以把它理解为:

  • $l(x)$:高目标值对应的参数分布
  • $g(x)$:低目标值对应的参数分布

你在讲的时候可以直接说:

$l(x)$ 描述的是“好结果区域中的参数长什么样”, $g(x)$ 描述的是“差结果区域中的参数长什么样”。

5. TPE 的采样原则

TPE 在后续搜索时,会优先选择那些让以下比值较大的参数点:

$ \frac{l(x)}{g(x)}$

这意味着:

  • 该参数在“好样本分布”里出现概率高
  • 在“坏样本分布”里出现概率低

因此,这个参数点更像历史上表现好的参数,而不像表现差的参数。

这就是 TPE 的核心直觉:

下一组参数,应该更接近历史成功样本,而远离历史失败样本。

6. TPE 与 Expected Improvement 的关系

贝叶斯优化中最常见的采集函数之一是 Expected Improvement(期望改进)。从理论上看,TPE 的构造可以证明,在一定条件下,最大化 Expected Improvement 与优先选择高 $l(x)/g(x)$ 的区域是一致的。

因此,TPE 并不是一种拍脑袋的启发式方法,而是在贝叶斯优化框架下,对采集函数进行重写和近似后得到的一种高效实现。

你可以概括成一句:

TPE 通过分别建模好样本和坏样本的参数分布,把“寻找高期望改进区域”的问题转化成了“寻找高 $l(x)/g(x)$ 区域”的问题。

7. Parzen Estimator 是什么

TPE 名称中的 Parzen Estimator,指的是一种核密度估计方法。在实践中,TPE 并不会显式写出 $l(x)$ 和 $g(x)$ 的解析式,而是通过已有试验样本对这两个概率密度做估计。

一般可以形式化写成:

$ l(x)\approx\sum_{i\in\mathcal{I}_{\mathrm{good}}}w_iK(x;x_i)$

$ g(x)\approx\sum_{i\in\mathcal{I}_{\mathrm{bad}}}w_iK(x;x_i)$

其中:

  • $K(\cdot)$ 是核函数
  • $x_i$ 是历史试验点
  • $w_i$ 是样本权重

直观理解就是:

  • 历史上表现好的参数,会被聚合成一个“好参数分布”
  • 历史上表现差的参数,会被聚合成一个“差参数分布”
  • 新参数的好坏,由它更像哪种分布来判断

8. 为什么 TPE 适合超参数优化

TPE 的优势在于,它非常适合处理混合型参数空间,包括:

  • 连续变量
  • 整数变量
  • 类别变量
  • 条件变量

而机器学习模型的超参数空间往往正是这种结构。例如:

  • n_estimators 是整数
  • max_depth 是整数
  • max_features 是浮点
  • 某些模型还有条件型参数

相比之下,传统高斯过程贝叶斯优化在高维、混合型参数空间中并不总是方便,而 TPE 更适合 AutoML 和调参场景。

十三、TPE 在本策略中的具体应用

在本策略中,需要优化的随机森林参数可以写为:

$ x=(n_estimators,\ max_depth,\ min_samples_leaf,\ min_samples_split,\ max_features)$

优化目标为:

$ f(x)=\operatorname{RankIC}_{\mathrm{val}}(x)$

因此整个超参数优化问题可以写为:

$ x^*=\arg\max_x\operatorname{RankIC}_{\mathrm{val}}(x)$

Optuna-TPE 的工作流程如下:

第一,先随机或半随机试几组参数; 第二,观察这些参数对应的 RankIC 结果; 第三,把表现好的参数样本和表现差的参数样本分别建模成两种分布; 第四,优先在更像“高 RankIC 区域”的地方继续搜索; 第五,找到当前滚动窗口下表现最好的参数组合,用它重新训练模型并用于当期选股。

十四、Optuna-TPE 在代码中的实现对应

1. 定义目标函数

数学上,目标函数为:

$ f(x)=\operatorname{RankIC}_{\mathrm{val}}(x)$

代码中对应的 objective(trial)

def objective(trial):
    n_estimators = trial.suggest_int("n_estimators", 50, 200)
    max_depth = trial.suggest_int("max_depth", 3, 10)
    min_samples_leaf = trial.suggest_int("min_samples_leaf", 2, 20)

    model = RandomForestRegressor(
        n_estimators=n_estimators,
        max_depth=max_depth,
        min_samples_leaf=min_samples_leaf,
        random_state=random_state,
        n_jobs=-1,
    )
    model.fit(X_tr, y_tr)
    pred = model.predict(X_va)
    score = calc_rank_ic(y_va, pred)
    return score

其中:

  • trial.suggest_int(...) 对应从参数空间中选取一个 $x$
  • score 对应目标函数值 $f(x)$

2. 构造训练集和验证集

在每个滚动训练窗口中,按时间顺序切分:

  • 前 80% 日期作为训练子集
  • 后 20% 日期作为验证集

数学上,若训练窗口日期集合为:

$ \mathcal{T}={t_1,\dots,t_m}$

则分割点为:

$ m_{\mathrm{split}}=\lfloor0.8m\rfloor$

训练子集与验证集分别为:

$ \mathcal{T}{\mathrm{train}}={t_1,\dots,t{m_{\mathrm{split}}}}$

$ \mathcal{T}{\mathrm{valid}}={t{m_{\mathrm{split}}+1},\dots,t_m}$

代码对应如下:

split_idx = int(len(unique_dates) * 0.8)
train_dates_sub = set(unique_dates[:split_idx])
valid_dates_sub = set(unique_dates[split_idx:])

之所以按时间切分而不是随机切分,是为了避免未来信息泄露,保持时序一致性。

3. 使用 TPE 采样器

代码如下:

sampler = optuna.samplers.TPESampler(seed=random_state)
study = optuna.create_study(direction="maximize", sampler=sampler)
study.optimize(objective, n_trials=n_trials, show_progress_bar=False)

这里:

  • TPESampler(...) 指定采用 TPE 作为采样方法
  • direction="maximize" 表示目标是最大化 RankIC
  • study.optimize(...) 表示执行多轮试验,每轮生成一组参数、训练模型并返回目标值

数学上,这对应不断扩展样本集:

$ \mathcal{D}t={(x_i,y_i)}{i=1}^{t}$

并基于历史结果继续搜索新的参数点。

4. 得到最优参数并重新训练

优化完成后,获得:

$ x^*=\arg\max_{x_i}f(x_i)$

代码对应:

best_params = study.best_params
best_value = study.best_value

然后用最优参数在完整训练集上重新训练模型:

model = RandomForestRegressor(
    n_estimators=best_params["n_estimators"],
    max_depth=best_params["max_depth"],
    min_samples_leaf=best_params["min_samples_leaf"],
    random_state=42,
    n_jobs=-1,
)
model.fit(X_train, y_train)

这样做的逻辑是:

  • 先用训练子集 + 验证集做参数选择
  • 再用完整训练窗口重新训练,提高样本利用率

十五、交易执行与组合构建

当模型对当前日期股票池打分后,按分数选出 Top30 股票,构建等权组合。

数学上:

$ \mathcal{S}t=\operatorname{Top30}\bigl(\hat{y}{i,t}\bigr)$

权重为:

$ w_{i,t}= \begin{cases} 1/30,& i\in\mathcal{S}_t
0,& \text{otherwise} \end{cases}$

代码实现如下:

selected = set(rank_df.nlargest(context.stock_num, 'score')['instrument'])
weight = 1.0 / context.stock_num

for sid in list(context.portfolio.positions.keys()):
    if context.portfolio.positions[sid].amount > 0 and sid not in selected:
        context.order_target_percent(sid, 0)

for instrument in selected:
    context.order_target_percent(instrument, weight)

这里先卖出不在目标组合中的持仓,再买入新的目标股票,实现组合更新。

十六、实验结果应如何理解

在比较“默认参数随机森林”和“Optuna-TPE 优化版随机森林”时,通常会关注以下指标:

  • 年化收益
  • 超额收益
  • 夏普比率
  • 最大回撤
  • 胜率
  • 收益回撤比

如果 Optuna-TPE 优化版显著优于默认参数版,说明超参数搜索有效提升了模型的排序能力和策略表现。

但即便优化效果不明显,也并不意味着 TPE 没有价值。更合理的解释可能是:

第一,默认参数本身已经较为稳健; 第二,当前特征体系下随机森林对部分参数不够敏感; 第三,优化效果还取决于搜索空间设计、试验次数和目标函数定义; 第四,超参数优化本质上是提升模型配置效率的工具,而不是保证收益提升的万能方法。

因此,TPE 的价值更多体现在: 在有限试验预算下,更系统、更高效地探索参数空间,并帮助研究者理解模型对参数的敏感性结构。

效果对比:不加入tpe

加入tpe

策略源码https://bigquant.com/square/ai/a60f6c59-1225-09d3-df75-f4ed6a768b2e

标签

随机森林量化选股
{link}