2024年诺贝尔物理学奖颁给了两位神经网络先驱,这件事在量化圈里也炸开了锅。你可能觉得奇怪:物理学奖怎么和神经网络扯上关系?但如果你平时喜欢研究交易指标,可能会更关心另一件事——这种物理学式的思路,能不能帮我们在行情里识别出反复出现的市场模式,然后做成一个真正能用的指标。这篇文章就是我基于这个想法做的实验记录:用神经网络原理设计一个交易指标,尝试解读市场模式。我尽量把思路、代码、踩坑都写清楚,适合对量化交易和机器学习都感兴趣的读者。
1. 从诺贝尔物理学奖到交易指标:思路缘起
1.1 物理学奖与神经网络的渊源
先说一个背景。2024年诺贝尔物理学奖颁给了约翰·霍普菲尔德和杰弗里·辛顿,原因是他们利用物理学方法为机器学习神经网络奠定了基础。霍普菲尔德提出了一个用能量函数描述的网络模型,网络能通过不断降低能量来“回忆”出存储过的模式;辛顿则把统计物理中的玻尔兹曼机思想引入神经网络训练,让网络可以自动学习数据背后的规律。
这件事对我最大的冲击是:原来神经网络不是一个纯数学游戏,它和物理系统有很多共通点。霍普菲尔德网络的能量景观,可以用来理解记忆如何形成、如何被唤醒;而交易市场里的K线形态、均线结构、量价关系,本质上也是一堆高维数据在动力系统里演化。你看到一根阳线,看到一段上涨,它不是随机产生的,而是背后有资金行为、市场情绪、信息冲击等因素共同作用的结果。
所以我想,如果神经网络能从物理世界中提取并识别模式,那同样也能从交易市场里提取和识别模式。但很少有人把这个思路真正落地成一个“指标”——一个像RSI、MACD那样有明确公式和输出曲线的工具。这篇文章想做的,就是把这个想法补全:用神经网络训练出一个概率输出,再把它平滑成一个方便观察和回测的交易指标,我暂时叫它模式记忆指标。
1.2 市场模式的“能量景观”
理解这个指标,可以先借用霍普菲尔德网络里的“能量景观”概念。在霍普菲尔德网络中,每个存储的记忆都是能量地形上的一个低谷,输入一个接近某种记忆的失真模式,网络会顺着能量下降的方向,把它“修复”成最接近的完整模式。
市场数据其实也类似。行情里反复出现的头肩顶、双底、突破回踩、放量拉升,本质上都是高维状态空间里的局部吸引子。当你输入一段当前行情窗口,模型要回答的核心问题是:当前这段走势在历史上更像哪种状态?接下来更可能的路径是哪条?
当然,真实市场比霍普菲尔德网络复杂得多,它没有固定的能量函数,市场本身也会演化。但物理直觉仍然有用:我们可以让神经网络隐式学习一个“准能量函数”,把输入窗口转换成模式匹配的概率,这个概率就比单看一根K线或一个技术指标更丰富。这也是为什么我不满足于传统指标,而要用神经网络来做这件事。
1.3 指标设计的三个原则
动手之前,我先给自己定了三条原则。
第一,不要黑盒。很多机器学习交易模型是“输入特征→输出信号”,中间完全不可解释。但指标应该能被拆开看:它到底在关注什么形态?趋势变化时它的反应是否合理?如果指标跟行情完全没关系,再高大上也不能用。
第二,要稳健。金融数据是出了名的非平稳,一套参数不可能通吃所有市场。初始版本我只在少数几个交易对上测试,但设计指标时会尽量让特征标准化,模型结构也简单一些,避免过度依赖某段历史行情。
第三,要实用。指标最终是要给人看、给策略用的。输出最好是一个0到1之间的值,或者一个有正负的震荡值,像RSI那样直接叠加在行情图上。这个目标和纯机器学习模型不太一样,我在后面会详细解释怎么把概率输出变成指标线。
2. 新建交易指标的整体框架
2.1 指标的目标与使用场景
我给这个指标定的任务是:在任意一个行情窗口,输出一个0到1之间的值,表示“当前形态与历史中某些具有统计优势的模式相似的程度”。我们叫它模式记忆强度PMI,数值越高,说明当前走势越接近历史上某些特定形态;数值越低,说明越接近另一类形态。
使用场景我目前只考虑两种:一是作为趋势过滤器和动量确认工具,跟传统的均线、MACD组合使用;二是作为风险指示,当PMI在极端区域出现背离时,提醒自己当前行情不确定性上升。注意,它不是一个自动交易信号,而是辅助人做判断的指标。后面我还会说,为什么我不建议直接拿它输出买卖点。
2.2 技术选型:为什么选CNN而不是纯RNN/LSTM
再看热词里的神经网络类型,有LSTM、RNN、图神经网络、卷积神经网络、BP神经网络等等。很多人的第一反应是:行情是时间序列,应该用LSTM。我也一度这么想,但实验下来发现,在这个指标场景里,一维卷积神经网络CNN表现更稳。
原因有三。
第一,局部形态识别是CNN的强项。市场模式往往体现在若干个连续K线的形态组合,一维卷积天然适合提取这种局部特征,就像图像识别里卷积能捕捉边缘和纹理一样。一个头肩顶形态,本质上就是特定时间跨度内价格曲线的形状,CNN的卷积核可以学习这种形状。
第二,CNN对序列顺序的建模方式更简单稳定。LSTM需要拟合长期依赖,容易过拟合短样本,而CNN的感受野可以通过堆叠层数扩大,效果更好控制。
第三,训练效率高。CNN可以并行计算,训练速度比LSTM快很多,这在反复调参时很重要。当然,我也在备选方案里保留了一个可选的LSTM分支,但作为默认版本,一维CNN是主力。
2.3 数据与标签:把K线切成固定窗口
构造样本时,我采用滚动窗口的方式。把行情数据按时间顺序切成长度为128根K线的窗口,每根K线包含开高低收和成交量。每个窗口对应一个标签,标签是三根K线后的未来收益是否超过一定阈值。
这里有个关键点:窗口和预测目标之间要留出间隔,避免信息重叠。也就是说,模型用第t根到第t+127根的行情,预测第t+130根的短期走势。中间空出来的几根K线作为缓冲,防止高频序列里的价格影响被直接算进特征。
标签不是简单的“涨或跌”,而是三分类:显著上涨、显著下跌、无明显方向。阈值根据标的近期波动率动态设定,通常取过去20根K线平均波幅的一半。这样做的好处是,模型不会只学习中性的震荡形态,而会重点关注那些后续有统计优势的极端形态。
2.4 模型结构:从输入到输出
整个模型结构并不复杂:一维卷积层堆叠提取局部形态,再接一个全局池化层压缩特征,最后接两个全连接层映射到三分类输出。中间用BatchNorm和Dropout控制过拟合。我用的是PyTorch,代码骨架大致如下:
import torch.nn as nn class PatternNet(nn.Module): def __init__(self, in_channels=5, num_classes=3): super().__init__() self.conv1 = nn.Conv1d(in_channels, 32, kernel_size=5, padding=2) self.conv2 = nn.Conv1d(32, 64, kernel_size=5, padding=2) self.conv3 = nn.Conv1d(64, 128, kernel_size=3, padding=1) self.pool = nn.AdaptiveAvgPool1d(1) self.fc1 = nn.Linear(128, 64) self.fc2 = nn.Linear(64, num_classes) self.drop = nn.Dropout(0.3) def forward(self, x): # x: (batch, channels, seq_len) x = torch.relu(self.conv1(x)) x = torch.relu(self.conv2(x)) x = torch.relu(self.conv3(x)) x = self.pool(x).squeeze(-1) x = torch.relu(self.fc1(x)) x = self.drop(x) return self.fc2(x)输入张量的形状是(batch, 5, 128),5个通道分别是归一化后的开盘、最高、最低、收盘、成交量。输出三个值对应三个类别的logit,训练时用交叉熵损失函数。
3. 实操过程与核心环节实现
3.1 特征构造:把K线变成可学习的形态
数据切好窗口之后,最容易被忽视的是特征标准化。如果直接用原始价格输入,模型会把价格高低本身当成特征,而不是形态。真正的模式识别应该只关心相对结构,不关心绝对价位。
我的做法是分两步标准化。第一步,对每个窗口内的5个通道分别做z-score归一化,让收盘价序列的均值为0,标准差为1。第二步,对成交量做对数变换后再归一化,避免极端放量主导整个特征。
还有一个细节:不要把整个窗口都归一化到0到1,那样会丢失涨跌幅信息。更好的方式是只减去窗口第一根K线的收盘价,然后除以窗口内价格标准差。这样保留了一定的趋势斜率信息,CNN也能通过卷积核学习到价格在不断上涨还是下跌。
我自己试过两种方案,最终选了第二种,因为指标对趋势段的识别明显更准。原始价格绝对水平对模型没有意义,但保留窗口内部的趋势信息,对后续是否延续或反转的判断很重要。
3.2 训练细节与调参记录
模型训练本身不复杂,但有几个参数直接影响指标质量。
首先是损失函数。三分类交叉熵足够直白,但类别数量在训练集上往往不均衡,尤其是无趋势的震荡样本偏多。我会给每个类别一个权重,让三个类别的loss贡献大致均衡。权重直接用该类样本量的倒数。
其次是优化器。我用了AdamW,初始学习率0.001,权重衰减设置在1e-4。训练轮数控制在30轮以内,提前停止的指标是验证集准确率。这里要特别注意,金融数据的验证集不能用随机划分,必须按时间顺序切分,比如用前80%的数据训练,后20%验证,否则会高估模型泛化能力。
第三是BatchNorm和Dropout的配合。经过卷积层后,特征图的分布变化较快,加BatchNorm能让训练稳定不少。全连接层之间加Dropout,概率设0.3,能明显缓解尾部形态样本的过拟合。
我在实际训练中见过一个现象:训练集准确率很快就超过70%,但验证集准确率只爬到55%左右,涨幅很小。这是典型的过拟合信号,后来我把卷积核数从128降到64,把Dropout概率提高到0.3,验证集准确率才稳定在57%~60%之间。对于三分类任务来说,这个准确率已经比随机33%高出不少,足够作为指标参考。
3.3 从概率到指标:平滑与阈值
模型输出的是三分类概率,但交易指标需要一个连续的值。我把模型预测的上涨类概率定义为p_up,下跌类概率定义为p_down,然后计算score = p_up - p_down。这个值理论上在-1到1之间,表示模型对当前模式后续方向的倾向。
直接用单次预测的score会很毛糙,因为单根K线可能造成模型输出剧烈跳动。我会对score做指数平滑,公式如下:
smoothed_score = alpha * raw_score + (1 - alpha) * smoothed_scorealpha取0.06左右,等效于对过去约15到20个窗口的结果做加权平均。这样得到的曲线平滑度类似RSI,方便叠加到行情图上观察。
为了让指标更容易被传统技术分析的人接受,再做一次归一化,把平滑后的score映射到0到100之间,映射公式是:PMI = 50 + smoothed_score * 50。当PMI大于65时,认为当前形态偏多;小于35时偏空;中间是震荡区。这个门槛不是固定的,不同品种、不同周期可能需要重新标定,后面我会讲怎么用回测去确定阈值。
4. 常见问题与排查技巧实录
4.1 过拟合:尤其是小样本过拟合
金融数据看起来量大,但真正的独立样本很少。如果两个窗口只隔了半根K线,它们的特征是高度重叠的,模型很容易“背下来”这些重叠数据,而不是学到通用规律。
我做过的第一版模型,训练集表现极好,验证集却经常失效。排查后发现,问题出在两处:一是窗口重叠太密,相邻训练样本几乎一样;二是模型容量太大。解决办法是增大窗口滑动的步长,比如原来每根K线都生成一个窗口,改为主K线步长3根K线;同时把模型里的卷积层通道数减少一半。
还有一个常用技巧:引入多个周期的数据样本。比如同时用5分钟、15分钟、1小时周期的K线生成训练样本,事实上等同于数据增强。这样模型面对不同时间尺度的形态,更容易学到普适的形态特征,而不是某一段特殊行情。
4.2 数据泄漏:未来函数是最容易踩的坑
要特别提醒:构造标签时如果处理不当,会把未来信息混进特征里。比如有的朋友喜欢把未来几根K线的收盘价也纳入标准化,或者不小心把后一段行情的统计量当作当前特征,这些都会造成严重的未来函数。
我踩过的一个坑是滚动计算波动率时,用了包含当前K线未来方向的窗口。具体来说,我在标注三分类标签时用了未来3根K线的平均收益率,但在特征标准化时把整个窗口128根K线的均值、标准差都算进去了。如果窗口尾部已经包含未来数据点,模型就会“偷看”未来,训练时表现很好,实盘中完全失效。
排查方法是把特征构造和标签构造完全分隔开,分别用两个函数计算,并且在代码里明确当前K线下标。另外,我会随机抽取几个窗口,手动检查携带的标签是否和未来行情对应得上。
4.3 参数漂移与滚动重训
市场不是静止的,一个模型在2018年有效,到2022年可能就完全失效。这就是参数漂移现象。就算模型结构不过拟合,市场结构变化了,指标也会失真。
应对方法就是滚动重训。我通常每三个月用最近半年的数据重新训练一次模型,然后用新模型生成之后的指标。但这会带来一个问题:指标的历史曲线在同一参数下发生过变化,你在对比不同时期的指标表现时,要特别注意这一点。
另外,每次重训时尽量保持模型结构和超参数不变,只替换训练数据。否则你很难判断指标变化是因为市场变了,还是因为模型变了。我自己的经验是,固定超参数、只滚动数据,指标跨期稳定性会好很多。
4.4 一张排查清单
新手做这类指标时,我建议按下面的顺序排查问题。
- 先检查数据有没有未来函数:把标签和特征各画出来,人工抽查几个时间点。
- 再检查训练验证切分是否按时间顺序,绝对不能随机切分。
- 看训练集和验证集的准确率差距,如果超过10个百分点,优先缩小模型容量或增加Dropout。
- 检查平滑后的指标是否存在明显跳变,如果跳变很频繁,说明模型输出不稳定,可以增大平滑系数或换更长的输入窗口。
- 最后做样本外回测,而且必须带上手续费和滑点,否则再好的指标也是纸上谈兵。
5. 回测中的坑与落地经验
5.1 回测框架里的滑点和手续费
很多人在验证指标时只看胜率和收益率,却忽略了交易成本。高频一点的交易信号,一个月可能触发几十次,佣金和滑点会吃光利润。做这类指标回测,我总结出三条硬规则:手续费按双边千二到千三估算;滑点至少要设一个最小跳动单位;信号触发后,用下一根K线开盘价成交,而不是用当前收盘价成交。
为什么强调下一根K线开盘价成交?因为指标信号往往是在收盘后计算的,你不可能在收盘那一瞬间以收盘价成交。回测时如果太理想,实际执行会差很多。我试过同样一套参数,回测年化收益率30%,加上滑点和手续费后立刻降到不到5%,这就是纪律的重要性。
5.2 指标评价:用信息系数而不是只看胜率
单独看胜率很容易被误导。胜率60%的策略也可能亏钱,因为亏的每次都比赚的多。所以我评价指标时会看两个东西:一是信息系数IC,即“指标数值”和“未来收益”的秩相关系数;二是分位数组合收益曲线,把指标值按高低分成五组,看高指标组未来收益是否系统性地高于低指标组。
如果高指标组的未来收益普遍为正且排名稳定,说明这个指标有一定方向性;如果不同分位数之间没有明显梯度,那指标基本没有有效信息。这两个指标比胜率可靠得多。
5.3 扩展方向:从周期到多峰值的融合
最后说一个后续可以扩展的方向。目前指标只用了价格和成交量,结构比较单一。你可以把小波变换加进来,对价格序列做多尺度分解,再把不同尺度的分量作为多通道输入,这在热词里也有涉及。这样做的好处是,模型能同时观察短期噪声和中期趋势,形态识别更全面。
另一个方向是把多个时间周期的模型输出当做一个融合指标。比如同时计算5分钟PMI和1小时PMI,当两者同方向共振时,信号可靠度会明显提升。这个思路跟传统多指标共振一模一样,只是底层指标换成了神经网络版本。
做这个指标的过程里,我最大的体悟是:神经网络不是用来制造玄学信号的,它更像一个特征提取器。通过合理的数据构造和严格回测,它可以把高维行情结构压缩成一条曲线,帮助我们理解当前市场状态。但最终是否要交易、怎么执行,思路还是要靠自己。希望这个物理启发式的指标实验,能给你带来一些有用的想法。