CORN 模式匹配组合:用相关性找历史上的『相似市场』再优化
CORN(Correlation-driven Nonparametric learning,Li-Hoi-Gopalkrishnan 2011)把在线组合选择从『对全部历史平均』升级为『只对相似历史优化』:拿最近 w 天的市场窗口和每一段历史窗口算相关系数,超过阈值 ρ 的日子构成『相似市场样本集』,然后只在这些样本的次日收益上做对数最优组合。1250 日反相位周期市场实测:CORN 终值 5.50,事后最优 CRP 只有 1.72,50/50 再平衡 0.94——模式匹配吃到了常数组合结构上吃不到的条件信息,权重轨迹与真实相位逐日对齐,而匹配集同相位纯度只需 66% 就够了。关键对照实验:同波动无模式市场上 CORN 终值 0.71,反而跑输 50/50 的 0.92——模式匹配在没有模式的世界里不是无害的,它把噪声当模式做多余交易。ρ 敏感性扫描完整呈现『噪声混入 vs 样本饥荒』的倒 U 形。三盆冷水:O(T²) 相关计算、低维窗口假相似、以及超额收益本质是对『历史会押韵』假设的风险暴露。
一句话版本#
CORN 每天问一个问题:历史上哪些日子长得像最近这几天? 找出来,然后只在这些”相似日的次日收益”上做对数最优组合优化。它不预测市场,但它赌一件事——相似的市场状态后面跟着相似的收益分布。
从 Cover 的遗留问题说起#
前面我们写过 Cover 通用投资组合 和 在线牛顿步 ONS。这两个算法有一个共同的世界观:市场是对抗的,我们不做任何统计假设,只保证不输给事后最优的常数再平衡组合(BCRP)。
这个世界观数学上很漂亮,但有个尴尬的推论:如果你的对标是 BCRP,那你的上限也差不多就是 BCRP。而 BCRP 本身是一个很弱的对标——它被迫用一套固定权重应付整段历史。如果市场里存在任何”状态依赖”的结构——今天该满仓 A 还是满仓 B 取决于最近的市场长什么样——常数组合在结构上就吃不到这部分收益。
Li、Hoi 和 Gopalkrishnan 在 2011 年提出的 CORN(CORrelation-driven Nonparametric learning)换了一个世界观:市场不是完全对抗的,历史模式会重复。如果这个假设成立哪怕一点点,我们就不该对所有历史一视同仁地平均,而应该:
- 识别出历史上和”现在”相似的时刻;
- 只用这些时刻的后续收益来优化今天的持仓。
这就是模式匹配(pattern matching)组合选择框架,最早由 Györfi 等人在 2006 年以核方法形式提出,CORN 把相似度度量换成了更符合金融直觉的相关系数。
CORN 的三步循环#
记 为第 天的价格相对(price relative)向量,即 。组合权重 在单纯形上,单日财富增长因子是 。
第一步:构造市场窗口。 把最近 天的价格相对拼成一个向量:
这个向量就是”最近市场长什么样”的数值化描述。
第二步:相关性筛选相似集。 对每一个历史时刻 ,计算历史窗口 与当前窗口的皮尔逊相关系数,超过阈值 的进入相似集:
用相关系数而不是欧氏距离是刻意的:相关系数自带去均值去尺度,两段行情一个日波动 1%、一个日波动 2%,只要涨跌节奏形状一致就算相似。这和 SAX、Matrix Profile 里 z-normalize 的动机同源——比对形态,不比对水平。
第三步:在相似集上做对数最优优化。 相似集里每个时刻 的次日价格相对构成经验分布,在它上面最大化对数财富:
相似集为空或样本太少时退回均匀组合。
整个算法没有”预测”步骤——不估均值、不估协方差、不拟合模型。它只是重新加权历史:Cover 的 UP 对所有历史等权,CORN 对相似历史全权、不相似历史零权。
实验设计:一个真的有模式的市场,和一个没有的#
模式匹配策略的实验有个常见毛病:只在有利环境里跑。我们这次做一对孪生实验:
模式市场:1250 个交易日、双资产,偶数日资产 A 日均漂移 +30bp、资产 B 是 -25bp;奇数日反过来。两资产日噪声 0.85%。这是一个刻意构造的”历史会押韵”世界:最近几天的涨跌节奏(相位)完全决定明天谁强。注意漂移不对称(+30 vs -25),所以纯持有 A 长期也是赚的——事后最优 CRP 不是废物,它就是满仓 A。
对照市场:同样的波动率、同样的资产数,但没有任何模式——纯噪声。模式匹配策略在这里的表现,才是它真实风险的度量。
核心实现(向量化后 1250 日几秒跑完;完整代码见文末):
def corn_run(X, w=5, rho=0.2, grid=101, min_samples=5):
T = len(X)
# 把所有窗口堆成矩阵,z-norm 后一次矩阵乘法得到全部相关系数
W = np.array([X[i:i+w].ravel() for i in range(T - w)])
Wz = (W - W.mean(1, keepdims=True)) / (W.std(1, keepdims=True) + 1e-12)
C = (Wz @ Wz.T) / W.shape[1]
b_grid = np.linspace(0, 1, grid)
B = np.vstack([b_grid, 1 - b_grid])
bs = np.full(T, 0.5)
for i in range(T - w):
t, jmax = i + w, i - w # 只匹配不重叠的历史窗口
if jmax <= 0:
continue
cand = np.nonzero(C[i, :jmax] >= rho)[0] + w
if len(cand) >= min_samples:
wealth = np.log(X[cand] @ B).sum(axis=0)
bs[t] = b_grid[int(np.argmax(wealth))]
return bspython两个工程细节值得注意:一是窗口不重叠约束(jmax = i - w),当前窗口和候选历史窗口共享数据会制造虚假相关;二是把 O(T²) 的逐对相关换成一次 Wz @ Wz.T 矩阵乘法——同样的复杂度,快两个数量级。
结果:模式市场碾压,对照市场露馅#

上图是 CORN 的核心动作:橙色是”当前窗口”(最近 5 日),绿色是历史上与它相关性最高的窗口。这些绿色窗口的”次日”就是 CORN 优化持仓的样本。

财富曲线(对数轴)一张图讲完本文最重要的两个结论:
| 策略 | 模式市场终值 | 对照市场终值 |
|---|---|---|
| 先知上限(已知相位满仓切换) | 23.08 | — |
| CORN (ρ=0.2, w=5) | 5.50 | 0.71 |
| 事后最优 CRP(b*=1.00,满仓A) | 1.72 | — |
| 50/50 每日再平衡 | 0.94 | 0.92 |
| 只持有 B | 0.49 | — |
结论一:在有模式的市场里,CORN 对 BCRP 是碾压。 终值 5.50 对 1.72,三倍有余。这不是 ONS 那篇里”负遗憾”式的小幅反超(2.23 vs 1.87),而是结构性的:BCRP 被锁死在一套权重里,相位信息它一个 bit 都用不了;CORN 的有效对标是条件最优组合——先知上限 23.08 说明相位信息的全部价值有多大,CORN 无监督地吃到了其中约一半(对数尺度)。
Györfi 等人对核方法版本证明过一个很强的渐近结论:市场若是平稳遍历过程,模式匹配策略的长期增长率逼近该过程的对数最优增长率——比”不输 BCRP”强得多。CORN 的相关系数筛选没有同样干净的证明,但共享同一直觉,而我们的实验就是这个定理的一次有限样本演示。
结论二:在没有模式的市场里,CORN 不是无害的。 对照市场上 CORN 终值 0.71,跑输朴素 50/50 的 0.92。原因不难理解:纯噪声里照样有窗口对通过 ρ=0.2 的筛选(低维向量的随机相关很容易超过 0.2),CORN 在这些假相似样本上优化出自信的极端权重,然后为噪声买单。模式匹配的代价不是”没模式时退化为平均”,而是”没模式时把噪声当模式”——这一点比任何渐近定理都值得刻在交易系统的墙上。
权重轨迹:它真的学会了相位#

上半图是全程权重:前一百多天样本不足、权重在 0.5 附近踌躇,之后迅速进入 0/1 之间的逐日切换状态。下半图放大第 800-860 日,把 CORN 权重和真实相位叠在一起:切换节奏完全对齐——算法从没见过”相位”这个变量,它只看了窗口相关性。
一个反直觉的数字:匹配集的同相位纯度只有 65.6%——通过 ρ≥0.2 筛选的历史窗口里,三分之一其实是反相位的假相似。但这不妨碍权重做对方向:对数最优优化在意的是样本集的平均倾向,65% 纯度的样本集已经足够把最优权重推到正确的极端。模式匹配不需要完美识别状态,只需要让相似集的条件分布和无条件分布有足够差异。这是它对噪声鲁棒的真正来源,也解释了为什么如此粗糙的相似度度量(5 日窗口的皮尔逊相关)在实践中能工作。
ρ 敏感性:噪声混入 vs 样本饥荒#

把 ρ 从 -1(不筛选)扫到 0.8,终值财富的倒 U 形揭示模式匹配的核心张力:
- ρ = -1(全历史对数最优):终值 1.58,和 BCRP 的 1.72 一个量级——不筛选就没有超额,符合预期;
- ρ ∈ [0.2, 0.5]:高原区,终值 4.8~5.7,峰值在 ρ=0.3(5.71);
- ρ ≥ 0.7:崩落,ρ=0.8 时只剩 1.21——能通过筛选的窗口太少,大量交易日退回均匀权重,偶尔凑够的小样本集又充满噪声。
两种失败模式此消彼长:ρ 太松,相似集混入太多不相干历史,条件信息被稀释;ρ 太紧,样本饥荒,策略在小样本上过拟合或干脆躺平。和 SAX 的字母表大小、Shapelet 的窗口长度一样,ρ 是真超参数,最优值不可能先验知道。
原版论文的应对是 CORN-U / CORN-K:同时跑一组不同 (w, ρ) 的专家,按历史财富加权(U 均匀、K 只留 top-K)。这是把 Cover 的”对 CRP 混合”思想套到专家层面——单个专家有超参数风险,专家池对冲这个风险。工程上这是模式匹配系策略几乎必做的一步。
三盆冷水#
第一盆:O(T²) 计算量,历史越长越慢。 每天要和全部历史窗口比一次相关,全程 O(T²·wm)。日频十年、百资产的规模必须向量化(本文的矩阵乘法方案)甚至上近邻索引。好消息是相关性筛选本质是最近邻检索,SAX 的哈希桶、局部敏感哈希都能做粗筛——模式匹配组合和时间序列检索在算法层是同一个问题。
第二盆:低维窗口的『假相似』。 w=5 双资产的窗口只有 10 个数,10 维向量的随机相关分布很宽——对照实验已经演示了后果:纯噪声市场里 CORN 也能凑出大量”相似集”,然后亏在自信的极端权重上。窗口拉长能压低随机相关,但会稀释时效性并加剧样本饥荒。实务上 w 通常取 3-7 天,这意味着 CORN 匹配的是超短期节奏形态,不是宏观状态——别指望它认出”2008 式危机”这种叙事级相似。
第三盆:一切建立在『历史会押韵』上。 模式市场的实验条件是理想的:模式真实存在、全程稳定、历史里有海量同相位样本。真实市场的可匹配结构(如果存在)强度远弱于此、且会漂移和失效。CORN 的超额收益本质上是对”历史会押韵”假设的风险暴露——对照市场那条 0.71 的紫色虚线就是这个风险的价格标签。模式匹配策略的回测普遍好看,部分原因正是回测里的”历史”和”未来”来自同一分布;这个前提在结构断裂时失效,而结构断裂恰恰是亏大钱的时刻。
和前作的关系:一条谱系线#
从上往下,假设越强、上限越高、保证越脆弱。UP/ONS 在任何市场都不会输得难看,但注定平庸;CORN 在”历史押韵”的市场里可以碾压一切常数组合,但没模式时它比躺平更差。没有免费的午餐,只有明码标价的假设。
A 股实操注记#
- T+1 与涨跌停:CORN 逐日切换权重,A 股 T+1 下当日买入无法当日卖出,0/1 式的快速翻转实际执行至少滞后一天;涨跌停日的价格相对被制度截断,会污染窗口相关性,匹配时建议跳过含停板的窗口。
- 成本敏感:本文实验里 CORN 学会模式后几乎每天满仓对调——这是零成本世界的最优解,含单边 10bp 成本后逐日翻仓的代价巨大。上线前必须做含成本回测(方法参考 ONS 那篇的成本扫描),并考虑加权重平滑或换仓阈值。
- 数据清洗:后复权价、剔除停牌与异常波动日是窗口相关性有意义的最低要求。
完整代码#
import numpy as np
rng = np.random.default_rng(7)
# ---- 反相位周期市场 + 无模式对照 ----
T, sig = 1250, 0.0085
parity = np.arange(T) % 2
mu = np.zeros((T, 2))
mu[parity == 0] = (+0.003, -0.0025)
mu[parity == 1] = (-0.0025, +0.003)
X = 1.0 + mu + sig * rng.standard_normal((T, 2)) # 模式市场
X_ctrl = 1.0 + sig * rng.standard_normal((T, 2)) # 对照市场
def corn_run(X, w=5, rho=0.2, grid=101, min_samples=5):
T = len(X)
W = np.array([X[i:i+w].ravel() for i in range(T - w)])
Wz = (W - W.mean(1, keepdims=True)) / (W.std(1, keepdims=True) + 1e-12)
C = (Wz @ Wz.T) / W.shape[1]
b_grid = np.linspace(0, 1, grid)
B = np.vstack([b_grid, 1 - b_grid])
bs = np.full(T, 0.5)
for i in range(T - w):
t, jmax = i + w, i - w
if jmax <= 0:
continue
cand = np.nonzero(C[i, :jmax] >= rho)[0] + w
if len(cand) >= min_samples:
bs[t] = b_grid[int(np.argmax(np.log(X[cand] @ B).sum(0)))]
return bs
for name, M in [("模式市场", X), ("对照市场", X_ctrl)]:
bs = corn_run(M)
w_corn = np.prod(M[:, 0]*bs + M[:, 1]*(1-bs))
w_5050 = np.prod(0.5*M[:, 0] + 0.5*M[:, 1])
print(f"{name}: CORN {w_corn:.2f} 50/50 {w_5050:.2f}")python小结#
- CORN 用相关系数在历史里检索”相似市场”,只在相似样本上做对数最优——把在线组合选择从”对全历史平均”升级为”对条件历史优化”。
- 模式市场实测 CORN 终值 5.50 vs 事后最优 CRP 1.72:模式匹配的上限是条件最优而非常数最优,且匹配集纯度 66% 就够用——它不需要完美识别状态。
- 对照实验是本文最重要的一张牌:无模式市场上 CORN 终值 0.71 跑输 50/50——模式匹配在没有模式的世界里把噪声当模式,这是它区别于 UP/ONS 的真实风险。
- ρ 是真超参数(倒 U 形,峰值 ρ≈0.3),实务解是 CORN-U/K 专家池;O(T²) 计算、低维假相似、“历史会押韵”的假设风险,三个代价明码标价。