☰
AI量化投资系统实战:从数据清洗到风控的完整链路解析
2026/10/26 5:52:35 网站建设 项目流程

最近看了一些关于 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\activate

3.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 滚动窗口训练:避免时间穿越

金融数据不是独立同分布的,今天和明天之间存在序列相关性。如果把数据随机打乱再做交叉验证,模型会学到未来信息,导致回测结果虚高。

正确做法是使用时序切分:

  1. 按时间排序数据。
  2. 前 70% 的时间段作为训练集,后 30% 作为测试集。
  3. 如果数据量足够大,可以使用滚动训练:不断把最新数据加入训练集,丢弃最旧数据,让模型适应市场变化。

本案例中,我们会采用一次性的时间切分来保持代码简单,但会说明滚动窗口的扩展方式。

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 执行简版回测并输出绩效

回测的核心逻辑是:

  1. 每一天,在当日可交易的股票中,选出评分最高的 2 只。
  2. 假设两只股票等权持有。
  3. 持有一天,计算组合当日的平均收益。
  4. 累计起来得到组合净值曲线。
  5. 计算年化收益、年化波动率、夏普比率、最大回撤。
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 回测盈利、实盘亏损

这是量化投资里最经典的落差,原因集中在四点:

  1. 回测没算交易成本。
  2. 回测假设可以按收盘价无限成交。
  3. 策略在小资金量下没问题,但资金量变大后冲击成本上升。
  4. 策略在历史数据上被反复调参,产生了过拟合。

解决方向不是追求更复杂的模型,而是先从回测引擎下手,把成交限制、成本模型、滑点、涨跌停、停牌全部加上,让回测结果更接近真实。

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 选股流水线,包含合成数据生成、特征计算、随机森林模型训练和简版回测。你可以把这份代码作为起点,逐步加入以下内容继续练习:

  1. 使用真实历史行情数据,替换合成数据。
  2. 增加更多因子,如成交量因子、基本面因子、行业因子。
  3. 用 LightGBM 替换随机森林,对比模型效果。
  4. 在回测中加入手续费、滑点、涨跌停限制。
  5. 用滚动训练替代一次性切分,观察模型稳定性。
  6. 添加风险监控模块,模拟极端行情下的组合表现。

如果遇到这篇文章里没有覆盖的报错或问题,欢迎在评论区把报错信息和你的代码结构发出来,一起讨论。也可以先收藏本文,需要的时候直接对照流程排查。

把这些关键步骤亲手跑通一遍,你才会真正理解“AI 赋能投资”这几个字背后的工程分量。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询