向导技巧(第76部分):利用AO形态与包络通道进行监督学习建模
📖

向导技巧(第76部分):利用AO形态与包络通道进行监督学习建模

AO 形态 + 包络通道,监督学习交易模型

MQL5 编程 难度 · 进阶 2026-07-24 6 分钟阅读
#向导技巧#AO#监督学习

背景与研究动机

在之前的文章里,我们介绍过动量震荡指标(AO)和包络通道的组合用法。通过对两年窗口的测试,十类形态里有七到八类在前向步进检验中表现尚可。但指标信号本质上依赖固定规则,当市场波动率、趋势节奏发生变化时,刚性规则容易失效。于是自然产生一个问题:能不能用监督学习给这些形态做一层过滤,只保留概率上更优的信号?本文就沿着这个思路,挑出此前测试中第4、8、9类形态,尝试用一种带跨时间注意力的卷积网络做信号增强。

需要明确,这里说的监督学习是指用历史标注数据训练模型,让模型输出一个介于0到1之间的分数,用来判断当前形态后续上涨或下跌的可能性。它并不是凭空生成交易信号,而是站在已有形态识别结果之上做二次筛选。对于中文交易者来说,这种思路的好处是:你不必完全抛弃自己熟悉的技术指标,只是多了一道概率闸门。

跨时间注意力点积核的原理

本文采用的模型核心是一种跨时间注意力点积核。简单说,它接收两组时间序列,比如不同时间步的特征,然后通过点积计算每对特征之间的关联分数。假设有序列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
✦ 偏差参数敏感
波动大的品种可将包络偏差调至5%左右,但需回测确认。复制数据框再追加列是非破坏性写法,能避免原始数据被指标函数修改,建议保持此习惯。

三类形态的特征构造

文中把此前文章定义的第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(时序卷积)等,各有适用场景。

⚠ 外汇高风险提示
上述测试为历史回测,样本外表现存在概率性衰减。外汇、贵金属杠杆交易可能导致本金部分或全部损失,任何信号过滤方案都须配合仓位与止损管理。

落地步骤与常见坑

如果你想把这套思路搬到自己的交易机上,步骤大致是:一、用历史数据算AO与包络通道;二、按特征函数生成标签列;三、用标注数据训练动态CNN并保存权重;四、在策略里调用模型分数,仅当分数大于阈值且形态特征为1时才下单。常见坑包括:数据未对齐时区导致形态偏移;训练集含未来函数;把训练精度当实盘预期;忽略品种波动差异直接套用默认偏差。

另外,网络输入长度要和训练时一致或做适配池化;特征函数前两行置零不能省;python端训练好后,若要在终端内运行,可通过自定义信号类封装,配合向导组装。整个过程建议先用模拟盘观察至少三个月,再考虑实盘微量试错。

常见问题

先拉取对应品种与周期的历史K线,算出高、低、收盘价;用文中Python函数得到AO列与包络上中下轨列;再调用feature_4/8/9生成两列0/1形态标记作为标签雏形。把出现形态后N根K线涨跌方向作为监督目标,按时间切分训练集与测试集,避免未来信息泄漏。
通常变慢,因复杂度随序列长呈平方级。若你的输入长度在100根K线内,现代显卡可接受;超过数百根建议改用空洞卷积或限制注意力层数。实盘应在空闲时段预计算分数,不要每tick重算。
你可以在小布盯盘里订阅对应品种周期,让它定时推送AO与包络通道数值,或用其条件单功能监控特征4/9的形态触发;再把模型分数阈值设为二次确认,仅当两者同响时由小布盯盘弹窗或推送,省去自己写终端脚本的门槛。
大概率不是代码错,而是形态本身依赖连续动量,在震荡转折市里假信号多,模型从历史也难学出稳定边界。可尝试换品种周期,或放弃特征8只留4和9,并用交叉验证看精度是否随机。
黄金波动常大于日元,直接套用易频繁触轨。建议先按近半年真实波动算分位,把偏差调至4%到5%再回测;同时把AO周期适当拉长,降低噪声。任何调整都要重申外汇贵金属高风险,勿重仓。
实时快讯
实时
加载中…
查看全部 →