对于做量化和 AI 工程的人来说,Two Sigma 几乎是绕不开的名字。这家成立于 2001 年的对冲基金管理着数百亿美元资产,核心打法是用机器学习、分布式计算和大数据分析来寻找市场中的非有效性。这次高质量访谈的内容不算长,但信息密度很高,涉及 AI 赋能投资的完整链路:数据怎么处理、模型怎么训练、研究怎么落地到生产、风险怎么控制。这篇博客就把访谈里的关键工程逻辑拆开,按“数据 -> 特征 -> 模型 -> 组合 -> 生产 -> 风控”的顺序展开,并补充可落地的代码示例和验证思路。
如果你是做量化研究、AI 工程、数据挖掘的读者,或者正想把机器学习用到金融场景里,这篇文章可以直接收藏。我们重点解决三个问题:Two Sigma 式的方法论到底强在哪里,AI 系统在投资链路里每个环节承担什么职责,以及普通团队能从中学到什么。
1. 高质量访谈的核心信息
Two Sigma 的研究思路不是“拿一个现成模型去预测股价”,而是把投资问题拆成一个系统工程。从访谈中可以提炼出几条关键原则。
1.1 数据比模型更早决定上限
访谈反复强调的一点是,模型结构只是整个系统里的一环。真正拉开差距的是数据覆盖度、数据质量、数据更新速度和数据间的关联方式。Two Sigma 会把新闻、财报、行情、另类数据、交易行为数据放在统一框架里处理,再用模型去发现这些数据之间的统计规律。
这意味着在实际工程中,数据清洗、对齐、去重、复权和特征计算的优先级,要高于选哪个模型框架。
1.2 机器学习解决的是“预测”问题,不是“决策”问题
Two Sigma 的常见路径是:先用机器学习模型对收益、波动、风险因子做预测,再把预测结果交给组合优化和风险控制模块。决策层仍然有严格的约束条件,比如行业暴露、个股集中度、换手率限制、流动性控制。
这个分层设计非常值得学习。它避免了一个常见错误——让模型直接输出“买什么”或“卖什么”。模型输出的是概率分布或期望收益,最终交易动作由风控体系确认。
1.3 研究到生产之间有一条完整的工程链路
访谈里最打动人的不是某个模型的精度,而是“研究端”和“生产端”如何协同。研究员在离线环境里跑回测,得到的策略并不是直接上线,而是经过纸面交易、模拟盘、小资金实盘、逐步放大资金这样一个验证链条。
这个链条需要工程团队提供:
- 稳定的数据管道;
- 可以复现的研究环境;
- 统一的特征计算服务;
- 低延迟或准实时的推理服务;
- 完善的日志与监控。
所以 Two Sigma 不是“招几个算法工程师就能复制”的公司,它更像一家把研究能力工程化的科技公司。
2. 数据体系设计
AI 赋能投资的第一步是建设数据体系。从工程角度看,这通常包含四层。
| 层级 | 职责 | 常见技术 |
|---|---|---|
| 数据接入层 | 从行情源、资讯源、第三方数据商获取原始数据 | Kafka、Flink、Datafeed Job |
| 数据存储层 | 保存 tick、分钟、日线、财务、舆情等数据 | ClickHouse、DuckDB、Parquet、S3 |
| 数据加工层 | 做清洗、对齐、复权、去重、特征衍生 | Spark、Polars、Ray |
| 数据服务层 | 以统一 API 向研究和生产提供特征和样本 | FastAPI、gRPC、Feature Store |
2.1 数据对齐是最大的隐性成本
股票数据最大的坑是时间对齐。不同数据源的时间戳格式不一样,有的用本地时间,有的用交易所时间,有的还带时区信息。如果对齐做得不好,模型学到的所谓“规律”很有可能来自时间错位。
下面是一段典型的数据清洗逻辑,建议直接用在特征管道里。
import pandas as pd def align_market_data( price: pd.DataFrame, volume: pd.DataFrame, freq: str = "1min" ) -> pd.DataFrame: # 统一时间索引,向前填充行情,避免未来数据 price = ( price.sort_index() .resample(freq) .last() .ffill() ) volume = ( volume.sort_index() .resample(freq) .sum() .fillna(0) ) df = price.join(volume, how="inner") # 去掉非交易时段 df = df.between_time("09:30", "15:00") return df这里的关键点是:
resample把不同频率的数据统一到同一时间轴;ffill保证价格在停牌区间不会出现空值;sum聚合成交量;between_time过滤非交易时段。
如果忽略数据对齐,后续所有因子计算都可能出现“未来函数”,这是量化研究里最致命的问题。
2.2 特征存储
研究环境里常出现一个现象:研究员花了很多时间计算特征,但生产环境又用另一套代码重新算了一遍,结果两边数值对不上。Two Sigma 这类机构的解法是把特征统一注册到特征存储系统。
一个简化版的特征注册表可以这样做。
# feature_store.py FEATURE_REGISTRY = {} def register_feature(name: str, version: str = "v1"): def decorator(func): FEATURE_REGISTRY[name] = {"version": version, "fn": func} return func return decorator @register_feature("momentum_5d") def momentum_5d(df: pd.DataFrame) -> pd.DataFrame: return df["close"].pct_change(5) @register_feature("volume_zscore_20d") def volume_zscore_20d(df: pd.DataFrame) -> pd.DataFrame: return (df["volume"] - df["volume"].rolling(20).mean()) / df["volume"].rolling(20).std()有了注册表之后,研究和生产可以用同一份特征定义,避免“研究时用 A 逻辑,生产时用 B 逻辑”的问题。
3. 机器学习在投资流程中的落地方式
Two Sigma 的访谈谈到机器学习时,强调的是“预测、置信度、不确定性”而不是“预测得很准”。在一个充满噪声的市场里,模型的价值在于给出有区分度的预测,并且能判断什么时候不该下注。
3.1 预测目标设计
模型不是直接预测“明天涨还是跌”,而是预测一个更稳定的目标,比如未来一段时间内的收益排序、波动率区间、因子暴露。用回归还是分类,取决于信号本身的性质。
一个常见的预测目标设计是:未来 5 日的收益率排名。
import numpy as np import pandas as pd def create_label( close: pd.Series, horizon: int = 5 ) -> pd.Series: # 未来 horizon 日的收益率 future_return = close.shift(-horizon) / close - 1.0 # 按截面排名,转为相对强弱标签 rank = future_return.rank(axis=1, pct=True) return rank这里加入rank的意义非常重大。直接用收益率做标签,会受到市场整体涨跌的影响;用截面排名做标签,可以过滤掉系统性风险,让模型专注于个股之间的相对强弱。
3.2 训练与验证的时间切分
量化项目最忌讳随机划分训练集和测试集。因为金融数据是时间序列,随机划分会导致信息泄漏。正确的做法是严格按时间顺序切分。
| 数据段 | 时间范围 | 用途 |
|---|---|---|
| 训练集 | 2015-2019 | 训练模型参数 |
| 验证集 | 2020-2021 | 调超参数、早停、选择特征 |
| 测试集 | 2022-2023 | 最终评估、模拟上线 |
代码示例:
train_end = "2019-12-31" valid_end = "2021-12-31" test_start = "2022-01-01" train = df.loc[:train_end] valid = df.loc[train_end:valid_end] # 注意边界顺序,可以用 open 或 close 时间精确切分 test = df.loc[test_start:]在训练时还需要做 walk-forward 验证,也就是滚动时间窗口。这比一次性切分更贴近真实生产场景。
from sklearn.ensemble import RandomForestRegressor def walk_forward_validate(df, features, label, window=252, step=63): results = [] for start in range(0, len(df) - window, step): end = start + window train_data = df.iloc[start:end] test_data = df.iloc[end:end + step] model = RandomForestRegressor( n_estimators=200, max_depth=6, n_jobs=-1, random_state=42 ) model.fit( train_data[features], train_data[label] ) pred = model.predict(test_data[features]) # 记录每个窗口的预测结果 results.append(pd.DataFrame({ "date": test_data.index, "pred": pred, "actual": test_data[label].values })) return pd.concat(results)walk_forward_validate模拟了模型在真实环境里“边预测边学习”的过程。它可以显著降低过拟合风险。
3.3 常见模型选择
Two Sigma 不会只用一个模型。从访谈和公开资料看,常见的选择包括:
| 模型 | 适用场景 | 优势 | 劣势 |
|---|---|---|---|
| 线性模型 | 因子收益分解 | 可解释性强 | 表达容量有限 |
| 树模型 | 非线性特征关系 | 训练快、稳定性好 | 对时序关系不敏感 |
| 神经网络 | 高维特征、文本、图像 | 表达能力强 | 数据需求大、调参成本高 |
| Transformer | 新闻、舆情、序列建模 | 能捕捉长依赖 | 推理成本高、数据要求高 |
访谈中强调,模型复杂度不等于收益。重要的是模型是否匹配数据量和任务。
4. 从研究到生产的工程化
访谈里最有工程价值的部分,是 Two Sigma 如何把研究成果转成生产服务。这里有一整套规范。
4.1 研究环境与生产环境分离
研究员通常使用 Python、Jupyter Notebook 和 GPU 服务器做探索,而生产系统使用微服务、容器化和独立的推理集群。两者之间通过特征存储、模型注册中心和配置中心连接。
一个落地做法是引入模型注册中心。
# 假设使用 mlflow 做模型注册 mlflow models serve \ -m "models:/stock_alpha_model/Production" \ -h 127.0.0.1 \ -p 8080模型上线前必须记录:
- 训练数据时间区间;
- 特征版本;
- 模型参数;
- 验证集效果;
- 数据漂移检查结果。
4.2 推理服务接口设计
生产环境通常暴露一个轻量 API,接收“股票代码 + 日期 + 特征”,返回预测值或信号。
from fastapi import FastAPI from pydantic import BaseModel import joblib import pandas as pd app = FastAPI() model = joblib.load("./models/stock_alpha_model.joblib") feature_cols = joblib.load("./models/feature_cols.joblib") class PredictRequest(BaseModel): stock_code: str features: dict @app.post("/predict") def predict(req: PredictRequest): df = pd.DataFrame([req.features])[feature_cols] pred = model.predict(df)[0] return { "stock_code": req.stock_code, "pred": round(float(pred), 6), "score": 1.0 }推理服务必须严格区分“特征缺失”和“特征为 0”。如果某个特征缺失却填 0,模型输出的置信度会失真。
4.3 模拟盘与逐步放量
访谈明确的流程是:先离线回测,再纸面交易,再小资金测试,最后逐步放量。
每一步的验证标准不一样:
| 阶段 | 观察指标 | 通过标准 |
|---|---|---|
| 回测 | 年化收益、最大回撤、夏普比率 | 回撤可解释、收益来源明确 |
| 纸面交易 | 信号延迟、滑点模拟 | 与回测偏差 < 20% |
| 小资金实盘 | 实际成交价差 | 交易成本在预期内 |
| 逐步放量 | 容量测试、冲击成本 | 收益衰减可控 |
这个流程看起来很慢,但它能避免一个致命的陷阱:回测很完美,实盘一塌糊涂。
5. 风险控制与绩效归因
AI 赋能投资时,模型可以负责预测,但风险控制必须由独立规则和人工约束一起完成。
5.1 组合层面的约束
即使模型给出一组股票的高预测分,组合优化器也不能把仓位全部压过去。常见约束包括:
- 单一股票仓位不超过组合的 2%;
- 行业暴露偏离基准不超过 3%;
- 换手率上限控制;
- 流动性限制,剔除日成交额过低的股票;
- 波动率目标控制。
5.2 风险指标监控
生产环境需要持续监控组合的风险指标。
def calculate_risk_metrics(nav: pd.Series, risk_free_rate: float = 0.02): ret = nav.pct_change().dropna() annual_ret = (1 + ret).prod() ** (252 / len(ret)) - 1 annual_vol = ret.std() * np.sqrt(252) sharpe = (annual_ret - risk_free_rate) / annual_vol # 计算最大回撤 cum = (1 + ret).cumprod() drawdown = cum / cum.cummax() - 1.0 max_drawdown = drawdown.min() return { "annual_return": round(annual_ret, 4), "annual_volatility": round(annual_vol, 4), "sharpe": round(sharpe, 4), "max_drawdown": round(max_drawdown, 4) }这类指标不是算完就结束,而是每天生成报告,发生异常时自动告警。
5.3 数据漂移监控
模型上线后,特征分布会随时间变化。如果市场风格切换,模型可能会失效。常见做法是监控特征分布和预测分布。
from scipy.stats import ks_2samp def check_drift(train_feature: pd.Series, live_feature: pd.Series, threshold: float = 0.05): stat, p_value = ks_2samp(train_feature, live_feature) return { "ks_stat": round(stat, 4), "p_value": round(p_value, 4), "drift": p_value < threshold }漂移告警不意味着立刻下线模型,但需要研究员介入判断。
6. 技术栈与基础设施
从访谈和公开技术分享来看,Two Sigma 的技术栈可以归纳为几个方向。
| 方向 | 技术选型思路 |
|---|---|
| 数据处理 | 大规模分布式计算,如 Hadoop、Spark、Ray |
| 数据存储 | 列式存储、时序数据库、对象存储结合使用 |
| 模型训练 | GPU 集群,混合使用 PyTorch 和传统 ML 框架 |
| 模型推理 | 低延迟服务,使用容器化和弹性扩缩容 |
| 工作流调度 | Airflow、Argo 或内部调度系统 |
| 实验管理 | 模型注册中心、特征存储、实验平台 |
这套技术栈并不神秘,它的难点在于把所有模块稳定地串起来。
6.1 可复现性优先
研究环境最大的风险是“换个环境结果就变了”。Two Sigma 类机构非常重视可复现性,通常会用 Docker 或 Kubernetes 固定 Python 版本、CUDA 版本和依赖包版本。
建议的工程规范:
- 使用
poetry或uv管理依赖; - 训练和推理共用同一个基础镜像;
- 固定随机种子;
- 记录训练数据的 hash 值;
- 每次实验保存完整配置。
下面是一个简化的配置示例。
# config.yaml data: start_date: "2015-01-01" end_date: "2023-12-31" universe: "hs300" features: version: "v3" list: ["momentum_5d", "volume_zscore_20d", "turnover_10d"] model: name: "lgbm_alpha" params: n_estimators: 500 learning_rate: 0.03 max_depth: 6 train: train_end: "2019-12-31" valid_end: "2021-12-31" random_seed: 427. 对普通开发和研究员的三点建议
访谈看下来,最值得普通团队借鉴的不是 Two Sigma 的千亿规模基础设施,而是三件事。
7.1 先把信号拆干净,再上模型
不要一开始就用 Transformer 预测股票涨跌。建议先把数据处理成可验证的特征,用线性模型或树模型做基线。如果基线模型都跑不出明显的 alpha,换复杂模型大概率只是过拟合。
7.2 用回测之外的方式验证模型
回测是最容易自欺欺人的环节。建议增加:
- 参数敏感性测试;
- 不同时间段的分段回测;
- 交易成本敏感性测试;
- 与随机预测的对比测试。
只有经过这些测试,模型才有一点上线的基础。
7.3 把风险控制写在代码里
信号只是一个输入,不应该直接变成交易。生产系统必须有独立的约束检查模块。
def check_order_constraints( target_stock: str, target_weight: float, max_single_weight: float = 0.02, blacklist: list = None ) -> bool: if blacklist and target_stock in blacklist: return False if target_weight > max_single_weight: return False return True这种检查规则越简单越可靠。复杂的逻辑反而更容易在异常行情下失效。
8. 边界、合规与风险提示
AI 赋能投资并不是“把模型丢进去就能赚钱”。以下几点必须保持清醒。
- 市场有效性会变化。过去有效的规律,未来不一定继续有效。
- AI 模型本质上是统计模型,无法避免尾部风险。
- 高频交易和算法交易受到严格监管,不同市场的合规要求不同。
- 使用新闻、社交媒体、卫星图像等另类数据时,必须确认数据来源合法、版权清晰。
- 涉及投资者资金时,任何回测结果都不能作为未来收益承诺。
研究 AI 投资技术时,建议只用模拟数据和公开数据做实验,不用于实盘荐股、代客理财等场景。如果要投入实际资金,必须获得相应牌照并接受监管。
9. 总结:从访谈里带走什么
这次访谈最值得记住的一句话可以概括为:AI 赋能投资不是“模型预测”,而是一整套数据、研究和工程系统。模型只是其中一颗螺丝钉,数据质量、研究流程、工程生产力和风险控制,共同决定了系统能否长期运行。
如果你想从零开始实践,顺序建议是:
- 搭建日线级别数据管道,先把数据对齐和清洗做好;
- 构建 3 到 5 个稳定的量价因子;
- 用线性模型或树模型训练一个基线预测模型;
- 严格按时间顺序做回测和 walk-forward 验证;
- 用模拟交易验证成本影响;
- 再加 API 服务、监控和告警。
最容易踩的坑有两个:一个是数据泄漏,回测很漂亮但实盘失真;另一个是忽略交易成本,策略毛利很高但净利为负。先把这两块解决,再谈复杂模型和更大规模的基础设施。