最近看了一些关于 Two Sigma 的公开访谈和工程分享,里面关于“AI 如何真正落地到投资场景”的内容,反复在讲几件事:数据质量、特征工程、回测的坑,以及模型失效时的风险控制。这些观点放到非金融的 AI 工程里也同样成立。这篇文章不打算逐字复述访谈,而是把其中核心的技术思想提取出来,再结合一个可运行的量化投资最小案例,拆解 AI 投资系统的完整链路。
适合的读者有三类:一是刚开始接触量化投资的算法工程师,想理解模型之外还有哪些关键环节;二是做 AI 平台建设、想了解金融场景如何落地机器学习的技术人;三是正在学习 Python 数据分析、想找一个综合性实战项目的开发者。
读完本文,你会理解 AI 投资的整体架构,掌握数据、特征、模型、回测、风控五个核心环节,并拿到一套可以直接运行的 Python 最小选股流水线。
先说一句必要的提醒:本文所有内容仅作为技术学习和工程思路参考,不构成任何投资建议。
1. Two Sigma 是谁,它的 AI 投资思路为什么值得学
1.1 Two Sigma 在量化投资行业的定位
Two Sigma 是一家成立于 2001 年的量化投资管理公司,创始人是 David Siegel 和 John Overdeck。业内对它的普遍印象是:一家更像科技公司的投资机构。它很早就把机器学习、人工智能、大数据和分布式计算应用到投资研究里,而不是靠传统的人工基本面分析为主。
在 Two Sigma 的公开资料里,经常出现几个关键词:
- 数据(Data)
- 研究平台(Research Platform)
- 机器学习(Machine Learning)
- 严谨回测(Rigorous Backtesting)
- 风险管理(Risk Management)
这里值得注意的一点是:Two Sigma 并不把自己定位成“纯 AI 公司”,而是金融公司里最会做 AI 工程化的那一类。它强调的不是某个模型多厉害,而是整套系统的稳定性和可扩展性。
1.2 AI 赋能投资的本质不是“用模型替代人”
关于“AI 赋能投资”,很多人的第一反应是:AI 是不是能自动选股、自动交易,然后躺赚?真实的量化投资远没有这么简单。
Two Sigma 的公开观点更接近下面这个模型:
人类研究员负责提出假设、定义问题、分析异常 AI 系统负责处理海量数据、生成特征、训练预测模型 风控与工程师负责控制回撤、监控模型漂移、保障系统稳定也就是说,AI 在投资中的作用是“放大器”和“加速器”,它把研究员原本需要几周才能完成的验证过程压缩到几小时,同时能够持续处理人类无法手工阅读的海量数据。但它并没有消除投资决策中的不确定性。
在实际工程中,这意味着我们要构建的不是一个“会预测涨跌的神奇模型”,而是一条完整的流水线:
数据接入 -> 数据清洗 -> 特征计算 -> 模型训练 -> 回测验证 -> 模拟交易 -> 实盘执行 -> 风险监控后面所有的技术内容,都会围绕这条流水线展开。
2. AI 投资系统的整体架构
从工程视角看,一套 AI 投资系统可以拆成五个层级。
2.1 数据层
数据是 AI 投资的起点。除了传统的量价数据(开盘价、收盘价、成交量、成交额),Two Sigma 这类机构还会使用大量另类数据,比如:
- 卫星拍摄的停车场车辆数,用于估计零售企业的经营情况
- 信用卡交易流水,用于推断消费趋势
- 天气数据,用于分析农业、能源等行业的短期波动
- 新闻情绪、社交舆情数据,用于事件驱动策略
但是,数据层最大的成本不是买数据,而是清洗数据。真实数据里普遍存在缺失值、异常值、复权价格调整、财报口径变化、公司停牌等问题。任何一步处理不到位,都会直接影响后续特征和模型的质量。
2.2 特征层
特征层是把原始数据加工成模型可用输入的地方。在量化投资里,特征通常被称为因子(Factor)。
常见的因子包括:
- 动量因子:过去 N 天的累计收益率
- 反转因子:过去 N 天涨多了,未来可能跌
- 波动率因子:过去 N 天的收益率标准差
- 流动性因子:成交量、换手率、买卖价差
- 基本面因子:市盈率、市净率、营收增速
特征层的核心挑战是:如何避免前视偏差(Look-ahead Bias)。简单说,就是用 T 日的数据去预测 T+1 日的收益,特征里不能包含任何 T+1 日才知道的信息。
2.3 模型层
模型层负责学习特征与未来收益之间的关系。传统量化里常用线性回归、时间序列模型;现在的 AI 量化系统更多使用:
- 梯度提升树:XGBoost、LightGBM,适合表格型特征
- 神经网络:LSTM、Transformer,适合序列数据和另类数据
- 深度强化学习:用于优化交易执行,而不是直接预测涨跌
Two Sigma 在公开分享中特别强调:模型复杂度不是第一优先级,稳定性和可解释性才是。一个简单模型如果特征扎实、数据干净,往往比一个复杂模型在实盘中的表现更稳定。
2.4 回测与执行层
回测是用历史数据验证策略收益和风险的过程。一套合格的回测系统必须处理:
- 交易成本:佣金、印花税、滑点
- 涨跌停限制:涨停时买不进,跌停时卖不出
- 最小交易单位:A 股一手是 100 股
- 停牌股票不可交易
- 下单延迟与市场冲击
如果回测系统过于简化,结果往往非常漂亮,但实盘表现会大打折扣。这也是 Two Sigma 反复提醒的“回测偏差”问题。
2.5 风险与监控层
金融场景中,风险控制比收益更重要。模型上线后必须持续监控几个关键指标:
- 策略净值回撤(最大回撤超过阈值时是否需要降仓)
- 行业暴露、风格暴露是否漂移
- 模型预测分布是否发生了显著变化
- 数据源是否出现缺失或延迟
- 交易执行是否偏离预期
一个优秀的 AI 投资系统,不是追求每笔都赚,而是确保长期风险可控、在极端行情下不会爆仓。
3. 环境准备与项目结构
下面进入实操环节。我们通过一个 Python 项目,模拟从数据生成到模型回测的完整流程。
3.1 运行环境
- 操作系统:Windows / macOS / Linux 均可
- Python 版本:3.8 或更高,推荐 3.10+
- 主要依赖库:
pandas numpy scikit-learn模型部分为了简单可视化,也可以额外安装 matplotlib,但本案例不强制使用。
安装方式:
pip install pandas numpy scikit-learn matplotlib如果你的环境里同时存在 Python 2 的环境,建议使用虚拟环境:
python -m venv quant_ai_venv source quant_ai_venv/bin/activate # Windows 下为 quant_ai_venv\Scripts\activate3.2 项目目录
我们创建一个名为quant_ai_demo的目录:
quant_ai_demo/ ├── data_generator.py # 生成合成行情数据 ├── pipeline.py # 主流程:特征、模型、回测 └── README.md实际项目中,更合理的结构会拆得更细:
quant_research/ ├── data/ │ ├── raw_data/ # 原始数据 │ └── processed_data/ # 清洗后数据 ├── features/ │ ├── factor_calc.py # 因子计算 │ └── factor_store.py # 因子存储 ├── models/ │ ├── train.py # 训练脚本 │ ├── predict.py # 预测脚本 │ └── evaluate.py # 模型评估 ├── backtest/ │ ├── engine.py # 回测引擎 │ └── report.py # 回测报告 └── risk/ └── monitor.py # 风险监控案例为了便于学习和展示,会简化成两个文件,但逻辑分层保持清晰。
4. 核心原理拆解:AI 投资中的关键工程要点
4.1 为什么特征比模型更关键
Two Sigma 的公开访谈里反复提到,他们花费大量时间在数据验证和特征工程上。原因很简单:机器学习模型只能学习数据中已有的规律,如果特征本身没有预测能力,换任何复杂模型都很难产生稳定的 alpha。
在时序金融数据上做特征工程,要特别小心以下问题:
- 用未来数据:这是最严重的错误,属于标签泄漏
- 用全样本做标准化:会引入未来统计量,在训练阶段就“偷看”了未来分布
- 因子与标签之间的因果关系不成立:比如用当天收盘后的网络舆情预测当天收益率
因此,在实际项目中,特征计算通常是逐日滚动进行的,每个特征只能使用“截至当日已经发生”的数据。
4.2 滚动窗口训练:避免时间穿越
金融数据不是独立同分布的,今天和明天之间存在序列相关性。如果把数据随机打乱再做交叉验证,模型会学到未来信息,导致回测结果虚高。
正确做法是使用时序切分:
- 按时间排序数据。
- 前 70% 的时间段作为训练集,后 30% 作为测试集。
- 如果数据量足够大,可以使用滚动训练:不断把最新数据加入训练集,丢弃最旧数据,让模型适应市场变化。
本案例中,我们会采用一次性的时间切分来保持代码简单,但会说明滚动窗口的扩展方式。
4.3 标签的定义决定模型上限
在监督学习中,标签决定了模型学习的方向。在 AI 选股场景里,常见标签有:
- 分类标签:未来 N 日收益率是否大于 0
- 回归标签:未来 N 日收益率的具体数值
- 排序标签:未来 N 日收益率的横截面排名
不同的标签会让模型变成不同的任务。分类任务简单易用,但丢失了收益率幅度信息;回归任务能预测具体收益,但噪声更大;排序任务更适合投资场景,因为它关注的是“哪只股票会更好”,而不是“绝对收益率是多少”。
本案例使用分类标签:未来 1 日收益率是否大于 0。
4.4 回测偏差:一个隐蔽但致命的坑
即使模型在训练集和测试集上都表现稳定,回测结果仍然可能失真,常见原因包括:
| 偏差类型 | 说明 | 常见来源 |
|---|---|---|
| 前视偏差 | 使用未来信息计算特征或信号 | 财报数据未做滞后处理 |
| 幸存者偏差 | 只选择当前仍存在的股票,忽略了退市股票 | 股票池未包含历史退市样本 |
| 过拟合偏差 | 回测参数被反复调整到历史最优 | 在回测集上不断试参数 |
| 交易成本偏差 | 回测忽略滑点、手续费、冲击成本 | 回测引擎未建模成交限制 |
避免回测偏差的关键不是消除某一个偏差,而是建立一套完整的“白盒回测机制”:每一次回测都详细记录数据版本、特征版本、模型参数、交易成本假设,确保任何结果都可以复现。
5. 完整实战:搭建一个最小 AI 选股流水线
下面我们实现一个完整可运行的 AI 选股最小案例。案例包含四个步骤:
- 生成合成行情数据
- 构造特征与标签
- 训练随机森林分类模型
- 执行简版回测并输出绩效指标
5.1 生成合成行情数据
我们先创建data_generator.py,用来生成模拟股票价格数据。这里使用几何随机游走,并加入一个微弱的动量效应,让数据不是完全随机。
# 文件路径:quant_ai_demo/data_generator.py import numpy as np import pandas as pd def generate_market_data(periods=1200, n_stocks=8, seed=42): """ 生成合成行情数据。 参数 ---- periods : int 交易日数量,默认 1200,约等于 5 年。 n_stocks : int 股票数量,默认 8。 seed : int 随机种子,便于结果复现。 返回 ---- DataFrame,行索引为日期,列名为 stock_0 ~ stock_{n_stocks-1}。 """ np.random.seed(seed) dates = pd.date_range("2020-01-01", periods=periods, freq="B") all_prices = {} for i in range(n_stocks): # 每只股票有不同的漂移项,模拟行业差异 drift = np.random.normal(0.0002, 0.0005) # 随机收益率 returns = np.random.normal(drift, 0.02, periods) # 加入微弱的动量:如果前一天上涨,今天更可能继续小幅上涨 for t in range(1, periods): if returns[t - 1] > 0: returns[t] += 0.0003 # 从 100 元起步,累积价格 price = 100 * np.exp(np.cumsum(returns)) all_prices[f"stock_{i}"] = price df = pd.DataFrame(all_prices, index=dates) return df这里有两个细节值得说明:
- 使用
np.exp(np.cumsum(returns))生成价格序列,可以避免价格变成负数。 - 加入的微弱动量项会让模型有一定学习空间,但不保证一定能获得显著超额收益,这是刻意为之,更接近真实世界的噪声环境。
5.2 构造特征与标签
创建pipeline.py,先写特征与标签构建函数。
# 文件路径:quant_ai_demo/pipeline.py import numpy as np import pandas as pd from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score from data_generator import generate_market_data def build_features_and_labels(df, feat_window=10): """ 将原始行情数据转换为特征和标签。 参数 ---- df : DataFrame 每股一列的价格数据。 feat_window : int 回看窗口,特征最多使用最近 N 个交易日数据。 返回 ---- DataFrame,包含特征列、标签列和未来收益列。 """ frames = [] for col in df.columns: tmp = pd.DataFrame(index=df.index) # 基础特征:收益率 tmp["stock"] = col tmp["ret_1"] = df[col].pct_change(1) tmp["ret_5"] = df[col].pct_change(5) tmp["ret_10"] = df[col].pct_change(10) # 波动率特征 tmp["vol_5"] = tmp["ret_1"].rolling(5).std() tmp["vol_10"] = tmp["ret_1"].rolling(10).std() # 标签与未来收益 # 未来 1 日收益率 tmp["future_ret_1"] = df[col].pct_change(1).shift(-1) # 标签:未来 1 日收益是否为正 tmp["label"] = (tmp["future_ret_1"] > 0).astype(int) frames.append(tmp) all_data = pd.concat(frames) all_data = all_data.dropna() return all_data这段代码的核心思想是:每只股票独立计算特征,因此特征列只依赖股票自身历史行情。shift(-1)表示我们把未来一期的收益率移动到当前行,作为标签或预测目标。
这里要注意,真实项目中还会有市场整体因子、行业因子,以及个股横截面相对强度等特征。本案例为了突出流程,只取了最简单的时序特征。
5.3 训练模型并生成预测
继续在pipeline.py中加入模型训练函数。
def train_and_predict(all_data, train_ratio=0.7, n_estimators=100): """ 切分训练集/测试集,训练随机森林,返回预测结果和模型。 """ feature_cols = ["ret_1", "ret_5", "ret_10", "vol_5", "vol_10"] # 保证按时间排序 all_data = all_data.sort_index() X = all_data[feature_cols].values y = all_data["label"].values # 按时间顺序切分,不能随机打乱! split_idx = int(len(all_data) * train_ratio) X_train, X_test = X[:split_idx], X[split_idx:] y_train, y_test = y[:split_idx], y[split_idx:] model = RandomForestClassifier( n_estimators=n_estimators, max_depth=5, random_state=42, ) model.fit(X_train, y_train) prob_test = model.predict_proba(X_test)[:, 1] pred_test = (prob_test >= 0.5).astype(int) acc = accuracy_score(y_test, pred_test) print(f"测试集准确率: {acc:.4f}") # 把预测概率放回测试数据中,便于后续回测 test_data = all_data.iloc[split_idx:].copy() test_data["prob"] = prob_test return test_data, model, feature_cols随机森林的predict_proba可以输出预测为“上涨”的概率,这个概率可以被当作股票评分。评分越高,代表模型认为该股票未来上涨的可能性越大。
5.4 执行简版回测并输出绩效
回测的核心逻辑是:
- 每一天,在当日可交易的股票中,选出评分最高的 2 只。
- 假设两只股票等权持有。
- 持有一天,计算组合当日的平均收益。
- 累计起来得到组合净值曲线。
- 计算年化收益、年化波动率、夏普比率、最大回撤。
def run_backtest(test_data, top_k=2): """ 极简回测:每天持有评分最高的 top_k 只股票,等权持仓。 注意:这是一个教学级回测,没有考虑手续费、滑点、停牌等细节。 """ test_data = test_data.sort_index() dates = test_data.index.unique() daily_returns = [] for dt in dates: day_df = test_data.loc[dt] # 如果某天股票数据不足,跳过 if isinstance(day_df, pd.Series): day_df = day_df.to_frame().T if len(day_df) < top_k: continue # 选评分最高的 top_k 只股票 top_k_df = day_df.nlargest(top_k, "prob") # 等权组合收益(简化版,没考虑交易成本) daily_ret = top_k_df["future_ret_1"].mean() daily_returns.append({"date": dt, "daily_ret": daily_ret}) ret_df = pd.DataFrame(daily_returns).set_index("date") ret_df["cum_ret"] = (1 + ret_df["daily_ret"]).cumprod() # 基准:全部股票等权,每天再平衡 bench_daily = test_data.groupby(test_data.index)["future_ret_1"].mean() bench_cum = (1 + bench_daily).cumprod() # 计算策略绩效指标 strategy_ann_ret = ret_df["daily_ret"].mean() * 252 strategy_ann_vol = ret_df["daily_ret"].std() * np.sqrt(252) sharpe = strategy_ann_ret / strategy_ann_vol if strategy_ann_vol > 0 else 0 # 最大回撤 cum_max = ret_df["cum_ret"].cummax() drawdown = ret_df["cum_ret"] / cum_max - 1 max_drawdown = drawdown.min() benchmark_ann_ret = bench_daily.mean() * 252 print("===== 策略绩效摘要(教学示例) =====") print(f"策略年化收益: {strategy_ann_ret:.2%}") print(f"策略年化波动: {strategy_ann_vol:.2%}") print(f"夏普比率: {sharpe:.2f}") print(f"最大回撤: {max_drawdown:.2%}") print(f"基准年化收益: {benchmark_ann_ret:.2%}") return ret_df, bench_cum def main(): print("1. 生成合成行情数据") df = generate_market_data(periods=1200, n_stocks=8, seed=42) print("2. 构建特征与标签") all_data = build_features_and_labels(df) print("3. 训练模型并预测") test_data, model, feature_cols = train_and_predict(all_data) print("4. 执行简版回测") ret_df, bench_cum = run_backtest(test_data, top_k=2) if __name__ == "__main__": main()5.5 运行与预期结果
在项目目录下执行:
python pipeline.py预期会输出类似下面的内容:
1. 生成合成行情数据 2. 构建特征与标签 3. 训练模型并预测 测试集准确率: 0.5xxx 4. 执行简版回测 ===== 策略绩效摘要(教学示例) ===== 策略年化收益: X.XX% 策略年化波动: XX.XX% 夏普比率: X.XX 最大回撤: -XX.XX% 基准年化收益: X.XX%由于我们设置了随机种子,输出是稳定可复现的。但需要理解,这只是演示流程,不代表真实市场中的策略水平。
如果你希望看到特征重要性,可以在train_and_predict中加一行:
print(dict(zip(feature_cols, model.feature_importances_)))真实场景里,特征重要性分析能帮你发现哪些因子对预测贡献最大,方便后续进一步迭代。
6. 常见问题与排查思路
在 AI 投资系统的开发过程中,下面这些问题几乎是必踩的坑。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 模型准确率很高(超过 90%) | 标签泄漏,特征包含未来信息 | 检查是否使用shift()滞后,检查特征时序 |
| 回测表现极好,实盘表现极差 | 回测交易成本设置过低,或模型过拟合 | 增加手续费、滑点模型,减少参数搜索次数 |
| 模型上线后每月收益持续衰减 | 市场风格切换,因子失效 | 建立因子监控体系,定期重训练模型 |
| 股票停牌导致回测买入失败 | 回测引擎未处理停牌状态 | 根据交易所停牌数据过滤不可交易日期 |
| 预测概率长期集中在 0.5 附近 | 特征本身区分度不高,或任务难度大 | 寻找更有效的因子,考虑使用横截面标签 |
| 训练集和测试集分布差异明显 | 市场发生结构变化 | 使用滚动训练,减小单次训练窗口 |
下面展开两个最典型的问题。
6.1 模型“准确率”很高但实际没用
如果你的分类模型在历史数据上准确率接近 100%,大概率不是发现了圣杯,而是发生了数据泄漏。最常见的一种泄漏是:
假设我们要用 T 日收盘后的数据预测 T 日收益,但实际代码里用了 T 日的最终收益去算特征。这等于答案已经写进题目里了。
排查方法:
- 把特征和标签按时间线画出来,确认特征总是早于标签。
- 检查所有
shift()参数的位置。 - 对任意一行样本,确认它用到的所有历史窗口都不包含未来数据。
6.2 回测盈利、实盘亏损
这是量化投资里最经典的落差,原因集中在四点:
- 回测没算交易成本。
- 回测假设可以按收盘价无限成交。
- 策略在小资金量下没问题,但资金量变大后冲击成本上升。
- 策略在历史数据上被反复调参,产生了过拟合。
解决方向不是追求更复杂的模型,而是先从回测引擎下手,把成交限制、成本模型、滑点、涨跌停、停牌全部加上,让回测结果更接近真实。
7. 从 Two Sigma 的访谈中总结的最佳实践
Two Sigma 的公开分享虽然讲的是投资场景,但背后的工程思想完全可以迁移到其他 AI 应用领域。这里总结五条我认为最值得记住的建议。
7.1 数据质量 > 模型复杂度
很多 AI 项目失败,不是模型不够先进,而是数据质量不过关。字段缺失、口径不一致、时间对齐错误,任何一个问题都会让模型学到错误的模式。
建议建立数据管道的自动校验机制,比如:
- 检查数据是否有重复日期
- 检查收益率是否超出合理范围
- 检查数据缺失比例是否超过阈值
- 记录每个数据文件的生成时间和版本号
7.2 模型要上线,先设计监控
很多团队花三个月训练模型,却只花一个下午做上线,这是反过来的。模型监控不是上线后的事,而是在设计阶段就要考虑的系统能力。
对于投资场景,至少要监控:
- 模型的预测概率分布是否发生漂移
- 特征分布是否发生显著变化
- 策略收益是否偏离预期区间
- 实际成交价格与模型假设的成交价格差异
7.3 可解释性不是非要线性模型
Two Sigma 并不是要求所有模型都可解释,而是强调“风险可解释”。也就是说,我们不需要完全理解模型内部每一个参数,但需要知道:
- 模型在哪些情况下容易失效
- 模型的预测是否过度集中在某些行业
- 模型是否在利用某个已经失效的历史规律
这可以通过特征重要性分析、Shapley 值分析、行业暴露分析等方法实现。
7.4 工程平台能力决定研究效率
Two Sigma 的内部研究平台,本质上是一个数据、计算、回测一体化的研发环境。研究员可以在平台上快速完成“提出假设 -> 找数据 -> 算特征 -> 跑回测 -> 看结果”的闭环。
对中小团队来说,即使没有自研平台,也可以通过以下组合搭建类似能力:
- 使用统一的数据仓库存储原始数据和特征数据
- 使用 Jupyter Notebook 做研究原型验证
- 使用 Airflow 调度每日数据更新和特征计算
- 使用 MLflow 记录每次实验的模型参数和指标
关键是让每一次实验都可复现、可比较、可追溯。
7.5 守好风险管理边界
在金融场景里,AI 模型输出的是“建议”,而不是“最终决策”。风险约束应该在模型之外单独存在,比如:
- 单只股票持仓上限
- 单一行业暴露上限
- 最大回撤触发降仓机制
- 极端行情下的熔断开关
这种“模型做预测、规则做约束”的架构,即使模型某个阶段失效,系统整体也不会失控。这个思路对其他 AI 应用同样适用,比如风控模型、推荐系统的兜底策略。
8. 总结与学习路线
这篇文章从 Two Sigma 的公开访谈和工程分享出发,梳理了 AI 赋能投资的完整链路:数据层、特征层、模型层、回测层、风险监控层。核心结论是,AI 在投资里不是玄学,而是一套系统工程;数据质量、特征设计、回测严谨性和风险约束,往往比模型算法本身更决定成败。
文中给出了一套最小可运行的 Python 选股流水线,包含合成数据生成、特征计算、随机森林模型训练和简版回测。你可以把这份代码作为起点,逐步加入以下内容继续练习:
- 使用真实历史行情数据,替换合成数据。
- 增加更多因子,如成交量因子、基本面因子、行业因子。
- 用 LightGBM 替换随机森林,对比模型效果。
- 在回测中加入手续费、滑点、涨跌停限制。
- 用滚动训练替代一次性切分,观察模型稳定性。
- 添加风险监控模块,模拟极端行情下的组合表现。
如果遇到这篇文章里没有覆盖的报错或问题,欢迎在评论区把报错信息和你的代码结构发出来,一起讨论。也可以先收藏本文,需要的时候直接对照流程排查。
把这些关键步骤亲手跑通一遍,你才会真正理解“AI 赋能投资”这几个字背后的工程分量。