您应该知道的 MQL5 向导技术(第 06 部分):傅里叶(Fourier)变换·进阶篇
(2/3)· 多数交易者盯着 K 线形态找规律,却忽略了价格本就是多层频率叠加的波,拆开看才知哪层在主导
把收盘价序列直接当信号读,等于把混在一起的低频趋势和高频噪声一口吞下。傅里叶变换给的不是一个新指标,而是一把能拆波的刀——可惜多数人连周转次数设多少都凭感觉。
跟单尾随类的参数校验与多仓止损逻辑
| 在自定义尾随止损模块里,CTrailingFT 的析构函数留空,真正的初始化门槛落在 ValidationSettings。它先调用基类 CExpertTrailing 的校验,再卡一道硬约束:m_index 必须落在 0 到 4 之间(原文写 m_index<0 | m_index>=5 即拒绝),否则向日志抛“index must be greater than 0 and less than epicycles”并返回 false。 |
|---|
InitIndicators 负责把指标和时序挂进传入的 CIndicators 指针;指针为空直接返 false,随后委派基类完成基础指标初始化。这一步没额外建指标,说明本类主要复用已有高低价序列与价差序列。 多仓尾随看 CheckTrailingStopLong:先 Refresh 最近一根 K 的高低价,取 StartIndex 定位,再用 ProcessFT 算出一个百分比系数 _ft,除以 100 得到 _type。ATR 代理值用“最高价-最低价”与 2 倍点差二者取大,再乘 _type。 止损线 = 本根最低价 - m_step*代理ATR。注意 brokers 的 StopsLevel 限制:new_sl 必须高于原止损(或开仓价)且低于 Bid - StopsLevel*Point 才写回 sl,否则 sl 保持 EMPTY_VALUE 不调整。外汇与贵金属杠杆高,这类尾随参数错配可能在滑点中放大回撤,建议先在 MT5 策略测试器用历史数据验证 m_step 与 m_index 的敏感性。
CTrailingFT::~CTrailingFT(class="type">void) { } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Validation settings class="kw">protected data. | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTrailingFT::ValidationSettings(class="type">void) { class=class="str">"cmt">//--- validation settings of additional filters if(!CExpertTrailing::ValidationSettings()) class="kw">return(class="kw">false); class=class="str">"cmt">//--- initial data checks if(m_index<class="num">0 || m_index>=class="num">5) { printf(__FUNCTION__+": index must be greater than class="num">0 and less than epicycles"); class="kw">return(class="kw">false); } class=class="str">"cmt">//--- ok class="kw">return(true); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Create indicators. | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTrailingFT::InitIndicators(CIndicators *indicators) { class=class="str">"cmt">//--- check pointer if(indicators==NULL) class="kw">return(class="kw">false); class=class="str">"cmt">//--- initialization of indicators and timeseries of additional filters if(!CExpertTrailing::InitIndicators(indicators)) class="kw">return(class="kw">false); class=class="str">"cmt">//--- class=class="str">"cmt">//--- ok class="kw">return(true); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Checking trailing stop and/or profit for class="type">long position. | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTrailingFT::CheckTrailingStopLong(CPositionInfo *position,class="type">class="kw">double &sl,class="type">class="kw">double &tp) { class=class="str">"cmt">//--- check if(position==NULL) class="kw">return(class="kw">false); m_high.Refresh(-class="num">1); m_low.Refresh(-class="num">1); class="type">int _x=StartIndex(); class="type">class="kw">double _ft=ProcessFT(_x); class="type">class="kw">double _type=_ft/class="num">100.0; class="type">class="kw">double _atr=fmax(class="num">2.0*m_spread.GetData(_x)*m_symbol.Point(),m_high.GetData(_x)-m_low.GetData(_x))*(_type); class="type">class="kw">double _sl=m_low.GetData(_x)-(m_step*_atr); class="type">class="kw">double level =NormalizeDouble(m_symbol.Bid()-m_symbol.StopsLevel()*m_symbol.Point(),m_symbol.Digits()); class="type">class="kw">double new_sl=NormalizeDouble(_sl,m_symbol.Digits()); class="type">class="kw">double pos_sl=position.StopLoss(); class="type">class="kw">double base =(pos_sl==class="num">0.0) ? position.PriceOpen() : pos_sl; sl=EMPTY_VALUE; tp=EMPTY_VALUE; if(new_sl>base && new_sl<level) sl=new_sl; class=class="str">"cmt">//---
◍ 空单 trailing 与傅里叶预测的实现细节
空单 trailing 的判定逻辑和的多头版本对称,但方向完全反过来:用近期最高价加步长乘波动基线来算候选止损,且只在候选值低于原基准、又高于经纪商最小止损位时才下发。代码里 base 的取法很关键——若持仓尚无止损,就以开仓价为基准,否则沿用现有 SL,避免把止损往亏损方向推。 候选 SL 的计算式为 _sl = m_high.GetData(_x) + (m_step*_atr),其中 _atr 取了两倍点差与波段振幅的较大值再乘 _type(傅里叶输出除以100)。若 new_sl < base 且 new_sl > level 才赋值 sl,否则保持 EMPTY_VALUE 表示本 tick 不调整。 ProcessFT 负责把收盘价序列喂给傅里叶变换,输出对价格变化的预测值。注释写明用 6 个数据点、5 个本轮(epicycle)做拟合,_output 为 matrix 类型、_f 为复数数组,这是 MT5 新矩阵/复数 API 的典型用法。 外汇与贵金属杠杆高、滑点随机,这套 trailing 在剧烈单边中可能频繁重算 SL;上 MT5 把 m_step 从默认调到 1.5 倍,观察回测中止损触发距离的变化倾向。
class="type">bool CTrailingFT::CheckTrailingStopShort(CPositionInfo *position,class="type">class="kw">double &sl,class="type">class="kw">double &tp) { class=class="str">"cmt">//--- check if(position==NULL) class="kw">return(class="kw">false); m_high.Refresh(-class="num">1); m_low.Refresh(-class="num">1); class="type">int _x=StartIndex(); class="type">class="kw">double _ft=ProcessFT(_x); class="type">class="kw">double _type=_ft/class="num">100.0; class="type">class="kw">double _atr=fmax(class="num">2.0*m_spread.GetData(_x)*m_symbol.Point(),m_high.GetData(_x)-m_low.GetData(_x))*(_type); class="type">class="kw">double _sl=m_high.GetData(_x)+(m_step*_atr); class="type">class="kw">double level =NormalizeDouble(m_symbol.Ask()+m_symbol.StopsLevel()*m_symbol.Point(),m_symbol.Digits()); class="type">class="kw">double new_sl=NormalizeDouble(_sl,m_symbol.Digits()); class="type">class="kw">double pos_sl=position.StopLoss(); class="type">class="kw">double base =(pos_sl==class="num">0.0) ? position.PriceOpen() : pos_sl; sl=EMPTY_VALUE; tp=EMPTY_VALUE; if(new_sl<base && new_sl>level) sl=new_sl; class=class="str">"cmt">//--- class="kw">return(sl!=EMPTY_VALUE); } class="type">class="kw">double CTrailingFT::ProcessFT(class="type">int Index) { class="type">class="kw">double _ft=class="num">0.0; class="type">int _index=Index;class=class="str">"cmt">//+StartIndex(); m_close.Refresh(-class="num">1); class="type">class="kw">double _a[]; matrix _output; al_complex _f[]; class=class="str">"cmt">//class="num">6 data points, class="num">5 epicycles
「用傅里叶变换算 trailing stop 的实测片段」
下面这段 MT5 代码把最近 6 根收盘价差分后做一维实数 FFT,再用频谱重构出 trailing 用的偏移量。外汇与贵金属波动无序,FFT 仅刻画历史周期成分,实盘信号失效概率不低,务必先在策略测试器跑样本。 数组先定长 6 并清零,输出矩阵 Init(6,5) 代表 6 行价格点、5 行频率分量;差分循环取 m_close[_index+p] 减后一根,得到 6 个收益率残差。 FFTR1D(_a,5,_f) 只取前 5 个频点,双重循环里用 1/5 做归一、用 p/6 当相位角,按 exp(-2π·(im/re)·angle) 复数衰减重构每个 (价格点,频率) 单元。最后 _ft 拿第 5 行某频点除以自身绝对值与收盘价差的最大值,乘 100 压到百分比尺度,返回给移动止损模块。 直接把这段塞进你自己的 CT trailing 类,把 m_index 设成想跟踪的频点序号,能在 EURUSD 的 M15 上直观看到重构幅度随价差收缩。
ArrayResize(_a,class="num">6);ArrayInitialize(_a,class="num">0.0); _output.Init(class="num">6,class="num">5);_output.Fill(class="num">0.0); for(class="type">int p=class="num">0;p<class="num">6;p++) { _a[p]=m_close.GetData(_index+p)-m_close.GetData(_index+p+class="num">1); } FFT.FFTR1D(_a,class="num">5,_f); for(class="type">int p=class="num">0;p<class="num">6;p++) { for(class="type">int s=class="num">0;s<class="num">5;s++) { class="type">class="kw">double _divisor=(class="num">1.0/class="num">5),_angle=(p);_angle/=class="num">6; _output[p][s]=(_divisor*_a[p]*MathExp(-class="num">2.0*__PI*(_f[s].im/_f[s].re)*_angle)); } } class="type">class="kw">double _close=m_close.GetData(_index)>m_close.GetData(_index+class="num">1); _ft=(_output[class="num">5][m_index]/fmax(m_symbol.Point(),fabs(_output[class="num">5][m_index])+fabs(_close)))*class="num">100.0; class="kw">return(_ft);
用傅里叶变换做跟踪止损的类骨架
MT5 里把傅里叶变换塞进 trailing stop,不是直接算价格,而是借频域视角重构止损参考。下面这段类声明定义了 CTrailingFT,它继承自 CExpertTrailing,核心成员是一个 CFastFourierTransform 实例。 类头部注释里暴露了三个可调参数:Points=6、Epicycles=5、Step=0.5。Points 控制采样点数量,Epicycles 是傅里叶级数截断项数,Step 是实际拖拽止损的步长(报价点)。 宏定义里 __PI 写成 245850922/78256779,这是用分数逼近 π(约 3.14159265),避免浮点字面量误差。外汇和贵金属波动剧烈,这类基于频域的止损在高杠杆下仍可能快速触损,属高风险用法。 类里只声明了接口:ValidationSettings 做参数校验,InitIndicators 挂指标,CheckTrailingStopLong/Short 分别处理多空仓的止损止盈刷新。具体傅里叶计算逻辑在别处实现,但从这个骨架能直接开 MT5 新建 trailing 类验证编译。
class=class="str">"cmt">//| Class=CTrailingFT | class=class="str">"cmt">//| Page=trailing_ct | class=class="str">"cmt">//| Parameter=Points,class="type">int,class="num">6,FT-Points | class=class="str">"cmt">//| Parameter=Epicycles,class="type">int,class="num">5,FT-Epicycles | class=class="str">"cmt">//| Parameter=Step,class="type">class="kw">double,class="num">0.5,Trailing Step | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">// wizard description end class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Class CTrailingFT. | class=class="str">"cmt">//| Appointment: Class traling stops with &class="macro">#x27;Fourier Transform&class="macro">#x27; v3 | class=class="str">"cmt">//| relative-sets concepts. | class=class="str">"cmt">//| Derives from class CExpertTrailing. | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#define __PI class="num">245850922/class="num">78256779 class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| | class=class="str">"cmt">//+------------------------------------------------------------------+ class CTrailingFT : class="kw">public CExpertTrailing { class="kw">protected: CFastFourierTransform FFT; class=class="str">"cmt">//--- adjusted parameters class="type">class="kw">double m_step; class=class="str">"cmt">// trailing step class="kw">public: class=class="str">"cmt">//--- methods of setting adjustable parameters class=class="str">"cmt">//--- method of verification of settings class="kw">virtual class="type">bool ValidationSettings(class="type">void); class=class="str">"cmt">//--- method of creating the indicator and timeseries class="kw">virtual class="type">bool InitIndicators(CIndicators *indicators); class=class="str">"cmt">//--- methods of checking if the market models are formed class="kw">virtual class="type">bool CheckTrailingStopLong(CPositionInfo *position,class="type">class="kw">double &sl,class="type">class="kw">double &tp); class="kw">virtual class="type">bool CheckTrailingStopShort(CPositionInfo *position,class="type">class="kw">double &sl,class="type">class="kw">double &tp); class=class="str">"cmt">//--- CTrailingFT(class="type">void); ~CTrailingFT(class="type">void); class=class="str">"cmt">//--- methods of setting adjustable parameters
◍ 把类骨架接进实盘平仓判定
上面这段是尾随止损类 CTrailingFT 的底层实现残片,负责把滤波逻辑挂到 EA 的持仓管理链上。构造里只做了一件事:把时间、点差、开高低收五类序列全部标记为可用,m_used_series 用位加写成 USE_SERIES_TIME+USE_SERIES_SPREAD+USE_SERIES_OPEN+USE_SERIES_HIGH+USE_SERIES_LOW+USE_SERIES_CLOSE,意味着后续 ProcessFT 能直接读这些缓存。 ValidationSettings 与 InitIndicators 都先调父类 CExpertTrailing 的同名方法,返回 false 就中断,等于把过滤器的准入门槛交回标准框架,你自己加的自定义校验得插在父类调用之后才有意义。 真正产生平仓信号的是 CheckTrailingStopLong:先 Refresh 最近一根 K 的 high/low,再用 StartIndex() 取当前索引送进 ProcessFT。注意 _type=_ft/100.0 这个除法——说明 ProcessFT 返回的是以「基点百分之一」为单位的整数化数值,比如返回 250 时实际滤波阈值倾向对应 2.5 个基点。外汇与贵金属杠杆高,这类尾随阈值过小可能在震荡里被反复触发止损,上 MT5 用策略测试器跑 EURUSD M15 改这个除数就能看到滑点敏感度的变化。 Step 方法只是把外部传入的 double 写进 m_step 成员,典型用法是 EA 参数面板把步长暴露出来。你复制这段代码时若报 m_high/m_low 未声明,说明头文件里少写了 CTimeseries 类型的 protected 成员,补上再编译。
class="type">void Step(class="type">class="kw">double value) { m_step=value; } class="kw">protected: class="type">class="kw">double ProcessFT(class="type">int Index); }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Constructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CTrailingFT::CTrailingFT(class="type">void) { class=class="str">"cmt">//--- initialization of class="kw">protected data m_used_series=USE_SERIES_TIME+USE_SERIES_SPREAD+USE_SERIES_OPEN+USE_SERIES_HIGH+USE_SERIES_LOW+USE_SERIES_CLOSE; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Destructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CTrailingFT::~CTrailingFT(class="type">void) { } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Validation settings class="kw">protected data. | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTrailingFT::ValidationSettings(class="type">void) { class=class="str">"cmt">//--- validation settings of additional filters if(!CExpertTrailing::ValidationSettings()) class="kw">return(class="kw">false); class=class="str">"cmt">//--- initial data checks class=class="str">"cmt">//--- ok class="kw">return(true); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Create indicators. | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTrailingFT::InitIndicators(CIndicators *indicators) { class=class="str">"cmt">//--- check pointer if(indicators==NULL) class="kw">return(class="kw">false); class=class="str">"cmt">//--- initialization of indicators and timeseries of additional filters if(!CExpertTrailing::InitIndicators(indicators)) class="kw">return(class="kw">false); class=class="str">"cmt">//--- class=class="str">"cmt">//--- ok class="kw">return(true); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Checking trailing stop and/or profit for class="type">long position. | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CTrailingFT::CheckTrailingStopLong(CPositionInfo *position,class="type">class="kw">double &sl,class="type">class="kw">double &tp) { class=class="str">"cmt">//--- check if(position==NULL) class="kw">return(class="kw">false); m_high.Refresh(-class="num">1); m_low.Refresh(-class="num">1); class="type">int _x=StartIndex(); class="type">class="kw">double _ft=ProcessFT(_x); class="type">class="kw">double _type=_ft/class="num">100.0;