English Русский Deutsch 日本語
preview
重新定义MQL5与MetaTrader 5指标

重新定义MQL5与MetaTrader 5指标

MetaTrader 5指标 |
20 1
Omega J Msigwa
Omega J Msigwa

概述

MQL5已不再仅仅是用于编写简单交易机器人的简单编程语言。如今,该语言已发展成熟,几乎可以创建出高度复杂的交易程序,并以更稳健、便捷的方式进行部署和测试。

在交易过程中,我们所有人都曾使用过“内置指标”或“自定义指标”。MetaTrader 5提供了简便的界面来加载和附加指标到图表上,这使得交易者(主要是手动交易者)能够以一种便捷的方式使用指标来分析市场。然而,在算法交易中,重要的并非图表上显示的内容,而是指标的计算结果。

例如,在手动交易中,人们可能需要观察移动平均线指标以检测趋势(如果这是他们的目标)。但在算法交易中,我们可能会根据收盘价是否高于或低于在特定数量K线周期上计算出的移动平均值来判断是否存在上升趋势。

在算法交易中,若要使用某个特定指标,例如周期为20的简单移动平均线(SMA),您需要:

int sma_handle; //Declare an indicator handle
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   sma_handle = iMA(Symbol(), Period(), 20, 0, MODE_SMA, PRICE_CLOSE); //Initialize the indicator inside the OnInit function

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

   double sma_buffer[];
   CopyBuffer(sma_handle, 0, 0, 1, sma_buffer); //Copy the indicator buffers inside the Ontick function
  }

这种方法至少还算方便且运行良好,但它却显得较为简略,无法让我们灵活、自主地决定可以向指标传递哪些信息以进行即时计算。

基于此方法,文档中指出移动平均线(MA)指标只能应用于文档中提供的价格常量之一。

ID

描述

PRICE_CLOSE

收盘价

PRICE_OPEN

开盘价

PRICE_HIGH

该时间段内的最高价

PRICE_LOW

该时间段内的最低价

PRICE_MEDIAN

中间价(中位价)=(最高价+最低价)/ 2

PRICE_TYPICAL

典型价格 =(最高价+最低价+收盘价)/ 3

PRICE_WEIGHTED

平均价格 =(最高价 + 最低价 + 收盘价 + 收盘价)/ 4

更不用说,您还受到文档中所述的平滑方法/技术的限制。

ID

描述

MODE_SMA

简单移动平均线

MODE_EMA

指数移动平均线

MODE_SMMA

平滑移动平均线

MODE_LWMA

线性加权移动平均线

当您想要尝试一些非传统的方法 时,会发生什么呢?比如说,想要计算最高价与最低价之差的移动平均值,按照目前的方法,这是不可能实现的,因为我们在使用指标时受到了诸多限制和约束。

我明白,大多数指标都是基于其数学定义和推导方式而设定的,但是,如果我们能够向指标传递不同的输入和参数,以便观察市场中的新模式,那岂不是太棒了吗?

技术分析库(TA-Lib) Python库的启发,这种创新方法旨在让交易者和算法系统开发者能够更灵活地控制输入到指标中的信息,以便立即进行计算。

在本文中,我们将采用这种即插即用的方法来实现一些(最常用的)指标。

按类别分组

趋势指标

  • 简单移动平均线指标
  • 指数移动平均线指标
  • 布林带
  • 抛物线转向
  • 标准差

振荡器

  • 移动平均收敛发散(MACD)
  • 相对强弱指数(RSI)
  • 随机振荡器
  • 平均真实波幅(ATR)
  • 动量指标

比尔.威廉姆斯

  • 加速振荡器
  • 动量振荡器


简单移动平均线(SMA)指标

这是最常用的技术指标之一,它计算指定周期内价格序列的平均值。

由以下公式给出:

SMA公式

其中:

  • 价格在此(例如,某个 时间段内的收盘价)。
  •  SMA的周期。

我们可以轻松地通过向量化函数实现这个公式。

文件:ta-lib.mqh

vector CTrendIndicators::SMA(const vector &price, uint period, uint shift = 0)
  {
   uint size = (uint)price.Size();

   if(!CheckShiftPeriod(size, period, shift))
      return price;

//---

   vector ma(size);
   ma.Fill(NaN);

   for(uint i = shift + period - 1; i < size; i++) //Loop through all the prices considering the period and shift
     {
      double sum = 0;
      for(uint j = i - period + 1; j <= i; j++)
         sum += price[j]; //sum of the prices

      ma[i] = sum / period; //divided by period to find the mean of that period
     }

   return ma;
  }

SMA指标就是这样计算的,无需部署缓冲区,也无需在为特定交易品种加载指标时检查MQL5错误等,因为这些操作可能会很繁琐。

这种极简方法仅检查偏移量周期值是否正确,如果正确,函数将继续进行指标计算,并以向量形式返回计算值。我决定使用向量格式,以便对最终结果拥有更多的控制和灵活性。

由于指标类中的所有函数都是静态的,因此我们可以轻松地在MQL5程序中访问指标值,例如,在一个简单的脚本中。

文件:自定义指标测试脚本.mq5

#include <ta-lib.mqh>
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
     vector prices = {1,2,3,4,5,6,7,8,9,10};
     int period = 3;
     
     Print("SMA values: ", CTrendIndicators::SMA(prices, period)); 
  }

输出:

2025.01.21 09:57:35.727 Custom Indicators test script (EURUSD,H1)       SMA values: [nan,nan,2,3,4,5,6,7,8,9]

像这样,我们得以根据一组数值向量计算出了SMA指标。稍后,还将讨论如何绘制这些数值以形成可视化指标。


指数移动平均线(EMA)指标

EMA的计算方式与简SMA类似,也是对特定周期内的价格序列求平均值,但不同的是,它给予近期价格更大的权重,因此与SMA相比,它对价格变化的反应更为灵敏。

由以下公式给出:

 

其中:

  •  时间t时的EMA值。
  • 时间t时的价格Pt
  •  上一期的EMA值。 
  •  平滑因子(其中n为周期)。

我们可以在MQL5中按照如下方式实现:

文件:ta-lib.mqh

vector CTrendIndicators::EMA(const vector &price, uint period, uint shift = 0)
  {
   uint size = (uint)price.Size();

   if(!CheckShiftPeriod(size, period, shift))
      return price;

//---

   double alpha = 2.0 / (period + 1.0);    // Smoothing factor

   vector res(size);
   res.Fill(NaN);

// Initialize the EMA with the SMA of the first period

   vector sma = SMA(price, period, shift);
   res[period - 1 + shift] = sma[period - 1 + shift];

// Calculate EMA for the rest of the prices

   for(ulong i = period + shift; i < size; i++)
      res[i] = alpha * price[i] + (1 - alpha) * res[i - 1];

   return res;
  }

我们可以采用与计算SMA时类似的方法,来获取计算出的指标值。

文件:自定义指标测试脚本.mq5 

void OnStart()
  {
     vector prices = {1,2,3,4,5,6,7,8,9,10};
     int period = 3;
     
     Print("EMA values: ", CTrendIndicators::EMA(prices, period));
  }

输出:

2025.01.21 10:19:54.291 Custom Indicators test script (EURUSD,H1)       EMA values: [nan,nan,2,3,4,5,6,7,8,9]

我们已分别计算了SMA和EMA指标,这与内置的IMA指标中的计算方式不同,内置IMA指标提供了选择平滑方法的选项。

这种分开计算的方法需要编写额外的代码行来实现所有移动平均线指标的平滑方法。

诸如MODE_SMMA(平滑平均)(MODE_LWMA)线性加权平均等平滑方法的函数尚未在库中实现,因为我认为它们不像对应的(SMA和EMA平滑方法)那样常用。


布林带指标

这是一个由三条带组成的波动率指标。

  1. 中轨是特定时间段内收盘价的SMA。

  2. 上轨是中轨加上同一时间段内收盘价标准差的一定倍数。

    下轨是中轨减去同一时间段内收盘价标准差的一定倍数。

    其中:

    •  该倍数通常为2(布林带的标准设置值),
    •  用于计算SMA和标准差的周期。

    由于该指标需要返回三个一维(1D)值(在使用内置布林带指标时,这些值被称为缓冲区),因此我们让该函数返回一个包含3个向量的结构体。

    文件:ta-lib.mqh

    struct BB_res_struct
      {
       vector            upper_band;
       vector            lower_band;
       vector            middle_band;
      };
    BB_res_struct CTrendIndicators::BollingerBands(const vector &price, uint period, uint shift = 0, double k = 2.0)
      {
       uint size = (uint)price.Size();
    
       BB_res_struct res;
    
    //--- Check for valid parameters
       if(!CheckShiftPeriod(size, period, shift))
          return res;
    
    //--- Initialize vectors
    
       res.upper_band.Resize(size);
       res.lower_band.Resize(size);
    
       res.upper_band.Fill(NaN);
       res.lower_band.Fill(NaN);
    
    //--- Calculate the middle band (SMA)
    
       res.middle_band = SMA(price, period, shift);
    
    //--- Calculate the upper and lower bands
    
       for(uint i = shift + period - 1; i < size; i++)
         {
          double sum_squared_diff = 0;
          for(uint j = i - period + 1; j <= i; j++)
            {
             sum_squared_diff += MathPow(price[j] - res.middle_band[i], 2);
            }
    
          double std_dev = MathSqrt(sum_squared_diff / period);
    
          res.upper_band[i] = res.middle_band[i] + (k * std_dev);
          res.lower_band[i] = res.middle_band[i] - (k * std_dev);
         }
    
       return res;
      }

    以下是轻松实现布林带指标并获取其值的操作方法。

    文件:自定义指标测试脚本.mq5 

    void OnStart()
      {
         vector prices = {1,2,3,4,5,6,7,8,9,10};
         int period = 3;
         
         BB_res_struct bb;
         bb = CTrendIndicators::BollingerBands(prices,period,0,2);
         
         Print("BB upper: ",bb.upper_band);
         Print("BB middle: ",bb.middle_band);
         Print("BB lower: ",bb.lower_band);
      }

    输出:

    RL      0       11:39:21.000    Custom Indicators test script (EURUSD,H1)       BB upper: [nan,nan,3.632993161855452,4.632993161855453,5.632993161855453,6.632993161855453,7.632993161855453,8.632993161855453,9.632993161855453,10.63299316185545]
    RO      0       11:39:21.000    Custom Indicators test script (EURUSD,H1)       BB middle: [nan,nan,2,3,4,5,6,7,8,9]
    FF      0       11:39:21.000    Custom Indicators test script (EURUSD,H1)       BB lower: [nan,nan,0.3670068381445479,1.367006838144548,2.367006838144548,3.367006838144548,4.367006838144547,5.367006838144547,6.367006838144547,7.367006838144547]
    


    抛物线转向指标(即停损转向操作点指标)

    这是一种趋势跟踪指标,用于识别市场中潜在的反转点。它会根据趋势方向在价格上方或下方放置点状标记。

    由以下公式给出:

    上行趋势

    下行趋势

    其中:

    •  = 下一周期的抛物线转向指标(SAR)值
    •  = 加速度因子(初始值为默认值,例如0.02,每当达到新的高点/低点时增加0.02,最高增至0.2)
    •  = 极值点(在上升趋势中为最高点,在下降趋势中为最低点)

    该指标在MQL5中可按如下方式实现。

    文件:ta-lib.mqh

    vector CTrendIndicators::ParabolicSAR(const vector &high,
                                          const vector &low,
                                          const vector &close,
                                          double step = 0.02,
                                          double max = 0.2)
      {
       uint size = (uint)close.Size();
       vector psar(size);
       psar.Fill(NaN);
    
    // Initialize variables
    
       double AF = step;    // Acceleration Factor
       double EP = high[0];      // Extreme Point
       double SAR = low[0];      // Initial SAR
       bool isUptrend = true;    // Assume uptrend at the start
    
    // Calculate Parabolic SAR
    
       for(uint i = 0; i < size; i++)
         {
          // Update SAR
          if(isUptrend)
             SAR = SAR + AF * (EP - SAR);
          else
             SAR = SAR + AF * (SAR - EP);
    
          // Determine if trend changes
          if(isUptrend && SAR > low[i])
            {
             // Switch to downtrend
             isUptrend = false;
             SAR = EP;            // Reset SAR to the most recent EP
             EP = low[i];         // Reset EP
             AF = step;      // Reset AF
            }
    
          else
             if(!isUptrend && SAR < high[i])
               {
                // Switch to uptrend
                isUptrend = true;
                SAR = EP;            // Reset SAR to the most recent EP
                EP = high[i];        // Reset EP
                AF = step;      // Reset AF
               }
    
          // Update EP and AF
          if(isUptrend)
            {
             if(high[i] > EP)
               {
                EP = high[i];
                AF = MathMin(AF + step, max);
               }
            }
          else
            {
             if(low[i] < EP)
               {
                EP = low[i];
                AF = MathMin(AF + step, max);
               }
            }
    
          // Store the SAR value
          psar[i] = SAR;
         }
    
       return psar;
      }

    与前两个仅需接收单个价格向量作为输入的趋势跟踪指标不同,抛物线转向指标需考虑三个价格值(最高价、最低价和收盘价)。 

    文件:自定义指标测试脚本.mq5

    void OnStart()
      {  
         vector close = {1,2,3,4,5,6,7,8,9,10};
         vector high = {1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5,10.5};
         vector low = {0.5,1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5};
         
         Print("Parabolic SAR values: ", CTrendIndicators::ParabolicSAR(high,low,close,0.02,0.2)); 
      }

    输出:

    2025.01.21 11:11:03.525 Custom Indicators test script (EURUSD,H1)       Parabolic SAR values: [1.5,0.5,0.54,0.6584000000000001,0.8888960000000001,1.25778432,1.782005888,2.46816518144,3.3126220560384,4.302602527072256]
    


    标准差指标

    该指标用于衡量价格与其平均值的变动幅度或离散程度。常用作评估市场的波动性。

    标准差值较高表明市场波动性较大,而标准差值较低则表明市场波动性较小。

    由以下公式给出:

    其中:

    •  = 标准差
    • n = 周期
    •  = 价格点
    •  = 数据点的均值 

    我们可以在MQL5中按如下方式实现该指标:

    文件:ta-lib.mqh

    vector CTrendIndicators::StandardDeviation(const vector &price, uint period, uint shift = 0)
      {
       uint size = (uint)price.Size();
    
    // Check if the period and shift are valid
       if(!CheckShiftPeriod(size, period, shift))
          return price;
    
    // Initialize standard deviation vector
       vector std_dev(size);
       std_dev.Fill(NaN);
    
    // Loop through the price data
       for(uint i = shift + period - 1; i < size; i++)
         {
          double sum = 0.0;
          double sum_sq_diff = 0.0;
    
          // Calculate mean
          for(uint j = i - period + 1; j <= i; j++)
             sum += price[j];
          double mean = sum / period;
    
          // Calculate squared differences
          for(uint j = i - period + 1; j <= i; j++)
             sum_sq_diff += MathPow(price[j] - mean, 2);
    
          // Calculate standard deviation
          std_dev[i] = MathSqrt(sum_sq_diff / period);
         }
    
       return std_dev;
      }
    

    这是另一个简单指标,可按如下方式调用。

    文件:自定义指标测试脚本.mq5

    void OnStart()
      {
         vector prices = {1,2,3,4,5,6,7,8,9,10};
         int period = 3;
        
         Print("Stddev values: ", CTrendIndicators::StandardDeviation(prices, period));
      }

    输出:

    2025.01.21 11:55:11.657 Custom Indicators test script (EURUSD,H1)       Stddev values: [nan,nan,0.816496580927726,0.816496580927726,0.816496580927726,0.816496580927726,0.816496580927726,0.816496580927726,0.816496580927726,0.816496580927726]
    


    移动平均收敛发散(MACD)

    该振荡器展示了证券价格两条移动平均线之间的关系。其被广泛用作技术分析工具。

    由以下公式给出:

    MACD主线

    其中:

    •  = 周期较短的EMA(例如,12周期)
    •  = 周期较长的EMA(例如,26周期)


    信号线

    这是MACD线的平滑移动平均线,通常周期值设为9。

    MACD柱状图

    这是MACD线与信号线之间的差值。

    与我们返回多个计算得出的布林带数值的方式类似,我们定义了一个结构体,用于返回MACD柱状图、主线(MACD线)和信号线的数值。

    文件:ta-lib.mqh 

    struct MACD_res_struct
      {
       vector            main;     // The MACD Line
       vector            signal;   // The Signal Line
       vector            histogram;    // The MACD Histogram
      };
    MACD_res_struct COscillatorIndicators::MACD(const vector &price, uint fast_ema = 12, uint slow_ema = 26, uint macd_sma = 9, uint shift = 0)
      {
       uint size = (uint)price.Size();
    
       MACD_res_struct res;
       if(!CheckShiftPeriod(size, slow_ema, shift))
          return res;
    
    //--- Calculate EMA(short), EMA(long), and MACD Line
    
       vector fast_ema_vector = CTrendIndicators::EMA(price, fast_ema, shift);
       vector slow_ema_vector = CTrendIndicators::EMA(price, slow_ema, shift);
    
       res.main.Resize(size);
       res.main.Fill(NaN);
    
       for(uint i = 0; i < size; i++)
          res.main[i] = fast_ema_vector[i] - slow_ema_vector[i];
    
    //--- Calculate Signal Line (SMA of MACD Line)
    
       res.signal = CTrendIndicators::SMA(price, macd_sma, shift);
    
    //--- Calculate MACD Histogram
    
       res.histogram.Resize(size);
       res.histogram.Fill(NaN);
    
       for(uint i = 0; i < size; i++)
          res.histogram[i] = res.main[i] - res.signal[i];
    
       return res;
      }

    我们可以在测试脚本中轻松获取计算得出的MACD振荡指标值,具体方法如下:

    文件:自定义指标测试脚本.mq5 

    void OnStart()
      {
         vector prices = {1,2,3,4,5,6,7,8,9,10};
    
         MACD_res_struct macd;
         
         macd = COscillatorIndicators::MACD(prices,2,3,4);
         
         Print("MACD main: ", macd.main); 
         Print("MACD signal: ", macd.signal); 
      }

    输出:

    RD      0       12:28:51.368    Custom Indicators test script (EURUSD,H1)       MACD main: [nan,nan,0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5]
    HO      0       12:28:51.368    Custom Indicators test script (EURUSD,H1)       MACD signal: [nan,nan,nan,2.5,3.5,4.5,5.5,6.5,7.5,8.5]
    


    相对强弱指标(RSI)

    这是一种动量振荡指标,用于衡量价格变动的速度和幅度。其数值在0到100之间振荡,交易者用它来识别市场中的超买和超卖状况。

    RSI指标的计算包含以下步骤:

    01:计算指定时间段内的平均收益和平均亏损

    02:计算相对强弱值(RS)

    03: 计算RSI

    MQL5实现

    vector COscillatorIndicators::RSI(const vector &price, uint period, uint shift = 0)
      {
       uint size = (uint)price.Size();
    
    //--- Check for valid parameters
       if(!CheckShiftPeriod(size, period, shift))
          return price;
    
    //--- Initialize vectors
       vector rsi(size), gains(size), losses(size);
       rsi.Fill(NaN);
       gains.Fill(0.0);
       losses.Fill(0.0);
    
    //--- Calculate gains and losses
       for(uint i = shift + 1; i < size; i++)
         {
          double change = price[i] - price[i - 1];
          gains[i] = (change > 0) ? change : 0;
          losses[i] = (change < 0) ? -change : 0;
         }
    
    //--- Initialize first average gain and loss (simple average for the first period)
       double avg_gain = 0, avg_loss = 0;
       for(uint i = shift + 1; i < shift + 1 + period; i++)
         {
          avg_gain += gains[i];
          avg_loss += losses[i];
         }
       avg_gain /= period;
       avg_loss /= period;
    
    //--- Compute RSI for the rest of the periods
       for(uint i = shift + period; i < size; i++)
         {
          // Apply smoothing for average gain and loss
          avg_gain = ((avg_gain * (period - 1)) + gains[i]) / period;
          avg_loss = ((avg_loss * (period - 1)) + losses[i]) / period;
    
          // Calculate RSI
          double rs = (avg_loss == 0) ? 0 : avg_gain / avg_loss;
          rsi[i] = (avg_loss == 0) ? 100 : (100 - (100 / (1 + rs)));
         }
    
       return rsi;
      }

    以下是获取RSI数值的方法:

    void OnStart()
      {
         vector prices = {1,2,3,4,5,6,7,8,9,10};
         int period = 3;
         
         Print("RSI values: ", COscillatorIndicators::RSI(prices,period)); 
      }

    输出:

    2025.01.21 12:51:29.640 Custom Indicators test script (EURUSD,H1)       RSI values: [nan,nan,nan,100,100,100,100,100,100,100]
    


    随机振荡器指标

    这是一种动量指标,用于将证券的收盘价与其在特定时间段内的价格区间进行比较。 

    该指标的计算公式包含以下部分:

    %K线

    其中:

    •  当期收盘价
    •  回溯期内的最低价
    •  回溯期内的最高价

    %D线

    这是%K线的移动平均值,通常采用3周期SMA来计算。

    以下是在MQL5中实现该指标的方法:

    Stochastic_struct COscillatorIndicators::StochasticOscillator(const vector &high, const vector &low, const vector &close, uint k_period = 5, uint d_period = 3, uint period = 3, uint shift = 0)
      {
       uint size = (uint)close.Size();
    
       Stochastic_struct res;
    
    // Check for valid parameters
       if(!CheckShiftPeriod(size, period, shift))
          return res;
    
    // Initialize vectors for %K and %D
       vector K(size), D(size);
       K.Fill(NaN);
       D.Fill(NaN);
    
    // Calculate %K
       for(uint i = shift + period - 1; i < size; i++)
         {
          double H_max = -DBL_MAX, L_min = DBL_MAX;
    
          // Find the highest high and the lowest low over the lookback period
          for(uint j = i - period + 1; j <= i; j++)
            {
             H_max = MathMax(H_max, high[j]);
             L_min = MathMin(L_min, low[j]);
            }
    
          // Calculate %K
          double K_value = (H_max - L_min != 0) ? ((close[i] - L_min) / (H_max - L_min)) * 100 : 0;
          K[i] = K_value;
         }
    
    // Smooth %K with a simple moving average (k_period)
       vector smoothedK(size);
       smoothedK.Fill(NaN);
    
       for(uint i = shift + k_period - 1; i < size; i++)
         {
          double sum = 0;
          for(uint j = i - k_period + 1; j <= i; j++)
            {
             sum += K[j];
            }
          smoothedK[i] = sum / k_period;
         }
    
    // Calculate %D (3-period moving average of %K)
    
       D = CTrendIndicators::SMA(smoothedK, period, shift);
    
       res.main = K;
       res.signal = D;
    
       return res;
      }
    

    我们以结构体的形式返回信号线和主线。

    void OnStart()
      {  
         vector close = {1,2,3,4,5,6,7,8,9,10};
         vector high = {1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5,10.5};
         vector low = {0.5,1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5};
         
         Stochastic_struct stoch;
         stoch = COscillatorIndicators::StochasticOscillator(high,low,close,5,3,3);
         
         Print("Stoch main: ", stoch.main); 
         Print("Stoch signal: ", stoch.signal); 
      }


    平均真实波幅(ATR)指标

    这是一个有助于了解市场波动性的实用指标。它用于衡量特定时间段内最高价与最低价之间的平均波动范围。

    由以下公式给出:

    01:计算真实范围(TR)

    其中:

    • H是当前周期的最高价。
    •  当前周期的最低价。
    •  上一周期的收盘价。

    02:计算ATR

    ATR是特定时间段内真实波幅的移动平均值。

     SMA是简单移动均线

    我们可以在MQL5中按如下方式实现该指标:

    vector COscillatorIndicators::ATR(const vector &high, const vector &low, const vector &close, uint period = 14, uint shift = 0)
      {
       uint size = (uint)close.Size();
    
    // Check for valid parameters
       if(!CheckShiftPeriod(size, period, shift))
          return close;
    
    // Initialize the True Range (TR) and ATR vectors
       vector TR(size);
       TR.Fill(NaN);
    
    // Calculate the True Range for each period
       for(uint i = shift + 1; i < size; i++)
         {
          double H = high[i];
          double L = low[i];
          double C_prev = close[i - 1];
    
          // Calculate the three possible True Range values
          double TR1 = H - L;
          double TR2 = MathAbs(H - C_prev);
          double TR3 = MathAbs(L - C_prev);
    
          // True Range is the maximum of the three
          TR[i] = MathMax(TR1, MathMax(TR2, TR3));
         }
    
    //--- Smooth the True Range using a simple moving average (SMA) over the specified period
    
       return CTrendIndicators::SMA(TR, period, shift);
      }

    以下是获取该指标值的方法:

    void OnStart()
      {  
         vector close = {1,2,3,4,5,6,7,8,9,10};
         vector high = {1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5,10.5};
         vector low = {0.5,1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5};
         
         Print("ATR values: ", COscillatorIndicators::ATR(high,low,close,3)); 
      }

    输出:

    2025.01.21 13:57:59.943 Custom Indicators test script (EURUSD,H1)       ATR values: [nan,nan,nan,1.5,1.5,1.5,1.5,1.5,1.5,1.5]
    


    动量指标

    这是一个简单的指标,用于衡量资产价格在特定时间段内的变动速度。它有助于识别趋势及潜在的趋势反转。

    通过一个简单的公式给出:

    其中:

    • Momentum(t) = 时间t时的动量值
    • Price(t) = 当前价格
    • Price(t - n) = n个周期前的价格
    • n = 计算动量所用的周期数

    计算动量时,部分变量采用比率形式。

    我们将采用这种变量来实现动量指标。

    MQL5实现:

    vector COscillatorIndicators::MomentumIndicator(const vector &price, uint period, uint shift = 0)
      {
       uint size = (uint)price.Size();
    
    // Check for valid input
       if(!CheckShiftPeriod(size, period, shift))
          return price;
    
    // Initialize the momentum vector
       vector momentum(size);
       momentum.Fill(NaN);
    
    // Calculate Momentum
       for(uint i = shift + period; i < size; i++)
         {
          //momentum[i] = price[i] - price[i - period]; // Momentum difference formula
    
          // using the ratio formula:
          momentum[i] = (price[i] / price[i - period]) * 100;
         }
    
       return momentum;
      }


    动量振荡器指标

    这是一个动量指标,用于计算中位价的两个SMA之间的差值。

    由以下公式给出:

    其中:

    •  = 周期较短的SMA(通常为5期)
    •  = 周期较长的SMA(通常为34期)

    以下是MQL5实现代码:

    vector CBillWilliamsIndicators::AwesomeOscillator(const vector &high, const vector &low, uint fast_period = 5, uint slow_period = 34, uint shift = 0)
      {
       uint size = (uint)high.Size();
       if(size != low.Size())
          return vector::Zeros(0); // Ensure high and low vectors are of the same size
    
       if(!CheckShiftPeriod(size, slow_period, shift))
          return vector::Zeros(0); // Validate shift and slow period
    
    // Initialize vectors
       vector ao(size), median_price(size);
       ao.Fill(NaN);
       median_price.Fill(NaN);
    
    // Calculate Median Price
       for(uint i = 0; i < size; i++)
          median_price[i] = (high[i] + low[i]) / 2;
    
    // Calculate Fast and Slow SMAs of the Median Price
       vector sma_fast = CTrendIndicators::SMA(median_price, fast_period, shift);
       vector sma_slow = CTrendIndicators::SMA(median_price, slow_period, shift);
    
    // Calculate AO
       for(uint i = 0; i < size; i++)
          ao[i] = sma_fast[i] - sma_slow[i];
    
       return ao;
      }

    由于我们有最高价和最低价的简单向量数据,因此可以将快速周期设为3,慢速周期设为5。

    void OnStart()
      {  
         vector close = {1,2,3,4,5,6,7,8,9,10};
         vector high = {1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5,10.5};
         vector low = {0.5,1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5};
         
         Print("AO values: ", CBillWilliamsIndicators::AwesomeOscillator(high,low,3,5)); 
      }

    输出:

    2025.01.21 14:25:50.590 Custom Indicators test script (EURUSD,H1)       AO values: [nan,nan,nan,nan,1,1,1,1,1,1]
    


    加速振荡指标

    加速振荡(AC)是一种技术分析工具,用于追踪价格动量变化的速度。该指标可显示市场驱动力是加速还是减速,帮助交易者预判潜在的行情反转。

    其计算方法为:用动量振荡指标减去该指标的5周期的SMA。

    由以下公式给出:

    我们可以在MQL5中按如下方式实现该指标:

    vector CBillWilliamsIndicators::AcceleratorOscillator(const vector &high,
          const vector &low,
          uint ao_period_fast = 5, // Fast period for AO
          uint ao_period_slow = 34, // Slow period for AO
          uint ac_period = 5 // Period for AC SMA
                                                         )
      {
       uint size = (uint)high.Size();
       if(size != low.Size())
          return vector::Zeros(0); // Ensure high and low vectors are of the same size
    
    
    // Validate shift and period
       if(!CheckShiftPeriod(size, ao_period_slow, 0))
          return vector::Zeros(0);
    
    // Calculate AO (Awesome Oscillator)
    
       vector ao = AwesomeOscillator(high, low, ao_period_fast, ao_period_slow);
    
    // Calculate AC (Accelerator Oscillator)
    
       vector ac(size);
       ac.Fill(NaN);
    
       vector ao_sma_ac = CTrendIndicators::SMA(ao, ac_period);
    
       for(uint i = 0; i < size; i++)
          ac[i] = ao[i] - ao_sma_ac[i];
    
       return ac;
      }

    由于加速振荡指标与动量振荡指标极为相似,因此,我们可以使用相似的参数来调用其函数。

    void OnStart()
      {  
         vector close = {1,2,3,4,5,6,7,8,9,10};
         vector high = {1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5,10.5};
         vector low = {0.5,1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5};
         
    //--- Bill williams indicator
    
         Print("AO values: ", CBillWilliamsIndicators::AwesomeOscillator(high,low,3,5)); 
         Print("AC values: ", CBillWilliamsIndicators::AcceleratorOscillator(high,low,3,5)); 
      }

    输出:

    MQ      0       14:40:36.296    Custom Indicators test script (EURUSD,H1)       AO values: [nan,nan,nan,nan,1,1,1,1,1,1]
    EL      0       14:40:36.296    Custom Indicators test script (EURUSD,H1)       AC values: [nan,nan,nan,nan,nan,nan,nan,nan,0,0]
    


    指标数据收集简化方案

    既然我们已经了解了库中部分指标的实现方式,那么现在可以尝试从市场中收集所需信息,并基于这些信息构建自定义指标。例如,我将计算开盘价、最高价、最低价和收盘价的移动平均线。

    我们首先需要获取OHLC(开盘价、最高价、最低价、收盘价)数据。

    int size = 1000;
    
    vector open, high, low, close;
    
    open.CopyRates(Symbol(), Period(), COPY_RATES_OPEN, 1, size);
    high.CopyRates(Symbol(), Period(), COPY_RATES_HIGH, 1, size);
    low.CopyRates(Symbol(), Period(), COPY_RATES_LOW, 1, size);
    close.CopyRates(Symbol(), Period(), COPY_RATES_CLOSE, 1, size);

    首先,我们计算应用于开盘价的20周期的SMA。

    vector sma_open = CTrendIndicators::SMA(open, 20); 

    我们可以轻松地针对不同周期和不同价格收集各种SMA指标。

    vector sma_open = CTrendIndicators::SMA(open, 20); 
    vector sma_high = CTrendIndicators::SMA(high, 50);
    vector sma_low = CTrendIndicators::SMA(low, 100);
    vector sma_close = CTrendIndicators::SMA(close, 20);

    让我们将这些指标数据整合到一个矩阵中,以便统一观察分析。

    matrix Indicators(size, 4);
        
    Indicators.Col(CTrendIndicators::SMA(open, 20), 0);
    Indicators.Col(CTrendIndicators::SMA(high, 50), 1);
    Indicators.Col(CTrendIndicators::SMA(low, 100), 2);
    Indicators.Col(CTrendIndicators::SMA(close, 20), 3);
        
    Print("Indicators matrix\n",Indicators);

    输出:

    NK      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.0485715,1.0484514,nan,1.048488]
    LL      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.0484425,1.0485544,nan,1.0484265]
    RP      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.048381,1.0486754,nan,1.048299]
    QG      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.0483055,1.0488004,nan,1.048152]
    KK      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.0481585,1.0489198,nan,1.048296]
    HL      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.048303,1.049033,nan,1.0485255]
    DS      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.048533,1.0491756,nan,1.0487015]
    OK      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.048658,1.0493158,1.0475226,1.0488295]
    KD      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.0487895,1.0494628,1.047473,1.0488985]
    JR      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.0488575,1.0494916,1.0474256,1.0489465]
    FR      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.0489055,1.049475,1.0473723,1.0490045]
    LL      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.048964,1.0494814,1.0473137,1.049052]
    HK      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.0490075,1.0494728,1.0472494,1.0491065]
    CF      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.049062,1.0494618,1.0471845,1.049044]
    RE      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.0489995,1.0494452,1.047114,1.048892]
    FQ      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.048848,1.0494558,1.047044,1.0487065]
    CL      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.0486625,1.0495002,1.0469762,1.0486305]
    DK      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.048586,1.0496014,1.0469234,1.048582]
    EE      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.0485375,1.0496714,1.0468866,1.048646]
    RE      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.0486015,1.0497982,1.046857200000001,1.04877]
    IP      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.0487245,1.0498646,1.0468378,1.0490025]
    DO      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.0489565,1.0499466,1.046833500000001,1.0492415]
    GN      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.0491975,1.050113,1.046846700000001,1.0497525]
    CK      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.0497085,1.0502838,1.046881000000001,1.0502025]
    IJ      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.050159,1.0503826,1.046898100000001,1.0506915]
    PF      0       18:30:25.100    Custom Indicators test script (EURUSD,H1)        [1.0506475,1.0504916,1.046935200000001,1.051158…]

    假设我想基于现有变量创建一个动量指标,来衡量这些变量的动量情况。

    Indicators.Col(COscillatorIndicators::MomentumIndicator(open, 20), 4);
    Indicators.Col(COscillatorIndicators::MomentumIndicator(high, 50), 5);
    Indicators.Col(COscillatorIndicators::MomentumIndicator(low, 100), 6);
    Indicators.Col(COscillatorIndicators::MomentumIndicator(close, 20), 7);

    输出:

    IN      0       18:39:34.172    Custom Indicators test script (EURUSD,H1)        [1.0441885,nan,nan,1.0438975,99.43679181343492,nan,nan,99.44502817843157]
    LN      0       18:39:34.172    Custom Indicators test script (EURUSD,H1)        [1.0438975,1.0495116,nan,1.043593,99.44407828753189,nan,nan,99.41864350150351]
    QS      0       18:39:34.172    Custom Indicators test script (EURUSD,H1)        [1.0435925,1.0492638,nan,1.0433225,99.4176888931316,98.82473464044848,nan,99.4833645288208]
    HG      0       18:39:34.172    Custom Indicators test script (EURUSD,H1)        [1.0436275,1.049148199999999,nan,1.043363,100.0668474731655,99.45161810609009,nan,100.0773424743863]
    EH      0       18:39:34.172    Custom Indicators test script (EURUSD,H1)        [1.0436685,1.0490618,nan,1.043385,100.0782973197491,99.59030385797199,nan,100.0420047732697]
    KO      0       18:39:34.172    Custom Indicators test script (EURUSD,H1)        [1.0436925,1.0489454,nan,1.0434175,100.0458251389074,99.4479854313681,nan,100.0620536907626]
    JQ      0       18:39:34.172    Custom Indicators test script (EURUSD,H1)        [1.0437225,1.048834,nan,1.043484,100.0572803299347,99.47183265534476,nan,100.1270939444036]
    JE      0       18:39:34.172    Custom Indicators test script (EURUSD,H1)        [1.043788,1.048718,nan,1.0435725,100.1251839535195,99.45010144680205,nan,100.1690625149243]
    LE      0       18:39:34.172    Custom Indicators test script (EURUSD,H1)        [1.043877,1.048595199999999,nan,1.0435315,100.1700192943244,99.41799844546816,nan,99.92180198737388]
    MI      0       18:39:34.172    Custom Indicators test script (EURUSD,H1)        [1.0438365,1.048457,nan,1.0437085,99.92275488503829,99.34503611305946,nan,100.3389538390831]
    HN      0       18:39:34.172    Custom Indicators test script (EURUSD,H1)        [1.044013,1.0483164,nan,1.044095,100.3379931060896,99.33386396801032,nan,100.7431263218612]
    KS      0       18:39:34.172    Custom Indicators test script (EURUSD,H1)        [1.0443985,1.048186,nan,1.044364,100.7411964891704,99.38227742565063,nan,100.5158345877638]
    EE      0       18:39:34.172    Custom Indicators test script (EURUSD,H1)        [1.0446665,1.0480502,nan,1.044532,100.5139219145509,99.35649569733499,nan,100.3225558712848…]


    绘制自定义函数生成的指标图表

    让我们尝试将通过该库计算得到的指标结果以可视化的形式呈现。

    尽管该方法主要面向数据收集而非可视化展示,但我们仍可以在自定义的MetaTrader 5指标中实现这些计算结果的可视化。

    20周期SMA指标的可视化效果。

    #property indicator_chart_window
    #property indicator_buffers 1
    #property indicator_plots 1
    
    #property indicator_color1 clrDodgerBlue
    #property indicator_type1 DRAW_LINE
    #property indicator_style1 STYLE_SOLID
    
    #include <ta-lib.mqh>
    
    input int period_ = 20; //SMA period
    
    double buffer[];
    //+------------------------------------------------------------------+
    //| Custom indicator initialization function                         |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- indicator buffers mapping
       
       SetIndexBuffer(0, buffer, INDICATOR_DATA);
       PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0);   
       
       PlotIndexSetString(0,PLOT_LABEL,"SMA("+string(period_)+")");
       PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,period_+1);
       
    //---
       return(INIT_SUCCEEDED);
      }
    //+------------------------------------------------------------------+
    //| Custom indicator iteration function                              |
    //+------------------------------------------------------------------+
    int OnCalculate(const int rates_total,
                    const int prev_calculated,
                    const datetime &time[],
                    const double &open[],
                    const double &high[],
                    const double &low[],
                    const double &close[],
                    const long &tick_volume[],
                    const long &volume[],
                    const int &spread[])
      {
    //---
    
       if (prev_calculated==0) //Initial indicator calculation, For Plotting the entire past 
         {        
           vector buffer_vector = CTrendIndicators::SMA(close, period_);
           
           VectorToArray(buffer_vector, buffer); //We assign the calculations into an array format
         }
        else //For plotting the current value
         {
           for (int i=prev_calculated-1; i<rates_total; i++) //Loop from the prev_calculated bars -1 to the total bars present 
            {
               double temp_close[]; //For storing temporary close values
               int size = period_*2; //We are going to copy an equivalent of period x 2 to leave a room for NaN values
               
               if (ArrayCopy(temp_close, close, 0, i - size, size) < 0)
                { 
                   printf("Failed to copy closing price values to a temporary array, err = %d",GetLastError());
                   continue;
                }
               
               vector indicator_values = CTrendIndicators::SMA(temp_close, period_); 
               ulong last_index = indicator_values.Size() - 1; //The last value in the vector is the recent calculated indicator value
               
               buffer[i] = indicator_values[last_index]; //Assing the last indicator value to the last value in the buffer
            }
         }
       
    //--- return value of prev_calculated for next call
       return(rates_total);
      }
    //+------------------------------------------------------------------+
    //|      A crude way to convert a vector into Array                  |
    //+------------------------------------------------------------------+
    template <typename T>
    void VectorToArray(const vector<T> &v, T &arr[])
     {
       ArrayResize(arr, (uint)v.Size());
       
       for (uint i=0; i<(uint)v.Size(); i++)
           arr[i] = v[i];
     }
    

    输出:

    SMA 20


    总结

    相较于其他特定任务,这种自定义收集指标值的方法在数据收集方面更为便捷实用,尤其适合从事数据分析的专业人士,以及那些寻求创新方式收集指标数据以供分析的爱好者。

    我常常需要一种快速的方法来获取多个指标值,而无需每次都初始化指标并执行那些繁琐的操作——这些操作往往还会引发MetaTrader 5报错。随着MQL5因新增支持复杂人工智能(AI)和机器学习(ML)模型功能而带来的数据需求激增,我们需要探索更多高效的方法来提取和可视化指标数据,以满足分析和机器学习模型训练的需求。

    告一段落。


    附件表

    文件名 说明/用法
    Include\ta-lib.mqh 本文所讨论的包含全部指标代码的MQL5库。
    Indicators\Trend Indicators Redefined.mq5  一个为演示目的而实现的包含SMA指标的示例。 
     Scripts\Custom Indicators test script.mq5 一个专为测试和调试指标代码而编写的简易脚本。 


    来源与参考文献

      本文由MetaQuotes Ltd译自英文
      原文地址: https://www.mql5.com/en/articles/16931

      附加的文件 |
      Attachments.zip (7.42 KB)
      最近评论 | 前往讨论 (1)
      Zhuo Kai Chen
      Zhuo Kai Chen | 25 1月 2025 在 03:14
      非常有趣的文章!我喜欢你介绍的创新方法。
      交易策略 交易策略
      各种交易策略的分类都是任意的,下面这种分类强调从交易的基本概念上分类。
      价格行为分析工具包开发(第九部分):外部数据流 价格行为分析工具包开发(第九部分):外部数据流
      本文将利用专为高级分析而设计的外部库,探索一个全新的分析维度。这些库(如pandas)提供了强大的工具,用于处理和解读复杂数据,使交易者能够更深入地洞察市场动态。通过整合此类技术,我们能够整合原始数据与可执行策略之间的差距。加入我们,共同为这一创新方法奠定基础,并释放技术与交易专业知识相结合的潜力。
      新手在交易中的10个基本错误 新手在交易中的10个基本错误
      新手在交易中会犯的10个基本错误: 在市场刚开始时交易, 获利时不适当地仓促, 在损失的时候追加投资, 从最好的仓位开始平仓, 翻本心理, 最优越的仓位, 用永远买进的规则进行交易, 在第一天就平掉获利的仓位,当发出建一个相反的仓位警示时平仓, 犹豫。
      开发回放系统(第 75 部分):新 Chart Trade(二) 开发回放系统(第 75 部分):新 Chart Trade(二)
      在本文中,我们将讨论 C_ChartFloatingRAD 类。这就是 Chart Trade 发挥作用的原因。然而,解释并未就此结束,我们将在下一篇文章中完成它,因为这篇文章的内容相当广泛,需要深入理解。此处提供的内容仅用于教育目的。在任何情况下,除了学习和掌握所提出的概念外,都不应出于任何目的使用此应用程序。