最近收到不少同学的私信,都在问量化研究员(机器学习方向)这个实习岗位到底做什么,尤其是那些标注“实习转正”机会的岗位描述。今天就拿一个典型的JD来拆解——工作地点深圳/上海,核心工作写得很统一:利用强化学习、深度学习等方法,对公司已有因子进行组合,产出对加密货币市场未来收益的预测。
这不是一篇招聘解读,我是把它当成一份“技术方向说明书”在聊。这个岗位看起来是量化,做的是金融数据,但骨子里拼的还是机器学习工程能力:数据处理、特征工程、时序建模、强化学习训练和回测避坑。适合正在投递类似岗位的在校生,也想转行量化ML方向的工程师认真读一遍,能帮你少走很多弯路。
1. 先从岗位JD说起:机器学习在量化研究里到底承担什么角色
1.1 一条业务链路捋下来
很多同学看量化ML岗的JD,第一反应是“这不就是调模型吗”,实际完全不是这么简单。真实量化团队的业务链路大概是这样的:
- 数据采集与清洗:把K线、成交记录、资金流、链上数据、情绪数据统一成干净的时间序列。
- 因子库维护:公司多年积累下来的因子,比如动量类、反转类、波动率类、资金流类。
- 因子有效性分析:IC、RankIC、分组收益、换手率,监控因子是否还在工作。
- 因子组合与预测:把几十上百个因子喂给模型,合成一个对未来的收益预测。
- 组合构建与风控:预测信号转成目标仓位,叠加手续费、滑点、最大回撤约束。
- 执行与复盘:下单、拆单、盘后归因。
机器学习研究员主要卡在“因子组合与预测”这一环。JD里写的“对公司已有因子进行组合,产出对加密货币市场未来收益的预测”,翻译成人话就是:给你一堆特征,让你训练一个模型,输出未来一段时间收益的预测值,并且这个预测值要稳定、可解释、能上实盘。
1.2 “因子组合预测”具体指什么
为什么是“组合”而不是“挖掘”?这是量化团队内部分工决定的。挖因子通常是另一拨Quant Researcher干的活,他们从海量数据里找出跟未来收益相关的特征,比如某个时刻的资金流指标。ML岗做的事情是把这些单因子做合成,因为单因子太弱、噪音太大,直接线性加权又浪费了因子之间非线性的交互关系。
举个实际的例子。假设你手上有一个动量因子和一个波动率因子,单独看IC都只有0.02左右,没什么卵用。但如果把两个因子放到一起,加上一些交互项,可能在某些市场状态下能到0.05以上。深度学习模型能自动学习这种条件关系,这就是“组合”对“单个因子”的增益。
有一类误解是“深度学习在因子挖掘上很厉害”,其实很多团队试下来,深度模型在单因子挖掘上未必比人工特征+线性IC筛选强多少,但在多因子合成阶段优势很明确。这也是JD只让你做组合预测的底层逻辑。
1.3 加密货币市场给模型提出了什么特殊要求
加密市场和股票市场差异太大了,模型设计思路也跟着变。第一个特点是7x24小时交易,没有开盘收盘概念,因子的“周期”不再天然按交易日走,你得自己决定用1小时、4小时还是24小时作为预测窗口。第二个特点是高波动和低信噪比,同样是预测收益,股票的日频数据噪音已经不小,币圈还要再加几倍,模型很容易学到噪声而不是规律。
更关键的是因子衰减速度。股票市场一个有效因子可能存活几个月甚至几年,加密市场因为参与者结构变化快、杠杆高、消息驱动强,很多因子几个星期就失效了。这就要求整套ML流程不能是“离线训练一次上线用半年”,而是要有滚动重训的机制,模型要不断适应新的市场状态。这也是为什么面试时问时间序列切分、数据泄漏、滚动验证,比问模型结构还频繁。
2. 因子组合预测的技术路线:从预处理到深度学习落地
2.1 因子预处理:数据质量决定了模型上限
网上很多教程一上来就搭神经网络,实际在量化场景里,数据预处理的时间通常占整个项目的一半以上。我见过太多实习生拿到数据直接就开始跑模型,后来发现一半的训练样本因为时间戳错位、因子缺失、极端值爆表,根本不能用。
因子预处理的常规动作有这么几项:
- 去极值:单因子分布尾部经常有极端值,容易拉偏模型。常用MAD(中位数绝对偏差)或百分位截尾。比如超过99%分位数的值,直接压到99%分位数。
- 标准化:不同因子量纲不同,动量可能是百分比,资金流可能是美元数量。一般用z-score或rank化。rank化在树模型里问题不大,但在神经网络里比z-score更稳一些。
- 缺失值处理:时间序列因子难免有缺失,最常见的是前向填充还是直接删除的问题。注意不能用全局均值填充,否则会引入未来信息泄漏。
- 中性化:在某些团队里,因子会对市值、币种风格做中性化处理,把因子中跟这些暴露相关的部分剔除,让组合后的信号更干净。
在加密市场还要多做一个事:时间对齐。不同交易所的撮合时间不一样,币安和OKX的成交时间戳可能差个几百毫秒,如果你用A交易所的价格特征去预测B交易所的收益,得先把两边K线重采样到统一时间轴。一般用固定间隔重采样加前向填充,防止用未来K线填到当前时刻。
2.2 标签构造:预测目标比模型更重要
这一步是新人最容易糊弄的地方,但标签直接决定了模型在学什么。预测收益的标签至少要考虑三个问题:
预测什么周期。短期(5分钟到1小时)和中期(4小时到24小时)的信号性格完全不同,模型结构、因子选择也不一样。JD里的“未来收益预测”没有明确周期,实操中一般先做中期,因为短期信号对交易执行要求太高,实习阶段很难拿出完整结果。
是预测绝对收益还是相对收益。在多个币种之间做横截面选币,标签更适合用未来某一时段的相对收益,或者未来收益的排名。这样做的好处是剔除了整体市场涨跌的影响,模型学的是“哪些币相对更强”,而不是去猜大盘方向。如果只做单一币种的择时,那就预测绝对收益,但信号波动会大很多。
要不要平滑标签。加密货币的价格噪声非常大,直接用未来1小时的收益当标签,模型拟合的很多是噪声。常见做法是对收益做指数加权平滑,或者用未来N个时间步的收益做加权平均,让标签更稳定。不过平滑过头会把预测变成滞后指标,实操里需要反复调平滑窗口。
我在实际项目里最常用的标签是:未来24小时的排名收益,也就是过去24小时累计收益减去同期所有币种收益的中位数。这个标签在横截面任务里稳定,而且和实际做多空组合的目标高度一致。
2.3 深度学习模型选型:先从基线开始
很多实习生上来就想用Transformer,但我的建议是先跑出一个简单的MLP或LightGBM基线,再逐步加深模型。没有基线,你根本不知道Transformer带来的增益是真的还是过拟合。
几个常用模型的定位:
- MLP:因子拼接后直接映射到收益预测,简单、训练快,适合做第一版基线和数据验证。
- LightGBM/XGBoost:树模型在表格型因子上的表现往往比深度模型还要好,尤其是在因子数量几百、样本量不太大的场景。很多团队线上用的就是树模型,深度模型反而跑在离线研究阶段。
- LSTM/GRU:用于捕捉因子的时序依赖,比如动量因子最近10根K线的变化趋势。但注意RNN在金融数据上容易过拟合,需要更强的正则。
- 1D-CNN:做因子序列的局部特征提取,训练比RNN稳定,主流中频信号里效果不错。
- Transformer:适合捕捉因子之间的长距离依赖,但需要足够的数据量和更精细的训练策略。加密市场数据量够大,但信噪比太低,裸Transformer很容易学到短期噪声。一般会配合更长的序列输入和更强的dropout。
另外有一个值得尝试的方向是多任务学习。同时预测多个周期的收益,比如同时预测1小时、4小时、24小时的收益,共享底层特征。这样模型会强迫自己学到多周期都有效的表征,而不是只拟合单一周期。实测下来,多任务模型比单任务模型要稳,尤其是预测短期收益的时候。
2.4 训练与验证:时间序列是绕不开的坎
我看到太多次比赛或项目里用随机K折来做时序数据验证,然后指标很好、实盘打脸。时间序列数据的验证必须考虑时间顺序,不能随机打乱,否则相当于把未来数据泄露给了模型。
推荐的验证策略有三种:
- 滚动窗口验证(Rolling Window):固定训练集长度,每次往后滚动一个窗口,训练窗口和验证窗口永远不重叠。
- 扩展窗口验证(Expanding Window):训练集不断累积,验证集是紧跟在训练集之后的未来数据。
- Purged K-Fold:在K折基础上剔除了训练集和验证集之间重叠标签的样本,专门解决标签窗口重叠带来的泄漏问题。
在加密市场里,滚动窗口更贴近实际,因为市场状态变化太快,早期数据不一定对近期预测有帮助。另外还要设置“样本外冷启动”测试:把最近2到4周的数据完全留出来,训练过程不许碰到,最后统一验证一次。这一步能比较真实地反映模型上线后的表现。
评估指标上,不要只看Loss或Accuracy。常用的是IC(预测值和未来收益的相关系数)、RankIC(秩相关)、ICIR(IC均值除以IC标准差)、分层回测(把预测值分5到10层看收益单调性)、换手率(信号稳定性)。一个模型的ICIR如果不到0.3,基本可以判定为没有实用价值。
3. 强化学习在组合优化中的真实做法
3.1 为什么要用强化学习?对比传统优化方法
讲到强化学习,很多同学第一反应是打游戏、机器人控制,但量化岗JD里明确写了要用于因子组合,这说明团队想做的不只是“预测收益”,而是“决策”。
传统做法是两步走:先用监督学习预测收益,再用优化器求解最优权重。比如预测出每个币未来24小时收益后,用均值方差优化或风险平价算权重。问题是监督学习的预测误差会被优化器放大,而且交易成本、换手约束很难塞进一个标准的监督学习框架。
强化学习的思路是把整个“预测+决策”过程当成一个序列决策问题。智能体在每个时刻观察市场状态,输出目标仓位,获得奖励,然后在下一个时刻继续。这个过程中,模型不追求单步预测绝对准,而是追求整条决策路径上的累计收益最高,同时可以自然地把手续费、滑点、换手率、回撤约束都揉进奖励函数里。
实测下来的感受是:强化学习做组合优化,训练难度比监督学习高一个量级,但它确实能学到一些有趣的行为,比如在市场波动率升高时主动降低仓位,而不是像监督学习那样子始终满仓。
3.2 MDP建模:状态、动作、奖励怎么设计
用强化学习做因子组合,第一步就是定义MDP四元组。这个定义的好坏,直接决定你后面能不能训出东西。
状态(State)至少包含三块:因子值(当前所有因子的标准化值)、预测信号(监督模型给出的收益预测)、当前仓位(当前每个币的持仓权重)。还可以加入市场状态特征,比如整体波动率、成交量的近期变化。状态要做成固定维度的向量,方便网络输入。
动作(Action)有两种常见定义。一种是直接输出每个币的目标权重,动作维度等于币的数量,连续动作空间,适合用Actor-Critic架构。另一种是输出动作增量,也就是相对当前仓位的调整量,然后对更新后的权重做归一化和约束。第二种在实盘里更常用,因为它天然把换手率控制住了。
奖励(Reward)是强化学习里最需要打磨的部分。我常用的形式是:
future_return - transaction_cost - turnover_penalty - drawdown_penalty
具体来说,future_return是未来N步的组合收益;transaction_cost用当前权重和前一步权重的差值估算;turnover_penalty是额外的换手惩罚,让策略不要频繁调仓;drawdown_penalty可以取最近K步组合净值回撤的一定比例。
关键一点:奖励函数不能只看总收益。如果不加成本项,智能体学出来的策略一定是疯狂调仓,回测收益好看,实盘手续费直接吃光利润。很多人训练RL策略失败,90%是奖励函数没设计好,而不是算法有问题。
3.3 从Actor-Critic到PPO:训练细节与关键参数
说一个概念:Actor-Critic。它是把策略(Actor)和值函数(Critic)分开建模。Actor决定“在某个状态应该怎么做”,Critic评估“这个状态值多少钱”。两者交替训练,Actor利用Critic的反馈改进策略,Critic利用实际奖励修正自己的评估。那为什么岗位JD里提到强化学习时候经常出现这类算法?因为在连续动作空间的金融决策场景,纯策略梯度方法方差太大,纯值函数方法又不好处理连续动作,Actor-Critic刚好是两者的折中。
PPO是目前量化RL里最主流的Actor-Critic实现。它做了两个改进:一是用重要性采样把当前策略和旧策略的样本关联起来,能做多轮更新;二是通过裁剪目标函数限制单次更新的幅度,防止步子迈太大导致训练崩溃。
再说说rollout。这个词在RL里指的是“和环境交互走完一条轨迹的过程”。PPO每轮更新之前,先用当前策略跑一批rollout,收集一批状态、动作、奖励序列,然后集中更新策略。在加密市场里,我们不可能实时跟交易所交互去收集rollout,一般是从历史数据里采样,把历史收益矩阵当成环境反馈。这其实是一种“数据驱动模拟器”的做法,把历史K线重放给智能体,让它在过去的时间线上做决策。
训练时有一组参数需要重点关注:
- 轨迹长度(rollout length):一般取几十步到几百步。太短学不到长期因果关系,太长训练效率低,且历史分布偏移更严重。
- 学习率:金融数据信噪比低,学习率稍微高一点策略就会剧烈震荡。建议从3e-5到1e-4之间试探,比图像任务小很多。
- 熵正则系数:鼓励探索,防止策略过早收敛到某个固定仓位。但要控制强度,太大会让策略变成随机乱动。
- 使用梯度裁剪:防止单步更新幅度过大。
另外,强化学习在量化里经常要配合集成使用。单独跑一个策略种子,结果是不可信的。我会用10个不同随机种子训练10个策略,然后对它们的动作取平均,或者做成一个小型策略组合。这招能显著降低随机性带来的虚高回测表现。
3.4 离线强化学习与多智能体的扩展方向
实习岗位的JD通常不会写太深,但你要知道团队后续大概率会往这两个方向探索。
第一个是离线强化学习。在线强化学习需要不断尝试才能学到策略,但在金融场景里,我们不能用真钱去试错,只能用历史数据模拟。可是历史数据只有一条固定轨迹,策略稍微变化一点,就可能走到没见过的状态,分布偏移非常严重。离线强化学习专门解决这个问题,通过约束策略不偏离行为策略太远来避免高估。IQL(Implicit Q-Learning)是目前讨论较多的离线算法,它不需要估计下一个状态的动作值,规避了离线场景里自举导致的高估问题。如果团队的历史交易数据积累得足够多,这个方向非常有价值。
第二个是多智能体强化学习。有些团队会把策略拆成多个子智能体,每个智能体负责一类因子的组合或一个特定的市场状态,然后用上层协调机制把不同智能体的决策聚合起来。理论上这对加密市场这种风格切换剧烈的环境有帮助,但工程复杂度高,实习阶段很少能直接做上线,更多是研究课题。
4. 环境搭建与最小可用系统的落地
4.1 环境搭建:从Python、CUDA到深度学习库
很多同学卡在环境上。量化ML岗日常工作离不开Python生态,环境搭建这件事看起来很基础,但版本不匹配会浪费大量时间。我的建议是直接用Miniconda管理环境,不要贪图方便用全局Python。
基础环境大致如下:
conda create -n quantml python=3.10 conda activate quantml pip install numpy pandas scikit-learn matplotlib seaborn jupyter pip install lightgbm xgboost pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118Torch的CUDA版本要和本机显卡驱动匹配。装之前先跑一下nvidia-smi看驱动支持的最高CUDA版本,再选择对应的Torch安装包。如果没有独立显卡,就用CPU版本先跑小规模数据,模型逻辑不变,只是慢一些,不影响学习。
强化学习库方面,我推荐先从stable-baselines3入手。它实现了PPO、DDPG、TD3、SAC等主流算法,接口简单,文档也友好,适合快速验证想法。如果后期要大规模并行采样,再考虑Sample Factory或者RLlib。手写一个简单PPO也不难,但实习阶段没必要重复造轮子,重点是验证思路。
4.2 数据管线:从交易所数据到特征矩阵
数据是量化研究的血液。加密市场的数据获取途径相对丰富,可以从公开API下载历史K线,不过要注意几个细节:
- 统一使用UTC时间,避免时区错乱。
- 时间戳对齐到统一的间隔,比如1小时K线,以开盘时间为准。
- 去重和排序:交易所API偶尔会有重复或乱序数据,处理不好会导致未来函数。
- 数据格式:优先用parquet或hdf5存储,比CSV快非常多。几百万行K线数据用CSV读一次要几十秒,parquet只要一两秒。
完成原始数据加载后,再计算因子和构造特征矩阵。我通常会写一个build_features.py脚本,输入是原始K线DataFrame,输出是干净的特征矩阵和标签列。这个脚本要设计成可重放的,以后每次更新数据跑一遍就能得到最新的训练集。特征计算这块强烈建议提前缓存,避免每次跑实验都重新算一遍几百个因子,很浪费时间。
4.3 用一套最小回测流程验证信号
很多新手拿到模型预测结果直接就开大回测框架,结果被各种细节折磨得不行。我建议先做一个向量化回测,快速验证信号是否有效。
逻辑很简单:拿到每个时间点每个币的预测值,按预测值从高到低排序,做多前N个、做空后N个。然后计算组合的未来收益序列,减去手续费和滑点。
import pandas as pd import numpy as np # pred_df: index为时间,columns为币种,值为预测值 # ret_df: index为时间,columns为币种,值为未来收益 # top_n: 做多/做空数量 def vectorized_backtest(pred_df, ret_df, top_n=5, fee=0.001): weights = pd.DataFrame(0.0, index=pred_df.index, columns=pred_df.columns) for dt in pred_df.index: row = pred_df.loc[dt].dropna().sort_values() if len(row) < 2 * top_n: continue weights.loc[dt, row.index[-top_n:]] = 1.0 / top_n weights.loc[dt, row.index[:top_n]] = -1.0 / top_n turnover = weights.diff().abs().sum(axis=1) net_ret = (weights.shift(1) * ret_df).sum(axis=1) - turnover * fee return net_ret.cumsum()这个回测有几个点要注意:权重要shift(1),因为t时刻的信号只能作用于t+1时刻的收益,否则就是未来函数;手续费按换手率扣除,不能按持仓量一刀切;短期预测信号换手率极高,所以回测里的手续费要按实际交易频次合理估算。
在向量化回测确认信号有效后,再考虑接入更接近实盘的事件驱动回测,把下单延迟、部分成交、市场冲击都模拟进去。大部分实习项目做到向量化回测的深度已经够支撑日常工作,事件驱动回测一般由团队更资深的同学或专门团队负责。
5. 新手实操最常踩的坑与排查速查表
5.1 数据泄漏:量化里最常见的“隐形错误”
数据泄漏是量化新人第一大坑,表现形式非常隐蔽。最常见的是特征归一化时用了全样本的均值和方差。比如你手上有2023年全年的数据,在训练模型前对整个DataFrame做了StandardScaler,那模型相当于在训练时偷偷看到了未来数据的分布,回测指标高到离谱,实盘立刻打回原形。
正确做法是保证一切统计量都只用训练集部分计算。特征是滚动标准化的,标签是不重叠的,验证集从不参与训练。另一个容易忽略的是样本重叠问题。如果预测周期是24小时,而训练样本每1小时取一个,那么相邻24个样本的标签是共享未来区间的,存在严重的自相关。Purged K-Fold就是专门解决这个问题的,过滤掉训练集和验证集之间标签重叠的样本。
判断是否泄漏的一个土办法:把预测值全体做滞后1期处理,回归收益,看滞后后的预测是否仍然显著。正常信号应该随着滞后而快速衰减,如果滞后了还能保持很高IC,说明可能用了未来数据。
5.2 强化学习训练不收敛的排查清单
强化学习训练不收敛,这是每个量化RL新人都会撞上的问题。排查顺序一般是这样的:
- 先看奖励值分布:奖励数值太大或太小都不行。太大策略梯度爆炸,太小学习缓慢。可以把奖励做缩放,控制在0.1到1的量级。
- 再看动作分布:如果动作接近边界值或者长时间不动,可能是探索不够。调大熵系数,或者改成高斯策略并调大初始标准差。
- 看Critic的Loss:如果Critic一直降不下去,说明状态表示不够好,或者奖励噪声太大。可以试试奖励平滑,或者加入更多状态特征。
- 最后看策略在回测里的行为:如果一个策略在训练曲线里收益很高,回测却不行,大概率是过拟合了历史轨迹。换成多种子集成,或者缩短rollout轨迹长度。
奖励函数本质是在定义“什么是好”,定义错了,算法再先进也白搭。我建议每次只改一个奖励项,观察策略行为变化,逐步调,而不是一次性改很多,否则出了问题根本不知道是哪一项引起的。
5.3 因子失效与过拟合:回测神话怎么破
每个做量化的都见过“完美回测曲线”,但实盘一跑就废。过拟合是核心原因:你在同一段历史上反复调整参数和阈值,模型已经“背下”了这段历史里的噪声形态。
破解方法有几个:一是设定一个样本外区间,整个调参过程中不许碰它,最后统一验证;二是优先选简单模型,因为简单模型的过拟合空间小;三是做滚动窗口验证,看模型在不同时间段的稳定性,如果一个模型只在特定市场状态下有效,实盘价值就很低。
加密市场的因子衰减特别快,我建议把因子监控做成一个固定节奏的例行任务。每周计算每个因子的滚动IC、换手率和分组收益,滚动IC一跌破某个阈值,就标记为失效因子,不再进入组合模型。模型重训也可以设定为两周一次,让模型始终跟得上市场变化。
5.4 面试和学习资源建议
最后简单说下学习和面试准备。量化ML岗面试基本绕不开几个话题:时间序列交叉验证怎么做、数据泄漏有哪些形式、梯度下降原理、Transformer的注意力机制、正则化方法、强化学习里PPO的原理和公式、因子IC计算方式。这些概念在周志华的《机器学习》、李航的《统计学习方法》和《动手学深度学习》里都有覆盖,可以系统过一遍。
学习资料不用贪多。我看过太多同学收集了一堆课程却什么都没学透,反而不如认真看完一本经典书加跑通两个实战项目。动手做一遍因子组合预测和强化学习组合优化,比看十篇论文的收获都大。
写在最后的一些体会
我接触过不少做量化机器学习实习的同学,发现最后能拿到转正名额的,往往不是模型调参最快的那个,而是数据意识最强、最不容易犯低级错误的人。一个模型能跑出多高的IC是一方面,能不能在三个月实盘里稳定工作、不出幺蛾子是更重要的一面。
如果你现在准备投这个方向,我的建议是先把数据预处理和时间序列验证做扎实,用LightGBM跑出一个靠谱的基线,再考虑用强化学习做组合优化。强化学习确实有趣,也有上限更高的空间,但它需要你先把底层的预测信号做扎实,否则就是空中楼阁。
如果这篇文章能帮你少踩几个坑,后面你在面试或实习里碰到具体问题,也可以回来翻一翻对应的章节。量化这条路上,每一个坑都是学费,能让人绕过去的文章就是有价值的。