Vasily's Blog

一个记录学习经历的站点

0%

因子化世界模型:三次迭代、七次实验、全部亏损

缘起:一个优雅的想法

2025年底读到一篇关于因子化世界模型的论文,讲的是将图像分解为"语义token"和"细节token"进行分治建模。核心思路很简洁:如果能把复杂系统的生成过程解耦成不同时间尺度的子过程,分别建模再组合,应该比端到端硬拟合更高效。

受此启发,我开始思考价格运动的分解。直觉上,价格变动可以分成两个物理过程:

  1. 趋势延续(Trend Continuation):价格沿着已形成的趋势惯性运行——这是一个低频、近似确定性的宏观过程。就像抛出去的球,会沿着当前方向继续飞行一段。
  2. 均值回归(Mean Reversion):价格因短期过度偏离而出现向均值回归的倾向——这是一个高频、类似弹性恢复的过程。就像被拉伸的橡皮筋,总有回缩的趋势。

传统的端到端价格预测试图用一个模型同时逼近这两个不同物理过程的复合。如果能拆开分别建模再组合,理论上应该更高效、更可解释。

于是开始了因子化世界模型(Factorized World Model, FWM)项目。前后迭代三个版本,七次全量实验,全部亏损,无一正收益。

我需要先说明一件事:这篇文章不是为了证明"某个特定的模型不行",而是想展示一个完整的研究周期——一个看起来很有前景的方向(因子化分解+世界模型预测),经历了三次根本性的假设修正、七次全量实验验证,最终被系统性地排除。每一次失败都教会了我们一些关于市场底层结构的东西。


V1:HP滤波 + Transformer(2026-06-25)

v1 架构设计

第一版的设计直接受论文启发:将价格序列解析为"趋势Token"和"回归残差"两部分,用Transformer学习它们的演化规律。

状态分解器(StateDecomposer)是整个系统的第一步,也是最关键的一步。它负责从原始OHLCV中提取出"趋势成分"和"回归成分":

  • HP滤波:用\(\lambda = 5000\)的Hodrick-Prescott滤波从收盘价中提取一条平滑的趋势线\(\tau_t\)。HP滤波是一种在"趋势光滑度"和"拟合度"之间找平衡的算法,\(\lambda\)越大,趋势线越平滑。
  • 趋势Token:计算趋势线在12h窗口内的斜率,做z-score标准化后离散化为5类——强下降(0)、弱下降(1)、横盘(2)、弱上升(3)、强上升(4)。阈值用\(\pm 0.3\sigma\)\(\pm 1.0\sigma\)划分。
  • 残差特征:定义残差\(r_t = p_t - \tau_t\),提取8维特征向量:标准化残差、RSI(14)、布林带%B位置、ATR比率、成交量比率、K线振幅比率、斜率z-score、EMA20偏离比率。
  • 均值回归标签:当标准化残差超过\(2\sigma\)且未来6根K线内价格向趋势线方向回归超过50%,标记为回归触发点。这是一个前视标签(使用了未来信息),仅用于训练。

模型架构如下:

1
2
3
4
5
6
7
8
输入: 趋势Token(5类) + 8维残差特征

Token嵌入(5→192) + 残差投影(8→192) → 逐元素相加 → 可学习位置编码

4层Transformer Decoder (因果掩码, d_model=192, 6头, FFN=768)

趋势预测头: Linear(192, 5) → softmax → 下一时刻趋势状态分布
回归信号头: Linear(192, 128) → GELU → Linear(128, 1) → sigmoid → MR概率

决策解释器将趋势概率与MR概率结合:上升趋势+低MR→做多;上升趋势+高MR→平仓;下降趋势+低MR→做空;下降趋势+高MR→平仓;横盘→观望。趋势概率熵过高时强制降仓。

实现过程中的三个Bug

写代码过程中修了三个Bug,每个都值得记录:

  1. HP滤波内存溢出\(48,000 \times 48,000\)的稠密矩阵需要17GB内存,直接OOM。改用scipy.sparse的五对角矩阵实现,从17GB降到几MB。
  2. 斜率z-score的O(T²)循环_compute_slope_z 函数对每个时间步都重算所有历史斜率,全量数据需要\(\sum_{t=24}^{47999} (t-12) \approx 1.15 \times 10^9\)次polyfit,预计运行数小时。改为一次卷积预计算所有斜率,O(T)搞定。
  3. 回测索引越界:最后一个bar没有下一个K线数据,超出边界。加上边界检查。

修复后全量48,000根K线的状态分解从数小时降至19.6秒。

初试:单次分割的假象

第一次训练用标准的70/15/15时序分割,结果看起来几乎完美:

1
2
3
4
5
6
7
8
趋势准确率: 96.0%
Cohen's Kappa: 0.95 (接近完美)
MR AUC: 0.89

回测(测试期2025.08-2026.05,BTC跌了35%):
完整模型: +904% 夏普 8.26
趋势跟随: +4579% 夏普 12.11
买入持有: -35%

夏普12在真实市场中不存在。文艺复兴大奖章基金的历史最佳也才3-4的水平。这个数字完美得不像真的——因为它就是假的。

问题是单次分割只测了最后15%的数据,而那段时间BTC处于单边下跌。模型只要无脑做空就能赚钱。MR预警全程负贡献:趋势跟随(无MR):+4579% vs 完整模型(有MR):+904%——MR信号全错了。

滚动训练:真相大白

构建了累积窗口的walk-forward训练模式,18个窗口覆盖2022-2026全年:

1
2
3
4
5
6
7
窗口 1:  训练 [0-10000]   测试 [10000-12000]  → 完整模型: -37.7%
窗口 2: 训练 [0-12000] 测试 [12000-14000] → 完整模型: -15.6%
窗口 3: 训练 [0-14000] 测试 [14000-16000] → 完整模型: -12.7%
窗口 4: 训练 [0-16000] 测试 [16000-18000] → 完整模型: -43.9%
...
窗口18: 训练 [0-44000] 测试 [44000-46000] → 完整模型: -48.5%
累计: -99.9%

18个窗口,全部亏损,无一例外。

这不是运气问题,这是系统性问题。在上涨的窗口、下跌的窗口、震荡的窗口——模型都在稳定亏钱。

换成山寨币试试

也许只是BTC太有效了?用同样的配置测了三个不同波动等级的山寨币:

币种 等级 累计收益 平均夏普 结论
SOLUSDT 高波主流 -100.0% -2.28 同上
DOGEUSDT 中高波meme -100.0% -4.07 同上
ZECUSDT 中波老币 -100.0% -2.18 同上

完全一致。全部-100%,无一幸免。

v1失败的根因诊断

现在复盘,有两个致命设计错误:

1. HP滤波的前视偏差

StateDecomposer对整个训练集做了一次性双向HP滤波:

1
trend = spsolve(A, series)  # 一次性求解整个序列

这意味着训练集里时间t的趋势是拿t以后的数据算出来的。模型学到的"规律"本质上包含了未来信息,到真没见过的新数据上自然崩溃。这解释了为什么单次分割(70/15/15)的测试结果看起来那么漂亮——验证集和测试集的数据也被同样方式分解了,信息泄漏是一体的。

2. OHLCV的单向预测信噪比太低

本质上在做的是:用\([t-47, t]\)的OHLCV数据预测\(t+1\)时刻的方向。这个任务的信噪比在加密货币1h级别上极低。无论是浅层模型(XGBoost)、深层模型(LSTM)、还是大型模型(Transformer/LLM),都无法从中提取出能稳定盈利的信号。

之前的一系列独立实验也得出了一致结论: - DeepSeek-LLM交易回测:50+技术指标,方向预测准确率44%(接近随机) - XGBoost/随机森林在BTC 1h上反复尝试:无法产生稳定正收益 - LSTM集成模型:同样没有正预期


V2:VAE + 三种概率体制(2026-06-26)

从c1+c2=1到P(z)总和为1

V1的根本问题在于核心假设:"市场每一时刻都能被确定性地分解为趋势成分+回归成分,且两者占比之和恒为1"。

这个假设有三个隐含的强论断:

  1. 互斥且完备:趋势和回归是全部。但大量时间市场处于随机游走状态,既没有趋势也没有回归。
  2. 比例可叠加:它们可以线性叠加。但趋势衰竭本身可能是触发回归的非线性阈值过程。
  3. 瞬时可分解:在每一个时间切片上都能计算出占比。但"趋势"和"回归"本质上是时间序列上的涌现属性,需要上下文才能定义。

修改后的假设改为概率框架:市场存在三种互斥但可切换的生成体制——趋势体制回归体制噪声体制。在任何时刻,市场仅处于其中一种体制,但我们只能以概率推断其归属:

\[P(\text{Trend}) + P(\text{Reversion}) + P(\text{Noise}) = 1\]

"c1+c2+c3=1"的数学结构被完整保留,但从「确定性成分占比」升级为「概率性体制归属」。

V2架构

采用VAE + Gumbel-Softmax离散潜变量框架。核心思路是用「结构先验」(自相关约束)替代人工标签,让三类自动形成趋势、回归、噪声的语义:

  • 编码器:LSTM(13维特征→64维隐藏,2层) → Linear → 3类logits → softmax得到\(q(z | x)\)- 趋势生成器:被约束为高自相关生成(目标AC=+0.3)
  • 回归生成器:被约束为负自相关生成(目标AC=-0.3)
  • 噪声生成器:被约束为零自相关生成(目标AC=0.0)
  • 损失函数:NLL重构损失 +$\(\boldsymbol{j}\)ot\(KL散度 +\)\(\boldsymbol{j}\)ot$自相关约束损失 + 熵奖励

每个生成器以编码器输出的体制后验为权重,进行加权混合生成最终收益分布: \[p(x_{t+1} | x_{window}) = \sum_{z} q(z | x_{window}) $\boldsymbol{j}$ot p_z(x_{t+1})\]

全量测试结果

数据:BTCUSDT 1h, 47,999行 (2020-12-07 ~ 2026-05-30)

指标 Ep 1 Ep 17(最佳) 说明
NLL 3.42 2.58 合理范围 ✅
KL 0.16 0.06 后验朝均匀分布坍缩 ⚠️
AC Loss 0.23 0.36 不降反升 ❌
验证Loss 2.26 2.20 几乎不降 ❌

VAE训练收敛但体制无意义。证据:

  1. 体制分布:Trend 53.2% / Reversion 43.6% / Noise 3.2%。Noise几乎不被分配,Trend和Reversion各一半。
  2. 切换率极低:Trend→Trend 98.4%,Rev→Rev 97.7%。体制状态几乎锁定,不是在做"区分趋势和回归",而是在做"区分高波动期和低波动期"。
  3. 自相关约束完全失效
生成器 实际自相关 目标值 结论
Trend +0.936 +0.30 ❌ 远高于目标
Reversion +0.996 -0.30 ❌ 完全错误的方向
Noise +0.064 0.00 ✅ 接近目标

回归生成器(目标是负自相关)实际自相关+0.996,比趋势生成器还要"趋势"。AC Loss在训练中完全被NLL和KL压制了。

预测器完全没有在学习

1
2
3
训练11个epoch
训练Loss: 12.9308 → 12.9299 (几乎没动)
验证Loss: 12.9078 → 12.9078 (完全不变)

交叉熵12.9对于一个三分类问题,对应的是几乎均匀分布的概率输出。预测器实际在输出常量分布,完全不依赖输入。

VAE失败的核心诊断

自相关约束这种弱结构先验,根本不足以让神经网络区分"趋势"和"回归"。 对于神经网络来说,从原始价格数据里学到"正自相关"和"负自相关"的区别是极其困难的,因为价格序列的自相关本身在大部分时间都非常微弱(有效市场)。

验证方法:随机采样被VAE高概率分配为Trend、Reversion、Noise的片段,用肉眼去看。结果是——三个体制的样本路径看起来完全一样:都是随机波动。自相关约束从未真正在生成器层面起过作用,VAE实际学到的是"波动率聚类",而不是"动力学分类"。


V3:EMA监督编码器(2026-06-26)

突破性的思路转变

V2的失败让我们想通了一件事:我们不需要模型自己去发现"什么是趋势/回归"——我们可以把定义直接写死。

于是有了EMA20/EMA50规则:

  • UPTREND:EMA20 > EMA50,EMA20斜率>0,价格在EMA20之上
  • DOWNTREND:EMA20 < EMA50,EMA20斜率<0,价格在EMA20之下
  • NOISE:均线缠绕(近期发生过交叉或间距 < ATR×0.5)、均线走平(斜率接近0)、价格无序穿越

这是一个客观、可回溯、无歧义的三分类标签。任何人在任何时间点用同样的EMA规则,都会得到完全相同的标签。

两阶段架构

阶段一:RegimeEncoder(学会"看")

用LSTM(128dim, 2层)从13维原始特征中识别当前EMA体制。输入中禁止包含任何均线数据——强制模型从K线形态、波动率、成交量等底层特征中学习EMA交叉的迹象。

训练结果:

1
2
3
4
5
6
7
8
Ep  1: TrLoss=0.47, TrAcc=79.2%, ValAcc=87.2%
Ep 20: TrLoss=0.10, TrAcc=96.1%, ValAcc=95.2%
Ep 72: 早停, 最佳ValAcc=95.8%

测试集准确率: 86.22%
UPTREND: 88.9% (4910样本)
DOWNTREND: 90.0% (4369样本)
NOISE: 82.3% (5121样本)

这是三个版本中第一次模型真正学到了有语义的东西。 它的内部隐藏状态确实编码了"什么是上涨趋势、什么是下跌趋势、什么是震荡"。而且是从原始K线模式中学会的——不是在计算EMA,而是在识别导致EMA交叉的价格行为模式。

阶段二:体制切换预测器(学会"想")

用因果Transformer(d=128,h=8,L=4)以前K步的体制后验+特征预测下一时步的体制。

1
2
3
最佳验证准确率: 86.6%
持久性基线("明天=今天"): 87.35%
预测器相对基线: -0.75%

预测器比懒人基线还差。 这个结果解释了一切亏损的根因。

真相:切换预测的"稀有事件困境"

看数据分布:

状态 占比 持续期
UPTREND 33.7% 平均10.3根K线
DOWNTREND 30.4% 平均10.1根K线
NOISE 35.8% 平均5.7根K线
切换事件 12.5%

在这个分布下,"明天和今天一样"这个懒人规则天然有87.35%的准确率。预测器辛辛苦苦训练了25个epoch,准确率86.6%,比懒人规则低了0.75个百分点。

然后交易逻辑怎么做?模型预测"要切换",于是提前开仓/平仓。但它的预测准确率低于懒人基线,意味着大多数"要切换"的预测都是错的。在应该继续持仓的时候平仓了,在应该继续观望的时候开仓了。交易越频繁,错得越多。

方向A/B/C:三次修正尝试

方向A:切换事件预测(Focal Loss + 二分类)

把三分类改为二分类——"下期是否发生切换"(是/否),使用Focal Loss(\(\gamma=3.0\))处理不平衡问题:

指标 数值
切换检测准确率 76.5%
切换召回率 65.9%
切换精确率 30.28%
方向预测准确率 86.3%
持久性基线 87.3%

切换召回率65.9%——模型确实抓住了三分之二的真切换。 但精确率只有30.28%,意味着70%的"切换预警"是假的。这是经典"狼来了"困境。模型学会了"当市场要变天时,我通常会知道",但同时也染上了致命的坏习惯:"当市场没变天时,我也总觉得它要变天"。

方向A回测:-99.09%。

方向B:编码器标签偏移

把训练标签前移K期,让编码器学习预测未来的EMA体制。这个想法是好的——滞后转化为预判能力——但EMA标签本身的切换率只有12.5%,前移后训练难度大增,效果有限。

方向C:概率矛盾交易

用编码器确信度(>0.7)和预测器警告(>0.3)的矛盾信号触发交易。理论上是完美的——矛盾意味着不确定性,高不确定性应该减仓。

实际结果:矛盾信号触发了10,333次(占总测试步的72%)。方向C的触发条件几乎无时无刻不在发生,完全失效。回测:-100.00%(夏普-51.95)。

为什么纯编码器也亏88%

模式B绕开了预测器,根据编码器识别的当前体制直接交易:UPTREND做多,DOWNTREND做空,NOISE观望。

结果:-88.40%。

即使你完美地(87%准确率)知道当下市场处于EMA定义的什么状态,你也无法用这个状态赚钱。因为市场已经为这个公开信息定价了。当EMA金叉确认时,趋势往往已经走完了一大段,剩下的空间风险回报比极差。

这个结果揭示了一个重要的研究结论:编码器成功学到的"知识",在市场上没有Alpha。 你训练了一个极其优秀的"描述模型",但它不是"预测模型"。

最终尝试:冻结编码器 + 收益方向预测

放弃所有与"体制切换"相关的预测。冻结编码器参数,在它的隐藏向量(128维,包含EMA趋势/震荡的语义信息)上训练一个简单的MLP,直接预测下一期收益方向。

1
2
3
ReturnPredictor验证结果:
最佳准确率: 52.50% (基线50.80%)
测试集准确率: 51.70% (基线50.45%)

这是三个版本七次实验中首次在验证集上超越了基线。 52.5% vs 50.8%——超过1.7个百分点。但它仍然不足以覆盖交易成本。

多阈值测试揭示了一个关键模式:

阈值 总收益 夏普 交易次数 持仓(根K) 频率(根/次)
0.50 -92.73% -3.25 2805 5.3 5.3
0.52 -98.90% -6.50 4597 3.3 3.3
0.55 -98.82% -9.11 4279 3.6 3.6
0.58 -61.48% -4.85 1065 13.9 13.9
0.60 -18.72% -2.85 138 106.9 106.9
0.65 +0.76% +1.12 12 >1000 >1000
0.70 接近0% ~0 <5

数据清晰地展示了收益方向预测器的置信度分布:几乎所有预测都集中在50-55%之间,极少达到60%以上。阈值抬到0.60时只剩138次交易(仍亏-18.72%),到0.65时虽然变正了但只剩12次交易。预测器从未达到过可以稳定交易的置信度。


三个版本的横向对比

维度 V1: HP滤波+Token V2: VAE+体制 V3: EMA监督
核心方法 人工趋势分解 无监督体制学习 监督标签学习+收益预测
编码器质量 数据泄露(HP滤波) 模式坍缩(Noise=3%) 87%准确✅
自相关约束 无(确定性分解) 完全失效(Trend=+0.94) 未使用(改用硬标签)
预测器 趋势Acc~0.32 KL=12.9(完全失败) 方向Acc=52.5%✅
最佳回测 -99.9%(18窗口) -77.91%(VAE+预测器) -18.72%(th=0.60)
交易成本影响 致命(频繁交易) 致命(1608笔) 致命(4279笔)
核心教训 前视偏差必死 弱先验不足以区分动力学 EMA知识无Alpha

亏损路径的共性

三次迭代虽然架构完全不同,但亏损的模式出奇一致:

  1. 信号微弱:无论是趋势Token分类(32%)、体制预测(KL=12.9)、收益方向预测(52.5%)——预测能力仅略高于随机(或低于基线)。
  2. 交易成本放大亏损:即使方向正确率略高于50%,每次交易的手续费(0.1%)和滑点足以将微弱的正预期抹平。
  3. 假信号过多:在87%状态不变的市场里,任何试图预测变化的模型都会产生大量假警报。切换精确率30%意味着70%的交易都是错误的。

五个关于量化研究的教训

教训一:不要相信单次分割

V1的70/15/15分割给出了夏普12的"完美结果"。滚动训练告诉你真相:18/18窗口全部亏损。Walk-forward测试是必需的,单次分割的夏普12是假象。

教训二:独立实验的负结论值得信任

这个项目串联了之前多次独立实验的结论:

方法 结果
DeepSeek-LLM (50+指标) 方向正确率44%
XGBoost/随机森林 无法稳定盈利
LSTM集成 无正预期
FWM V1 (HP+Transformer) 18窗口全部亏损
FWM V2 (VAE+体制) 全亏损, -77.91%
FWM V3 (EMA监督+收益预测) 全亏损, best -18.72%

六种完全不同方法(LLM/树模型/LSTM/Transformer/VAE/监督分类)在两万小时数据上的一致性负结论,这个结论本身值得信任。M个独立实验得到同样的负结论,这条路行不通的概率极高。

教训三:"描述能力"不等同于"预测能力"

V3的编码器在"从原始数据识别当前EMA状态"这个描述任务上达到了87%的准确率——这是一个了不起的工程成果。但把这个描述能力直接拿来交易,亏了88%。

描述模型回答的是"现在是什么",预测模型回答的是"接下来会怎样"。 在金融市场上,前者是公开信息(已经体现在价格中),后者才是Alpha的来源。两者之间的差距,就是你在回测中亏掉的钱。

教训四:稀有事件预测有结构性天花板

当目标事件只占12.5%(状态切换),而"不变"占87.5%时,任何试图预测变化的模型都面临结构性困难。Focal Loss能把切换召回率从接近0%提升到65.9%,但切换精确率只有30.28%。

这不是调参能解决的问题。EMA规则生成的状态序列天然"黏滞"——EMA20/EMA50是平滑的慢速指标,交叉后会持续很长时间不交叉。在这种标签设计下,"不变"是默认正确选项,任何"预测变化"的尝试都在跟统计上的先验对抗。

教训五:知道什么时候停止

有时候最有价值的实验结果是排除了一个方向,而不是找到了一个赚钱的策略。

这个项目共产生了约7000行代码、3个模型文件、17次全量训练运行。从一个看起来很有前景的想法(因子化分解+世界模型预测),经历了三次核心假设修正,最终被系统性地排除。这个结果的真实价值——比一次"成功的回测"更大——因为它排除了一个很有迷惑性的方向,让精力可以集中在真正有效的事情上:

当前确认有效的策略是低波山寨币暴跌抄底(胜率57.5%,夏普4.01),继续实盘模拟。


代码归档

  • ml_models/factorized_world_model.py — FWM V1核心模型
  • ml_models/train_factorized_world_model.py — FWM V1训练+回测
  • ml_models/fwm_v2.py — FWM V2: VAE概率体制模型
  • ml_models/fwm_v3.py — FWM V3: EMA监督编码器+ReturnPredictor
  • notes/量化总结.md — 全部实验结果汇总