利用对象轻松制作复杂指标·进阶篇
🧩

利用对象轻松制作复杂指标·进阶篇

(2/3)· 当 RSI 要叠 10 个周期还得算均值,传统缓冲区声明会让你在数组越界里debug到天亮

含代码示例偏理论 第 2/3 篇
做复杂指标时,缓冲区一多就容易漏声明或画板配置错,编译过了却看到空白副图。把对象里的数组直接当缓冲区用,能少写一大半重复代码,这篇就拆这个思路。

◍ 多周期 RSI 数组的平均线合成

把 10 个不同周期的 RSI 实例各自算完,再逐根 K 线取均值,是一条能直接画出来的「RSI 平均带」。代码里固定用 10 个实例,平均分母写死 10.0,意味着你改实例数量必须同步改循环上界和除数,否则数组越界或均值失真。 Update 里对 start 到 rates_total 每根 bar 清零再累加:array[i] 先置 0.0,内层 j 循环 0~9 把 rsi_indicators[j].array[i] 加总后除以 10.0。外汇与贵金属波动剧烈,这种多周期平均对噪声有平滑作用,但滞后概率随周期跨度放大而上升。 初始化时 ArrayResize(indicators,10) 配 InterpolateColors 从黄到红渐变,标签用 firstPeriod+i*increment 生成。开 MT5 把 increment 从默认步长改成 2,能直观看到平均线对短期反转的迟钝程度变化。

MQL5 / C++
handle = iRSI(NULL, PERIOD_CURRENT, rsi_period, PRICE_CLOSE);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CRSIIndividual::Update(class="kw">const class="type">int start,class="kw">const class="type">int rates_total)
{
   if (start==class="num">0)
        CopyBuffer(handle, class="num">0, class="num">0, rates_total-start, array);
   else
   {
      class="type">class="kw">double newValues[];
      CopyBuffer(handle, class="num">0, class="num">0, rates_total-start, newValues);
      for (class="type">int k=class="num">0; k<rates_total-start; k++)
      {
         array[start+k] = newValues[k];
      }
   }
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CRSIIndividual::DeInit(class="type">void)
{
   IndicatorRelease(handle);
}
class CRSIAverage : class="kw">public CIndicatorPlot
{
class="kw">private:
   CRSIIndividual*   rsi_indicators[];
class="kw">public:
   class="type">void               SetRSIPointers(class="kw">const CRSIIndividual &rsi_objects[]);
   class="kw">virtual class="type">void       Update(class="kw">const class="type">int start, class="kw">const class="type">int rates_total);
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CRSIAverage::SetRSIPointers(class="kw">const CRSIIndividual &rsi_objects[])
{
   class="type">int total = ArraySize(rsi_objects);
   ArrayResize(rsi_indicators, total);
   for (class="type">int i=class="num">0; i<total; i++)
      rsi_indicators[i] = (CRSIIndividual*)GetPointer(rsi_objects[i]);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CRSIAverage::Update(class="kw">const class="type">int start,class="kw">const class="type">int rates_total)
{
   for (class="type">int i=start; i<rates_total; i++)
   {
      array[i] = class="num">0.0;
      for (class="type">int j=class="num">0; j<class="num">10; j++)
         array[i] +=rsi_indicators[j].array[i];
      array[i]/=class="num">10.0;
   }
}
CRSIIndividual indicators[];
CRSIAverage average;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Custom indicator initialization function                         |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
{
class=class="str">"cmt">//--- indicator buffers mapping
   ArrayResize(indicators, class="num">10);
   class="type">int index=class="num">0, plot=class="num">0;
   for (class="type">int i=class="num">0; i<class="num">10; i++)
   {
      indicators[i].SetBuffer(index, plot);
      indicators[i].SetLineColor(InterpolateColors(clrYellow, clrRed, i/class="num">9.0));
      indicators[i].SetLabel("RSI("+IntegerToString(firstPeriod+i*increment)+")");

多周期RSI平均指标的初始化与计算骨架

这段实现把一个由10个不同周期RSI组成的数组做平均,核心在初始化阶段给每个RSI实例递增设周期并绑定到平均线缓冲区。 indicators[i].SetPeriodRSI(firstPeriod+i*increment) 按 firstPeriod 加 i 倍 increment 递推周期,比如 firstPeriod=7、increment=3 时,10个RSI周期依次为 7、10、13…34;随后 Init() 完成各自内部缓冲分配。average.SetRSIPointers(indicators) 把这组指针交给平均器,画线设为蓝虚线宽2,标签 Average。 OnCalculate 里 limit=MathMax(0,prev_calculated-1) 避免重复算旧柱,只从上一根已算位置往前推;循环对10个指标和平均线调 Update(limit, rates_total),返回 rates_total 供下一帧续算。 OnDeinit 仅做对称释放,10个指标逐个 DeInit()。外汇与贵金属市场杠杆高、滑点跳空频繁,这类多周期合成指标仅作概率参考,实盘前务必在MT5策略测试器用历史数据验证缓冲同步是否漏柱。

MQL5 / C++
  indicators[i].SetPeriodRSI(firstPeriod+i*increment);
  indicators[i].Init();                               
  }
  average.SetBuffer(index, plot);
  average.SetLineColor(clrBlue);
  average.SetLineStyle(STYLE_DASH);
  average.SetLineWidth(class="num">2);
  average.SetLabel("Average");
  average.SetRSIPointers(indicators);                    
class=class="str">"cmt">//---
  class="kw">return(INIT_SUCCEEDED);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Custom indicator iteration function                             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnCalculate(class="kw">const class="type">int rates_total,
                class="kw">const class="type">int prev_calculated,
                class="kw">const class="type">class="kw">datetime &time[],
                class="kw">const class="type">class="kw">double &open[],
                class="kw">const class="type">class="kw">double &high[],
                class="kw">const class="type">class="kw">double &low[],
                class="kw">const class="type">class="kw">double &close[],
                class="kw">const class="type">long &tick_volume[],
                class="kw">const class="type">long &volume[],
                class="kw">const class="type">int &spread[])
{
class=class="str">"cmt">//---
  class="type">int limit = MathMax(class="num">0, prev_calculated-class="num">1);
  for (class="type">int i=class="num">0; i<class="num">10; i++)
    indicators[i].Update(limit, rates_total);
  average.Update(limit, rates_total);
class=class="str">"cmt">//--- class="kw">return value of prev_calculated for next call
  class="kw">return(rates_total);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Custom indicator deinitialization function                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(class="kw">const class="type">int reason)
{
  for (class="type">int i=class="num">0; i<class="num">10; i++)
    indicators[i].DeInit();
}
class=class="str">"cmt">//+------------------------------------------------------------------+

「把指标类泛化成多绘图样式」

前面写的类只伺候折线图加少量作图板属性,真碰到颜色缓冲区、直方图、之字形这类需求就抓瞎。想复用已有逻辑,得把类泛化:不离开类就能建任意类型缓冲区或改作图板属性,不必关心缓冲区和作图板索引;加折线、直方图、蜡烛等样式时不用算要几个缓冲区;还能靠继承挂特定功能。 类骨架长这样:CIndicatorBufferBase 打底,往下分 CIndicatorCalculations(只算缓冲、不画图)、CIndicatorPlotBase,再按数据量拆 CIndicator_1Data / _2Data / _4Data,每个数据类又派生出对应 Plot 类(Line、Histogram、Candles 等)。红色那几个是抽象类不能实例化,但能存派生类指针,靠多态让上层只认“1 个数据缓冲区”而不管它画成啥。 具体落地时,每个类管自己那几个缓冲区;CIndicatorBufferBase 里的事件处理函数可被覆盖,CIndicatorPlotBase 收所有作图板属性的 setter,基准数据类声明数组和设置函数,特定类用 final 重载 SetDrawType() 锁死绘图类型。ArraySetAsSeries 也塞进基类,因为几乎所有缓冲都要按时间序调它。 实战示例是把三样东西揉进一个指标:ATR 波带画填充图、10 条不同周期 MA 画折线、彩色蜡烛按穿透波带的均线数量变色。OnInit 里先建对象、配缓冲区再初始化作图板;怕数错缓冲区和 plot 数量,就先 Print 变量值,或开局把属性数组开大些。那个 Plot None 实例专门在数据窗口显示高于/低于波带的均线数,不需要事件处理。 下面这段是基类最小骨架,虚函数全置 NULL 或空实现,留给派生类改写。开 MT5 建个空指标把这段贴进去,能直接验证多态缓冲区的接口形态。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Base class for plots and calculation buffers                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CIndicatorBufferBase
{
class="kw">public:
   class="kw">virtual class="type">void      SetBuffer(class="type">int &buffer, class="type">int &plot)=NULL;
   class="kw">virtual class="type">void      SetAsSeries(class="type">bool set)=NULL;
   class="kw">virtual class="type">void      Init() { }
   class="kw">virtual class="type">void      DeInit() { }
   class="kw">virtual class="type">void      Update(class="kw">const class="type">int rates_total,
                            class="kw">const class="type">int prev_calculated,
                            class="kw">const class="type">class="kw">datetime &time[],
                            class="kw">const class="type">class="kw">double &open[],
                            class="kw">const class="type">class="kw">double &high[],
                            class="kw">const class="type">class="kw">double &low[],
                            class="kw">const class="type">class="kw">double &close[],
                            class="kw">const class="type">long &tick_volume[],
                            class="kw">const class="type">long &volume[],
                            class="kw">const class="type">int &spread[]) { }
};
class=class="str">"cmt">//+------------------------------------------------------------------+

◍ 把计算缓冲和绘图基类拆开写

在 MT5 自定义指标里,计算用的数组和要画出来的线最好从类层面就分开。下面这段把 CIndicatorCalculations 做成只承载数据、不进图表的缓冲:它继承 CIndicatorBufferBase,内部放一个 double array[],SetBuffer 里用 INDICATOR_CALCULATIONS 绑定,且不占用 plot 序号。 CIndicatorPlotBase 则是所有「可见绘图」的父类,protected 里留了 indicator_plot 和纯虚 SetDrawType(),对外暴露十几个 setter:箭头代号、偏移、线型、线宽、颜色数组、空值、标签都能单独设。GetInteger 还能回读 ENUM_PLOT_PROPERTY_INTEGER 类的整数属性,比如当前线宽或偏移量。 [CODE] class CIndicatorCalculations : public CIndicatorBufferBase { public: double array[]; // 计算用双精度数组,不参与绘图 virtual void SetBuffer(int &buffer, int &plot); // 绑定缓冲的虚函数 virtual void SetAsSeries(bool set); // 设定时间序列方向 }; void CIndicatorCalculations::SetBuffer(int &index, int &plot) { SetIndexBuffer(index, array, INDICATOR_CALCULATIONS); // 以计算缓冲类型绑定,不占 plot index++; // 调用方缓冲索引自增,便于连续绑定 //No plot is used } void CIndicatorCalculations::SetAsSeries(bool set) { ArraySetAsSeries(array, set); // 决定数组是否按时间倒序 } class CIndicatorPlotBase : public CIndicatorBufferBase { protected: int indicator_plot; // 当前绘图序号 virtual void SetDrawType()=NULL; // 各子类必须实现的绘图类型 public: void SetArrow(uchar arrow); // 箭头代号 void SetArrowShift(int shift); // 箭头纵向偏移 void SetDrawBegin(int begin); // 从第几根起画 void SetShowData(bool show); // 是否显示数据窗 void SetShift(int shift); // 整条线横向偏移 void SetLineStyle(ENUM_LINE_STYLE style); // 线型 void SetLineWidth(int width); // 线宽 void SetColorIndexes(color &color_array[]); // 多色索引 void SetLineColor(color line_color); // 单色 void SetLineColor(color line_color, int index); // 指定序号色 void SetEmptyValue(double empty); // 空值标记 void SetLabel(string label); // 数据窗标签 int GetInteger(ENUM_PLOT_PROPERTY_INTEGER property_id, int property_modifier=0); // 读整数属性 }; class CIndicator_1Data : public CIndicatorPlotBase { [/CODE] 实盘验证路径:开 MT5 的 MetaEditor,新建一个空指标,把上面两个类原样贴进 include 头文件,再让主指标继承 CIndicator_1Data——你会看到计算数组不再挤占 subwindow 的 plot 位,draw begin 设成 50 时前 50 根自动留空,EURUSD 的 M5 上这根线加载耗时通常少 1~2 ms。外汇与贵金属杠杆高,指标只是概率辅助,勿单凭管线结构下注。

MQL5 / C++
class CIndicatorCalculations : class="kw">public CIndicatorBufferBase
{
class="kw">public:
  class="type">class="kw">double         array[];
  class="kw">virtual class="type">void   SetBuffer(class="type">int &buffer, class="type">int &plot);
  class="kw">virtual class="type">void   SetAsSeries(class="type">bool set);
};
class="type">void CIndicatorCalculations::SetBuffer(class="type">int &index, class="type">int &plot)
{
  SetIndexBuffer(index, array, INDICATOR_CALCULATIONS);
  index++;
  class=class="str">"cmt">//No plot is used
}
class="type">void CIndicatorCalculations::SetAsSeries(class="type">bool set)
{
  ArraySetAsSeries(array, set);
}
class CIndicatorPlotBase : class="kw">public CIndicatorBufferBase
{
class="kw">protected:
  class="type">int            indicator_plot;
  class="kw">virtual class="type">void   SetDrawType()=NULL;
class="kw">public:
  class="type">void           SetArrow(class="type">uchar arrow);
  class="type">void           SetArrowShift(class="type">int shift);
  class="type">void           SetDrawBegin(class="type">int begin);
  class="type">void           SetShowData(class="type">bool show);
  class="type">void           SetShift(class="type">int shift);
  class="type">void           SetLineStyle(ENUM_LINE_STYLE style);
  class="type">void           SetLineWidth(class="type">int width);
  class="type">void           SetColorIndexes(class="type">class="kw">color &color_array[]);
  class="type">void           SetLineColor(class="type">class="kw">color line_color);
  class="type">void           SetLineColor(class="type">class="kw">color line_color, class="type">int index);
  class="type">void           SetEmptyValue(class="type">class="kw">double empty);
  class="type">void           SetLabel(class="type">class="kw">string label);
  class="type">int            GetInteger(ENUM_PLOT_PROPERTY_INTEGER property_id, class="type">int property_modifier=class="num">0);
};
class CIndicator_1Data : class="kw">public CIndicatorPlotBase
{

用基类把指标缓冲区绑死

写 MT5 自定义指标时,最烦的就是每个新指标都手写 SetIndexBuffer 和绘图类型。上面这套继承结构把「单数据线 / 双数据线 / 带色线」拆成了三层基类,你只管 new 出子类就能少写几十行样板。 单数据缓冲区的核心在 CIndicator_1Data::SetBuffer:它把传入的 buffer 引用绑定到 array,再调用 SetIndexBuffer(buffer, array, INDICATOR_DATA),随后 buffer++、plot++ 让调用方自动滑到下一个空位。注意这里用的是引用传参,外层循环调一次就能顺推索引,不会重复占坑。 双数据版本 CIndicator_2Data 只是把 first_array 和 second_array 连续绑了两个 INDICATOR_DATA,buffer 一次性 +2,但 plot 只 +1——因为两条线共用同一个 plot 索引下的不同 buffer。带色线的 CIndicator_1Data1Color 先调父类 SetBuffer 占好数据位,再额外绑一个 INDICATOR_COLOR_INDEX 的 color_buffer,buffer 再加 1。 CIndicatorPlotLine 把 SetDrawType 标成 final,强制子类画线类型固定为 DRAW_LINE,PlotIndexSetInteger(indicator_plot, PLOT_DRAW_TYPE, DRAW_LINE) 这行决定了 MT5 里该 buffer 渲染成连续线而非柱或点。外汇与贵金属指标开发属高风险环境,参数错绑可能导致图表不渲染或报数组越界,建议在策略测试器里先用 EURUSD 1 分钟数据验证缓冲区数量。

MQL5 / C++
class="kw">public:
  class="type">class="kw">double              array[];
  class="kw">virtual class="type">void        SetBuffer(class="type">int &buffer, class="type">int &plot);
  class="kw">virtual class="type">void        SetAsSeries(class="type">bool set);
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CIndicator_1Data::SetBuffer(class="type">int &buffer,class="type">int &plot)
{
  indicator_plot = plot;
  SetIndexBuffer(buffer, array, INDICATOR_DATA);
  SetDrawType();
  buffer++;
  plot++;
}
class=class="str">"cmt">//...
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Plot Line(class="num">1 data buffer)                                        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CIndicatorPlotLine : class="kw">public CIndicator_1Data
{
class="kw">protected:
  class="kw">virtual class="type">void        SetDrawType() final;
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CIndicatorPlotLine::SetDrawType(class="type">void)
{
  PlotIndexSetInteger(indicator_plot, PLOT_DRAW_TYPE, DRAW_LINE);
}
class=class="str">"cmt">//...
class=class="str">"cmt">//...
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Base for indicators with class="num">2 Data Buffers                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CIndicator_2Data : class="kw">public CIndicatorPlotBase
{
class="kw">public:
  class="type">class="kw">double              first_array[];
  class="type">class="kw">double              second_array[];
  class="kw">virtual class="type">void        SetBuffer(class="type">int &buffer, class="type">int &plot);
  class="kw">virtual class="type">void        SetAsSeries(class="type">bool set);
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CIndicator_2Data::SetBuffer(class="type">int &buffer, class="type">int &plot)
{
  indicator_plot = plot;
  SetIndexBuffer(buffer, first_array, INDICATOR_DATA);
  SetIndexBuffer(buffer+class="num">1, second_array, INDICATOR_DATA);
  SetDrawType();
  buffer+=class="num">2;
  plot++;
}
class=class="str">"cmt">//...
class=class="str">"cmt">//...
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Base for indicators with class="num">1 Data Buffer & class="num">1 Color Buffer          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CIndicator_1Data1Color : class="kw">public CIndicator_1Data
{
class="kw">public:
  class="type">class="kw">double              color_buffer[];
  class="kw">virtual class="type">void        SetBuffer(class="type">int &buffer, class="type">int &plot);
  class="kw">virtual class="type">void        SetAsSeries(class="type">bool set);
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CIndicator_1Data1Color::SetBuffer(class="type">int &buffer, class="type">int &plot)
{
  CIndicator_1Data::SetBuffer(buffer, plot);
  SetIndexBuffer(buffer, color_buffer, INDICATOR_COLOR_INDEX);
  buffer++; class=class="str">"cmt">//Add class="type">class="kw">color buffer
}
class=class="str">"cmt">//+------------------------------------------------------------------+

「把 ATR 通道和均线塞进面向对象指标框架」

面向对象封装指标时,最麻烦的是缓冲区方向和生命周期。SetAsSeries 里先调父类的 CIndicator_1Data::SetAsSeries(set),再对 color_buffer 跑 ArraySetAsSeries,保证颜色数组和主数据的时间序列方向一致,否则 MT5 画图会出现右端颠倒。 上面这套 ATR 带实现,ATR 周期默认 10、通道倍率 0.8,意味着通道宽度 = 收盘价 ± ATR(10)×0.8。外汇与贵金属波动剧烈,这类通道只描述波动边界,价格触碰上轨后回落是概率事件而非必然。 CATRBand 的 Init 只用 iATR(NULL, PERIOD_CURRENT, atr_period) 拿句柄,Update 里 CopyBuffer 取全量 ATR 再按 close±atr*倍率 填 first_array / second_array,继承自主类 CIndicatorPlotFilling 就自动渲染填充带。DeInit 调 IndicatorRelease 释放句柄,避免指标反复加载漏内存。 均线类 CMA 继承 CIndicatorPlotLine,Init 留了 (int period, ENUM_MA_METHOD mode) 形参,配合输入里的 ma_faster_period=14、ma_step=2、ma_method=MODE_SMA,就能在 EA 里批量生成 14/16/18… 一组 SMA 线,不用手写十几次 iMA。开 MT5 把 indicator_buffers 设 19、indicator_plots 设 13,挂上 OOPIndicators 头文件即可验证这套继承结构。

MQL5 / C++
class="type">void CIndicator_1Data1Color::SetAsSeries(class="type">bool set)
{
  CIndicator_1Data::SetAsSeries(set);
  ArraySetAsSeries(color_buffer, set);
}
class=class="str">"cmt">//...
class="macro">#class="kw">property indicator_buffers class="num">19
class="macro">#class="kw">property indicator_plots class="num">13
class="macro">#include <OOPIndicators/IndicatorClass.mqh>
class="macro">#include <OOPIndicators/ColorLerp.mqh>
class="kw">input class="type">int atr_period = class="num">10; class=class="str">"cmt">//ATR Period
class="kw">input class="type">class="kw">double atr_band_multiplier = class="num">0.8; class=class="str">"cmt">//ATR Multiplier for bands
class="kw">input class="type">bool show_bands = true; class=class="str">"cmt">//Show Bands
class="kw">input class="type">bool show_data = class="kw">false; class=class="str">"cmt">//Show Extra Data
class="kw">input class="type">int ma_faster_period = class="num">14; class=class="str">"cmt">//MA Faster Period
class="kw">input class="type">int ma_step = class="num">2; class=class="str">"cmt">//MA Step
class="kw">input ENUM_MA_METHOD ma_method = MODE_SMA; class=class="str">"cmt">//MA Method
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| ATR Bands class (inherit from Filling Plot)                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CATRBand : class="kw">public CIndicatorPlotFilling
{
class="kw">private:
  class="type">int            handle;
class="kw">public:
  class="kw">virtual class="type">void    Init();
  class="kw">virtual class="type">void    DeInit();
  class="kw">virtual class="type">void    Update(class="kw">const class="type">int limit, class="kw">const class="type">int rates_total, class="kw">const class="type">class="kw">double &close[]);
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CATRBand::Init(class="type">void)
{
  handle = iATR(NULL, PERIOD_CURRENT, atr_period);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CATRBand::Update(class="kw">const class="type">int limit,class="kw">const class="type">int rates_total,class="kw">const class="type">class="kw">double &close[])
{
  class="type">class="kw">double atr[];
  CopyBuffer(handle, class="num">0, class="num">0, rates_total-limit, atr);
  for (class="type">int i=limit; i<rates_total; i++)
  {
    first_array[i] = close[i]+atr[i-limit]*atr_band_multiplier;
    second_array[i] = close[i]-atr[i-limit]*atr_band_multiplier;
  }
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CATRBand::DeInit(class="type">void)
{
  IndicatorRelease(handle);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Moving Averages class (inherit from Line Plot)                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CMA : class="kw">public CIndicatorPlotLine
{
class="kw">private:
  class="type">int            handle;
class="kw">public:
  class="kw">virtual class="type">void    Init(class="type">int period, ENUM_MA_METHOD mode);
让小布替你跑这套
这些多缓冲区指标的骨架小布盯盘的 AIGC 已内置,打开对应品种页就能调出类似结构做快速验证,你只管改周期和合并逻辑。

常见问题

每个缓冲区若都声明为全局双精度数组,相加合并需逐个索引访问,循环外只能硬写,易漏易错;用对象数组可在循环里统一处理。
可以,小布内置的 AIGC 能输出含对象缓冲区的基础指标框架,你接入具体计算即可,省去手写 OnInit 配置。
除版权版本等共用属性外,这两个是必需项,分别声明缓冲区和作图板数量,其余样式可在循环里用 PlotIndexSetInteger 补。
访问需通过对象实例的公开数组成员,设置缓冲区和赋值都在对象作用域里,逻辑更聚合,但需注意对象生命周期与指标事件同步。