周五上午,你在上海的一家 量化 私募 ——明汯、 幻方、 九坤、 灵均 风格 的 多 因子 私募。 L3 把 四 条 信号 正交化 完了: mom_12_1, book_to_market, gross_profitability, pead_sue 都 残差化 通过 了 IC_break_even 门槛。 桌面 上 还 没有 量产 复合 信号。 投决会 在 下午 2 点。 研究 主管 问 你: 「等 权重 简单 平均 行不行?」 在 沪海 2018-2022 样本内 IR 是 0.9; 在 2023 样本外 IR 是 0.85 ——可 用 但 还有 提升 空间。 你 还 在 试 Markowitz w = Σ^{-1} * IC: 样本内 IR 是 1.5, 样本外 暴跌 到 0.6 ——明显 过拟合。 Ledoit-Wolf 收 缩 Markowitz: 样本内 1.3, 样本外 1.1 ——shrinkage benefit 是 0.25 IR, 显著 高于 0.1 IR 门槛, 这 就 是 你 要 上线 的 复合。 本 课 是 整 个 4.2.3 craft 的 capstone: 怎 么 把 一 组 正交 信号 合 成 单 一 量产 评分, 怎 么 选 加 权 方案, 怎 么 防 过 拟 合。
五 种 合成 方法
业内 五 种 标准 加权 方案 在 这 个 顺序 上 适用 不同 数据 体 量:
(1) equal_weight (等 权重, w = (1/K, ..., 1/K)) ——稳 健 默认; 在 验证 集 长度 T < 5K 时 击败 Markowitz, 因 Markowitz 噪声 dominate optimisation gain;
(2) markowitz_optimal (Markowitz 最 优, w* = Σ^{-1} * IC) ——样本内 最 优 解; 没有 正则化 时 样本外 极 不 稳定;
(3) ledoit_wolf_shrinkage (Σ_shrunk = (1 - τ*) * Σ_sample + τ* * (tr(Σ_sample)/K) * I, w = Σ_shrunk^{-1} * IC) ——量产 默认, 适用 中 等 验证 集 长度;
(4) stacking (堆叠, 元 模型 ——线性 / ridge / LightGBM ——在 基 信号 的 out-of-fold 预测 上 训练) ——捕 捉 线性 加权 无法 表 达 的 非线性 交互;
(5) ensembling (集成, bagging 多 种子 + 跨 视界 model averaging + 跨 模型 家族 robust mean) ——多 层 防 御 过 拟 合。
规则: 量产 复合 = (orthogonalise + combine + regime-check); T > 20K 时 默 认 对 称 Gram-Schmidt + Ledoit-Wolf 收 缩 Markowitz, T < 5K 时 默 认 对 称 Gram-Schmidt + 等 权重。
五 元 复合 诊断 包
复合 信号 上 线 之前 携 带 五 个 诊断 数字:
(1) composite_ir_in_sample_vs_oos (样本内 vs 样本外 IR, gap > 50% 意 味 过 拟 合);
(2) per_signal_weight_contribution_and_stability (每 条 信号 权 重 与 跨 验证 折 稳 定 性);
(3) composite_turnover_and_break_even_ic (复合 必 须 在 复合 换 手率 下 满足 L2 break-even IC 门槛);
(4) regime_conditional_composite_ic (牛市 / 熊市 / 高 波动 / 低 波动 ——单 regime 失效 的 复合 不 可 上 线);
(5) shrinkage_benefit (收 缩 Markowitz vs 等 权重 的 样本外 IR 提升; > 0.1 IR 上线 收 缩 版, 否则 上线 等 权重)。
规则: 任 何 缺 失 五 元 任一 的 复合 不 进入 4.4 组合 构造 步骤。
Markowitz 与 收 缩 的 数学 形式
Markowitz 最 优 权 重 的 闭 形 解:
收 缩 协方差 的 Ledoit-Wolf 形式:
Ledoit-Wolf 收 缩 Markowitz
Markowitz 在 样本内 是 IC 加 权 的 最 优 解: w* = Σ^{-1} * IC 最大化 IR = w^T * IC / sqrt(w^T * Σ * w)。 问 题: 协方差矩阵 Σ 在 短 验证 集 上 病 态 ——特征值 散布 极 宽, 最小 特征值 接近 零, 求 逆 后 权 重 极 端 (一 条 信号 50% 权 重, 另 一 条 -30%)。 样本外 噪声 dominate。
Ledoit-Wolf 2004 提 出 解析 收 缩 估 计 子: 把 样本 协方差 朝 一 个 良 态 目标 (单位 阵 乘 tr(Σ)/K) 收 缩, 收 缩 参数 τ* 由 MSE 最小化 解析 计算。 三 步:
(1) Sigma_sample = numpy.cov(ic_matrix.T) ——样 本 IC 协方差;
(2) lw = sklearn.covariance.LedoitWolf().fit(ic_matrix) 然后 tau_star, Sigma_shrunk = lw.shrinkage_, lw.covariance_ ——解 析 收 缩 参 数 和 收 缩 协方差;
(3) w_shrunk = numpy.linalg.solve(Sigma_shrunk, ic_mean_vector) ——正 则 化 后 的 Markowitz 权 重。
规则: 收 缩 权 重 在 Markowitz 最 优 (τ=0) 与 等 权重 基线 (τ=1) 之 间 插 值。
import numpy as np
import sklearn.covariance
def ledoit_wolf_markowitz(ic_matrix: np.ndarray) -> dict:
# 1. 样本 协方差
Sigma_sample = np.cov(ic_matrix.T)
# 2. 解析 收 缩 估 计
lw = sklearn.covariance.LedoitWolf().fit(ic_matrix)
tau_star = lw.shrinkage_
Sigma_shrunk = lw.covariance_
# 3. 求 解 Sigma_shrunk @ w = ic_mean
ic_mean = ic_matrix.mean(axis=0)
weights = np.linalg.solve(Sigma_shrunk, ic_mean)
cond_before = np.linalg.cond(Sigma_sample)
cond_after = np.linalg.cond(Sigma_shrunk)
return {'tau_star': tau_star, 'sigma_shrunk': Sigma_shrunk,
'weights': weights, 'condition_number_before': cond_before,
'condition_number_after': cond_after}
ledoit_wolf_markowitz 的 函数名、 参数 ic_matrix、 sklearn API sklearn.covariance.LedoitWolf, 以及 返回 字典 键, 在 中英 两 版 中 字 节 一 致; 仅 注 释 翻译。
Formula Explorer
Sigma_shrunk = (1 - tau) * Sigma + tau * trace_Sigma_over_K * IStacking ——元 模型 加 权
线 性 Markowitz 假 设 信号 与 收益 之 间 是 线 性 关 系。 LightGBM ranker 上 的 实际 量产 信号 通常 非 线 性: 极 端 值 的 边 际 贡 献 与 中 间 值 不 同, 信号 之 间 有 交 互 项 (动量 强 时 价 值 信号 反 而 弱)。 Stacking 用 一 个 元 模型 (linear / ridge / LightGBM) 学 习 这 些 非 线 性 关 系。 四 步:
(1) oof_predictions = purged_kfold_predict(base_signals, target_returns, n_folds=5, embargo=21) ——4.2.1 L2 的 净 化 K 折 ——产 出 每 条 基 信号 的 样 本 外 OOF 预 测;
(2) meta_model = sklearn.linear_model.Ridge(alpha=1.0) 或 lightgbm.LGBMRanker(...) ——元 模型, 业 内 私募 量产 通 常 是 lightgbm.LGBMRanker(objective='rank_xendcg');
(3) meta_model.fit(oof_predictions, target_returns) ——元 模型 在 OOF 预 测 上 训练;
(4) composite_score = meta_model.predict(test_predictions) ——元 模型 在 测试 集 上 给 出 复合 评 分。
规则: 当 基 信号 本 身 是 复 杂 模型 时 用 stacking; 元 模型 捕 捉 线 性 权 重 无 法 表 达 的 非 线 性 交 互。 净 化 K 折 的 21 日 embargo 在 沪深300 上 阻止 21 日 前瞻 收 益 的 OOF 预 测 看 到 训 练 集 信 息 ——4.2.1 L2 的 操 作 关 键。
集成 ——三 种 模 式
集成 是 复合 的 最 后 一 层 防 御:
(1) bagging_across_seeds ——训 练 K' = 5-20 个 不 同 种 子 的 基 ML 信号; 平 均 预 测; 方 差 减 少 ~ 1/K';
(2) model_averaging_across_horizons ——把 h = 1, h = 5, h = 21 上 的 信号 用 视界 特 定 权 重 (来自 L2 半 衰 期) 合 成;
(3) robust_mean_across_model_families ——公式驱动 复合、 事件驱动 复合、 ML 驱动 复合 的 中位数 / 截 尾 均值 作 为 顶 层 主 信号 输入。
规则: 集成 层 是 过 拟 合 防 御 的 最 后 一 道; 量产 复合 = (orthogonalise + combine + ensemble) 三 层。
复合 决 策 规 则
四 条 数据-体-量-条件 默 认:
(1) if T < 5K: ship equal_weight ——短 验证 集, Markowitz 噪 声 dominate;
(2) if 5K <= T <= 20K: ship ledoit_wolf_shrinkage ——中 等 验证 集, 收 缩 安 全 插 值;
(3) if T > 20K: ship markowitz_with_constraints ——长 验证 集, 加 多空 / 权 重 上限 / 杠 杆 约 束 的 Markowitz 解 稳定;
(4) if shrinkage_benefit < 0.1_IR: prefer equal_weight ——更 简 单 的 复合 对 regime 变化 更 稳 健。
业内 私募 (明汯、 幻方、 九坤、 灵均、 鸣 石、 衍 复) 量产 复合 普 遍 组合 50-200 条 正交 信号, 复合 IR 在 1.0-1.5 样本外, 用 LightGBM stacking + bagging 跨 种 子 + Ledoit-Wolf 协方差 收 缩 ——业内 公 开 报告 描 述 的 流 水 与 上 述 完 全 一 致。
复合 构 造 函数
把 上 面 五 种 方法 dispatch 进 一 个 接 口:
import numpy as np
import sklearn.covariance, sklearn.linear_model
def construct_composite(orthogonal_signals: np.ndarray, forward_returns: np.ndarray,
method: str = 'ledoit_wolf') -> dict:
T, K = orthogonal_signals.shape
if method == 'equal_weight':
weights = np.ones(K) / K
elif method == 'markowitz':
# 1. 样本 IC 协方差 与 样本 平均 IC
ic_matrix = orthogonal_signals * forward_returns[:, None]
Sigma = np.cov(ic_matrix.T)
ic_mean = ic_matrix.mean(axis=0)
# 2. w = Σ^{-1} * IC
weights = np.linalg.solve(Sigma, ic_mean)
elif method == 'ledoit_wolf':
ic_matrix = orthogonal_signals * forward_returns[:, None]
lw = sklearn.covariance.LedoitWolf().fit(ic_matrix)
ic_mean = ic_matrix.mean(axis=0)
weights = np.linalg.solve(lw.covariance_, ic_mean)
elif method == 'stacking':
ridge = sklearn.linear_model.Ridge(alpha=1.0).fit(orthogonal_signals, forward_returns)
weights = ridge.coef_
# 3. 复合 评分 与 样本内 IR
composite_score = orthogonal_signals @ weights
in_sample_ir = composite_score.mean() / composite_score.std() * np.sqrt(12)
return {'weights': weights, 'composite_score': composite_score,
'in_sample_ir': in_sample_ir, 'oos_ir': None}
construct_composite 的 函数名、 参数 orthogonal_signals / forward_returns / method、 method 值 equal_weight / markowitz / ledoit_wolf / stacking, 以及 返回 字典 键, 在 中英 两 版 中 字 节 一 致; 仅 注 释 翻译。
走 通 的 例子 ——四 信号 复合
把 L3 输出 的 四 条 正交 信号 (mom_12_1_orth, book_to_market_orth, gross_profitability_orth, pead_sue_orth) 在 沪深300 universe 上 2018-2022 样本内 / 2023 样本外 走 一 遍:
(1) 等 权重 w = (0.25, 0.25, 0.25, 0.25): 样本内 IR ≈ 0.95, 样本外 IR ≈ 0.85;
(2) 无 约 束 Markowitz: 样本内 IR ≈ 1.50, 样本外 IR ≈ 0.55, 协方差矩阵 Σ 条件 数 ≈ 200 ——病 态;
(3) Ledoit-Wolf 收 缩 Markowitz: τ* ≈ 0.4-0.7 (沪深300 验证 集 短, 收 缩 较 高), 样本内 IR ≈ 1.30, 样本外 IR ≈ 1.10, 协方差矩阵 收 缩 后 条件 数 ≈ 8 ——良 态;
(4) shrinkage benefit = 1.10 - 0.85 = 0.25 IR, > 0.1 IR 门槛 ——上 线 收 缩 Markowitz 复合;
(5) 复合 诊断: 样本内-vs-样本外 gap 是 (1.30 - 1.10) / 1.30 ≈ 15%, 远 低 于 50% 门槛; 每 条 信号 权 重 (动量 ≈ 0.32, 价 值 ≈ 0.18, 质量 ≈ 0.20, PEAD ≈ 0.30 ——PEAD 因 正交 IC 高 得 到 高 权 重); 复合 月度 单 边 换手率 ≈ 90% (低 于 单 信号 ——正交化 + 收 缩 平 滑 信号 序列), 在 10 bp 往返 TC 下 break-even IC ≈ 0.015 (实 际 复合 IC ≈ 0.06, 远 高 于); regime-conditional 复合 IC 在 牛市 / 熊市 / 高 波动 / 低 波动 四 个 桶 中 都 > 0.04 ——通 过 regime check。 复合 进入 4.4 组合 构造。
过 拟 合 与 deflated-Sharpe 修 正 的 实 操 注 解
私募 业 内 每 年 跑 通 1000+ 候 选 信号 走 这 个 评估 流 水, 在 每 个 复合 上 跑 100+ 变化 ——不 同 正交化 方法、 不 同 协方差 收 缩 估 计 子、 不 同 stacking 元 模型、 不 同 bagging 计 数、 不 同 特 征 子 集、 不 同 前瞻 收 益 视 界。 每 个 变化 都 在 4.2.1 L3 的 试 验 计 数 器 上 加 一, 累 计 多 重 检 验 inflation 可 以 显 著。 deflated-Sharpe-ratio 修 正 (Bailey & López de Prado 2014) 提 供 一 个 试 验 数 与 IR 分 布 高 阶 矩 的 闭 形 罚 项; 在 写 报告 把 复合 IR 上 报 投决会 之 前 应 用 这 个 修 正。
第 二 个 操 作 注 解: 单 次 收 缩 Markowitz 跑 出 的 每 条 信号 权 重 在 跨 验证 折 上 不 稳 定, 即 使 复合 IR 是 稳 定 的。 某 条 信号 在 第 一 折 拿 权 重 0.30、 第 二 折 0.10 但 始 终 正 值 是 稳 健 贡献; 某 条 信号 第 一 折 0.30、 第 二 折 -0.20 ——无 论 样本内 IR 多 高, 它 都 危 险。 per_signal_weight_contribution_and_stability 诊 断 抓 的 就 是 这 个 ——跨 折 权 重 标 准 差 与 均 值 同 时 报 告。 沪深300 上 验证 集 短 (后-2015 约 2400 个 交易 日, 月度 调仓 约 120 个 调仓 期), 折 数 通 常 是 5, 每 折 24 个 调仓 期 ——足 以 算 权 重 稳 定 性 但 也 足 以 暴 露 不 稳 定 信号 的 异 常 摆 动。 业 内 普 遍 用 5 折 是 一 个 经验 经 济 取 舍, 不 是 理 论 最 优。 量产 私募 (明汯、 幻方、 九坤) 公开 报告 中 描述 的 复合 构造 流 水 通 常 包 括: 在 训 练 集 上 跑 100+ 信号 通 过 L3 正交化 与 因 子 中 性 化, 然后 在 验证 集 上 通 过 LightGBM 元 模型 做 stacking, 跨 5-20 个 随 机 种 子 做 bagging, 跨 多 个 视 界 (h=1, h=5, h=21) 做 model averaging ——这 是 业 内 公 开 的 量产 复合 构造 标 准 流 水, 与 学 术 文 献 描 述 完 全 一 致 但 在 实 际 部 署 上 工程 化 更 深。
与 4.4 的 衔接 与 模块 结 束
L4 是 整 个 4.2.3 craft 的 capstone, 也 是 整 个 subject 4.2 alpha research 的 闭 环。 4.2.1 教 你 怎 么 设 计 一 个 诚 实 的 研究 实验 (预 注 册、 样本内 / 样本外 分 割、 多 重 检验 修正、 净 化 K 折); 4.2.2 教 你 怎 么 构 造 信号 (DSL、 公式驱动 / 事件驱动 / ML 驱动 三 个 信号 家族、 标 准 化 流 水); 4.2.3 教 你 怎 么 评估 与 合 成 (IC / IR / Grinold-Kahn / 衰 减 / 换 手 / 容 量 / 正交化 / 复合)。 三 个 模块 加 起 来: 你 现 在 可 以 接 到 一 个 alpha 研究 任务 ——构 造 K 条 候选 信号、 每 条 跑 L1 + L2 诊断、 通过 L3 正交化 与 因 子 中 性 化 把 它 们 残 差 化、 在 L4 用 收 缩 Markowitz 或 stacking 合 成 复合, 然后 把 复合 评 分 交 给 4.4 portfolio construction 转 成 实 际 持 仓。 多 重 检验 inflation 在 整 个 研 究 流 程 上 用 4.2.1 L3 的 deflated-Sharpe 修 正 调 整; 实 际 backtest 与 transaction cost 全 套 framework 在 4.5; risk model 在 4.4.2; 因 子 zoo 与 academic 因子 模型 文 献 在 4.3.1。
课程 组件 (Lesson components)
Inline-code 列表 五 种 经典 合 成 方法:
equal_weight(w = (1/K, ..., 1/K), 稳 健 默 认);markowitz_optimal(w* = Σ^{-1} * IC, 样本内 最 优);ledoit_wolf_shrinkage(Σ_shrunk = (1 - τ*) * Σ_sample + τ* * (tr(Σ_sample)/K) * I);stacking(元 模型 ——linear / ridge / LightGBM);ensembling(bagging + 跨 视界 model averaging + robust mean)。
量产 复合 = (orthogonalise + combine + regime-check)。
Inline-code 列表 五 元 复合 诊断 包: composite_ir_in_sample_vs_oos (gap > 50% 过 拟 合)、 per_signal_weight_contribution_and_stability、 composite_turnover_and_break_even_ic、 regime_conditional_composite_ic (牛市 / 熊市 / 高 波动 / 低 波动)、 shrinkage_benefit (> 0.1 IR 上 线 收 缩 版)。
Inline-code 列表 Ledoit-Wolf 收 缩 三 步: Sigma_sample = numpy.cov(ic_matrix.T)、 lw = sklearn.covariance.LedoitWolf().fit(ic_matrix)、 w_shrunk = numpy.linalg.solve(Sigma_shrunk, ic_mean_vector)。
Inline-code 列表 stacking 四 步: oof_predictions = purged_kfold_predict(base_signals, target_returns, n_folds=5, embargo=21)、 meta_model = sklearn.linear_model.Ridge(alpha=1.0) 或 lightgbm.LGBMRanker(...)、 meta_model.fit(oof_predictions, target_returns)、 composite_score = meta_model.predict(test_predictions)。
Inline-code 列表 三 种 集成 模 式: bagging_across_seeds (5-20 种 子 平 均, 方 差 减少 1/K')、 model_averaging_across_horizons (h=1, 5, 21)、 robust_mean_across_model_families (公式驱动 + 事件驱动 + ML 驱动 三 个 顶 层 输入)。
Inline-code 列表 四 条 复合 决 策 规 则: if T < 5K: ship equal_weight、 if 5K <= T <= 20K: ship ledoit_wolf_shrinkage、 if T > 20K: ship markowitz_with_constraints、 if shrinkage_benefit < 0.1_IR: prefer equal_weight。
两 个 fenced python 块: construct_composite 函数 (五 种 方法 dispatch) 和 ledoit_wolf_markowitz 函数 (协方差 收 缩 + Markowitz 求 解)。
L4 用 到 的 词汇: 组合优化 (portfolio optimization, Markowitz 是 经典 解); 协方差矩阵 (Markowitz 与 收 缩 的 核 心 对 象); 特征值 (协方差 良 态 性 与 病 态 性 由 特 征 值 散 布 决 定); 特征分解 (PCA 与 谱 分 析); Barra 模型 (因 子 模型 的 商 业 实 现); 因子模型 (合 成 前 的 因 子 中 性 化 引 用); 信息比率 (L4 的 头条 指 标); 夏普比率 (复合 与 long-short 组合 的 IR-Sharpe 等 价 性); Alpha 衰减 (跨 验证 集 的 复合 IR 衰减); 交易成本 (复合 换 手率 的 break-even IC 约 束)。
练习
Exercise
给 定 一 个 orthogonal_signals_df (以 (date, symbol) 为 MultiIndex, 四 列 由 L3 对 称 Gram-Schmidt 正交化 产 出: mom_12_1_orth, book_to_market_orth, gross_profitability_orth, pead_sue_orth) 和 一 个 returns_df (以 (date, symbol) 为 MultiIndex, 列 fwd_21d_return), 在 沪深300 universe 上。 用 2018-2022 作 为 样本内 窗 口, 2023 作 为 样本外 窗 口。
(i) 算 等 权重 复合 (w = (0.25, 0.25, 0.25, 0.25)); 报 告 样本内 IR 与 样本外 IR。
(ii) 算 无 约 束 Markowitz 复合 (w* = Σ^{-1} * IC, Σ 与 IC 在 样本内 窗 口 估 计); 报 告 样本内 IR、 样本外 IR、 与 Σ 的 条件 数。
(iii) 用 sklearn.covariance.LedoitWolf 算 Ledoit-Wolf 收 缩 Markowitz 复合; 报 告 最 优 τ*、 样本内 IR、 样本外 IR、 与 收 缩 后 Σ_shrunk 的 条件 数。
(iv) 算 shrinkage benefit = 收 缩 Markowitz 样本外 IR 减 等 权重 样本外 IR; 判 断 量产 复合 应 该 是 收 缩 Markowitz (如 果 shrinkage benefit > 0.1 IR) 还 是 等 权重 (否 则)。
(v) 算 复合 诊断 包 ——样本内-vs-样本外 gap、 每 条 信号 权 重、 在 TC = 10 bps 下 的 复合 换 手率 与 break-even IC、 牛市 / 熊市 / 高 波动 / 低 波动 桶 上 的 regime-conditional 复合 IC。 判 断 复合 是否 可 以 上 4.4 portfolio construction。
提示
(composite.mean() / composite.std()) * sqrt(12); Markowitz w = numpy.linalg.solve(np.cov(ic_matrix.T), ic_matrix.mean(axis=0)); Σ 条件 数 是 numpy.linalg.cond(Sigma), 大 值 (> 50) 警 示 病 态。提示
lw = sklearn.covariance.LedoitWolf().fit(ic_matrix); tau_star = lw.shrinkage_; weights = numpy.linalg.solve(lw.covariance_, ic_mean); shrinkage benefit > 0.1 IR 是 上 线 收 缩 版 的 门槛。