频域中的滤波和特征提取·进阶篇
(2/3)·从 DFT 到同相/正交/正交镜滤波器,搞懂频域特征提取的计算账与失真权衡
很多交易者把移动平均线当成唯一的平滑工具,却没意识到时域卷积在大型数据集上会拖慢回测节奏。频域滤波不是花哨数学,它只是把同一段行情拆成不同周期再拼回来。用错滤波器形状,特征没提取到,反而把转折抹平了。
「同相滤波器与带通基底」
波形在时间轴上的位置即相位。同相滤波器的核心约束是:输出与输入在通带内保持相同时间关系,不发生相位偏移。这一点在 MT5 里做信号对齐时很关键,否则你会把滞后误判成反转。 带通滤波器是同相结构的一种,也是多数其他滤波器的构造基底。它只放行中心频率附近的一段频带,带外频率被衰减。经验上,通带越窄,噪声剔除越狠,但响应也越迟钝。 低通由带通改来:中心频率及以下乘 1.0 放行,之上衰减,中心频率越低输出越平滑,适合抓大趋势;高通反之,压掉低速波动只留快速变化,用来剥离趋势找脉冲。外汇与贵金属杠杆高,平滑过头会漏掉关键刺透,需按品种波动调中心频。 文中几类同相变体都具备「圆形滤波器形状」这一特质,圆形度越好,通带与阻带过渡越对称,实盘重构信号时的形变越小。
90度相移能抓到的突变
正交滤波器对输入信号施加了 90 度(π/2 弧度)相移,单独用场子很窄,大多得和同相滤波器搭伙。它真正的价值在相位关系识别:把一段短周期序列送进带通滤波,同相输出走势和原序列对齐,而正交版本的输出峰值刚好落在原信号零轴交叉处。 实测看,正交输出的波峰波谷对应输入信号的快速变化;输入走平,输出就贴着零轴。也就是说,正交通道对特定速率的变化敏感,原序列稳定时它几乎没动静。 换一组急剧上拉的数据验证:同相带通很难判定那一下爆发,正交通道却清楚标出了突变位置。两者同参数并联就是正交镜滤波器(QM 滤波器),在 MT5 里把 BandPass 与 Quadrature 版指标叠在同一窗口就能肉眼比对。 外汇与贵金属品种波动跳变频繁,用正交通道抓刺透零轴那一刻只是概率性提示,不代表后续方向,杠杆品种高风险务必轻仓验证。
◍ 用正交镜看周期分量的实虚组合
QM 滤波器在做的事,是给价格序列里的周期性分量做局部实例检测。它同时吐出两路输出:同相(in-phase)和正交(quadrature)。当这段行情里压根没有对应周期的事件,两路输出都会落在零附近,这是判断“有没有东西可测”的第一道门槛。 把同相输出当实部、正交输出当虚部拼起来,就得到一个复数形式的滤波结果。由此能直接算经滤波波形的幅度与相位——幅度说强度,相位说它处在周期里的哪一脚。对外汇和贵金属这类高波动品种,这种幅相分离能帮你把噪声里的伪周期先剔掉一部分,但信号失效时仍可能连续多根 K 线给出近零输出,别当确定性结论用。 在真正翻代码之前,有一件事比写函数更靠前:任何时间序列分析都得先想清楚输入序列的预处理与边界,否则滤波器跑出来的相位会漂。
「转换前先给序列去趋势和补零」
DFT 默认输入序列是无限周期的,直接把有限长度、非周期的价格序列丢进去,首尾衔接处会出现环绕失真,频谱被扯歪。 填补(padding)是这步的基操:往序列尾端补常数,把取样总数拉到 2 的幂,FFT 的计算效率会明显提升;同时更密的取样点带来更细的频率分辨率,频谱泄漏也压下去了。补零最常见,也可以补序列均值。 但金融序列大多非稳态,若原序列带缓慢漂移的趋势项,补进去的常值跟两端真实值落差太大,频域里会冒出本不存在的突出频率分量,污染结果。 所以转换前要先做去趋势化,用去掉趋势后的数值替代原始值再做 DFT;等要从频域还原回时域,再把趋势加回去。外汇与贵金属波动大、非稳态特征强,这套预处理做漏了,后续频域信号可信度直接掉档。
CFilter 怎么把序列送进频域做滤波
CFilter 这个类把三种频域滤波器的底层逻辑收拢在 Filter.mqh 里,头一件事就是引 ALGLIB 的 fasttransforms.mqh,不然 DFT 变换没法跑。构造时你丢给它的不只是原始序列,还有 min_padding 和 detrend 两个开关:min_padding 设 0 时补零数量完全由样本数决定,否则它至少补到离 2 的幂最近的长度,去趋势化则在填充前先剥离线性趋势写进 m_buffer。 构造里任何参数不对劲,m_initialized 就是 false,所以实例化后第一句必须调 IsInitialized() 确认返回 true,否则后面所有公开方法都是空转。 滤波方法大多吃 freq 和 width 一对参数,对应通带中心频率和带宽,结果写进你传的 out 数组。Lowpass() 多一个 add_trend 布尔,决定当初剥掉的趋势要不要加回滤波输出;Qmf() 则输出复数数组,它把同相高斯函数和乘了 i 的正交版(纯虚)相加,高于 0.5 奈奎斯特频率的 DFT 项直接置 0,只算低频半边,逆变换回时域。 开 MT5 把下面类声明贴进自定义指标工程,先喂一段 EURUSD 的收盘价数组、min_padding 留 0、detrend 开 true,跑完 IsInitialized() 打印看是否为 1,就能验证填充长度是否按 2 的幂对齐。外汇与贵金属杠杆品种波动剧烈,这类频域处理只作信号参考,实盘仍属高风险。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CFilter - class implementing select filters in the freq domain | class=class="str">"cmt">//+------------------------------------------------------------------+ class CFilter { class="type">int m_length; class=class="str">"cmt">//length of original series class="type">int m_padded_len; class=class="str">"cmt">//modified length of series class="type">int m_half_padded_len; class=class="str">"cmt">//half of modded series class="type">class="kw">double m_slope, m_intercept; class=class="str">"cmt">//slope and intercept of trend in series class="type">class="kw">double m_buffer[]; class=class="str">"cmt">//general internal buffer class="type">bool m_initialized; class=class="str">"cmt">//initialization flag complex m_dft[]; class=class="str">"cmt">//general complex buffer class="kw">public: CFilter(class="type">class="kw">double &series[],class="type">uint min_padding=class="num">0, class="type">bool detrend=true); ~CFilter(class="type">void); class="type">void Lowpass(class="type">class="kw">double freq,class="type">class="kw">double width,class="type">class="kw">double &out[],class="type">bool add_trend); class="type">void Highpass(class="type">class="kw">double freq,class="type">class="kw">double width, class="type">class="kw">double &out[]); class="type">void Bandpass(class="type">class="kw">double freq,class="type">class="kw">double width, class="type">class="kw">double &out[]); class="type">void Qmf(class="type">class="kw">double freq,class="type">class="kw">double width, complex &out[]); class="type">bool IsInitialized(class="type">void) { class="kw">return m_initialized; } }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//|constructor | class=class="str">"cmt">//+------------------------------------------------------------------+