拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Python机器学习量化研究源码包:数据处理、特征工程到回测实战

简介面向有Python基础、想入门机器学习量化投资的读者提供一套编译后可直接运行的完整项目源码与数据集。项目覆盖线性回归、岭回归、Lasso、弹性网络、决策树、随机森林、GBDT、XGBoost、SVM、RNN/LSTM及集成学习等主流算法通过实际数据展示特征筛选、模型训练、结果对比与策略回测流程便于观察不同模型在基本面量化场景下的表现差异。资源包共219个文件约145.79MB以167个CSV结果/数据文件、36个模型文件、11个Python脚本和3份PDF说明为主数据与代码分离结构清晰省去环境搭建与数据准备成本。已有49人学习浏览源码评审分95以上项目文档较完整可用于课程设计、算法对比实验或作为个人量化策略研究起点。使用前可根据市场变化调整参数并注意量化投资本身的风险。1. 量化研究的第一步这份 Python 与机器学习驱动源码包为什么值得马上拆开很多想入手量化投资的人卡在同一个地方——手里有行情数据却不知道下一步怎么走。直接用机器学习建模数据量不够、特征不会构造、回测又踩到未来函数结果就是模型在训练集上表现漂亮一到实盘就翻车。我拆过不少量化项目最关键的从来不是算法本身而是围绕数据清洗、特征工程和回测验证的一整套工程流程。这个源码包的价值就在这它不是给你一堆零散的Python脚本而是把机器学习驱动量化研究的完整链路——从CSV数据加载、预处理、特征构造到多模型训练与回测——全串好了附带的股票数据作为数据集你可以直接跑通。适合正在学习机器学习、想用Python动手实践量化策略、以及需要一份可复现基线代码的从业者。数据、源码、脚本都齐了拿到手就能开始调试。2. 数据处理与特征引擎构建可靠的投研底稿2.1 时间序列对齐一切建模前先解决索引问题项目自带的数据集基本都是日线行情典型字段包括日期、开盘价、最高价、最低价、收盘价、成交量和成交额。量化建模的第一步不是建模型而是把这些数据整理成模型能吃的形态。常见做法是统一用日期做索引按股票代码分组处理。这个步骤要是做不好后面所有窗口计算、标签生成都会错位。import pandas as pd df pd.read_csv(data/stock_daily.csv, parse_dates[date]) df.sort_values([symbol, date], inplaceTrue) df[returns] df.groupby(symbol)[close].pct_change() df df.dropna(subset[returns])上面这段代码的逻辑是先读取CSV并让pandas自动解析日期列然后按股票代码和日期排序——这一步很关键面板数据必须保证每个股票内部时间是有序的随后分组计算日收益率pct_change也就是当天的涨跌幅这是量化研究最基础的自变量之一。需要注意pct_change在每组第一行会产生NaN所以直接dropna掉。实际项目里如果你要做的是全市场选股别忘了后面统一时间戳把不同交易日的数据对齐到同一张表上。2.2 特征构建动量、波动率与量价复合因子有了基础价格数据就可以开始构造模型输入特征。机器学习在量化里用得最多的不是价格本身而是从价格与成交量中导出的因子。这个项目里已经封装了一组通用特征工程函数包括动量、波动率、成交量乖离率等。构建特征时有一个要点所有窗口统计都必须只使用当前时刻及之前的数据任何“偷看未来”的窗口计算都会在样本外测试中暴露出来。def build_features(df, window20): df[mom_20] df[close] / df[close].shift(window) - 1 df[volatility_20] df[returns].rolling(window).std() df[volume_mean_20] df[volume].rolling(window).mean() df[volume_ratio] df[volume] / df[volume_mean_20] df[high_low_ratio] df[high] / df[low] - 1 return df.dropna() df df.groupby(symbol).apply(build_features).reset_index(dropTrue)这段代码里mom_20是20日动量度量当前价格相对20天前的涨跌幅度volatility_20是20日日收益率的标准差用来捕捉价格波动区间volume_mean_20是20日平均成交量volume_ratio是当日成交量与20日均量的比值放大成交量异动信息high_low_ratio是日内振幅。窗口长度20是日线级别比较常见的默认值如果你想做短线改成5或10做中线趋势跟踪则可以用60。构建特征完成后记得统一去掉NaN行因为shift和rolling在窗口前段会产生无效值。2.3 标签设计预测未来N日收益还是涨跌方向特征决定了模型能看到什么而标签决定了模型要学什么。这个项目提供了两种标签可选一种是回归任务的未来N日收益率另一种是分类任务的未来N日是否上涨。设计标签时要注意收益率的计算必须滞后于特征也就是用截止到今天的特征去预测明天之后的价格变化这样才符合真实交易的时序。df[label_reg] df.groupby(symbol)[close].transform(lambda x: x.shift(-5) / x - 1) df[label_cls] (df[label_reg] 0).astype(int) df df.dropna(subset[label_reg])transform配合shift(-5)的含义是取未来第5天的收盘价与今天收盘价之比再减1得到的label_reg就是未来5日收益率。label_cls将其二值化未来上涨为1下跌或持平为0。如果你做的是T1短线交易把shift(-5)改成shift(-1)做月度调仓改成shift(21)。dropna一定要放在最后因为最后5行的未来收益是缺失的。2.4 数据切分时序数据不能随意打乱机器学习常见的train_test_split在量化场景里要格外小心因为股票数据是时间序列随机打乱会让模型学到未来信息。这个项目中已经按时间顺序手动切分了训练集与测试集前80%做训练后20%做样本外验证。若要做更严谨的验证可以采用滚动时间窗口重训模型但作为基线一次切分就足够发现主要问题。split_idx int(len(df) * 0.8) train_df df.iloc[:split_idx].copy() test_df df.iloc[split_idx:].copy() X_train train_df[feature_cols].values y_train train_df[label_cls].values这段代码的核心就是宁可按行号切分也不要用随机切分。训练集在前、测试集在后模拟的是“用历史预测未来”的自然过程。实际操作中我一般会在切分前把数据按时间戳再做一次全局排序避免因为多股票拼接导致的时间乱序。另外特征列中不要包含symbol、date、open等非预测性字段只用上面构建的特征矩阵。3. 多模型训练与集成线性、树模型与神经网络怎么选3.1 逻辑回归与正则化一切模型对比的基线任何量化建模都应该先跑一个最简单的线性模型作为基线。逻辑回归在这个项目里承担的就是这个角色。它算法透明、训练快、结果稳定并且能给出特征权重——哪几个因子在驱动预测结果一目了然。项目里封装了带L2正则化的逻辑回归默认参数C1.0实际调参可以把C值放开到0.1或10观察验证集表现。from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score, roc_auc_score lr LogisticRegression(C1.0, max_iter1000, random_state42) lr.fit(X_train, y_train) y_pred lr.predict(X_test) y_prob lr.predict_proba(X_test)[:, 1] print(acc:, accuracy_score(y_test, y_pred)) print(auc:, roc_auc_score(y_test, y_prob))逻辑回归使用L2范数约束权重防止某个单一因子主导预测结果。max_iter设到1000避免收敛警告。量化里AUC往往比准确率更值得关注因为它不依赖阈值选择直接衡量模型排序能力。注意类别分布不均衡问题如果预测目标1占比只有30%准确率会虚高此时更适合直接看AUC或F1。3.2 随机森林与XGBoost表格数据的主力模型树模型是量化研究公认的强者——能处理非线性关系、自动捕捉特征交互、对异常值不敏感。这个项目集成里包含了随机森林和XGBoost两套树模型实现。随机森林用Bagging思路并行建多棵树取平均XGBoost用Boosting思路逐棵拟合残差在因子选股场景里XGBoost的效果通常更好但随机森林更稳、不易过拟合。from sklearn.ensemble import RandomForestClassifier rf RandomForestClassifier( n_estimators300, max_depth5, min_samples_leaf20, random_state42, n_jobs-1 ) rf.fit(X_train, y_train) y_prob_rf rf.predict_proba(X_test)[:, 1]这段代码的关键参数是max_depth5和min_samples_leaf20这两个参数直接控制树的复杂度。量化金融数据信噪比很低树太深很容易把噪音当信号学进去。n_estimators设为300是为了在收敛和训练时间之间取平衡如果你用的是小样本数据集150就够。min_samples_leaf设置稍大相当于强制叶子节点有足够多样本支持是抑制过拟合的有效手段。n_jobs-1表示用满全部CPU核心。3.3 LSTM让神经网络处理时间依赖关系如果数据量足够大LSTM模块就可以派上用场。它适合捕捉价格走势中的短期时间依赖比如连续几天的放量下跌后紧跟反弹这类局部模式。这个项目用PyTorch构建了两层LSTM输入维度等于特征数。要注意LSTM对特征尺度极其敏感进入网络前必须做标准化。import torch import torch.nn as nn class LSTMModel(nn.Module): def __init__(self, input_size, hidden_size32, num_layers2): super().__init__() self.lstm nn.LSTM(input_size, hidden_size, num_layers, batch_firstTrue) self.fc nn.Linear(hidden_size, 1) def forward(self, x): out, _ self.lstm(x) return self.fc(out[:, -1, :])hidden_size32控制每层LSTM的记忆容量。hidden_size太小记不住长期依赖太大会导致训练变慢且在小样本上严重过拟合。num_layers2表示两层堆叠第二层在第一层输出的基础上继续提取抽象特征。forward函数中out[:, -1, :]取最后一个时间步的输出——因为我们只需要最后一个时刻的预测结果。batch_firstTrue是为了让输入张量维度直接是[batch_size, seq_len, feature_size]更符合直觉。3.4 模型集成让多个模型投票决策单一模型容易在某个市场阶段失灵。解决办法是把逻辑回归、随机森林、XGBoost、LSTM四类模型的预测结果做集成。项目里提供了软投票与硬投票两种方案软投票是取各个模型预测概率的平均值硬投票是取多数结果。我的经验是软投票的鲁棒性更高尤其在市场风格剧烈切换时。final_prob (0.2 * y_prob_lr 0.3 * y_prob_rf 0.3 * y_prob_xgb 0.2 * y_prob_lstm) final_pred (final_prob 0.5).astype(int)这里的权重分配完全可以按单模型的样本外表现来自由调整。我建议先用验证集分别计算每个模型的AUC然后给AUC更高的模型更大权重。总体原则是不要把所有权重押在单一模型上尤其是LSTM在数据量不够时会大幅波动给它20%的权重就足够——它可以贡献独立的非线性信息又不会因为自身的预测错误拖垮整体。集成后记得做一次完整的回测确认集成结果确实优于最好的单模型否则这步属于无效劳动。4. 回测与评估指标看收益更要看回撤和换手4.1 向量化回测框架快速检验策略可行性模型输出的预测只是信号能否转化成实际收益还差一个回测环节。这个项目提供了一个轻量的向量化回测模块基于每日信号直接计算持仓收益支持多空仓位配置。向量化回测的好处是运算极快适合跑参数组合但它的假设比较理想——按收盘价成交、没有冲击成本所以回测结果通常比实际情况乐观。def backtest(prob, price, cost0.0005, threshold0.55): position (prob threshold).astype(int) daily_ret price.pct_change().fillna(0) strategy_ret position.shift(1) * daily_ret net_ret strategy_ret - cost * position.shift(1).diff().abs().fillna(0) net_wealth (1 net_ret).cumprod() return net_wealth, net_ret这段代码的核心是position.shift(1)——用昨天的信号决定今天的持仓这个滞后就是为了避免未来函数。threshold0.55代表只有当模型预测上涨概率超过55%时才持有多头比默认的0.5更保守可以有效过滤低置信度信号。cost是交易成本包括佣金和滑点A股双边大约万五到千一我一般按万分之五做乐观估计、千分之二做压力测试。持仓变化时才会产生换手成本所以扣费要基于持仓变化的diff值。4.2 最大回撤与夏普比率衡量风险的核心参数只看收益曲线会误导人。项目里封装了夏普比率、最大回撤、胜率和盈亏比四项核心评估指标。夏普比率度量每承担一单位波动能换来多少超额收益年化后一般大于1就算不错的策略最大回撤表示策略从净值高点跌到低点的最大幅度这是实盘中最容易让人心理崩溃的数字。def max_drawdown(net_wealth): peak net_wealth.cummax() drawdown (net_wealth - peak) / peak return drawdown.min() def sharpe_ratio(net_ret, periods252): excess net_ret - 0.02 / periods return excess.mean() / excess.std() * np.sqrt(periods) if excess.std() 0 else 0max_drawdown用cummax计算出历史最高净值序列再用当前净值减峰值的差值除以峰值就得到了回撤比例。sharpe_ratio里0.02是无风险利率的年化假设很多初学者会忽略这一项导致夏普比率虚高如果你用的是H股或美股数据无风险利率可以改成美债收益率水平。返回值乘np.sqrt(252)是把日度夏普比率年化——因为一年大约252个交易日方差随天数线性累加标准差随天数平方根增长。4.3 分组回测检验策略在不同个股上的稳定性项目的数据集包含多只股票所以回测还可以按股票代码分组做子样本回测。这个方法非常有用——一只策略如果在某只股票上赚来的收益掩盖了其他股票的亏损那它的可复制性就很差。下面是分组回测的实现逻辑核心就是对每只股票单独跑一遍收益归因。results {} for symbol, group_df in df.groupby(symbol): price group_df.set_index(date)[close] prob group_df.set_index(date)[prob] wealth, ret backtest(prob, price) results[symbol] { max_drawdown: max_drawdown(wealth), sharpe: sharpe_ratio(ret), total_return: wealth.iloc[-1] - 1 }按股票分组独立回测能看出策略是普适有效还是只在个别标的上有效。如果某只股票贡献了策略80%的收益而其余股票几乎不赚钱这样策略的容量和适用性都会打折扣。实际操作中我会把分组结果导出成表格检查收益率分布的中位数和离散程度中位数接近全样本收益、离散度不高那策略风格就比较稳定反之就要检查是不是某个板块或某只权重股带偏了整体结果。5. 常见坑位与排查手册翻车现场全记录5.1 归一化时不小心把测试集信息混入训练过程现象模型训练时验证集AUC高得异常接近0.99但实盘模拟收益却很一般。原因在对特征做标准化时用了全部数据的均值和方差去transform而不仅仅是训练集的统计量。这种数据泄露会让模型在验证阶段间接“看到”未来的分布特征。解决先fit训练集再transform训练集和测试集或者使用sklearn的Pipeline将标准化步骤包进交叉验证流程里。从那以后我每次写特征工程代码都会额外检查一遍是否启动了“先split再fit”的顺序。scaler StandardScaler() X_train_scaled scaler.fit_transform(X_train) X_test_scaled scaler.transform(X_test)这段写法就是标准解法scaler只在训练集上拟合测试集只做transform保证测试集信息不进入任何参数计算。如果在fit_transform之后才做train_test_split就已经把测试数据污染了。5.2 回测结果非常好但明细月收益连续亏损现象整体年化收益超过30%但按月统计发现大半年都在亏钱赚钱集中在两三个月份。原因策略可能只在特定市场环境下有效比如趋势策略在震荡市就会连续止损。项目回测模块里如果只输出全样本收益这类问题很容易被掩盖。解决在回测模块里加上月度收益透视表按年、按月聚合净值变化。如果发现连续亏损月份超过整体回测期的40%说明策略的适应面偏窄需要叠加市场状态过滤条件。5.3 LSTM模型训练时loss震荡不收敛现象训练过程中损失值忽高忽低到了后期仍然没有下降趋势。原因是LSTM对学习率极其敏感默认0.01的学习率在短时间序列上容易导致梯度震荡。解决把学习率降到0.001或者0.0005同时把batch size调大。经验值是batch size64对日线数据比较稳定。optimizer torch.optim.Adam(model.parameters(), lr0.001, weight_decay1e-5) scheduler torch.optim.lr_scheduler.StepLR(optimizer, step_size20, gamma0.5)这里使用Adam优化器并加上weight_decay做L2正则化能显著抑制过拟合。StepLR每20个epoch把学习率降为原来的50%相当于训练后期做精细微调这是让LSTM在金融数据上稳定收敛的常用手段。如果再配一个早停机制——连续10个epoch验证损失没有下降就停止训练——基本能消除不收敛问题。5.4 label顺滑化缺失导致模型对拐点反应迟钝现象模型预测准确率尚可但净值曲线上明显在拐点处反应滞后。原因用的是未来5日收益率的硬分类标签它直接预测“第5天是否高于今天”但中间4天的价格起伏完全没被建模。解决给标签做平滑处理比如用未来5日收益率的连续值做回归或者使用TopK分位数做三分类。如果坚持用二分类可以把预测期的收益率从“未来第5天”改成“未来5天的平均收益”这样能把中间几天走势的信息也纳入标签预测目标更贴近真实交易体验。5.5 多股票拼接时groupby后的状态泄漏现象策略整体回测看起来不错但分股票回测后发现只有最早上市的那只股票在赚钱。原因分组做特征时使用了全量的rolling窗口导致后一只股票的rolling窗口包含了前一只股票的数据——简单说就是拼接处数据串味了。解决groupby之后必须重置索引并且每次窗口计算都要验证首尾边界数据是否为NaN。最容易检查的方法就是打印每个股票拼接处的close是否连续——不同股票的价格水平差异极大如果拼接处出现断崖式跳变而rolling数据却连续那你一定踩了这坑。6. 进阶技巧滚动窗口训练与外推验证的最后一公里基线代码跑通后要逼近实战性能我习惯做两个改动一是把单次切分改成滚动重训二是加入波动率目标缩仓。滚动窗口训练就是每次用过去N天数据训练模型预测未来M天然后窗口向后推进。这个项目的源码已经提供了train_predict_rolling的基础函数核心逻辑如下def rolling_predict(df, model_fn, train_days250, pred_days5): preds [] for start in range(0, len(df) - train_days - pred_days, pred_days): train df.iloc[start:start train_days] test df.iloc[start train_days:start train_days pred_days] model model_fn() model.fit(train[feature_cols], train[label_cls]) prob model.predict_proba(test[feature_cols])[:, 1] preds.extend(prob) return predstrain_days250约等于一年的交易日保证了训练集覆盖完整的市场周期比如包含一个完整的上涨和下跌阶段。pred_days5与标签周期保持一致这样每次预测5天窗口步进5天。这个循环天然规避了未来函数——每一步的测试数据都在训练数据之后并且训练集始终只用过去的信息。波动率目标缩仓是我认为最实用的风控技巧它能明显改善回撤。核心思想是当市场波动加剧时自动降低仓位波动平缓时恢复仓位。在回测模块里把每日收益除以当日过去20日的年化波动率再缩放到目标波动率水平rolling_vol daily_ret.rolling(20).std() * np.sqrt(252) position_adjusted position * (target_vol / rolling_vol).clip(0, 2)target_vol设为0.15表示目标年化波动率15%一旦市场波动率升到30%仓位自动降到原来的一半clip(0,2)限制最高加仓倍数不超过2避免在低波动期盲目放大杠杆。这比固定仓位策略在震荡市里生存率高很多。量化项目的本质就是反复迭代这四件事数据、特征、模型、风控。我每次拿到新数据和源码包都会强制走一遍完整流程——从未来函数检查开始到滚动窗口重训结束确认每一步都没有偷看未来再上模拟盘。这套源码包的优势就在于把这些环节全部打通了你要做的不是改模型而是沿着数据流把每个环节调成适合自己的参数。希望帮到你。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门