- 运行样例代码
penalized_linear.py
- 确认代码处理了数据标准化、滞后变量、样本分割(训练、验证、测试)。
- 掌握 Lasso、Ridge、Elastic Net 模型的使用和超参数调优过程。
- 思考改进方向,例如:
- 引入其他机器学习模型(随机森林、XGBoost、神经网络等)。
- 使用特征选择方法(如 PCA 或基于特征重要性的选择)减少噪声。
portfolio_analysis.py
:如何基于预测结果构建投资组合。- 确保投资组合的设计符合任务要求(每月 50-100 支股票、定期再平衡)。
- 改进投资组合策略,例如:
- 优化股票选择规则(如权重分配、风险控制)。
- 添加多样化或风控机制(如最大持仓权重限制)。
- 使用 Out-of-Sample R² 评估预测模型性能。
- 评估投资组合表现,计算以下指标:
- 夏普比率、累计收益率、平均收益率、波动率、最大回撤、单月最大损失。
- 绘制关键图表(如累计收益曲线、分位收益比较)。
- penalized_linear.py: the original version provided by the teacher
- penalized_linear-v1.py: the first version, changed in mainly these modules
- 新增模型
- 添加 Random Forest 和 Gradient Boosting 回归模型
- 使用 sklearn.ensemble 中的 RandomForestRegressor 和 GradientBoostingRegressor
- 超参数调优
- 为每个模型设计了超参数搜索流程,通过验证集优化超参数
- 将新模型的预测结果存储到 output.csv,并计算 Out-of-Sample R^2
- 新增模型
- penalized_linear-v2.py: the second version, changed in mainly features
- 使用 RandomForestRegressor 获取特征重要性,选取最重要的 20 个特征
- 对选定特征进行主成分分析(PCA),降维至 10 个主成分
- 将所有模型的输入调整为 PCA 降维后的特征
- penalized_linear-v3.py: the third version, optimize
- 引入 GridSearchCV
- 使用网格搜索优化随机森林的超参数,包括 n_estimators 和 max_depth
- 确保最佳参数通过交叉验证选择
- 引入 BayesSearchCV
- 使用贝叶斯优化对梯度提升模型进行超参数调优
- 优化参数包括 n_estimators、learning_rate 和 max_depth
- 性能提升
- 网格搜索和贝叶斯优化结合更有效率地探索参数空间,确保模型在验证集上表现最佳
- 引入 GridSearchCV
- penalized_linear-v4.py: the fourth version, data process
- 数据平滑处理
- 使用 Savitzky-Golay 滤波器对特征数据进行平滑,以减少噪声干扰
- 交叉验证
- 引入 KFold 交叉验证,用于随机森林和梯度提升模型的超参数优化
- 提高模型的稳定性和泛化性能
- 数据平滑处理
为了观察因子对股票价格的影响显著性以及IC的表现,可以按照以下步骤进行分析并结合已有的代码进行实现:
确保在penalized_linear.py
中提取到所有因子的预测值,生成output.csv
文件。这个文件包含每只股票每个月的预测收益和真实收益。
- 因子名在
factor_char_list.csv
中,可以在penalized_linear.py
中动态加载这些因子用于建模。 - 标准化因子值(例如
rank transform
至[-1, 1]
)以确保数据一致性。
IC是预测值与实际收益的横截面相关系数: 能够帮助判断因子的预测能力,IC绝对值越大说明因子越有效。
根据IC值筛选有效因子:
- 设置阈值(例如
mean_IC > 0.02
),筛选出表现良好的因子。 - 根据IC正负决定因子方向:正相关的因子值越大越好,负相关的因子值越小越好。
利用portfolio_analysis.py
中提供的组合构建框架:
- 将筛选后的因子应用于组合策略。
- 确保满足投资组合中股票数量限制(50到100之间),并在每个月重新平衡。
结合Sharpe Ratio、最大回撤等指标对组合进行评估。
- 非线性模型:在
penalized_linear.py
中引入如XGBoost、Random Forest等非线性模型。 - 多因子组合:对不同因子赋权,利用优化算法选择最优因子权重。
-
数据类型优化:
- 使用
pandas
的astype
方法将数据类型压缩为最小必要类型。例如,将浮点数降级为float32
,将整型降级为int32
。
for col in df.select_dtypes(include=['float']).columns: df[col] = df[col].astype('float32') for col in df.select_dtypes(include=['int']).columns: df[col] = df[col].astype('int32')
- 使用
-
按需加载:
- 使用
chunksize
按块加载数据,仅处理必要的字段或行。
chunks = pd.read_csv("stock_sample.csv", chunksize=100000) for chunk in chunks: process_chunk(chunk) # 自定义数据处理函数
- 使用
-
并行化:
- 使用
joblib
或concurrent.futures
并行处理每月数据。例如,按date
分组后,将每组数据并行处理。
from joblib import Parallel, delayed def process_group(group): # 数据处理逻辑 return processed_group results = Parallel(n_jobs=-1)(delayed(process_group)(group) for _, group in df.groupby("date")) processed_data = pd.concat(results)
- 使用
-
分布式框架:
- 使用
Dask
或PySpark
处理超大数据集。
import dask.dataframe as dd ddf = dd.read_csv("stock_sample.csv") ddf = ddf.groupby("date").apply(process_group, meta={...}) ddf.compute()
- 使用
-
矢量化计算:
- 避免
for
循环,尽量使用numpy
或pandas
的矢量化操作。例如:
# 现有代码逐列 rank,改为矢量化 ranks = df[stock_vars].rank(axis=1, method='dense') - 1 max_vals = ranks.max(axis=1) df[stock_vars] = (ranks.T / max_vals).T * 2 - 1
- 避免
-
缺失值处理:
- 针对缺失值填充,先计算所有列的中位数,然后用矩阵操作一次性填充。
medians = df[stock_vars].median(skipna=True) df[stock_vars] = df[stock_vars].fillna(medians)
-
时间窗口过滤:
- 按时间分段仅加载需要的年份范围。
filtered_data = df[(df["date"] >= "2015-01-01") & (df["date"] <= "2023-12-31")]
-
特征选择:
- 使用特征重要性筛选最相关的变量。例如,通过相关性过滤冗余特征:
corr_matrix = df[stock_vars].corr().abs() upper = corr_matrix.where(np.triu(np.ones(corr_matrix.shape), k=1).astype(bool)) to_drop = [column for column in upper.columns if any(upper[column] > 0.95)] df = df.drop(columns=to_drop)
- 预加载设置:
- 检查数据类型,优化内存占用;
- 使用
chunksize
分块加载,减少内存压力。
- 矢量化预处理:
- 使用矢量化处理变量的归一化和缺失值填充;
- 统一对时间序列分段处理。
- 分布式加速:
- 并行化分组处理,利用多核计算提升速度;
- 数据规模过大时,使用
Dask
处理。
- 输出存储优化:
- 保存中间处理结果为高效格式(如
Parquet
)以减少后续读取时间。
- 保存中间处理结果为高效格式(如
-
计算绝对总和:
abs_sum = df[model].abs().sum()
这行代码计算了
model
列(即XGBoost_pred
)中所有预测值的绝对值之和。这里使用abs()
取绝对值,确保无论预测值是正还是负,都能够得到它们的总贡献。 -
根据预测值分配权重:
if abs_sum > 0: df["weight"] = df[model] / abs_sum else: df["weight"] = 0
- 如果
abs_sum
大于零,说明model
列中至少有一个预测值。此时,权重计算为每个预测值与abs_sum
的比值。这样,每个股票的权重与其预测值的相对大小成比例。 - 如果
abs_sum
为零(即model
列中所有值都是零或不存在有效数据),则所有股票的权重都设为零。
- 如果
-
限制权重范围:
df["weight"] = df["weight"].clip(lower=-0.1, upper=0.1)
这行代码将计算出来的权重限制在 -0.1 到 0.1 之间。即使模型的预测值很大或者很小,权重也不会过度偏离这个范围。
- 标准化权重:
- 依据绝对总和计算权重: 通过将每个预测值除以所有预测值的绝对和,权重被标准化了,确保所有股票的预测值的相对权重和为 1(假设所有预测值不为零)。这样,模型的预测值就可以根据其在总体中的贡献来进行比较。该方法的一个关键优势是,不同股票的预测值可以直接比较,而不必担心它们的绝对尺度差异。
- 防止权重过大:
- 权重范围限制: 使用
clip()
函数限制权重范围(-0.1 到 0.1),有助于避免极端的预测值导致不合理的过大权重。例如,假设某个股票的预测值非常大,如果不进行限制,它的权重可能会变得不成比例。限制权重范围能有效控制过度偏向某些股票的风险,使得策略更加稳健。
- 权重范围限制: 使用
- 避免极端情境:
- 处理零总和的情况: 如果所有预测值为零或者预测值的总和为零,模型会为所有股票分配零权重,这避免了在无效预测情况下引发计算问题。并且,这种情况下不会导致模型做出不合理的投资决策。
- 根据模型输出调整:
- 动态权重分配: 权重的动态分配与模型预测的大小成正比,即模型预测越大,分配的权重也越大。这样,权重的分配能够准确反映模型对每个股票的预测置信度,使得有更高预测置信度的股票获得更多的投资权重。这是一个直观且合理的做法,符合金融投资的基本原则——“高预测收益应分配更多权重”。
- 简单而有效:
- 简单的比例计算方法: 这种基于比例的计算方式简单且直观,易于理解和实现,同时避免了复杂的回归或机器学习算法,在没有强大计算资源或数据时,可以快速得出比较合理的权重分配结果。
- 有效的风险控制: 通过限制权重范围,可以避免因单一因子的过度预测导致的极端结果,这有助于控制投资组合的风险。
- 适应性强: 这种基于预测值的动态加权方式使得权重分配随着预测结果的变化而变化。如果某个月的预测结果更加偏向某些股票,则会自动增加这些股票的权重,符合市场动态的变化。
- 简洁且稳定: 采用绝对和标准化的方式来计算权重,并加入权重限制,能够避免一些常见的计算误差或过拟合情况,使得策略在面对实际市场数据时具有更高的稳定性和可靠性。
总的来说,这种权重计算方法是合理且稳健的,能够根据预测模型的输出灵活地分配权重,同时通过对权重范围的限制和零总和处理,确保投资策略在不同情境下都能有效工作。
决定系数 ( R^2 ) 用来评估模型预测值与真实值之间的拟合程度,其公式为: [ R^2 = 1 - \frac{\sum_{i=1}^n (y_i - \hat{y}i)^2}{\sum{i=1}^n (y_i - \bar{y})^2} ]
- ( y_i ):真实值。
- ( \hat{y}_i ):预测值。
- ( \bar{y} ):真实值的均值。
- 使用 LightGBM 模型生成预测值:
- 使用训练好的模型对测试集或验证集进行预测。
- 调用
r2_score
函数计算 ( R^2 ):r2_score
接受真实值和预测值作为输入。
- ( R^2 ) 的取值范围通常为 ( (-\infty, 1] ):
- ( R^2 = 1 ):模型的预测值与真实值完全一致,拟合效果最好。
- ( R^2 > 0 ):模型比简单的均值预测更准确。
- ( R^2 = 0 ):模型的预测效果与简单的均值预测相当。
- ( R^2 < 0 ):模型的预测效果比简单的均值预测还差。
-
模型的预测范围:
- LightGBM 的预测值通常是连续值,确保目标变量的分布与模型预测值一致。
-
数据分割:
- 计算 ( R^2 ) 时,建议对 测试集 或 验证集 计算,而不是训练集,以避免过拟合的影响。
-
非线性关系:
- 如果目标变量与特征之间存在非线性关系,可能需要调整特征或模型参数以提升 ( R^2 )。