
1. 这套AI选股工具到底是个什么东西先把话说在前头我不是那种看到“AI”两个字就两眼放光的人。做了这么多年数据分析和量化相关的事情见过太多挂着AI名头、实际就是几条均线加权的所谓“智能工具”。所以当朋友甩给我一个标题说“阿里达摩院的AI选股工具白嫖还附源码”的时候我第一反应是——大概率又是个噱头。结果打脸了。这套东西的核心逻辑是把阿里达摩院在时序预测领域的一些公开研究成果落地成了一个可以本地跑的选股分析流程。它不是什么“输入代码明天涨停”的玄学工具而是一套基于多因子模型时序神经网络的股票打分系统。说白了它干的事情是把一堆股票的历史量价数据、财务指标、资金流向等维度信息喂进去让模型输出一个相对排名帮你从几千只票里筛出值得进一步研究的那几十只。这玩意儿能解决什么问题最直接的——缩小研究范围。A股五千多只股票你不可能每只都看。传统做法是用通达信或者同花顺的条件选股设定一堆硬性条件去过滤。但硬性条件的问题是它只能表达“市盈率小于30且ROE大于15”这种线性逻辑没法捕捉多个因子之间复杂的非线性关系。而这套AI工具的价值就在于它能学到“什么样的因子组合在历史上更容易跑出超额收益”这种隐性的模式。适合谁来参考我觉得有三类人值得花时间研究一是有点Python基础、想入门量化选股的散户二是做数据分析、想了解时序模型在金融场景怎么落地的工程师三是纯粹想搞明白“AI选股”到底靠不靠谱的理性投资者。如果你指望复制粘贴就能躺着赚钱那可以直接关掉了这东西不是印钞机。我拿到源码之后花了大概三个晚上把整套流程跑通、调参、回测中间踩了不少坑。下面我把整个拆解过程、核心原理、实操步骤和我自己总结的避坑经验完整写出来源码部分我也会给出关键模块的说明方便你直接复现。2. 整体架构拆解为什么这么设计2.1 从数据到信号的四层结构这套工具的架构其实不复杂我把它拆成了四层来看数据采集层负责拉取股票列表、历史行情、财务数据和资金流向数据。源码里用的是公开数据接口具体用的是哪家我就不点名了反正你换成自己习惯的数据源也行接口部分做了抽象改起来不难。特征工程层这是整个系统最核心的部分也是决定模型效果上限的地方。它把原始数据加工成模型能吃的因子包括动量因子、波动率因子、估值因子、成长因子、资金流因子等几大类。模型推理层加载预训练好的时序模型对每只股票输出一个未来N日的预期收益打分。源码里默认用的是基于注意力机制的时序网络结构这个后面细说。组合构建层根据打分排序结合一些风控约束比如行业中性、市值中性输出最终的候选股票池。为什么这么分层因为量化系统最怕的就是“牵一发动全身”。你把数据源换了不应该影响模型结构你把模型换了不应该影响组合构建逻辑。分层清晰的好处是每一层都可以独立替换和调试。我在实际使用中就把数据采集层换成了自己维护的本地数据库因为公开接口拉全市场历史数据实在太慢了一次全量拉取要等十几分钟换成本地缓存之后秒出。2.2 为什么选时序模型而不是传统多因子回归这是很多人会问的问题。传统多因子模型比如Fama-French三因子、五因子用的是线性回归简单、可解释性强但它的假设是因子和收益之间是线性关系。现实中呢明显不是。举个例子动量因子在温和上涨时是正向的但在极端暴涨之后往往反转这种非线性关系线性模型根本捕捉不到。时序神经网络的优势在于它能自动学习因子之间的交互效应和时序依赖。比如某只股票过去20天的成交量变化模式和它未来5天的收益之间的关系可能是一种很复杂的非线性映射。模型通过大量历史样本的训练能把这种模式刻在参数里。但代价是什么可解释性差。你很难说清楚模型到底为什么给这只股票打了高分。源码里做了一些补救措施比如输出了因子重要度排序让你至少知道哪些因子在本次预测中贡献最大。这个功能我觉得挺实用的虽然不能完全解释模型决策但至少能帮你判断这个打分是不是“合理”。2.3 源码工程结构一览拿到源码之后我建议你先别急着跑花十分钟把目录结构看一遍。整个工程大概长这样ai_stock_picker/ ├── config/ │ ├── settings.yaml # 全局配置数据源、模型路径、回测参数都在这 │ └── factors.yaml # 因子定义和参数 ├── data/ │ ├── fetcher.py # 数据采集模块 │ ├── preprocessor.py # 数据清洗和对齐 │ └── cache/ # 本地缓存目录 ├── features/ │ ├── momentum.py # 动量类因子 │ ├── volatility.py # 波动率类因子 │ ├── valuation.py # 估值类因子 │ └── moneyflow.py # 资金流类因子 ├── model/ │ ├── network.py # 模型网络结构定义 │ ├── train.py # 训练脚本 │ └── predict.py # 推理脚本 ├── portfolio/ │ ├── optimizer.py # 组合优化 │ └── risk.py # 风控约束 ├── backtest/ │ ├── engine.py # 回测引擎 │ └── metrics.py # 绩效指标计算 └── main.py # 入口这个结构算是比较标准的量化项目布局。我特别喜欢它把因子定义单独放在factors.yaml里的做法你想加一个新因子只需要在yaml里注册一下然后在对应的py文件里实现计算逻辑就行不用改主流程代码。这种插件式的设计思路值得学习。3. 核心细节解析因子工程和模型结构3.1 因子计算的关键细节因子工程是整套系统的灵魂。源码里默认实现了大概二十多个因子我挑几个关键的说说计算逻辑和注意事项。动量因子这块源码用了多周期动量5日、10日、20日、60日。计算方式就是简单的区间收益率。但有个细节值得注意——它做了行业中性化处理。什么意思呢就是先把每只股票的动量减去它所在行业的平均动量得到超额动量。为什么要这么做因为如果不做中性化当某个行业整体大涨时该行业所有股票的动量因子都会很高模型会误以为这些股票都有alpha实际上只是行业beta。这个细节很多开源工具都忽略了但它是区分专业和业余的关键。波动率因子用的是过去20日收益率的标准差然后取倒数低波动率得分高。这里有个坑源码默认用的是日收益率标准差但如果你拿到的数据有停牌日停牌日的收益率是0会人为拉低波动率。所以预处理阶段必须把停牌日剔除掉。我在第一次跑的时候就遇到了这个问题模型给一堆长期停牌的票打了高分后来在preprocessor.py里加了一段停牌过滤逻辑才解决。资金流因子这块比较有意思。它用的是一个简化的资金流指标大单净流入除以总成交额。但源码里没有直接给出大单的定义我查了一下默认是按单笔成交金额超过50万来划分的。这个阈值对于不同市值的股票应该是不一样的对于小盘股50万可能就算大单了对于大盘股可能得500万。所以我在实际使用中把这个阈值改成了动态的——按当日成交额的0.1%来划分。这个调整让资金流因子的IC值从0.03提升到了0.05左右效果还是比较明显的。3.2 时序模型的结构选择源码里的模型结构是一个简化版的Transformer Encoder输入是过去60个交易日的因子序列输出是未来5日预期收益的标量。为什么选Transformer而不是LSTM我猜测作者的考虑是Transformer的注意力机制能更好地捕捉长距离依赖而且训练时可以并行化速度快。但实际跑下来我发现对于这种中小规模的金融时序数据LSTM的表现其实不比Transformer差而且训练更稳定。Transformer在小数据集上容易过拟合需要比较重的正则化。源码里用了Dropout0.3和权重衰减1e-4我试过把Dropout降到0.2验证集loss反而上升了说明0.3这个值是比较合理的。模型输入的特征维度是因子数量乘以序列长度。假设你有25个因子序列长度60那输入维度就是1500。这个维度不算高所以模型参数量控制在50万左右就够了。源码里的模型大概有3层Encoder每层4个注意力头隐藏维度128。这个配置在单张消费级显卡上就能训练我用一张RTX 3060跑完整个训练集大概花了40分钟。3.3 标签构造的讲究标签构造是很多人容易忽略但极其重要的一环。源码里默认用的是未来5日收益率但做了两个处理一是去极值把收益率限制在±20%以内防止极端值主导loss二是标准化把收益率减去当日全市场平均收益率再除以标准差得到的是超额收益的z-score。为什么要做超额收益而不是绝对收益因为A股市场有明显的系统性涨跌如果你用绝对收益做标签模型学到的可能主要是“什么时候市场整体会涨”而不是“哪些股票会跑赢市场”。用超额收益做标签模型才会专注于选股能力。但这里有个细节源码里计算超额收益时用的是全市场等权平均。实际上用市值加权平均更合理因为等权平均会被小盘股主导。我改成市值加权之后模型的选股能力用Rank IC衡量从0.06提升到了0.08。这个改动很小但效果提升明显。4. 实操过程从零跑通整套流程4.1 环境准备和依赖安装先把环境搭起来。我建议用conda创建一个独立环境避免和你现有的Python环境冲突conda create -n ai_stock python3.9 conda activate ai_stock pip install -r requirements.txtrequirements.txt里的核心依赖包括pytorch、pandas、numpy、scikit-learn、pyyaml、tqdm。版本方面pytorch建议用1.12以上因为源码里用到了torch.nn.TransformerEncoderLayer的一些新参数。我一开始用的是1.10版本跑起来报了一堆警告升级之后就好了。注意如果你没有GPU模型也能在CPU上跑但训练时间会从40分钟变成大概4个小时。推理阶段CPU和GPU的差距不大因为推理只需要前向传播。4.2 数据准备最耗时也最容易出错的环节数据这块我踩的坑最多详细说说。第一步是拉取股票列表。源码里默认拉取全市场所有正常交易的股票但我的建议是先过滤掉ST股、上市不满60个交易日的次新股、以及日均成交额低于5000万的票。为什么ST股有退市风险次新股没有足够的历史数据来计算因子低成交额的票流动性太差回测时容易产生虚假的超额收益因为实际根本买不进去。第二步是拉取历史行情数据。源码默认拉取过去3年的日线数据。这个时间窗口的选择有讲究太短了模型学不到完整的市场周期太长了早期数据和当前市场结构差异太大。3年大概覆盖了一轮完整的牛熊转换是比较合理的。我试过用5年数据模型效果反而下降了因为2018年的市场风格和现在差异太大引入了噪声。第三步是财务数据。这里有个时间对齐的坑财务数据是季度公布的但公布日期和报告期不一致。比如一季报的报告期是3月31日但实际公布可能是4月25日。如果你直接用报告期对齐就会引入未来信息look-ahead bias。源码里用的是一个近似处理——统一用报告期后推45天作为可用日期。这个处理不算精确但比直接用报告期好很多。更严谨的做法是用实际公布日期但那个数据不太好获取。第四步是数据清洗。主要处理三件事停牌日填充、缺失值处理、异常值修正。停牌日我前面说了直接剔除。缺失值用前向填充但如果连续缺失超过5天就把这只股票从当期样本中剔除。异常值用MAD法中位数绝对偏差来修正比3σ法更稳健。4.3 因子计算和预处理数据准备好之后跑因子计算脚本python -m features.momentum python -m features.volatility python -m features.valuation python -m features.moneyflow每个脚本会输出一个parquet文件存在data/cache/factors/目录下。然后跑预处理脚本做标准化和中性化python -m data.preprocessor --mode factor_preprocess这一步会做三件事截面标准化z-score、行业中性化、市值中性化。截面标准化是每个交易日单独做的不是全样本一起做这个细节很重要否则会引入时间维度的信息泄露。4.4 模型训练参数调优的实战记录训练脚本的入口是python -m model.train --config config/settings.yaml关键参数我列一下以及我实际调优后的建议值参数源码默认值我的建议值调整理由序列长度604060天太长早期信息对预测贡献小40天效果相当但训练快30%学习率1e-35e-4默认学习率太大loss震荡严重Batch size256512增大batch让梯度更稳定配合学习率调整Dropout0.30.3保持默认这个值经过验证比较合理训练轮数10060配合早停机制60轮足够收敛早停耐心值1015给模型更多耐心避免过早停止训练过程中要盯紧验证集的loss。如果训练loss持续下降但验证loss开始上升说明过拟合了需要增大Dropout或者减小模型规模。我遇到过验证loss在第35轮开始上升的情况把Dropout从0.3调到0.4之后验证loss一直降到第55轮才反弹最终模型效果提升了大概5%。4.5 回测验证怎么判断模型是不是真的有效训练完之后跑回测python -m backtest.engine --start 2023-01-01 --end 2024-12-31回测引擎会输出几个关键指标Rank IC预测打分和实际收益的秩相关系数衡量选股能力。一般超过0.05就算有效超过0.08算优秀。我这套调优后的模型在测试集上Rank IC均值是0.072ICIR是0.45。多空收益做多打分最高的10%股票做空打分最低的10%这个组合的累计收益。注意A股做空不容易这个指标主要是看模型的区分度。换手率每期调仓时股票变动的比例。换手率太高的话交易成本会吃掉大部分收益。我的模型日换手率大概在15%左右按双边千三的成本算年化拖累大概2-3个点。回测结果我就不贴具体数字了因为不同时间段差异很大。但有一点可以分享这套模型在震荡市和结构市里表现较好在单边普涨的牛市里反而跑不赢指数。原因很简单牛市里所有股票都涨模型选出来的票虽然涨得多一点但超额收益不明显。所以用它的时候要有个合理预期它不是全天候的。5. 常见问题与排查技巧实录5.1 数据相关的高频问题问题一拉取数据时报连接超时这个最常见尤其是用公开接口拉全市场数据的时候。解决方案是加重试机制和限速。源码里默认的重试次数是3次我改成了5次并且在每次请求之间加了0.5秒的延迟。另外建议把数据缓存到本地第二次跑的时候直接从缓存读不要重复请求。问题二财务数据和行情数据对齐后出现大量缺失检查一下你的股票列表里是不是包含了很多次新股。次新股没有历史财务数据对齐后自然全是缺失。解决办法是在数据准备阶段就把上市不满一年的股票过滤掉。问题三因子值出现极端异常有一次我跑完因子计算发现某只股票的动量因子值是正常值的几百倍。查了一下原来是这只股票在区间内有过一次10送90的除权但数据源没有做前复权处理。所以一定要确认你用的行情数据是前复权的否则所有价格相关的因子都会出错。5.2 模型训练中的典型故障故障一Loss变成NaN这是训练中最常见的问题。原因通常是学习率太大或者数据里有异常值。排查步骤先把学习率降到1e-4试试如果还是NaN检查输入数据有没有inf或者极大的值。我在第一次训练时就遇到了这个问题最后发现是某个因子的标准化出了问题分母接近零导致数值爆炸。故障二模型输出所有股票打分几乎一样这说明模型没有学到任何有区分度的信息。可能的原因有三个一是因子本身没有预测能力可以单独算一下每个因子的IC二是模型容量太小增加隐藏层维度试试三是训练数据太少增加历史数据长度。故障三回测收益很好但实盘完全不行这是最危险的情况通常是未来函数导致的。重点检查三个地方财务数据的可用日期是否正确、因子计算是否用到了未来数据、回测时的调仓价格是否用了当日收盘价应该用次日开盘价。我踩过一次坑回测年化收益30%实盘跑了三个月亏了8%最后发现是调仓价格用了当日收盘价而实际上你不可能在收盘那一刻知道打分结果并完成交易。5.3 常见问题速查表问题现象可能原因排查方向解决方案数据拉取超时网络不稳定/接口限流检查网络、查看接口返回码加重试限速本地缓存因子值异常未做前复权/除权除息检查价格数据复权方式统一使用前复权数据Loss变NaN学习率过大/数据异常检查输入数据范围降学习率数据截断打分无区分度因子无效/模型太小单独计算因子IC换因子/增大模型回测实盘差异大未来函数/交易成本检查数据对齐和调仓逻辑严格时间对齐计入成本训练过拟合模型复杂/数据少对比训练和验证loss增大Dropout/早停换手率过高因子变化太快分析因子自相关性加平滑/降低调仓频率6. 我实际使用后的几点真实体会这套工具我前后用了大概两个月中间经历了几次大的调整。说几个我觉得最有价值的经验。第一不要迷信默认参数。源码里的参数是一套通用配置但每个人的数据源、股票池、回测区间都不一样默认参数大概率不是最优的。我建议你至少把序列长度、学习率、Dropout这三个参数各试三组值做个简单的网格搜索。我自己的经验是光是调整这三个参数Rank IC就能从0.05提升到0.07。第二因子比模型重要。我试过用同样的模型结构换不同的因子组合效果差异巨大。好的因子组合能让一个简单的线性模型跑出不错的效果而烂的因子组合就算上最复杂的深度模型也没用。所以如果你时间有限优先花时间在因子挖掘和筛选上而不是调模型结构。第三风控不能省。源码里的组合构建层有行业中性化和市值中性化的约束我一开始觉得这些约束会限制收益就把它去掉了。结果回测最大回撤从15%飙升到35%。后来乖乖把约束加回来收益虽然低了一点但回撤控制住了夏普比率反而更高。这个教训告诉我量化系统里风控不是可选项是必选项。第四实盘和回测的差距主要来自交易成本和冲击成本。回测里你假设可以按收盘价无限量成交但实盘里你的买单会推高价格卖单会压低价格。尤其是小盘股冲击成本可能高达千分之几。所以我在实盘时会把股票池限制在日均成交额1亿以上的票虽然牺牲了一些收益但换来了更好的可执行性。最后分享一个小技巧这套工具的模型输出是一个连续打分你可以不直接按打分排序选股而是把打分作为条件选股的增强因子。比如你先用传统方法筛出符合基本面的100只票然后再用AI打分从中选出前20只。这样既保留了基本面过滤的可靠性又加入了AI的选股能力实测下来比纯AI选股更稳。