1. 量化交易当下最值得盯的几条技术线
这两年做量化的人应该都有个明显感受:传统多因子模型越来越卷,单纯靠量价因子和基本面因子的组合,超额收益衰减得非常快。我从2019年开始接触量化策略开发,最开始做的是经典的动量+均值回归组合,后来逐步转向机器学习,再到这两年重点研究强化学习和Transformer架构在交易信号上的应用。说实话,这个领域的迭代速度比我预想的快得多。
量化交易本质上是用数学模型和计算机程序去捕捉市场中的定价偏差,而当前最热门的几个方向,恰好对应着几种全新的"捕捉偏差"的思路。强化学习解决的是序贯决策问题——什么时候买、什么时候卖、仓位怎么调,这天然就是一个马尔可夫决策过程。Transformer解决的是长序列依赖建模问题——金融时间序列里那些跨越几百个时间步的隐性关联,传统RNN和LSTM很难有效捕捉。元学习解决的是快速适应问题——市场风格切换时,模型能不能用极少的新样本快速调整参数。区块链则在交易结算、策略审计和数据溯源层面提供了新的基础设施思路。
这篇文章适合三类人看:一是有一定Python基础、想从传统因子模型转型到深度学习方法的量化开发者;二是对强化学习或Transformer感兴趣但不知道怎么落地到交易场景的算法工程师;三是想了解当前技术前沿、判断下一步学习方向的学生或研究者。我会尽量把每个方向的核心思路、关键实现细节和我自己踩过的坑都讲清楚,代码部分给出可直接跑的框架。
2. 强化学习在量化交易中的落地路径
2.1 为什么强化学习天然适合交易决策
传统监督学习做交易信号预测,本质上是把问题建模成"给定特征X,预测标签Y"。比如用过去20天的量价数据预测明天涨跌。但这种建模方式有个根本缺陷:它忽略了决策的序贯性和仓位管理。你预测明天涨,但涨多少?该用多大仓位?如果跌了什么时候止损?这些都不是单步预测能解决的。
强化学习的框架是智能体在环境中不断交互,根据当前状态选择动作,获得奖励,然后优化策略。映射到交易场景:状态就是当前的市场特征(价格、成交量、持仓、账户余额等),动作就是买/卖/持有以及对应的仓位比例,奖励就是盈亏或风险调整后的收益。这个建模方式天然契合交易的本质。
我最初尝试的是DQN(深度Q网络),把动作空间离散化成{全仓买入、半仓买入、持有、半仓卖出、全仓卖出}五个动作。实测下来效果一般,主要问题是离散动作空间太粗糙,而且Q值估计在高维状态空间下不稳定。后来转向PPO和SAC这类连续动作空间的算法,效果明显好很多。
2.2 从零搭建一个强化学习交易环境
不管你用什么算法,第一步都是搭建环境。我建议直接用Gym的接口规范来写,这样后续换算法时不用改环境代码。
import gym import numpy as np from gym import spaces class TradingEnv(gym.Env): def __init__(self, df, initial_balance=100000, commission=0.0003): super().__init__() self.df = df.reset_index(drop=True) self.initial_balance = initial_balance self.commission = commission self.action_space = spaces.Box(low=-1, high=1, shape=(1,), dtype=np.float32) # 状态: [价格特征..., 持仓比例, 账户余额比例] self.observation_space = spaces.Box( low=-np.inf, high=np.inf, shape=(self.df.shape[1] + 2,), dtype=np.float32 ) self.reset() def reset(self): self.current_step = 0 self.balance = self.initial_balance self.position = 0 # 持仓数量 self.total_asset = self.initial_balance return self._get_obs() def _get_obs(self): row = self.df.iloc[self.current_step].values position_ratio = self.position * self.df.iloc[self.current_step]['close'] / self.total_asset balance_ratio = self.balance / self.total_asset return np.concatenate([row, [position_ratio, balance_ratio]]).astype(np.float32) def step(self, action): price = self.df.iloc[self.current_step]['close'] target_position_ratio = (action[0] + 1) / 2 # 映射到[0,1] target_value = self.total_asset * target_position_ratio current_value = self.position * price delta_value = target_value - current_value delta_shares = delta_value / price # 执行交易 cost = abs(delta_shares) * price * self.commission self.balance -= delta_shares * price + cost self.position += delta_shares self.current_step += 1 done = self.current_step >= len(self.df) - 1 new_price = self.df.iloc[self.current_step]['close'] if not done else price self.total_asset = self.balance + self.position * new_price # 奖励: 对数收益率 reward = np.log(self.total_asset / (self.total_asset - (self.position * (new_price - price) - cost) + 1e-8)) return self._get_obs(), reward, done, {}这个环境有几个关键设计点需要解释。动作空间用连续值[-1,1]表示目标仓位比例,比离散动作更灵活。奖励函数用对数收益率而不是绝对盈亏,是因为对数收益率具有时间可加性,而且对极端值的惩罚更温和,训练更稳定。状态里加入了持仓比例和余额比例,让智能体知道自己当前的仓位状态,这对学习仓位管理策略至关重要。
注意:回测环境里最容易犯的错误是未来函数。确保
_get_obs里用到的所有特征在current_step时刻都是已知的,不能用到未来数据。我一般会在数据预处理阶段就把所有特征做shift(1)处理。
2.3 PPO训练实战与调参经验
PPO是目前量化交易强化学习里最常用的算法之一,稳定性和样本效率比较平衡。我用stable-baselines3库来训练,省去很多底层实现的麻烦。
from stable_baselines3 import PPO from stable_baselines3.common.vec_env import DummyVecEnv from stable_baselines3.common.callbacks import EvalCallback env = DummyVecEnv([lambda: TradingEnv(train_df)]) eval_env = DummyVecEnv([lambda: TradingEnv(val_df)]) model = PPO( "MlpPolicy", env, learning_rate=3e-4, n_steps=2048, batch_size=64, n_epochs=10, gamma=0.99, gae_lambda=0.95, clip_range=0.2, ent_coef=0.01, verbose=1, tensorboard_log="./ppo_trading_tensorboard/" ) eval_callback = EvalCallback(eval_env, best_model_save_path="./best_model/", eval_freq=5000, deterministic=True, render=False) model.learn(total_timesteps=500000, callback=eval_callback)调参这块我踩过的坑比较多,说几个关键点。gamma设0.99意味着智能体关注未来约100步的累积收益,对应到日线数据大概就是半年左右的视野,这个比较合理。如果做日内高频,gamma可以设到0.95甚至更低。ent_coef是熵系数,控制探索程度,设太小容易陷入局部最优(比如一直持有不动),设太大策略会太随机。我一般从0.01开始试,根据训练曲线调整。clip_range是PPO的核心参数,控制策略更新的幅度,0.2是比较安全的默认值,如果训练不稳定可以降到0.1。
还有一个很重要的技巧:奖励归一化。原始的对数收益率数值很小(日线级别大概在0.001量级),直接拿来训练梯度信号很弱。我一般会用RunningMeanStd对奖励做标准化,或者直接把奖励乘以一个缩放系数(比如100)。
2.4 离线强化学习:IQL和CQL的适用场景
在线强化学习有个致命问题:训练过程中智能体需要不断与环境交互,但真实市场只有一个,你不能让一个未训练好的策略去实盘试错。离线强化学习(Offline RL)解决的就是这个问题——从已有的历史交易数据中学习策略,不需要在线交互。
IQL(Implicit Q-Learning)和CQL(Conservative Q-Learning)是目前离线RL里比较主流的两个算法。CQL的核心思路是在Q函数更新时加一个正则项,惩罚对训练数据分布外动作的过高估计。IQL则更巧妙,它完全避免了查询分布外动作的Q值,通过expectile回归来逼近最优Q函数。
我实测下来,IQL在金融数据上表现更稳定,因为它对分布外动作的保守程度更可控。CQL有时候会过于保守,导致学出来的策略倾向于不交易。如果你手头有大量历史逐笔成交数据或者分钟级数据,离线RL是一个很值得尝试的方向。
3. Transformer架构如何重塑时序预测
3.1 从LSTM到Transformer:为什么注意力机制更适合金融数据
金融时间序列和自然语言有一个共同点:长距离依赖很重要。比如一个政策事件的影响可能持续几十个交易日,某个技术形态的形成可能需要上百根K线的积累。LSTM理论上能捕捉长距离依赖,但实际上由于梯度消失和门控机制的局限性,有效记忆长度通常只有几十步。
Transformer的自注意力机制直接计算序列中任意两个位置之间的关联权重,不受距离限制。而且它是并行计算的,训练效率比LSTM高很多。但直接把原始Transformer拿来做金融预测效果未必好,因为金融数据的信噪比极低,标准注意力机制很容易过拟合噪声。
我在实际项目中的做法是对标准Transformer做几个关键修改。第一,用因果掩码(causal mask)确保每个位置只能看到之前的信息,避免未来函数。第二,在注意力层后加Dropout,比例设到0.2-0.3,比NLP任务里常用的0.1要高。第三,用较小的模型(2-4层,4-8个注意力头),金融数据的有效模式数量远少于语言,大模型纯粹是浪费算力还容易过拟合。
3.2 手写一个轻量Transformer预测价格
下面是一个我常用的轻量Transformer结构,专门针对金融时序做了优化:
import torch import torch.nn as nn import math class PositionalEncoding(nn.Module): def __init__(self, d_model, max_len=500): super().__init__() pe = torch.zeros(max_len, d_model) position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1) div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model)) pe[:, 0::2] = torch.sin(position * div_term) pe[:, 1::2] = torch.cos(position * div_term) self.register_buffer('pe', pe.unsqueeze(0)) def forward(self, x): return x + self.pe[:, :x.size(1)] class FinancialTransformer(nn.Module): def __init__(self, n_features, d_model=64, nhead=4, num_layers=3, dropout=0.2): super().__init__() self.input_proj = nn.Linear(n_features, d_model) self.pos_encoder = PositionalEncoding(d_model) encoder_layer = nn.TransformerEncoderLayer( d_model=d_model, nhead=nhead, dim_feedforward=d_model*4, dropout=dropout, activation='gelu', batch_first=True ) self.transformer_encoder = nn.TransformerEncoder(encoder_layer, num_layers=num_layers) self.output_head = nn.Sequential( nn.Linear(d_model, 32), nn.GELU(), nn.Dropout(dropout), nn.Linear(32, 1) ) def forward(self, x): # x: (batch, seq_len, n_features) x = self.input_proj(x) x = self.pos_encoder(x) # 因果掩码 seq_len = x.size(1) mask = torch.triu(torch.ones(seq_len, seq_len), diagonal=1).bool().to(x.device) x = self.transformer_encoder(x, mask=mask) # 取最后一个时间步的输出做预测 return self.output_head(x[:, -1, :])这个模型输入是(batch, seq_len, n_features),输出是下一个时间步的收益率预测。几个设计细节值得说明:位置编码用的是经典的正弦编码,金融时序里时间间隔是均匀的,正弦编码足够用。激活函数用GELU而不是ReLU,GELU在Transformer类模型里通常表现更好。输出头用了两层MLP加Dropout,增加非线性表达能力的同时控制过拟合。
训练时我一般用MSE损失加上一个方向性惩罚项——如果预测方向错了,额外加一个惩罚。这样模型不仅关注数值精度,还关注方向准确性,对交易策略更有意义。
3.3 视觉Transformer在K线图形态识别中的应用
Vision Transformer(ViT)这两年在图像领域很火,其实它在金融领域也有一个很有意思的应用场景:把K线图当成图像来处理,用ViT识别技术形态。
传统做法是手工定义形态(头肩顶、双底、三角形整理等),然后写规则去匹配。但手工定义的形态覆盖面有限,而且不同市场、不同时间周期的形态差异很大。用ViT可以直接把一段时间的K线图渲染成图片,让模型自己去学习哪些视觉模式对预测未来收益有用。
具体做法是:取过去60根K线,渲染成224x224的图片(用mplfinance库),然后送入预训练的ViT模型做微调。我试过用ImageNet预训练的ViT-Base,冻结前面几层,只微调最后几层和分类头。在A股数据上,这种方法对短期反转形态的识别准确率比手工规则高了大概8-12个百分点。
Swin Transformer也值得关注,它的层次化窗口注意力机制对局部形态的捕捉更精细,而且计算效率比标准ViT高。如果你的数据量够大(几十万张K线图),Swin Transformer是个不错的选择。
3.4 Transformer预测正弦数据:一个必做的 sanity check
在把Transformer用到真实金融数据之前,我强烈建议先在一个合成数据集上验证你的实现是否正确。最经典的就是正弦波预测任务。
def generate_sine_data(n_samples=10000, seq_len=50): t = np.linspace(0, 100, n_samples + seq_len) data = np.sin(t) + 0.1 * np.random.randn(n_samples + seq_len) X, y = [], [] for i in range(n_samples): X.append(data[i:i+seq_len]) y.append(data[i+seq_len]) return np.array(X)[..., np.newaxis], np.array(y)如果你的Transformer在正弦数据上能收敛到很低的MSE(大概0.01以下),说明模型结构和训练流程没问题。如果连正弦都拟合不了,那肯定是代码有bug,不用浪费时间在金融数据上试。这个check我每次搭新模型都会做,能省很多调试时间。
4. 元学习与因果强化学习的前沿探索
4.1 元学习解决的核心问题:快速适应市场风格切换
金融市场有个很讨厌的特点:非平稳性。去年有效的因子今年可能就失效了,上个月的趋势策略这个月可能一直亏。传统做法是定期重新训练模型,但重训练需要大量新数据,而且训练周期长,等模型适应过来可能行情又变了。
元学习的思路是"学会学习"——不是学一个特定的策略,而是学一个能快速适应新任务的初始化参数或学习算法。MAML(Model-Agnostic Meta-Learning)是最经典的元学习算法之一。它的核心思想是找到一组初始参数,使得模型在面对新任务时,只需要少量梯度更新就能达到好的表现。
映射到量化交易:每个"任务"可以是一个特定的市场状态(趋势市、震荡市、高波动、低波动),元学习的目标是找到一个初始策略参数,使得在面对新的市场状态时,用最近几天的数据微调几步就能适应当前环境。
我实测下来,MAML在期货跨品种套利策略上效果不错。不同品种的价差关系有相似的结构,但具体参数不同。用元学习预训练一个通用模型,然后每个品种用少量数据微调,比每个品种单独训练收敛快很多,而且样本外表现更稳定。
4.2 因果强化学习(CRL)的核心机制
因果强化学习是最近两年很热的一个方向,核心思路是把因果推断的工具嵌入强化学习流程。传统RL学的是相关性——在什么状态下做什么动作能获得高奖励。但它不知道因果关系,容易学到虚假相关。
举个例子:假设训练数据里每次央行降准后股市都涨,RL会学到"降准→买入"的策略。但如果实际上涨的原因是降准同时伴随的其他政策组合,单独降准并不一定导致上涨,那这个策略就不可靠。因果RL通过构建因果图、做干预推断,能区分真正的因果效应和虚假相关。
具体实现上,CRL一般包含几个关键组件:因果发现模块(从数据中学习变量间的因果结构)、干预模块(在因果图上做do-calculus推断)、策略优化模块(基于因果效应而非相关性来优化策略)。这个方向目前还比较学术,工业界落地案例不多,但我觉得未来两三年会有更多实际应用。
4.3 基于模型的强化学习在交易中的优势
无模型RL(如PPO、SAC)直接从交互中学习策略,样本效率低。基于模型的RL(MBRL)先学一个环境模型(预测下一状态和奖励),然后在这个学到的模型里做规划或生成虚拟经验来训练策略。
在量化交易里,MBRL有个天然优势:我们可以用历史数据学一个市场动态模型,然后用这个模型生成大量虚拟市场路径,在这些虚拟路径上训练策略。这样既利用了历史数据,又增加了样本多样性。我试过用Dreamer风格的MBRL做仓位管理,在样本效率上确实比PPO高不少,大概用1/3的数据量就能达到差不多的效果。
但MBRL的风险在于模型误差。如果学到的环境模型和真实市场偏差太大,策略在实盘上会表现很差。我的做法是用模型集成(ensemble)来估计不确定性,当模型预测的不确定性高时,降低策略的激进程度。
5. 区块链在量化交易基础设施中的角色
5.1 策略审计与交易溯源
区块链在量化交易里最直接的应用是策略审计和交易溯源。传统模式下,策略的决策逻辑和交易记录都存在中心化服务器上,存在被篡改的可能。如果把每次策略决策的关键参数和交易信号上链,就能实现不可篡改的审计追踪。
具体做法是:策略每次产生交易信号时,把时间戳、信号值、特征快照的哈希值写入区块链。这样事后可以验证策略是否严格按照预定逻辑执行,有没有人为干预。对于需要向投资者披露策略执行情况的资管产品,这个方案能大幅降低信任成本。
我参与过一个私募基金的项目,他们用联盟链记录所有策略信号和交易执行,合规部门可以实时审计。技术实现上用的是Fabric框架,每秒能处理几千笔交易记录,完全够用。
5.2 去中心化数据市场与因子交易
量化交易的核心资产之一是数据。传统的数据采购模式是买方直接向数据供应商购买,价格不透明,而且数据质量参差不齐。区块链可以构建一个去中心化的数据市场,数据供应商把数据加密后上传,买方用代币购买解密密钥,智能合约自动完成结算。
更进一步,因子也可以作为一种资产在链上交易。你开发了一个有效的alpha因子,可以把它加密后挂到链上,其他策略开发者付费调用。智能合约确保调用次数和付费的透明性。这个方向目前还在早期探索阶段,但逻辑上是通的。
5.3 区块链盲盒机制在策略组合中的启发
区块链盲盒(Blind Box)是NFT领域的一个玩法:用户购买时不知道具体内容,开盒后随机获得不同稀有度的资产。这个机制其实可以启发策略组合的构建。
想象一下:你把多个策略打包成一个"策略盲盒",投资者购买时不知道具体配置,但知道整体的风险收益特征。开盒后根据市场状态动态调整策略权重。这种模式在合规前提下,可以降低投资者对短期业绩的过度关注,更注重长期配置。当然,这只是一个思路,实际落地需要考虑很多合规和运营问题。
6. 实操中的常见问题与排查技巧
6.1 强化学习训练不收敛怎么办
这是最常见的问题。排查顺序我一般是这样的:
| 排查项 | 检查方法 | 常见原因 |
|---|---|---|
| 奖励尺度 | 打印每episode的累积奖励 | 奖励太小导致梯度消失,乘以100试试 |
| 状态归一化 | 检查状态各维度的均值和方差 | 未归一化导致网络训练困难 |
| 动作空间 | 确认动作范围和映射逻辑 | 映射错误导致智能体无法执行有效动作 |
| 超参数 | 学习率、gamma、clip_range | 学习率太大导致震荡,太小导致收敛慢 |
| 环境bug | 用随机策略跑一遍 | 环境本身有逻辑错误 |
我遇到最多的情况是奖励尺度问题。金融数据的收益率数值很小,直接做奖励,PPO的梯度信号非常弱。解决办法是对奖励做RunningMeanStd标准化,或者直接乘以一个常数。
6.2 Transformer过拟合的识别与处理
金融数据信噪比低,Transformer很容易过拟合。识别过拟合的信号:训练loss持续下降但验证loss开始上升;训练集上的策略夏普比率很高但样本外很差。
处理方法我一般用这几招:增加Dropout(0.2-0.3);减小模型规模(层数降到2-3层,d_model降到32-64);加权重衰减(1e-4到1e-3);用早停(验证loss连续10个epoch不下降就停);数据增强(加噪声、时间窗口裁剪、多尺度采样)。
还有一个很有效的技巧:在注意力权重上加稀疏正则,鼓励模型只关注少数关键时间步。金融数据里真正有预测力的时间点往往很少,稀疏注意力能降低过拟合风险。
6.3 回测与实盘差异的排查清单
回测表现好但实盘差,这是量化交易永恒的痛点。我整理了一个排查清单:
- 交易成本假设是否合理(佣金、滑点、冲击成本)
- 回测是否用了未来数据(特征计算、信号生成、执行价格)
- 流动性假设是否过于乐观(小盘股、涨跌停、停牌)
- 市场冲击是否被低估(大资金量的策略尤其要注意)
- 策略容量是否足够(资金规模大了之后alpha会衰减)
- 实盘执行延迟是否被忽略(信号生成到订单成交的时间差)
我自己的经验是,回测夏普比率打个6-7折大概就是实盘的预期水平。如果回测夏普3.0,实盘能有1.8-2.0就不错了。心理预期要放平。
6.4 离线RL中分布偏移问题的处理
离线RL最大的挑战是分布偏移:训练数据由某个行为策略生成,但学到的策略可能选择训练数据中从未出现过的动作。这时候Q函数对这些分布外动作的估计完全不可靠。
处理方法主要有三类:一是保守方法(CQL),惩罚分布外动作的Q值;二是不查询分布外动作(IQL),通过expectile回归避免查询;三是加行为克隆正则,让学到的策略不要偏离行为策略太远。我一般会组合使用,先用CQL做预训练,再用IQL微调,最后加一个行为克隆的KL散度约束。
实操心得:离线RL在金融数据上,数据质量比算法选择更重要。如果行为策略本身就很差(比如随机交易),学出来的策略也不会好。尽量用成熟策略的历史交易数据作为离线数据集。
7. 工具链与学习资源推荐
7.1 核心工具链
做量化+深度学习,工具链的选择直接影响开发效率。我的推荐组合是:
- 数据处理:pandas + numpy + polars(大数据量时polars比pandas快很多)
- 深度学习:PyTorch(灵活,调试方便)+ stable-baselines3(RL算法库)
- 回测框架:backtrader(功能全)或 vectorbt(速度快)
- 实验管理:Weights & Biases或MLflow(追踪实验参数和结果)
- 可视化:matplotlib + plotly(交互式图表)
7.2 学习路径建议
如果你是刚入门,我建议的顺序是:先把强化学习的基本概念搞清楚(MDP、值函数、策略梯度),推荐David Silver的强化学习课程,讲得非常系统。然后学Transformer,The Illustrated Transformer那篇博客是最好的入门材料,图解方式把注意力机制讲得很透。最后再把这些方法应用到金融数据上,从简单的任务开始(比如用RL做单资产仓位管理),逐步增加复杂度。
不要一上来就搞多资产、高频、复杂模型。我见过太多人一开始就搭一个巨大的Transformer+RL系统,结果调了三个月连baseline都没跑通。先从简单的开始,跑通了再迭代。
7.3 关于"量化交易之路"这类资源的看法
市面上有很多量化交易的入门资料,质量参差不齐。我的建议是:不要花太多时间在收集资料上,选一两本经典的(比如《主动投资组合管理》),然后直接动手写代码。量化交易是一门实践性极强的技能,看一百本书不如自己写一个完整的策略从回测到实盘跑一遍。
代码方面,GitHub上有大量开源的量化策略和RL实现,但要注意甄别质量。很多repo的回测结果有未来函数或者过拟合问题。我一般会重点看它的数据处理部分和回测逻辑,这两块没问题才值得参考。
最后分享一个我自己的习惯:每次尝试一个新方法,我都会先在模拟数据上验证,确认实现正确后再上真实数据。这个习惯帮我省了无数调试时间。金融数据的噪声太大,如果实现有bug,你很难区分是模型不行还是代码有问题。用合成数据做sanity check,是区分这两者最有效的方法。