建立一个频谱分析程序·综合运用
📘

建立一个频谱分析程序·综合运用

第 3/3 篇

MT5里跑通FFT频谱分析的内核逻辑

在 MT5 指标里做频谱分析,底层是对输入序列的 1024 个刻度跑快速傅里叶变换(FFT)。核心计算全塞在 fft_calc() 里:先调 realfastfouriertransform() 把 InpData[] 就地换成频域结果,再对每个谐波取实部与虚部平方和写进 Spectrum[],且 Spectrum[0] 恒置零——因为指标不计算直流分量。 输入数据从哪来,由 get_input_data() 按 InputSource 分支决定。为 0 时从 SAInpData.mq5 的零缓冲区拷 1024 点;为 1/2/3 时分别灌入 SMA(3)、SMA(32)、LWMA(12) 的脉冲序列。利用「滤波器脉冲特性的傅里叶变换等于其幅频特性」这一点,选测试序列就能直接观测均线的频率衰减形状。 外部序列要走 iCustom(NULL,0,"SAInpData") 拿句柄,SAInpData.mq5 必须丢进客户端 \MQL5\Indicators 目录,否则 ExtHandle 是 INVALID_HANDLE,CopyBuffer 一句不会执行。 频谱结果既绘图也用 5 个「标签」对象打文本行,由 list_levels() 填 ListArray[]。改 ListPointer 变量(或点右侧按钮)就能平移显示的起始谐波索引,方便盯着中段频段看。外汇与贵金属行情具有高波动高风险,频谱估计只揭示周期成分的概率分布,不构成方向结论。

MQL5 / C++
class="type">void fft_calc()
  {
  class="type">int i,k;
  realfastfouriertransform(InpData,ArraySize(InpData),class="kw">false);       class=class="str">"cmt">// FFT
  for(i=class="num">1;i<ArraySize(Spectrum);i++)
    {
    k=i*class="num">2;
    Spectrum[i]=InpData[k]*InpData[k]+InpData[k+class="num">1]*InpData[k+class="num">1];    
    }
  Spectrum[class="num">0]=class="num">0.0;                                                  class=class="str">"cmt">// 清除组件水平常数
  }
class="type">void get_input_data()
  {
  class="type">int i;
  ArrayInitialize(InpData,class="num">0.0);
  class="kw">switch(InputSource)
    {
    case class="num">0:                                                         class=class="str">"cmt">// 外部数据
      if(ExtHandle!=INVALID_HANDLE)
        CopyBuffer(ExtHandle,class="num">0,class="num">0,ArraySize(InpData),InpData);
      class="kw">break;
    case class="num">1:                                                         class=class="str">"cmt">// 测试 class="num">1. SMA(class="num">3)
      for(i=class="num">0;i<class="num">3;i++)InpData[i]=class="num">1;
      class="kw">break;
    case class="num">2:                                                         class=class="str">"cmt">// 测试 class="num">2. SMA(class="num">32)
      for(i=class="num">0;i<class="num">32;i++)InpData[i]=class="num">1;
      class="kw">break;
    case class="num">3:                                                         class=class="str">"cmt">// 测试 class="num">3. LWMA(class="num">12)
      for(i=class="num">0;i<class="num">12;i++)InpData[i]=class="num">12-i;
      class="kw">break;
    }
  }
class="type">int OnInit()
{
ExtHandle=iCustom(NULL,class="num">0,"SAInpData");  class=class="str">"cmt">// 外部指标句柄
class="kw">return(class="num">0);
}
class="type">void list_levels()

◍ 频谱列表的游标与双轴标签渲染

这段逻辑负责把频谱数据数组里的连续 5 个采样点,刷到图表的 5 个文本标签上。Y 轴显示模式由 YdBFlag 切换:0 为线性坐标,格式串是 "%3u %.5f",保留 5 位小数;非 0 则走分贝坐标,格式串为 "%3u %6.1f dB",只留 1 位小数。 游标边界先算数组可用上限 m = ArraySize(ListArray) - 5,再把 ListPointer 夹在 [1, m] 区间,避免越界读空。之后 Label17~Label21 依次取 ListPointer 到 ListPointer+4 五个下标,用 StringFormat 按当前格式串填值。 在 MT5 里跑这段代码时,把 ListArray 换成你自己的指标缓冲,就能直接看到线性与 dB 两种刻度下的数值跳变。外汇与贵金属市场波动剧烈,此类频谱面板仅作辅助观测,信号滞后与误报概率不可忽视。

MQL5 / C++
  {
  class="type">int m;
  class="type">class="kw">string str;
  
  if(YdBFlag==class="num">0) str="%3u    %.5f";              class=class="str">"cmt">// 如果 Y-轴模式 = 线性
  else str="%3u  %class="num">6.1f dB";                      class=class="str">"cmt">// 如果 Y-轴模式 = 分贝
  m=ArraySize(ListArray)-class="num">5;
  if(ListPointer<class="num">1)ListPointer=class="num">1;
  if(ListPointer>m)ListPointer=m;
  GObj.LabelTxt("Label17",StringFormat(str,ListPointer,ListArray[ListPointer]));
  GObj.LabelTxt("Label18",StringFormat(str,ListPointer+class="num">1,ListArray[ListPointer+class="num">1]));
  GObj.LabelTxt("Label19",StringFormat(str,ListPointer+class="num">2,ListArray[ListPointer+class="num">2]));
  GObj.LabelTxt("Label20",StringFormat(str,ListPointer+class="num">3,ListArray[ListPointer+class="num">3]));
  GObj.LabelTxt("Label21",StringFormat(str,ListPointer+class="num">4,ListArray[ListPointer+class="num">4]));
  }

「从原型到可换肤指标的路」

SpecAnalyzer.mq5 在本文里只是用图形对象做频谱展示的雏形,并非成品级工具。若要在 MT5 实盘里长期用,外观与交互都还得重做。 一个可行方向是用「位图」图形对象当底图:拿外部图像编辑器画好前面板,作为衬底贴进图表,再把按钮、数值等控件叠在上面。这样就能做可换皮肤的指标界面。 频谱估计算法本身也有改进空间,原型的估计方式偏基础,真要做完整分析程序还得换更稳的窗函数与补零策略。外汇与贵金属波动受消息驱动大,这类工具仅作辅助,信号失效概率不低。

信号工程的两本底稿

做价格行为信号过滤,绕不开两本偏工程向的底稿。L. Rabiner 与 B. Gold 的《数字信号处理理论与应用》把 FIR/IIR 滤波、采样定理讲透,MT5 里自写指标做平滑去噪时,底层逻辑就出自这里。 S.L. Marple 的《应用程序数字频谱分析》则补了频谱估计这块,对识别周期成分(比如贵金属 4H 图里常见的 30~40 根 K 线波动段)有直接参考价值。 Yukio Sato 的《信号管理简介》更偏框架,适合在写 EA 信号路由前翻一遍,避免把噪声当主信号喂给下单模块。外汇与贵金属杠杆高,信号误判会快速放大亏损,工程化过滤不是可选项。

◍ 别急着下结论

这套频谱分析指标在 MT5 里以三个文件落地:SpecAnalyzer.mq5 主指标约 15.39 KB,SpecAnalyzer.mqh 包含文件 20.79 KB,SAInpData.mq5 管外部数据访问仅 3.29 KB。下载解压后直接丢进 MQL5/Indicators,编译即可在图表上调出带 GUI 的分析面板。 论坛里早期用户争议很直接:有人只看到把傅立叶库搬到了 MT5,怀疑光谱仪在实盘没用;也有人把它当现成模板,往里嵌自己的统计方法。外汇与贵金属波动受宏观与流动性扰动,频谱峰值只反映历史周期倾向,不代表后续必现,实盘验证前先想清楚你要测的是什么。 真要上手,先开 MT5 把 SAInpData.mq5 挂一个符号跑一遍,看它吐出的外部数据结构和原文描述是否对得上,再决定要不要基于 SpecAnalyzer 改你自己的周期检测逻辑。

常见问题

先用数组把价格序列去趋势后做FFT变换,取模长得到各频率能量,再按频率倒序存进结果数组,就能在图表上画出能量分布。
用独立游标变量记录选中索引,左轴标频率、右轴标振幅,每次重绘时按索引同步两轴文本坐标即可避免错位。
可以,小布内置了信号分解与频谱诊断,打开对应品种页就能直接看到主周期和能量占比,不用手动跑脚本。
漏掉把颜色参数暴露到输入项并绑定重绘事件,否则切换皮肤时频谱图不会跟着刷新。
不能,高频能量可能只是噪声,贵金属和外币对波动高风险大,需结合多周期验证再下概率性判断。