首页 / 交易学院 / 小布知识库 / 把指标实现为类:以峰谷指标与 ATR 为例
把指标实现为类:以峰谷指标与 ATR 为例

把指标实现为类:以峰谷指标与 ATR 为例

MQL5 编程 难度 · 高阶 2011 12 分钟阅读
指标类prev_calculatedATR

一、为什么把指标做成类

断线或历史同步中断时,prev_calculated 会被清零,触发针对整个历史的重算。影响因素越多(大周期、复杂计算、多品种、机器差),重算代价越大。把指标封装成类、缓存中间结果,是缓解此问题的工程化手段。

二、prev_calculated 与重算陷阱

⚠ 断线会清零
连接中断或重大历史同步时,prev_calculated 被设为 0,导致整段重算。设计指标时必须能“从任意位置续算”,不能假设总是增量更新。

三、峰谷指标类

峰谷(ZigZag)指标维护一个极值点序列 ZZBar。类持有 m_prev_calculated 与缓冲,只对新柱计算,避免每 tick 全量重算。

class CCustZigZagPPC
{
   int    m_prev_calculated;
   ZZBar  m_bars[];          // 极值点序列
public:
   bool Init(void);
   int  Calculate(const int rates_total, const int prev_calculated);
};

struct ZZBar { double price; int index; int type; };

四、ATR 类示例

ATR 类封装 iATR 句柄与缓冲,对外暴露 Calc(pos) 增量计算,便于在多个指标间复用。

class CCustATR
{
   double m_buf[];
public:
   bool Init(void) { /* 建句柄 */ return true; }
   bool Calc(int pos)
   {
      m_buf[pos] = iATR(_Symbol, PERIOD_CURRENT, 14, pos);
      return true;
   }
};

五、在 OnCalculate 中调用

主指标把子指标组合进来,统一用 prev_calculated 控制增量区间,既清晰又可复用。

int OnCalculate(const int rates_total,
                  const int prev_calculated, ...)
{
   if(!atr.Calc(rates_total-1)) return 0;
   if(!zz.Calculate(rates_total, prev_calculated)) return 0;
   return rates_total;
}

六、为什么把指标写成类

把指标逻辑封装成类(而非散函数或脚本),好处是状态内聚:缓冲、参数、中间变量都是成员,初始化一次、全程复用,调用方只拿到清晰的接口(如 Calculate(shift) 返回算好的值)。

类还能继承与组合:一个基类管生命周期,具体指标子类填计算,新增指标只需写子类。比起每次复制粘贴一套缓冲管理代码,类结构可维护性高一个量级。

七、类的生命周期

类的 OnInit 里创建缓冲、注册参数;OnCalculate 里被调用时算指定 shift 的值并写缓冲;OnDeinit 里释放。把「创建-计算-释放」收进类,调用方不必关心内部,指标之间也不互相污染状态。

class CMyIndicator
{
   int m_handle;
public:
   bool Init() { m_handle = iMA(...); return m_handle!=INVALID_HANDLE; }
   double Calc(int shift) { return CopyBuffer(m_handle,0,shift,1,buf)>0 ? buf[0] : 0; }
};
✦ 状态内聚
指标类把缓冲与参数收进成员,调用方只需 Init 和 Calc。状态内聚,复用干净,不互相污染。

八、复用与测试

类化后,同一个指标能在 EA、另一个指标、脚本里直接 new 出来用,不用重写。配合单体测试(喂已知数据断言输出),指标逻辑的错误能在最早被发现,而不是等到回测曲线诡异才查。

这也是专业代码和脚本的区别:把可复用的计算资产沉淀成类库,越写越省事、越稳。

九、常见指标类错误

两个高频错:一是缓冲索引错位,写错 buf 号导致画错线或读错值;二是 OnCalculate 里没正确处理 prev_calculated,重复计算或漏算新 bar。两者都会让指标时好时坏。

调试时 Comment 打印缓冲首尾值、用数据窗口核对实时数值,能快速定位是计算错还是绘制错。指标类写稳了,调用它的 EA 才可信。

十、指标类也是可测试资产

把指标类当「一等公民」测试:喂已知序列,断言输出符合公式;改了计算逻辑重跑测试。指标是 EA 的眼睛,眼睛坏了 EA 全程瞎做,所以指标的测试优先级不亚于信号逻辑。

沉淀指标类库后,新策略可以直接复用,不必每个 EA 重写一遍缓冲管理。越往后写,你不是在「从零造轮子」,而是在「组合已有资产」,速度和理解都复利增长。

把指标封装成类后,务必在析构函数里释放指标句柄与缓冲区,否则反复 new/delete 会造成句柄泄漏、跑久了终端变卡。多实例共用时给每个实例的缓冲起独立前缀,避免指标名冲突互相覆盖。类名与对外接口尽量稳定,下游策略引用后才不会被你一次重构全部打断。

写单元测试覆盖核心计算逻辑,比靠肉眼看曲线可靠得多。指标算错往往不抛异常,只在回测里悄悄输出错误信号,等实盘发现时已经亏了一截,所以类内部的关键运算必须可测可验。

十一、小结

✦ 类封装的收益
指标即类让逻辑可复用、可组合、可单元测试,也便于在断线重算时只补算差异部分,显著提升多品种多周期下的性能。

常见问题

断线或历史同步中断时终端会清零,触发整段重算;类缓存可缓解。
类可持有状态、组合多个子指标、避免重复计算,更适合复杂逻辑。
若每个图表实例都重算会慢;类封装+限制计算深度能显著提速。