不使用额外的缓冲区,为中间计算进行系列价格的平均化·综合运用
(3/3)·把平均线算法收进类里,省掉多余缓冲区还能保住执行速度,收尾篇给出落地写法
平均线类带来的性能与便利实利
把平均线类直接写进代码,最实在的好处是省掉了反复调用技术面和自定义指标的开销,开发出的模块跑起来明显更轻。对 MT5 用户来说,这不是理论优化,而是编译后肉眼可见的响应提升。 另一个常被忽略的点是可读性。William Blau《动量、方向和背离》里那套思路,用这类平均线重写后,指标代码被压到极短,且大多只做单根 K 线重算,逻辑一眼能看完。 平均线算法池子很宽,经典型和技术型都能搭。换不同平滑方式,往往能比传统均线更早抓到趋势苗头,假信号也偏少——但外汇和贵金属波动受事件驱动,早触发不等于胜率必然高,上 MT5 回测自己的品种再说。
◍ 不同均线算法的实战取舍
把同一套指标分别套上不同均线算法,肉眼就能看出曲线形态和滞后程度差得很远。这说明不存在一种算法在所有行情里都占优,硬定死规则没意义,但按算法脾气用还是能少走弯路。 钱德和考夫曼的 AMA 类算法本职是抓趋势,不是拿来当噪音过滤器做二次平滑的。给这类算法喂数据,最好直接丢原始价格或指标原值,别先求一遍平均。用考夫曼算法处理 Momentum 指标值的 4c_Momentum_AMA.mq5 就是照这个思路写的。 经典 SMA、EMA 不用多说,适用性本来就广。凡是原本用基础均线的地方,JMA、T3、超线性、抛物线这四种基本都能顶上。比如 JMACD.mq5 里把 MACD 的 EMA 和 SMA 全换成 JMA,曲线对趋势的确认更直接;而 JMomentum.mq5 是拿 JMA 去平滑算完的指标值,不是换算法确认趋势,两者目的不一样。 市场状态一直在变,幻想找个「唯一完美算法」一劳永逸不现实。我自己在变来变去的环境里,常用 JFATL.mq5 和 J2JMA.mq5 做快、中期趋势跟踪,基于它们的判断胜率够用。外汇和贵金属波动大、滑点突兀,换算法不等于降风险,回测也要带点保守。 均线算法能叠着用。对已经平均过的值再跑一遍平均,往往还能再压一点毛刺。本文开头分析的 MAx4.mq5 就是连续四次套均线的例子,你可以直接挂到 MT5 里看四级平滑后信号怎么钝化。
「均线内核的动态缓冲与增量求和」
写均线指标时,第一步不是算平均值,而是把输入序列塞进一个 m_SeriesArray[] 动态数组。多数算法在检测到关键计算信息出现时,调用 CMovSeriesTools 的 SeriesArrayResize() 一次性分配内存,避免每根 K 线反复申请释放。 每个新柱进来,当前价格系列值要写进数组最老位置,并用 m_count 记偏移编号,这步交给 Recount_ArrayZeroPos() 做。想取相对当前柱平移 iii 根的元素,用 Recount_ArrayNumber() 换算下标——最新值放零位、其余顺移覆盖,比每次重排整个数组更省资源。 代码里三类 BarCheck 负责边界:BarCheck1 在 K 线数不够时直接返 EMPTY_VALUE;BarCheck2 在首次满足数据量时初始化一次变量;BarCheck4/BarCheck5 则分别保存和恢复未平仓柱上的多重计算状态,防止实时 tick 重算错位。 SMA 这类算法若每柱都重新加总周期内的 series 值,开销随周期线性放大。更合理的做法是初始化时算一次总和,之后每柱只加新值、减最老值——前一柱和与当前柱和的差,就是一个新值进、一个老值出的关系。下面这段摘出来的核心片段就是这套机制: 别把缓冲当普通数组 m_SeriesArray 是带环形偏移的滚动窗口,kkk = Recount_ArrayNumber(m_count, Length_, iii) 算出的下标可能绕回头部,直接下标访问会读到错位数据,调试时优先打印 kkk 与 m_count。
class=class="str">"cmt">//----+ 改变变量数组的大小 if(bar==begin && !SeriesArrayResize(__FUNCTION__, Length, m_SeriesArray, m_Size_)) class="kw">return(EMPTY_VALUE); class=class="str">"cmt">//----+ m_SeriesArray 数组单元的转移和初始化 Recount_ArrayZeroPos(m_count, Length_, prev_calculated, series, bar, m_SeriesArray); for(iii=class="num">1; iii<=Length; iii++, rrr--) { kkk = Recount_ArrayNumber(m_count, Length_, iii); m_sum += m_SeriesArray[kkk] * rrr; m_lsum += m_SeriesArray[kkk]; m_weight += iii; } class=class="str">"cmt">//----+ 检查可靠用于计算的起点 if(BarCheck1(begin, bar, set)) class="kw">return(EMPTY_VALUE); class=class="str">"cmt">//----+ 初始化零值 if(BarCheck2(begin, bar, set, Length)) class=class="str">"cmt">//----+ 保存变量值 if(BarCheck4(rates_total, bar, set)) class=class="str">"cmt">//----+ 恢复变量值 if(BarCheck5(rates_total, bar, set))
记住这一条就够了
把平滑算法封装成简单类,是降低 MQL5 指标学习曲线的直接办法。随文给的 Include_zh.zip 只含两份文件,却足够编译 Indicators.zip 里全部指标;测试 EA 则单独放在 Experts.zip,体积 5.03 KB,可立即拖进 MT5 回测。 上面这段枚举定义了 10 种平滑方式,从 MODE_SMA_ 到 MODE_AMA,GetStartBars() 则按所选 Method 与 Length、Phase 返回算法所需最少 bar 数——改一个枚举值,就能切换均线内核。 外汇与贵金属波动剧烈、杠杆高风险突出,这类封装只解决代码组织问题,不预示任何收益。打开 MT5 把 Smooth_Method 里的 MODE_JJMA 换成 MODE_T3,编译跑一遍,比读十篇概述都实在。
enum Smooth_Method { MODE_SMA_, class=class="str">"cmt">//SMA MODE_EMA_, class=class="str">"cmt">//EMA MODE_SMMA_, class=class="str">"cmt">//SMMA MODE_LWMA_, class=class="str">"cmt">//LWMA MODE_JJMA, class=class="str">"cmt">//JJMA. MODE_JurX, class=class="str">"cmt">//JurX MODE_ParMA, class=class="str">"cmt">//ParMA MODE_T3, class=class="str">"cmt">//T3 MODE_VIDYA, class=class="str">"cmt">//维迪亚 MODE_AMA class=class="str">"cmt">//AMA }; class=class="str">"cmt">//+X================================================================X+ class=class="str">"cmt">//| 计算 XMA 算法所需的最小条数 class=class="str">"cmt">//+X================================================================X+ class="type">int GetStartBars(Smooth_Method Method,class="type">int Length,class="type">int Phase)