Python机器学习驱动的基本面量化投资:财务因子到策略回测

发布时间:2026/9/16 15:56:56

Python机器学习驱动的基本面量化投资:财务因子到策略回测 简介这是一份面向金融量化入门与进阶学习者的Python机器学习驱动的基本面量化投资研究项目完整打包了源码与配套数据集利用多种机器学习算法对基本面因子进行建模与预测适合具备一定Python基础、希望将数据科学与金融场景结合的人士用于课程设计、毕业设计或个人研究。压缩包共183个文件约145.6MB包含167个csv数据集、11个py源码文件、3个pdf说明文档及1个md说明文件csv涵盖ROA、ROE、NOA、tang等重要财务指标与因子数据py脚本实现数据预处理、特征构建、模型训练与结果评估等完整流程pdf与md辅助理解项目思路与使用细节。目前已有307人学习浏览源码经本地编译可运行评审分达95分以上项目难度适中且经由助教审定学习路线清晰。下载后可获得一套可复现的量化研究案例既能学习机器学习在金融数据上的应用细节也能基于现有因子框架扩展自己的研究。1. 基本面量化不是玄学是特征工程加模型选型做量化研究的人经常争论一句话基本面分析靠不靠谱很多人的结论是价值投资有效但滞后技术指标噪声太大。但把基本面指标和机器学习放在一起情况就变了。这个项目给的是一整套可复现的研究框架从ROE、ROA、NOA这些财务因子出发用Python把数据清洗、特征构造、模型训练、策略回测串起来最终目标是找出基本面指标对收益的预测能力。它不是让机器自动选股那么简单而是把会计学里的财务比率变成能喂给分类器和回归模型的数值特征。适合三类人正在做金融机器学习课程设计的同学、想验证基本面因子有效性的量化初学者、以及写论文需要基线实验的从业者。项目源码本地可跑通数据集是真实的财务指标序列这一点比很多只给代码不给数据的资源要实在。2. 财务数据集解读与特征工程从CSV文件列表到ML-ready的数据结构2.1 项目里那些CSV文件到底是什么拿到压缩包先看文件列表最直观的信息全在命名上。项目给出了8个关键CSV文件命名规则是“编号财务指标缩写”。我按会计含义和金融用途拆一下避免你对着数据发懵。文件名缩写含义在量化模型中的角色68ROE.csv净资产收益率盈利能力的核心指标常做第一特征69ROA.csv总资产收益率衡量资产利用效率与ROE互补65NOA.csv净经营资产反映经营资产规模与财务杠杆相关76RDsale.csv研发费用/销售额创新投入强度成长股重要特征75rd_mve.csv研发费用/市值市场对研发的定价效率因子94tang.csv有形资产比率资产可抵押性影响融资约束70CT.csv资本周转相关指标运营效率因子17LM.csv对数市值或流动性代理规模因子控制小盘股效应这些文件大概率是宽表结构第一列是股票代码或日期后续列是不同时间截面下的指标值。用pandas读入后先看shape和head不要急着合并。2.2 数据清洗的标准动作CSV文件多了以后最大的坑是索引对齐。每个文件的股票代码顺序可能不一致日期也可能缺行。我一般会先建立一个统一的日期索引再按股票代码merge。import pandas as pd import numpy as np # 读取ROE文件指定第一列为索引 roe pd.read_csv(68ROE.csv, index_col0, parse_datesTrue) roa pd.read_csv(69ROA.csv, index_col0, parse_datesTrue) # 合并两个因子用外连接保留所有时间点 factor pd.merge(roe, roa, left_indexTrue, right_indexTrue, howouter, suffixes(_roe, _roa)) # 处理缺失值金融数据不要直接dropna用前向填充限制填充次数 factor factor.fillna(methodffill, limit3) # 去除极端值按分位数缩尾避免单一财报异常值主导梯度 def winsorize(s, lower0.01, upper0.99): q_low, q_high s.quantile([lower, upper]) return s.clip(q_low, q_high) factor factor.apply(winsorize) print(factor.shape) print(factor.describe())merge时howouter能保住所有时间截面但后续训练必须对齐标签数据否则会出现前视偏差。fillna(methodffill, limit3)是处理财务数据缺失的惯用手段因为季报披露本身就有滞后前向填充模拟的是“我知道上一期值但最新一期还没出”的真实场景。缩尾处理用分位数裁剪参数0.01到0.99意味着上下1%的极端值被拉到边界这比z-score归一更能抵抗财务造假式的离群点。2.3 标签构造预测未来一期的收益方向特征准备好后最关键的是构造训练标签。经典做法是T期因子预测T1期收益方向这样做严格避免同期信息泄露。# 假设你有收盘价数据close_price计算未来一期收益率 future_ret close_price.pct_change(periods1).shift(-1) # 将收益转成二分类标签涨1跌0 label (future_ret 0).astype(int) # 对齐特征和标签丢弃没有标签的尾部数据 ml_data pd.concat([factor, label.rename(label)], axis1, joininner) ml_data ml_data.dropna() # 按时间顺序切分严禁随机打乱时间序列数据会破坏独立性假设 train_size int(len(ml_data) * 0.7) train_data ml_data.iloc[:train_size] test_data ml_data.iloc[train_size:]标签构造里shift(-1)是灵魂操作它把当期因子和下一期收益配对。很多人在这里直接pct_change()就开训结果模型在回测里表现惊艳实盘却一塌糊涂。原因就是没有shift模型悄悄“偷看”了未来。按时间顺序切分是第二个必须遵守的纪律随机Shuffle会把时间相关性搅碎让验证集变得过于乐观。3. 多种机器学习算法实现与参数对比从逻辑回归到XGBoost3.1 为什么先跑逻辑回归机器学习算法在量化里最容易犯的错误是一上来就XGBoost调参结果过拟合到财务报表的噪音里。我的建议永远是先拿逻辑回归当基线因为它给出概率输出系数还能解释因子方向。from sklearn.linear_model import LogisticRegression from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline # 标准化逻辑回归C控制正则化强度 model_lr Pipeline([ (scaler, StandardScaler()), (lr, LogisticRegression(C1.0, solverliblinear, max_iter1000)) ]) model_lr.fit(train_data.drop(label, axis1), train_data[label]) test_acc model_lr.score(test_data.drop(label, axis1), test_data[label]) # 输出因子系数正负代表方向 coef_df pd.DataFrame({ feature: train_data.columns[:-1], coef: model_lr.named_steps[lr].coef_[0] }).sort_values(coef, ascendingFalse) print(coef_df)StandardScaler是逻辑回归的必需品因为ROE和RD_MVE量纲差异极大不标准化会让带正则项的损失函数被大数值特征支配。solverliblinear适合小数据集和二分类C1.0是默认值如果特征间共线性重可以调低到0.5增强正则。系数表里正值表示因子值越高未来上涨概率越大负值反之这一步能快速验证会计直觉。3.2 随机森林捕捉非线性关系财务指标和未来收益的关系通常不是线性的。比如ROE在中间区间可能没有区分度但极高和极低都是危险信号。随机森林天然能处理这种U型关系。from sklearn.ensemble import RandomForestClassifier # 随机森林不需要特征标准化但要控制树深度 model_rf RandomForestClassifier( n_estimators300, max_depth4, min_samples_leaf20, max_featuressqrt, random_state42, n_jobs-1 ) model_rf.fit(train_data.drop(label, axis1), train_data[label]) # 特征重要性排序 importances pd.Series(model_rf.feature_importances_, indextrain_data.columns[:-1]).sort_values(ascendingFalse) print(importances.head(10))max_depth4在财务数据里已经足够深树太深会把单一公司的特异模式背下来。min_samples_leaf20强制每个叶子节点至少20个样本相当于给预测做了平滑防止某个极端财报值单独决定一片叶子。特征重要性只能看相对排名不能看绝对大小它衡量的是特征对节点分裂的贡献度。3.3 XGBoost梯度提升的精度上限XGBoost到目前为止仍然是表格数据上综合表现最好的算法之一关键参数是学习率、树深度和正则化系数。它比随机森林强在能逐步拟合残差但也更容易过拟合需要配合早停。from xgboost import XGBClassifier model_xgb XGBClassifier( n_estimators1000, learning_rate0.05, max_depth3, subsample0.8, colsample_bytree0.8, reg_alpha0.1, reg_lambda1.0, use_label_encoderFalse, eval_metriclogloss, random_state42 ) # 训练时使用早停防止过拟合 model_xgb.fit( train_data.drop(label, axis1), train_data[label], eval_set[(test_data.drop(label, axis1), test_data[label])], verboseFalse )learning_rate0.05配n_estimators1000意味着模型要走很多步才能拟合但每步很小配合早停能找到精度和泛化的平衡点。subsample0.8是行采样每棵树只看80%的样本增加随机性colsample_bytree0.8是列采样每棵树只用80%的特征这在特征只有8个的时候能明显降低方差。reg_alpha和reg_lambda分别是L1和L2正则财务指标共线性强加L1能自动丢弃不重要的因子。早停看的是测试集logloss当连续50轮不下降时停止这个数比准确率更敏感。3.4 SVM与其他算法什么时候值得试支持向量机在金融数据里不常用因为核函数的计算复杂度高且对噪声敏感。但如果你发现逻辑回归和树模型的效果都不理想可以拿RBF核的SVM做个对照实验注意必须要做特征标准化。from sklearn.svm import SVC model_svm SVC( kernelrbf, C1.0, gammascale, probabilityTrue, class_weightbalanced ) model_svm.fit(train_data.drop(label, axis1), train_data[label])class_weightbalanced在涨跌样本不均衡时很有用它会根据类别频率自动调整权重让模型不要总是预测多数类。gammascale是经验值它对特征的标准差做了归一比手动调参更稳。4. 模型评估与量化策略回测准确率不是唯一标准4.1 分类指标的真相金融预测里准确率很容易骗人。如果市场上涨天数占55%一个永远预测“涨”的模型准确率就是55%而你辛苦训练的分类器可能只有52%。所以必须看AUC、F1-score和每年的分年度收益。from sklearn.metrics import classification_report, roc_auc_score # 预测概率而不是标签用于后续排序 test_prob model_xgb.predict_proba(test_data.drop(label, axis1))[:, 1] # AUC衡量的是排序能力而不是概率校准 auc_score roc_auc_score(test_data[label], test_prob) # 输出每个类别的查准率和召回率 print(fAUC: {auc_score:.4f}) print(classification_report(test_data[label], test_prob 0.5))AUC大于0.55就有意义因为这意味着模型对样本的排序能力优于抛硬币。分类报告里的macro avg比准确率更值得关注特别是在涨跌比例失衡时。预测概率随后要用于构造投资组合而不是直接用0.5这个阈值做买卖决策。4.2 策略回测框架按预测概率分层把模型预测概率从高到低排序取前30%作为买入池是基本面量化里常用的截面对比方法。这一层逻辑把机器学习输出变成了可交易信号。import numpy as np # 构建模拟组合预测概率最高的小组 test_data test_data.copy() test_data[pred_prob] test_prob # 按时间排序后每天取前30%概率的股票 test_data[rank] test_data.groupby(level0)[pred_prob].rank(pctTrue) selected test_data[test_data[rank] 0.70] # 等权组合计算每日收益假设持有未来一期 selected[portfolio_ret] selected[future_ret].groupby(level0).transform(mean) # 计算累计净值 portfolio_nav (1 selected.groupby(level0)[portfolio_ret].first()).cumprod() benchmark_nav (1 test_data.groupby(level0)[future_ret].mean().first()).cumprod()groupby(level0)假设索引第一层是日期如果数据是MultiIndex要按实际结构调整。rank(pctTrue)是截面排序保证每个交易日的选股数一致。等权组合的日收益是当日被选中的所有股票的平均收益这只是一种简化实际交易还会考虑滑点和交易成本。这里的核心是验证模型预测是否真正转化为超额收益。4.3 风险锚点夏普比率与最大回撤策略好不好不能只看净值曲线。夏普比率衡量的是每承担一分波动能换来多少超额收益最大回撤则告诉你最坏情况下会亏多少。# 计算策略超额收益 strategy_nav portfolio_nav.reindex(benchmark_nav.index).fillna(methodffill) excess_ret strategy_nav.pct_change() - benchmark_nav.pct_change() # 年化夏普比率假设年化252个交易日 sharpe np.sqrt(252) * excess_ret.mean() / excess_ret.std() # 最大回撤计算最高净值回落到最低点的幅度 rolling_max strategy_nav.cummax() drawdown (strategy_nav - rolling_max) / rolling_max max_drawdown drawdown.min() print(fSharpe: {sharpe:.2f}) print(fMax Drawdown: {max_drawdown:.2%})夏普比率里如果excess_ret.std()是0说明策略天天和市场同步没有alpha负值说明模型跑输基准这时候回去看模型是不是过拟合了。最大回撤30%的动量策略和15%的防守策略在不同的资金规模下评价完全不同小资金可以承受高回撤换高收益但机构资金很难。5. 模型落地与工程化保存、部署、监控的三个细节模型训练完只是研究的一半真正让它产生价值的是推上线之后的工程细节。这里我给三个最容易被专业课忽略的实用技巧。第一个技巧是保存完整预处理流程而不是只存模型。很多人在notebook里跑通了模型换个环境重跑发现预测结果完全不同原因就是StandardScaler的均值和方差没有随模型一起保存。正确做法是用Pipeline打包标准化和预估器整体pickle持久化。import joblib # 将整个pipeline保存加载后直接predict joblib.dump(model_xgb, xgb_factor_model.pkl) loaded_model joblib.load(xgb_factor_model.pkl) new_pred loaded_model.predict_proba(new_factors) # 检查输入特征顺序是否和训练时一致 expected_cols train_data.columns[:-1].tolist() unseen_cols [c for c in new_factors.columns if c not in expected_cols] print(f缺失特征: {unseen_cols})第二个技巧是警惕数据对齐灾难CSV文件里不同因子的日期索引错位一格就可能让模型学到“未来信息”的假象。我会在合并时加一个校验逻辑打印每个文件的时间范围。# 归一化索引后检查时间一致性 all_indexes [df.index for df in [roe, roa, noa]] common_start max([x.min() for x in all_indexes]) common_end min([x.max() for x in all_indexes]) print(f对齐区间: {common_start} ~ {common_end})第三个技巧是滚动再训练机制。财务报表是季度更新的模型训练的因子分布特性每个季度都在变固定模型跑半年往往性能衰减严重。我一般按季度重训练一次保留最近8个季度的数据作为训练集参数用上一季度的最优值作为起点这样既不需要重新调参又能适应财务特征的缓慢漂移。监控指标就一个滚动AUC如果连续两个季度低于设定阈值触发告警并回滚到上一季度版本。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/9/16 18:42:25

大模型如何革新游戏开发:从自然语言到智能生成

1. 项目背景与行业观察最近参加了一场关于游戏开发与智能技术融合的行业闭门会,现场演示的几款原型产品让我深刻感受到,游戏行业的技术迭代速度正在以肉眼可见的方式加快。其中有个demo让我印象深刻:开发者仅用自然语言描述游戏规则&#xff…

2026/9/16 18:42:25

WristArc实战:用Bootstrap 5构建响应式手表电商页

简介:这是一份基于HTML、CSS与Bootstrap实现的时尚手表电商网站页面设计包,适合前端初学者或希望快速搭建电商展示页的开发者练习参考。资源围绕“WristArc”品牌构建了完整的浏览型站点界面,包含首页、列表、详情等常见页面模块,…

2026/9/16 18:42:25

Java自研轻量区块链毕设:医疗存证全链路实现

简介:本资源是一套基于Java开发的区块链医疗记录存储系统毕业设计完整方案,面向计算机、通信、人工智能等专业的本科生及指导教师,解决传统医疗数据孤岛、隐私难保障、共享不可溯等核心问题。压缩包为ZIP格式,大小11.22MB&#xf…

2026/9/16 18:42:25

CAD绘制一级圆柱齿轮减速器工程图全流程指南

1. 项目概述:CAD绘制一级圆柱齿轮减速器工程图刚入行的机械设计新人常会遇到一个经典练手项目——用CAD软件绘制一级圆柱齿轮减速器的装配图和零件图。这不仅是机械制图课程的常见作业,更是实际工作中齿轮传动设计的入门必备技能。我十年前第一次用AutoC…

2026/9/16 12:52:37

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/16 0:04:09

PHP源码部署实战:从环境配置到运行情侣游戏全攻略

简介:这是一套面向情侣互动场景的PHP完整源码,集成情侣飞行棋、真心话大冒险、情趣骰子等玩法,并内置完整分销制度,可自定义多种返佣比例,源码完全开源无加密,支持微信无感自动授权登录与第三方授权&#x…

2026/9/15 14:22:53

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/15 21:31:11

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/15 11:42:23

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码