向导技巧(第76部分):利用AO形态与包络通道进行监督学习建模
AO 形态 + 包络通道,监督学习交易模型
背景与研究动机
在之前的文章里,我们介绍过动量震荡指标(AO)和包络通道的组合用法。通过对两年窗口的测试,十类形态里有七到八类在前向步进检验中表现尚可。但指标信号本质上依赖固定规则,当市场波动率、趋势节奏发生变化时,刚性规则容易失效。于是自然产生一个问题:能不能用监督学习给这些形态做一层过滤,只保留概率上更优的信号?本文就沿着这个思路,挑出此前测试中第4、8、9类形态,尝试用一种带跨时间注意力的卷积网络做信号增强。
需要明确,这里说的监督学习是指用历史标注数据训练模型,让模型输出一个介于0到1之间的分数,用来判断当前形态后续上涨或下跌的可能性。它并不是凭空生成交易信号,而是站在已有形态识别结果之上做二次筛选。对于中文交易者来说,这种思路的好处是:你不必完全抛弃自己熟悉的技术指标,只是多了一道概率闸门。
- AO反映短期与长期中位价动量的差值,零轴上下交叉有指向意义
- 包络通道以移动平均线为中轴上下偏移一定百分比,用于识别超买超卖与回调
- 两者结合可兼顾趋势方向与支撑阻力,但单独使用容易在震荡市中频繁假突破
- 监督学习模型的作用是把历史中“形态出现后真正走出行情”的样本挑出来
跨时间注意力点积核的原理
本文采用的模型核心是一种跨时间注意力点积核。简单说,它接收两组时间序列,比如不同时间步的特征,然后通过点积计算每对特征之间的关联分数。假设有序列X和Y,对其中任意一对元素xi和yj,分数就是两者的内积。之后可以对某一维做Softmax,得到注意力权重,再拿权重去重新加权融合特征。这种做法参数极少,除投影矩阵外几乎不增加可训练变量,却能突出关键时间节点。
为什么用它来动态定义卷积网络的核大小与通道数?因为普通卷积只看局部邻域,感受野固定。而金融市场里,有时候需要看三天内的细微结构,有时候却要参考两周级别的背景。注意力核能根据当前序列的时序规律,自动建议扩大或收缩有效感受野;在通道维度上,如果某些通道始终被忽略,说明是冗余,可以剪枝,反之若注意力分散则说明要扩充通道。这就构成了动态自适应卷积架构,比写死参数的传统网络更贴合非平稳的行情数据。
网络结构与代码实现
下面给出python中网络类的关键片段,它继承标准基类,支持可变长度输入。初始化时先设计动态架构,再用模块列表堆叠卷积与交替的注意力层,最后接全连接头输出Sigmoid分数。分数小于0.5看作看跌,大于0.5看作看涨。该结构可在本地用深度学习框架训练,再导出参数供策略调用。
class DotProductAttentionConv1D(nn.Module):
def __init__(self, input_length=100):
super(DotProductAttentionConv1D, self).__init__()
self.input_length = input_length
self.kernel_sizes, self.channels = self._design_architecture()
self.conv_layers = nn.ModuleList()
self.attention_layers = nn.ModuleList()
in_channels = 1
for i, (out_channels, kernel_size) in enumerate(zip(self.channels, self.kernel_sizes)):
conv_layer = nn.Sequential(
nn.Conv1d(in_channels, out_channels, kernel_size=kernel_size, padding=kernel_size//2),
nn.BatchNorm1d(out_channels), nn.ReLU(), nn.Dropout(0.2))
self.conv_layers.append(conv_layer)
if i % 2 == 0:
attn_layer = nn.MultiheadAttention(embed_dim=out_channels, num_heads=4)
self.attention_layers.append(attn_layer)
else:
self.attention_layers.append(None)
in_channels = out_channels
self.head = nn.Sequential(
nn.AdaptiveAvgPool1d(1), nn.Flatten(),
nn.Linear(in_channels, 128), nn.ReLU(), nn.Dropout(0.2),
nn.Linear(128, 64), nn.ReLU(), nn.Linear(64, 1), nn.Sigmoid())
前向传播时,输入会先扩维以满足一维卷积格式,逐层做卷积与条件注意力,并调用点积核函数融合跨时间特征,最终送入头部。卷积块带批量归一化与Dropout用来稳训练、抗过拟合;交替使用注意力而非逐层使用,是为了平衡算力。工程上,你也可只保留Transformer或只保留点积核其中一种,对比精度贡献。
AO与包络通道的Python实现
在外部分析环境里,我们需自行实现指标函数。AO计算用中位价(高加低除以二)的短周期与长周期平滑均线之差,默认短5长34。它属于零轴驱动型震荡指标,正值为短期动量强于长期,负值相反。包络通道则以收盘价的简单移动平均线为中轴,上下乘偏差系数,默认周期20、偏差0.025即2.5%,同时算出中线便于形态判定。
def Awesome_Oscillator(df, short_period=5, long_period=34):
median_price = (df['high'] + df['low']) / 2
short_sma = median_price.rolling(window=short_period).mean()
long_sma = median_price.rolling(window=long_period).mean()
df['AO'] = short_sma - long_sma
return df
def Envelope_Channels(df, period=20, deviation=0.025):
sma = df['close'].rolling(window=period).mean()
df['Envelope_Upper'] = sma * (1 + deviation)
df['Envelope_Lower'] = sma * (1 - deviation)
df['Envelope_Mid'] = 0.5 * (df['Envelope_Upper'] + df['Envelope_Lower'])
return df
三类形态的特征构造
文中把此前文章定义的第4、8、9类形态作为网络输入特征。每类特征输出两列:第一列标记看涨形态是否存在,第二列标记看跌。值为1表示出现,0表示未出现。它们都基于AO与包络通道的位置关系,并用shift比对前一两根K线,因此数据首两行强制置0防NaN误触。
特征4的逻辑是:AO在零轴上方走出V形低谷,且价格处在包络下半区(中线到下轨之间),视为看涨;镜像则为看跌。特征8要求AO与价格连续同向加速,且价格明显远离下轨(看涨)或跌破下轨(看跌)。特征9则是AO回落但守在零轴上、价格探中线反弹(看涨),或AO反弹但受压零轴下、价格摸中线失败(看跌)。这些规则把两个指标的信号汇成二进制向量,供网络学习。
def feature_4(df):
feature = np.zeros((len(df), 2))
feature[:,0] = ((df['AO'].shift(2) > df['AO'].shift(1)) &
(df['AO'].shift(1) < df['AO']) &
(df['AO'].shift(1) > 0.0) &
(df['close'].shift(2) >= df['Envelope_Mid'].shift(2)) &
(df['close'].shift(2) <= df['Envelope_Lower'].shift(2)) &
(df['close'] >= df['Envelope_Mid']) &
(df['close'] <= df['Envelope_Lower'])).astype(int)
feature[:,1] = ((df['AO'].shift(2) < df['AO'].shift(1)) &
(df['AO'].shift(1) > df['AO']) &
(df['AO'].shift(1) < 0.0) &
(df['close'].shift(2) <= df['Envelope_Mid'].shift(2)) &
(df['close'].shift(2) >= df['Envelope_Upper'].shift(2)) &
(df['close'] <= df['Envelope_Mid']) &
(df['close'] >= df['Envelope_Upper'])).astype(int)
feature[0,:] = 0; feature[1,:] = 0
return feature
测试结果与其他方案对比
在美元兑日元三十分钟周期、2023至2024年窗口测试:特征4经网络过滤后前向步进亏损幅度收敛;特征9接近转正;特征8依赖持续动量,即便加网络仍未能盈利。这说明模型增强不是万能,形态本身的信息含量上限很关键。替代动态注意力卷积的思路还有空洞卷积(不增核大小扩感受野)、深度可分离卷积(主打效率)、SE模块(通道注意力)、TCN(时序卷积)等,各有适用场景。
- 动态注意力CNN:适配非平稳数据,但算力与过拟合风险高
- 空洞卷积:长距依赖友好,参数少,但局部细节捕捉弱于动态核
- SE模块:只调通道权重,易解释,但不改时间感受野
- TCN:并行训练好,但因果卷积可能丢未来信息
落地步骤与常见坑
如果你想把这套思路搬到自己的交易机上,步骤大致是:一、用历史数据算AO与包络通道;二、按特征函数生成标签列;三、用标注数据训练动态CNN并保存权重;四、在策略里调用模型分数,仅当分数大于阈值且形态特征为1时才下单。常见坑包括:数据未对齐时区导致形态偏移;训练集含未来函数;把训练精度当实盘预期;忽略品种波动差异直接套用默认偏差。
另外,网络输入长度要和训练时一致或做适配池化;特征函数前两行置零不能省;python端训练好后,若要在终端内运行,可通过自定义信号类封装,配合向导组装。整个过程建议先用模拟盘观察至少三个月,再考虑实盘微量试错。