English Русский Deutsch 日本語 Português
preview
为EA交易提供指标的现成模板(第2部分):交易量和比尔威廉姆斯指标

为EA交易提供指标的现成模板(第2部分):交易量和比尔威廉姆斯指标

MetaTrader 5示例 | 14 三月 2024, 11:00
482 0
Artyom Trishkin
Artyom Trishkin

目录


概述

本文继续讨论在EA中使用指标的现成模板的主题。在这里,我们将研究与EA的关联以及使用交易量和比尔威廉姆斯的指标。我们将在本系列的第一篇文章中创建的仪表板上显示从指标接收的数据。面板也得到了改进。在文章的最后,我们将简要介绍它的变化和改进。

对于所探讨的每个指标,本文将提供现成的模板,供自定义程序使用:

  • 输入变量和全局变量,
  • 初始化变量并创建指标句柄,
  • 去初始化,
  • 从指标接收EA中的数据,
  • 在仪表板上显示获取的数据的示例。
与本系列的前一篇和后续文章一样,这篇文章仅供参考,但是也有实际好处,因为它允许我们简单地将文章中的代码用作复制粘贴。


交易量指标

交易量指标是计算交易量的,对于外汇市场,“交易量”是指在该时间间隔内出现的分时数量(价格变化)。就股票而言,证券交易量是指已执行交易的交易量(以合约或货币形式)。


集散指标

集散指标(A/D,Accumulation Distribution)由价格和数量的变化决定。交易量在价格变化时起着加权系数的作用——系数(交易量)越高,价格变化(在这段时间内)对指标价值的贡献就越大。

事实上,该指标是更常用的(平衡交易量)指标的一个版本。它们都用于通过测算各自的销售量来确认价格变化。

当集散指标增长时,它意味着特定证券的累积(购买),因为销售额的压倒性份额与价格的上升趋势有关。当指标下降时,意味着证券的分散(出售),因为大多数出售都发生在价格下跌期间。

集散指标与证券价格之间的背离表明价格即将发生变化。通常,在出现这种背离的情况下,价格趋势会朝着指标移动的方向移动。因此,如果指标在增长,而证券价格在下跌,那么价格应该会出现好转。



参数

iAD()函数用于创建指标句柄:

返回集散指标的句柄。只有一个缓冲区。

int  iAD(
   string               symbol,             // symbol name
   ENUM_TIMEFRAMES      period,             // period
   ENUM_APPLIED_VOLUME  applied_volume      // type of volume used for calculations
   );

symbol

[in] 其数据将用于计算指标的金融工具的交易品种名称。NULL表示当前交易品种。

period

[in] 周期值可以是ENUM_TIMEFRAMES枚举值之一,0表示当前时间框架。

applied_volume

[in]   使用的交易量,ENUM_APPLIED_VOLUME 中的任一种。

返回指定技术指标的句柄,如果失败,则返回 INVALID_HANDLE。 要从未使用的指标中释放计算机内存,请使用指标句柄传递到 IndicatorRelease()

在EA中声明输入参数全局变量以创建指标:

//+------------------------------------------------------------------+
//|                                                 TestVolumeAD.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Undefined state
   LINE_STATE_UP,          // Upward
   LINE_STATE_DOWN,        // Downward
   LINE_STATE_TURN_UP,     // Upward reversal
   LINE_STATE_TURN_DOWN,   // Downward reversal
   LINE_STATE_STOP_UP,     // Upward stop
   LINE_STATE_STOP_DOWN,   // Downward stop
   LINE_STATE_ABOVE,       // Above value
   LINE_STATE_UNDER,       // Below value
   LINE_STATE_CROSS_UP,    // Crossing value upwards
   LINE_STATE_CROSS_DOWN,  // Crossing value downwards
   LINE_STATE_TOUCH_BELOW, // Touching value from below 
   LINE_STATE_TOUCH_ABOVE, // Touch value from above
   LINE_STATE_EQUALS,      // Equal to value
  };
//--- input parameters
input ENUM_APPLIED_VOLUME  InpVolume   =  VOLUME_TICK;   /* Applied Volume */
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description

创建 ENUM_LINE_STATE 枚举是为了简化获取指标线的状态,即其相对于另一个指标或任何级别的线的形状和位置。
在上一篇文章的ATR参数部分中可以查找有关枚举的更多信息。

在EA中使用仪表板时,声明全局变量包括面板类文件

//+------------------------------------------------------------------+
//|                                                 TestVolumeAD.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- includes
#include <Dashboard\Dashboard.mqh>
//--- enums
enum ENUM_LINE_STATE
  {
   LINE_STATE_NONE,        // Undefined state
   LINE_STATE_UP,          // Upward
   LINE_STATE_DOWN,        // Downward
   LINE_STATE_TURN_UP,     // Upward reversal
   LINE_STATE_TURN_DOWN,   // Downward reversal
   LINE_STATE_STOP_UP,     // Upward stop
   LINE_STATE_STOP_DOWN,   // Downward stop
   LINE_STATE_ABOVE,       // Above value
   LINE_STATE_UNDER,       // Below value
   LINE_STATE_CROSS_UP,    // Crossing value upwards
   LINE_STATE_CROSS_DOWN,  // Crossing value downwards
   LINE_STATE_TOUCH_BELOW, // Touching value from below 
   LINE_STATE_TOUCH_ABOVE, // Touch value from above
   LINE_STATE_EQUALS,      // Equal to value
  };
//--- input parameters
input ENUM_APPLIED_VOLUME  InpVolume   =  VOLUME_TICK;   /* Applied Volume */
//--- global variables
int      handle=INVALID_HANDLE;  // Indicator handle
int      ind_digits=0;           // Number of decimal places in the indicator values
string   ind_title;              // Indicator description
//--- variables for the panel
int      mouse_bar_index;        // Index of the bar the data is taken from
CDashboard *panel=NULL;          // Pointer to the panel object


初始化

设置指标的全局变量值并创建其句柄:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Indicator
//--- Set the indicator name and the number of decimal places
   ind_title="A/D";
   ind_digits=0;
//--- Create indicator handle
   ResetLastError();
   handle=iAD(Symbol(),PERIOD_CURRENT,InpVolume);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }

如果EA涉及使用仪表板,在此处创建它

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);

//--- Indicator
//--- Set the indicator name and the number of decimal places
   ind_title="A/D";
   ind_digits=0;
//--- Create indicator handle
   ResetLastError();
   handle=iAD(Symbol(),PERIOD_CURRENT,InpVolume);
   if(handle==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
      return INIT_FAILED;
     }

//--- Dashboard
//--- Create the panel
   panel=new CDashboard(1,20,20,199,225);
   if(panel==NULL)
     {
      Print("Error. Failed to create panel object");
      return INIT_FAILED;
     }
//--- Set font parameters
   panel.SetFontParams("Calibri",9);
//--- Display the panel with the "Symbol, Timeframe description" header text
   panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
//--- Create a table with ID 0 to display bar data in it
   panel.CreateNewTable(0);
//--- Draw a table with ID 0 on the panel background
   panel.DrawGrid(0,2,20,6,2,18,97);

//--- Create a table with ID 1 to display indicator data in it
   panel.CreateNewTable(1);
//--- Get the Y2 table coordinate with ID 0 and
//--- set the Y1 coordinate for the table with ID 1
   int y1=panel.TableY2(0)+22;
//--- Draw a table with ID 1 on the panel background
   panel.DrawGrid(1,2,y1,3,2,18,97);
   
//--- Display tabular data in the journal
   panel.GridPrint(0,2);
   panel.GridPrint(1,2);
//--- Initialize the variable with the index of the mouse cursor bar
   mouse_bar_index=0;
//--- Display the data of the current bar on the panel
   DrawData(mouse_bar_index,TimeCurrent());

//--- Successful initialization
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+


析构

在EA的 OnDeinit() 处理函数中释放指标句柄:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Release handle of the indicator
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Clear all comments on the chart
   Comment("");
  }

创建的仪表板对象在EA析构时被删除:

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
//--- Release handle of the indicator
   ResetLastError();
   if(!IndicatorRelease(handle))
      PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
//--- Clear all comments on the chart
   Comment("");
   
//--- If the panel object exists, delete it
   if(panel!=NULL)
      delete panel;
  }


读取数据

下面提供了通过指标句柄获取数据的一般函数,在关于将振荡指标连接到EA的文章中,已经对这些函数进行了回顾。所提供的函数可以在自定义程序中“按原样”使用:

//+------------------------------------------------------------------+
//| Return the indicator data on the specified bar                   |
//+------------------------------------------------------------------+
double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
  {
   double array[1]={0};
   ResetLastError();
   if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
     {
      PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
      return EMPTY_VALUE;
     }
   return array[0];
  }
//+------------------------------------------------------------------+
//| Return the state of the indicator line                           |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
  {
//--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
   const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Line upward reversal (value2>value1 && value0>value1)
   if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_TURN_UP;
//--- Line upward direction (value2<=value1 && value0>value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
      return LINE_STATE_UP;
//--- Line upward stop (value2<=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_UP;
//--- Line downward reversal (value2<value1 && value0<value1)
   if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_TURN_DOWN;
//--- Line downward direction (value2>=value1 && value0<value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
      return LINE_STATE_DOWN;
//--- Line downward stop (value2>=value1 && value0==value1)
   else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
      return LINE_STATE_STOP_DOWN;
//--- Undefined state
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Return the state of the line relative to the specified level     |
//+------------------------------------------------------------------+
ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
  {
//--- Get the values of the indicator line with the shift (0,1) relative to the passed index
   const double value0=IndicatorValue(ind_handle,index,  buffer_num);
   const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
//--- If at least one of the values could not be obtained, return an undefined value 
   if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
      return LINE_STATE_NONE;
//--- Define the second level to compare
   double level=(level1==EMPTY_VALUE ? level0 : level1);
//--- The line is below the level (value1<level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_UNDER;
//--- The line is above the level (value1>level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_ABOVE;
//--- The line crossed the level upwards (value1<=level && value0>level0)
   if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
      return LINE_STATE_CROSS_UP;
//--- The line crossed the level downwards (value1>=level && value0<level0)
   if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
      return LINE_STATE_CROSS_DOWN;
//--- The line touched the level from below (value1<level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- The line touched the level from above (value1>level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_TOUCH_BELOW;
//--- Line is equal to the level value (value1==level0 && value0==level0)
   if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
      return LINE_STATE_EQUALS;
//--- Undefined state
   return LINE_STATE_NONE;
  }
//+------------------------------------------------------------------+
//| Return the indicator line state description                      |
//+------------------------------------------------------------------+
string LineStateDescription(const ENUM_LINE_STATE state)
  {
   switch(state)
     {
      case LINE_STATE_UP         :  return "Up";
      case LINE_STATE_STOP_UP    :  return "Stop Up";
      case LINE_STATE_TURN_UP    :  return "Turn Up";
      case LINE_STATE_DOWN       :  return "Down";
      case LINE_STATE_STOP_DOWN  :  return "Stop Down";
      case LINE_STATE_TURN_DOWN  :  return "Turn Down";
      case LINE_STATE_ABOVE      :  return "Above level";
      case LINE_STATE_UNDER      :  return "Under level";
      case LINE_STATE_CROSS_UP   :  return "Crossing Up";
      case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
      case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
      case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
      case LINE_STATE_EQUALS     :  return "Equals";
      default                    :  return "Unknown";
     }
  }

使用仪表板时,数据会使用以下函数显示在面板上:

//+------------------------------------------------------------------+
//| Display data from the specified timeseries index to the panel    |
//+------------------------------------------------------------------+
void DrawData(const int index,const datetime time)
  {
//--- Declare the variables to receive data in them
   MqlTick  tick={0};
   MqlRates rates[1];

//--- Exit if unable to get the current prices
   if(!SymbolInfoTick(Symbol(),tick))
      return;
//--- Exit if unable to get the bar data by the specified index
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
      return;

//--- Set font parameters for bar and indicator data headers
   int  size=0;
   uint flags=0;
   uint angle=0;
   string name=panel.FontParams(size,flags,angle);
   panel.SetFontParams(name,9,FW_BOLD);
   panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
   panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
//--- Set font parameters for bar and indicator data
   panel.SetFontParams(name,9);

//--- Display the data of the specified bar in table 0 on the panel
   panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
   panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
   panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
   panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
   panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
   panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);

//--- Display the indicator data from the specified bar on the panel in table 1
   panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
   double value=IndicatorValue(handle,index,0);
   string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
   panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,90);
   
//--- Display a description of the indicator line state
   panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
   ENUM_LINE_STATE state=LineState(handle,index,0);
   panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,90);
   
//--- Redraw the chart to immediately display all changes on the panel
   ChartRedraw(ChartID());
  }

此外,当使用仪表板时,面板事件处理程序在OnChartEvent()EA事件处理函数中调用,以及处理用于接收光标下的柱形索引的事件:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Handling the panel
//--- Call the panel event handler
   panel.OnChartEvent(id,lparam,dparam,sparam);

//--- If the cursor moves or a click is made on the chart
   if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
     {
      //--- Declare the variables to record time and price coordinates in them
      datetime time=0;
      double price=0;
      int wnd=0;
      //--- If the cursor coordinates are converted to date and time
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- write the bar index where the cursor is located to a global variable
         mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         //--- Display the bar data under the cursor on the panel 
         DrawData(mouse_bar_index,time);
        }
     }

//--- If we received a custom event, display the appropriate message in the journal
   if(id>CHARTEVENT_CUSTOM)
     {
      //--- Here we can implement handling a click on the close button on the panel
      PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
     }
  }

编译EA并在图表上启动后,我们可以监控面板上指标值和线的状态:


您可以在文章附带的文件中查看TestVolumeAD.mq5测试EA。


货币流量指数

货币流量指数 (Money Flow Index,MFI) 是一个技术指标, 它指示资金被投资到证券中然后从中提取的比率。指标的构建和解释类似于相对强度指数,唯一的区别是交易量对MFI很重要。

在分析货币流量指数时,需要考虑以下几点:

  • 指标和价格走势之间的背离。如果价格上涨,而货币流量指数下跌(反之亦然),那么价格很有可能发生转折;
  • 货币流量指数值超过80或低于20,相应地预示着市场的潜在峰值或底部。



    参数

    iMFI()函数用于创建指标句柄:

    int  iMFI(
       string               symbol,             // symbol name
       ENUM_TIMEFRAMES      period,             // period
       int                  ma_period,          // averaging period
       ENUM_APPLIED_VOLUME  applied_volume      // type of volume used for calculations
       );
    

    symbol

    [in] 其数据将用于计算指标的金融工具的交易品种名称。NULL表示当前交易品种。

    period

    [in] 周期值可以是ENUM_TIMEFRAMES枚举值之一,0表示当前时间框架。

    ma_period

    [in] 指标计算的周期(柱数)。

    applied_volume

    [in]  使用的交易量. ENUM_APPLIED_VOLUME 中的任一种。

    返回指定技术指标的句柄,如果失败,则返回 INVALID_HANDLE。 要从未使用的指标中释放计算机内存,请使用指标句柄传递到 IndicatorRelease()

    在EA中声明输入参数全局变量以创建指标:

    //+------------------------------------------------------------------+
    //|                                                TestVolumeMFI.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    //--- enums
    enum ENUM_LINE_STATE
      {
       LINE_STATE_NONE,        // Undefined state
       LINE_STATE_UP,          // Upward
       LINE_STATE_DOWN,        // Downward
       LINE_STATE_TURN_UP,     // Upward reversal
       LINE_STATE_TURN_DOWN,   // Downward reversal
       LINE_STATE_STOP_UP,     // Upward stop
       LINE_STATE_STOP_DOWN,   // Downward stop
       LINE_STATE_ABOVE,       // Above value
       LINE_STATE_UNDER,       // Below value
       LINE_STATE_CROSS_UP,    // Crossing value upwards
       LINE_STATE_CROSS_DOWN,  // Crossing value downwards
       LINE_STATE_TOUCH_BELOW, // Touching value from below 
       LINE_STATE_TOUCH_ABOVE, // Touch value from above
       LINE_STATE_EQUALS,      // Equal to value
      };
    //--- input parameters
    input uint                 InpPeriod   =  14;            /* Period         */
    input ENUM_APPLIED_VOLUME  InpVolume   =  VOLUME_TICK;   /* Applied Volume */
    input double               InpOverbough=  80;            /* Overbough level*/
    input double               InpOversold =  20;            /* Oversold level */
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      period=0;               // RSI calculation period
    int      ind_digits=0;           // Number of decimal places in the indicator values
    double   overbough=0;            // Overbought level
    double   oversold=0;             // Oversold level
    string   ind_title;              // Indicator description
    
    

    在EA中使用仪表板时,声明全局变量包括面板类的文件

    //+------------------------------------------------------------------+
    //|                                                TestVolumeMFI.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    //--- includes
    #include <Dashboard\Dashboard.mqh>
    //--- enums
    enum ENUM_LINE_STATE
      {
       LINE_STATE_NONE,        // Undefined state
       LINE_STATE_UP,          // Upward
       LINE_STATE_DOWN,        // Downward
       LINE_STATE_TURN_UP,     // Upward reversal
       LINE_STATE_TURN_DOWN,   // Downward reversal
       LINE_STATE_STOP_UP,     // Upward stop
       LINE_STATE_STOP_DOWN,   // Downward stop
       LINE_STATE_ABOVE,       // Above value
       LINE_STATE_UNDER,       // Below value
       LINE_STATE_CROSS_UP,    // Crossing value upwards
       LINE_STATE_CROSS_DOWN,  // Crossing value downwards
       LINE_STATE_TOUCH_BELOW, // Touching value from below 
       LINE_STATE_TOUCH_ABOVE, // Touch value from above
       LINE_STATE_EQUALS,      // Equal to value
      };
    //--- input parameters
    input uint                 InpPeriod   =  14;            /* Period         */
    input ENUM_APPLIED_VOLUME  InpVolume   =  VOLUME_TICK;   /* Applied Volume */
    input double               InpOverbough=  80;            /* Overbough level*/
    input double               InpOversold =  20;            /* Oversold level */
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      period=0;               // RSI calculation period
    int      ind_digits=0;           // Number of decimal places in the indicator values
    double   overbough=0;            // Overbought level
    double   oversold=0;             // Oversold level
    string   ind_title;              // Indicator description
    //--- variables for the panel
    int      mouse_bar_index;        // Index of the bar the data is taken from
    CDashboard *panel=NULL;          // Pointer to the panel object
    
    


    初始化

    设置指标的全局变量值并创建其句柄:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set and adjust the calculation period and levels if necessary
       period=int(InpPeriod<1 ? 14 : InpPeriod);
       overbough=InpOverbough;
       oversold=(InpOversold>=overbough ? overbough-0.01 : InpOversold);
    //--- Set the indicator name and the number of decimal places
       ind_title=StringFormat("MFI(%lu)",period);
       ind_digits=Digits();
    //--- Create indicator handle
       ResetLastError();
       handle=iMFI(Symbol(),PERIOD_CURRENT,period,InpVolume);
       if(handle==INVALID_HANDLE)
         {
          PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
          return INIT_FAILED;
         }
    
    //--- Successful initialization
       return(INIT_SUCCEEDED);
      }
    
    

    如果EA涉及使用仪表板,在此处创建它

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set and adjust the calculation period and levels if necessary
       period=int(InpPeriod<1 ? 14 : InpPeriod);
       overbough=InpOverbough;
       oversold=(InpOversold>=overbough ? overbough-0.01 : InpOversold);
    //--- Set the indicator name and the number of decimal places
       ind_title=StringFormat("MFI(%lu)",period);
       ind_digits=Digits();
    //--- Create indicator handle
       ResetLastError();
       handle=iMFI(Symbol(),PERIOD_CURRENT,period,InpVolume);
       if(handle==INVALID_HANDLE)
         {
          PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
          return INIT_FAILED;
         }
    
    //--- Dashboard
    //--- Create the panel
       panel=new CDashboard(1,20,20,229,243);
       if(panel==NULL)
         {
          Print("Error. Failed to create panel object");
          return INIT_FAILED;
         }
    //--- Set font parameters
       panel.SetFontParams("Calibri",9);
    //--- Display the panel with the "Symbol, Timeframe description" header text
       panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
    //--- Create a table with ID 0 to display bar data in it
       panel.CreateNewTable(0);
    //--- Draw a table with ID 0 on the panel background
       panel.DrawGrid(0,2,20,6,2,18,112);
    
    //--- Create a table with ID 1 to display indicator data in it
       panel.CreateNewTable(1);
    //--- Get the Y2 table coordinate with ID 0 and
    //--- set the Y1 coordinate for the table with ID 1
       int y1=panel.TableY2(0)+22;
    //--- Draw a table with ID 1 on the panel background
       panel.DrawGrid(1,2,y1,4,2,18,112);
       
    //--- Display tabular data in the journal
       panel.GridPrint(0,2);
       panel.GridPrint(1,2);
    //--- Initialize the variable with the index of the mouse cursor bar
       mouse_bar_index=0;
    //--- Display the data of the current bar on the panel
       DrawData(mouse_bar_index,TimeCurrent());
    
    //--- Successful initialization
       return(INIT_SUCCEEDED);
      }
    
    


    析构

    在EA的 OnDeinit() 处理函数中释放指标句柄:

    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- destroy timer
       EventKillTimer();
       
    //--- Release handle of the indicator
       ResetLastError();
       if(!IndicatorRelease(handle))
          PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
    //--- Clear all comments on the chart
       Comment("");
      }
    
    

    创建的仪表板对象在EA析构时被删除:

    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- destroy timer
       EventKillTimer();
       
    //--- Release handle of the indicator
       ResetLastError();
       if(!IndicatorRelease(handle))
          PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
    //--- Clear all comments on the chart
       Comment("");
       
    //--- If the panel object exists, delete it
       if(panel!=NULL)
          delete panel;
      }
    


    读取数据

    下面提供了通过指标句柄获取数据的一般函数,在关于将振荡指标连接到EA的文章中,已经对这些函数进行了回顾。所提供的函数可以在自定义程序中“按原样”使用:

    //+------------------------------------------------------------------+
    //| Return the indicator data on the specified bar                   |
    //+------------------------------------------------------------------+
    double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
      {
       double array[1]={0};
       ResetLastError();
       if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
         {
          PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
          return EMPTY_VALUE;
         }
       return array[0];
      }
    //+------------------------------------------------------------------+
    //| Return the state of the indicator line                           |
    //+------------------------------------------------------------------+
    ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
      {
    //--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
       const double value0=IndicatorValue(ind_handle,index,  buffer_num);
       const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
       const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
    //--- If at least one of the values could not be obtained, return an undefined value 
       if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
          return LINE_STATE_NONE;
    //--- Line upward reversal (value2>value1 && value0>value1)
       if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
          return LINE_STATE_TURN_UP;
    //--- Line upward direction (value2<=value1 && value0>value1)
       else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
          return LINE_STATE_UP;
    //--- Line upward stop (value2<=value1 && value0==value1)
       else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
          return LINE_STATE_STOP_UP;
    //--- Line downward reversal (value2<value1 && value0<value1)
       if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
          return LINE_STATE_TURN_DOWN;
    //--- Line downward direction (value2>=value1 && value0<value1)
       else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
          return LINE_STATE_DOWN;
    //--- Line downward stop (value2>=value1 && value0==value1)
       else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
          return LINE_STATE_STOP_DOWN;
    //--- Undefined state
       return LINE_STATE_NONE;
      }
    //+------------------------------------------------------------------+
    //| Return the state of the line relative to the specified level     |
    //+------------------------------------------------------------------+
    ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
      {
    //--- Get the values of the indicator line with the shift (0,1) relative to the passed index
       const double value0=IndicatorValue(ind_handle,index,  buffer_num);
       const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
    //--- If at least one of the values could not be obtained, return an undefined value 
       if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
          return LINE_STATE_NONE;
    //--- Define the second level to compare
       double level=(level1==EMPTY_VALUE ? level0 : level1);
    //--- The line is below the level (value1<level && value0<level0)
       if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
          return LINE_STATE_UNDER;
    //--- The line is above the level (value1>level && value0>level0)
       if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
          return LINE_STATE_ABOVE;
    //--- The line crossed the level upwards (value1<=level && value0>level0)
       if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
          return LINE_STATE_CROSS_UP;
    //--- The line crossed the level downwards (value1>=level && value0<level0)
       if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
          return LINE_STATE_CROSS_DOWN;
    //--- The line touched the level from below (value1<level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_TOUCH_BELOW;
    //--- The line touched the level from above (value1>level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_TOUCH_BELOW;
    //--- Line is equal to the level value (value1==level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_EQUALS;
    //--- Undefined state
       return LINE_STATE_NONE;
      }
    //+------------------------------------------------------------------+
    //| Return the indicator line state description                      |
    //+------------------------------------------------------------------+
    string LineStateDescription(const ENUM_LINE_STATE state)
      {
       switch(state)
         {
          case LINE_STATE_UP         :  return "Up";
          case LINE_STATE_STOP_UP    :  return "Stop Up";
          case LINE_STATE_TURN_UP    :  return "Turn Up";
          case LINE_STATE_DOWN       :  return "Down";
          case LINE_STATE_STOP_DOWN  :  return "Stop Down";
          case LINE_STATE_TURN_DOWN  :  return "Turn Down";
          case LINE_STATE_ABOVE      :  return "Above level";
          case LINE_STATE_UNDER      :  return "Under level";
          case LINE_STATE_CROSS_UP   :  return "Crossing Up";
          case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
          case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
          case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
          case LINE_STATE_EQUALS     :  return "Equals";
          default                    :  return "Unknown";
         }
      }
    
    

    使用仪表板时,数据会使用以下函数显示在面板上:

    //+------------------------------------------------------------------+
    //| Display data from the specified timeseries index to the panel    |
    //+------------------------------------------------------------------+
    void DrawData(const int index,const datetime time)
      {
    //--- Declare the variables to receive data in them
       MqlTick  tick={0};
       MqlRates rates[1];
    
    //--- Exit if unable to get the current prices
       if(!SymbolInfoTick(Symbol(),tick))
          return;
    //--- Exit if unable to get the bar data by the specified index
       if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
          return;
    
    //--- Set font parameters for bar and indicator data headers
       int  size=0;
       uint flags=0;
       uint angle=0;
       string name=panel.FontParams(size,flags,angle);
       panel.SetFontParams(name,9,FW_BOLD);
       panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
       panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
    //--- Set font parameters for bar and indicator data
       panel.SetFontParams(name,9);
    
    //--- Display the data of the specified bar in table 0 on the panel
       panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
       panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
       panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
       panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
       panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
       panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);
    
    //--- Display the indicator data from the specified bar on the panel in table 1
       panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
       double value=IndicatorValue(handle,index,0);
       string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
       panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,100);
       
    //--- Display a description of the indicator line state relative to the overbought level
       string ovb=StringFormat("%+.2f",overbough);
       panel.DrawText("Overbough", panel.CellX(1,2,0)+2, panel.CellY(1,2,0)+2);
       panel.DrawText(ovb, panel.CellX(1,2,0)+66, panel.CellY(1,2,0)+2);
       ENUM_LINE_STATE state_ovb=LineStateRelative(handle,index,0,overbough);
    //--- The label color changes depending on the value of the line relative to the level
       color clr=(state_ovb==LINE_STATE_CROSS_DOWN ? clrRed : clrNONE);
       string ovb_str=(state_ovb==LINE_STATE_ABOVE ? "Inside the area" : LineStateDescription(state_ovb));
       panel.DrawText(ovb_str,panel.CellX(1,2,1)+2,panel.CellY(1,2,1)+2,clr,100);
       
    //--- Display a description of the indicator line state relative to the oversold level
       panel.DrawText("Oversold", panel.CellX(1,3,0)+2, panel.CellY(1,3,0)+2);
       string ovs=StringFormat("%+.2f",oversold);
       panel.DrawText(ovs, panel.CellX(1,3,0)+68, panel.CellY(1,3,0)+2);
       ENUM_LINE_STATE state_ovs=LineStateRelative(handle,index,0,oversold);
    //--- The label color changes depending on the value of the line relative to the level
       clr=(state_ovs==LINE_STATE_CROSS_UP ? clrBlue : clrNONE);
       string ovs_str=(state_ovs==LINE_STATE_UNDER ? "Inside the area" : LineStateDescription(state_ovs));
       panel.DrawText(ovs_str,panel.CellX(1,3,1)+2,panel.CellY(1,3,1)+2,clr,100);
       
    //--- Display a description of the indicator line state
       panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
       ENUM_LINE_STATE state=LineState(handle,index,0);
    //--- The label color changes depending on the location of the line in the overbought/oversold areas
       clr=(state_ovb==LINE_STATE_ABOVE || state_ovb==LINE_STATE_CROSS_DOWN ? clrRed : state_ovs==LINE_STATE_UNDER || state_ovs==LINE_STATE_CROSS_UP ? clrBlue : clrNONE);
       panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clr,100);
       
    //--- Redraw the chart to immediately display all changes on the panel
       ChartRedraw(ChartID());
      }
    
    

    指标线在超买/超卖区域的位置用文本颜色标记在面板上。

    此外,当使用仪表板时,面板事件处理程序在OnChartEvent()EA事件处理函数中调用,以及处理用于接收光标下的柱形索引的事件:

    //+------------------------------------------------------------------+
    //| ChartEvent function                                              |
    //+------------------------------------------------------------------+
    void OnChartEvent(const int id,
                      const long &lparam,
                      const double &dparam,
                      const string &sparam)
      {
    //--- Handling the panel
    //--- Call the panel event handler
       panel.OnChartEvent(id,lparam,dparam,sparam);
    
    //--- If the cursor moves or a click is made on the chart
       if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
         {
          //--- Declare the variables to record time and price coordinates in them
          datetime time=0;
          double price=0;
          int wnd=0;
          //--- If the cursor coordinates are converted to date and time
          if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
            {
             //--- write the bar index where the cursor is located to a global variable
             mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
             //--- Display the bar data under the cursor on the panel 
             DrawData(mouse_bar_index,time);
            }
         }
    
    //--- If we received a custom event, display the appropriate message in the journal
       if(id>CHARTEVENT_CUSTOM)
         {
          //--- Here we can implement handling a click on the close button on the panel
          PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
         }
      }
    
    

    编译EA并在图表上启动后,我们可以监控面板上指标值和线的状态:


    您可以在文章附件文件中查看TestVolumeMFI.mq5测试EA。


    能量潮指标

    能量潮指标(OBV,On Balance Volume)是一种动量技术指标,将成交量与价格变化联系起来。这个指标是由 Joseph Granville 提出的,它非常简单。如果当前柱的收盘价高于上一个柱的成交价,则当前柱中的成交量将添加到上一个OBV中。如果当前柱收盘价低于前一收盘价,则从前一OBV中减去当前成交量。

    关于OBV指标的分析,基本假设是OBV变化先于价格变化。理论上,可以看到聪明的货币通过不断上升的OBV流入某个证券,当公众进入该证券时,证券价格和平衡交易量都将激增。

    如果证券的价格变动先于OBV变动,则发生“未确认(non-confirmation)”状况。未确认可能发生在牛市顶部(当证券价格上涨,而OBV不上涨或不提前上涨)或熊市底部(当证券价格下跌时,OBV不下跌或者不提前下跌)。

    当每个新峰值高于前一峰值并且每个新波谷高于前一波谷时,OBV呈上升趋势。同样,当每个连续峰值低于前一峰值且每个连续波谷低于前一波谷时,OBV呈下降趋势。当OBV平盘移动,没有连续出现高点和低点时,它处于一个不确定的趋势。

    一旦一种趋势确立,它就会一直有效,直到被打破。有两种方法可以打破OBV的趋势。第一种情况发生在趋势从上升趋势变为下降趋势,或从下降趋势变为上升趋势时。

    打破OBV趋势的第二种方法是,如果趋势转变为不确定趋势,并在三天以上保持不确定状态。因此,如果证券从上升趋势变为不确定趋势,并且在变回上升趋势之前仅保持不确定状态两天,则认为OBV一直处于上升趋势。当OBV变为上升或下降趋势时,就发生了“突破”。

    由于OBV突破通常先于价格突破,投资者应在OBV上行突破时买入多头。同样,当OBV出现下行突破时,投资者应该卖空。应持有头寸,直到趋势发生变化。



    参数

    iOBV()函数用于创建指标句柄:

    返回OBV指标的句柄。只有一个缓冲区。

    int  iOBV(
       string                symbol,             // symbol name
       ENUM_TIMEFRAMES       period,             // period
       ENUM_APPLIED_VOLUME   applied_volume      // type of volume used for calculations
       );
    
    symbol

    [in] 其数据将用于计算指标的金融工具的交易品种名称。NULL表示当前交易品种。

    period

    [in] 周期值可以是ENUM_TIMEFRAMES枚举值之一,0表示当前时间框架。

    applied_volume

    [in]  使用的交易量. ENUM_APPLIED_VOLUME 枚举值中的任意一个。

    返回指定技术指标的句柄,如果失败,则返回 INVALID_HANDLE。 要从未使用的指标中释放计算机内存,请使用指标句柄传递到 IndicatorRelease()

    在EA中声明输入变量和全局变量以创建指标:

    //+------------------------------------------------------------------+
    //|                                                TestVolumeOBV.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    //--- enums
    enum ENUM_LINE_STATE
      {
       LINE_STATE_NONE,        // Undefined state
       LINE_STATE_UP,          // Upward
       LINE_STATE_DOWN,        // Downward
       LINE_STATE_TURN_UP,     // Upward reversal
       LINE_STATE_TURN_DOWN,   // Downward reversal
       LINE_STATE_STOP_UP,     // Upward stop
       LINE_STATE_STOP_DOWN,   // Downward stop
       LINE_STATE_ABOVE,       // Above value
       LINE_STATE_UNDER,       // Below value
       LINE_STATE_CROSS_UP,    // Crossing value upwards
       LINE_STATE_CROSS_DOWN,  // Crossing value downwards
       LINE_STATE_TOUCH_BELOW, // Touching value from below 
       LINE_STATE_TOUCH_ABOVE, // Touch value from above
       LINE_STATE_EQUALS,      // Equal to value
      };
    //--- input parameters
    input ENUM_APPLIED_VOLUME  InpVolume   =  VOLUME_TICK;   /* Applied Volume */
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      ind_digits=0;           // Number of decimal places in the indicator values
    string   ind_title;              // Indicator description
    
    

    在EA中使用仪表板时,声明全局变量包括面板类的文件

    //+------------------------------------------------------------------+
    //|                                                TestVolumeOBV.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    //--- includes
    #include <Dashboard\Dashboard.mqh>
    //--- enums
    enum ENUM_LINE_STATE
      {
       LINE_STATE_NONE,        // Undefined state
       LINE_STATE_UP,          // Upward
       LINE_STATE_DOWN,        // Downward
       LINE_STATE_TURN_UP,     // Upward reversal
       LINE_STATE_TURN_DOWN,   // Downward reversal
       LINE_STATE_STOP_UP,     // Upward stop
       LINE_STATE_STOP_DOWN,   // Downward stop
       LINE_STATE_ABOVE,       // Above value
       LINE_STATE_UNDER,       // Below value
       LINE_STATE_CROSS_UP,    // Crossing value upwards
       LINE_STATE_CROSS_DOWN,  // Crossing value downwards
       LINE_STATE_TOUCH_BELOW, // Touching value from below 
       LINE_STATE_TOUCH_ABOVE, // Touch value from above
       LINE_STATE_EQUALS,      // Equal to value
      };
    //--- input parameters
    input ENUM_APPLIED_VOLUME  InpVolume   =  VOLUME_TICK;   /* Applied Volume */
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      ind_digits=0;           // Number of decimal places in the indicator values
    string   ind_title;              // Indicator description
    //--- variables for the panel
    int      mouse_bar_index;        // Index of the bar the data is taken from
    CDashboard *panel=NULL;          // Pointer to the panel object
    
    


    初始化

    设置指标的全局变量值并创建其句柄:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set the indicator name and the number of decimal places
       ind_title="OBV";
       ind_digits=0;
    //--- Create indicator handle
       ResetLastError();
       handle=iOBV(Symbol(),PERIOD_CURRENT,InpVolume);
       if(handle==INVALID_HANDLE)
         {
          PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
          return INIT_FAILED;
         }
    
    //--- Successful initialization
       return(INIT_SUCCEEDED);
      }
    
    

    如果EA涉及使用仪表板,在此处创建它

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set the indicator name and the number of decimal places
       ind_title="OBV";
       ind_digits=0;
    //--- Create indicator handle
       ResetLastError();
       handle=iOBV(Symbol(),PERIOD_CURRENT,InpVolume);
       if(handle==INVALID_HANDLE)
         {
          PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
          return INIT_FAILED;
         }
    
    //--- Dashboard
    //--- Create the panel
       panel=new CDashboard(1,20,20,199,225);
       if(panel==NULL)
         {
          Print("Error. Failed to create panel object");
          return INIT_FAILED;
         }
    //--- Set font parameters
       panel.SetFontParams("Calibri",9);
    //--- Display the panel with the "Symbol, Timeframe description" header text
       panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
    //--- Create a table with ID 0 to display bar data in it
       panel.CreateNewTable(0);
    //--- Draw a table with ID 0 on the panel background
       panel.DrawGrid(0,2,20,6,2,18,97);
    
    //--- Create a table with ID 1 to display indicator data in it
       panel.CreateNewTable(1);
    //--- Get the Y2 table coordinate with ID 0 and
    //--- set the Y1 coordinate for the table with ID 1
       int y1=panel.TableY2(0)+22;
    //--- Draw a table with ID 1 on the panel background
       panel.DrawGrid(1,2,y1,3,2,18,97);
       
    //--- Display tabular data in the journal
       panel.GridPrint(0,2);
       panel.GridPrint(1,2);
    //--- Initialize the variable with the index of the mouse cursor bar
       mouse_bar_index=0;
    //--- Display the data of the current bar on the panel
       DrawData(mouse_bar_index,TimeCurrent());
    
    //--- Successful initialization
       return(INIT_SUCCEEDED);
      }
    
    


    析构

    在EA的 OnDeinit() 处理函数中释放指标句柄:

    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- destroy timer
       EventKillTimer();
       
    //--- Release handle of the indicator
       ResetLastError();
       if(!IndicatorRelease(handle))
          PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
    //--- Clear all comments on the chart
       Comment("");
      }
    
    

    创建的仪表板对象在EA析构时被删除:

    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- destroy timer
       EventKillTimer();
       
    //--- Release handle of the indicator
       ResetLastError();
       if(!IndicatorRelease(handle))
          PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
    //--- Clear all comments on the chart
       Comment("");
       
    //--- If the panel object exists, delete it
       if(panel!=NULL)
          delete panel;
      }
    


    读取数据

    下面提供了通过指标句柄获取数据的一般函数,在关于将振荡指标连接到EA的文章中,已经对这些函数进行了回顾。所提供的函数可以在自定义程序中“按原样”使用:

    //+------------------------------------------------------------------+
    //| Return the indicator data on the specified bar                   |
    //+------------------------------------------------------------------+
    double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
      {
       double array[1]={0};
       ResetLastError();
       if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
         {
          PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
          return EMPTY_VALUE;
         }
       return array[0];
      }
    //+------------------------------------------------------------------+
    //| Return the state of the indicator line                           |
    //+------------------------------------------------------------------+
    ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
      {
    //--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
       const double value0=IndicatorValue(ind_handle,index,  buffer_num);
       const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
       const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
    //--- If at least one of the values could not be obtained, return an undefined value 
       if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
          return LINE_STATE_NONE;
    //--- Line upward reversal (value2>value1 && value0>value1)
       if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
          return LINE_STATE_TURN_UP;
    //--- Line upward direction (value2<=value1 && value0>value1)
       else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
          return LINE_STATE_UP;
    //--- Line upward stop (value2<=value1 && value0==value1)
       else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
          return LINE_STATE_STOP_UP;
    //--- Line downward reversal (value2<value1 && value0<value1)
       if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
          return LINE_STATE_TURN_DOWN;
    //--- Line downward direction (value2>=value1 && value0<value1)
       else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
          return LINE_STATE_DOWN;
    //--- Line downward stop (value2>=value1 && value0==value1)
       else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
          return LINE_STATE_STOP_DOWN;
    //--- Undefined state
       return LINE_STATE_NONE;
      }
    //+------------------------------------------------------------------+
    //| Return the state of the line relative to the specified level     |
    //+------------------------------------------------------------------+
    ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
      {
    //--- Get the values of the indicator line with the shift (0,1) relative to the passed index
       const double value0=IndicatorValue(ind_handle,index,  buffer_num);
       const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
    //--- If at least one of the values could not be obtained, return an undefined value 
       if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
          return LINE_STATE_NONE;
    //--- Define the second level to compare
       double level=(level1==EMPTY_VALUE ? level0 : level1);
    //--- The line is below the level (value1<level && value0<level0)
       if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
          return LINE_STATE_UNDER;
    //--- The line is above the level (value1>level && value0>level0)
       if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
          return LINE_STATE_ABOVE;
    //--- The line crossed the level upwards (value1<=level && value0>level0)
       if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
          return LINE_STATE_CROSS_UP;
    //--- The line crossed the level downwards (value1>=level && value0<level0)
       if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
          return LINE_STATE_CROSS_DOWN;
    //--- The line touched the level from below (value1<level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_TOUCH_BELOW;
    //--- The line touched the level from above (value1>level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_TOUCH_BELOW;
    //--- Line is equal to the level value (value1==level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_EQUALS;
    //--- Undefined state
       return LINE_STATE_NONE;
      }
    //+------------------------------------------------------------------+
    //| Return the indicator line state description                      |
    //+------------------------------------------------------------------+
    string LineStateDescription(const ENUM_LINE_STATE state)
      {
       switch(state)
         {
          case LINE_STATE_UP         :  return "Up";
          case LINE_STATE_STOP_UP    :  return "Stop Up";
          case LINE_STATE_TURN_UP    :  return "Turn Up";
          case LINE_STATE_DOWN       :  return "Down";
          case LINE_STATE_STOP_DOWN  :  return "Stop Down";
          case LINE_STATE_TURN_DOWN  :  return "Turn Down";
          case LINE_STATE_ABOVE      :  return "Above level";
          case LINE_STATE_UNDER      :  return "Under level";
          case LINE_STATE_CROSS_UP   :  return "Crossing Up";
          case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
          case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
          case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
          case LINE_STATE_EQUALS     :  return "Equals";
          default                    :  return "Unknown";
         }
      }
    
    

    使用仪表板时,数据会使用以下函数显示在面板上:

    //+------------------------------------------------------------------+
    //| Display data from the specified timeseries index to the panel    |
    //+------------------------------------------------------------------+
    void DrawData(const int index,const datetime time)
      {
    //--- Declare the variables to receive data in them
       MqlTick  tick={0};
       MqlRates rates[1];
    
    //--- Exit if unable to get the current prices
       if(!SymbolInfoTick(Symbol(),tick))
          return;
    //--- Exit if unable to get the bar data by the specified index
       if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
          return;
    
    //--- Set font parameters for bar and indicator data headers
       int  size=0;
       uint flags=0;
       uint angle=0;
       string name=panel.FontParams(size,flags,angle);
       panel.SetFontParams(name,9,FW_BOLD);
       panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
       panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
    //--- Set font parameters for bar and indicator data
       panel.SetFontParams(name,9);
    
    //--- Display the data of the specified bar in table 0 on the panel
       panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
       panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
       panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
       panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
       panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
       panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);
    
    //--- Display the indicator data from the specified bar on the panel in table 1
       panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
       double value=IndicatorValue(handle,index,0);
       string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
       panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,90);
       
    //--- Display a description of the indicator line state
       panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
       ENUM_LINE_STATE state=LineState(handle,index,0);
       panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,90);
       
    //--- Redraw the chart to immediately display all changes on the panel
       ChartRedraw(ChartID());
      }
    
    

    此外,当使用仪表板时,面板事件处理程序在OnChartEvent()EA事件处理函数中调用,以及处理用于接收光标下的柱形索引的事件:

    //+------------------------------------------------------------------+
    //| ChartEvent function                                              |
    //+------------------------------------------------------------------+
    void OnChartEvent(const int id,
                      const long &lparam,
                      const double &dparam,
                      const string &sparam)
      {
    //--- Handling the panel
    //--- Call the panel event handler
       panel.OnChartEvent(id,lparam,dparam,sparam);
    
    //--- If the cursor moves or a click is made on the chart
       if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
         {
          //--- Declare the variables to record time and price coordinates in them
          datetime time=0;
          double price=0;
          int wnd=0;
          //--- If the cursor coordinates are converted to date and time
          if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
            {
             //--- write the bar index where the cursor is located to a global variable
             mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
             //--- Display the bar data under the cursor on the panel 
             DrawData(mouse_bar_index,time);
            }
         }
    
    //--- If we received a custom event, display the appropriate message in the journal
       if(id>CHARTEVENT_CUSTOM)
         {
          //--- Here we can implement handling a click on the close button on the panel
          PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
         }
      }
    
    

    编译EA并在图表上启动后,我们可以监控面板上指标值和线的状态:


    您可以在文章附件中查看TestVolumeOBV.mq5测试EA。


    成交量

    对于外汇市场,成交量指标是所选时间段内每个时段内价格变化数量的指标。对于股票交易品种,这是实际交易量(合约、货币、单位等)的指标



    参数

    iVolumes()函数用于创建指标句柄:

    返回描述交易量的指标的句柄。只有一个缓冲区。

    int  iVolumes(
       string               symbol,             // symbol name
       ENUM_TIMEFRAMES      period,             // period
       ENUM_APPLIED_VOLUME  applied_volume      // volume type
       )
    

    symbol

    [in] 其数据将用于计算指标的金融工具的交易品种名称。NULL 表示当前交易品种。

    period

    [in] 周期值可以是ENUM_TIMEFRAMES枚举值之一,0表示当前时间框架。

    applied_volume

    [in]  使用的交易量. ENUM_APPLIED_VOLUME 枚举值中的任意一个。

    返回指定技术指标的句柄,如果失败,则返回 INVALID_HANDLE。 要从未使用的指标中释放计算机内存,请使用指标句柄传递到 IndicatorRelease()

    在EA中声明输入变量和全局变量以创建指标:

    //+------------------------------------------------------------------+
    //|                                                TestVolumeOBV.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    //--- enums
    enum ENUM_LINE_STATE
      {
       LINE_STATE_NONE,        // Undefined state
       LINE_STATE_UP,          // Upward
       LINE_STATE_DOWN,        // Downward
       LINE_STATE_TURN_UP,     // Upward reversal
       LINE_STATE_TURN_DOWN,   // Downward reversal
       LINE_STATE_STOP_UP,     // Upward stop
       LINE_STATE_STOP_DOWN,   // Downward stop
       LINE_STATE_ABOVE,       // Above value
       LINE_STATE_UNDER,       // Below value
       LINE_STATE_CROSS_UP,    // Crossing value upwards
       LINE_STATE_CROSS_DOWN,  // Crossing value downwards
       LINE_STATE_TOUCH_BELOW, // Touching value from below 
       LINE_STATE_TOUCH_ABOVE, // Touch value from above
       LINE_STATE_EQUALS,      // Equal to value
      };
    //--- input parameters
    input ENUM_APPLIED_VOLUME  InpVolume   =  VOLUME_TICK;   /* Applied Volume */
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      ind_digits=0;           // Number of decimal places in the indicator values
    string   ind_title;              // Indicator description
    
    

    在EA中使用仪表板时,声明全局变量包括面板类的文件

    //+------------------------------------------------------------------+
    //|                                                TestVolumeOBV.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    //--- includes
    #include <Dashboard\Dashboard.mqh>
    //--- enums
    enum ENUM_LINE_STATE
      {
       LINE_STATE_NONE,        // Undefined state
       LINE_STATE_UP,          // Upward
       LINE_STATE_DOWN,        // Downward
       LINE_STATE_TURN_UP,     // Upward reversal
       LINE_STATE_TURN_DOWN,   // Downward reversal
       LINE_STATE_STOP_UP,     // Upward stop
       LINE_STATE_STOP_DOWN,   // Downward stop
       LINE_STATE_ABOVE,       // Above value
       LINE_STATE_UNDER,       // Below value
       LINE_STATE_CROSS_UP,    // Crossing value upwards
       LINE_STATE_CROSS_DOWN,  // Crossing value downwards
       LINE_STATE_TOUCH_BELOW, // Touching value from below 
       LINE_STATE_TOUCH_ABOVE, // Touch value from above
       LINE_STATE_EQUALS,      // Equal to value
      };
    //--- input parameters
    input ENUM_APPLIED_VOLUME  InpVolume   =  VOLUME_TICK;   /* Applied Volume */
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      ind_digits=0;           // Number of decimal places in the indicator values
    string   ind_title;              // Indicator description
    //--- variables for the panel
    int      mouse_bar_index;        // Index of the bar the data is taken from
    CDashboard *panel=NULL;          // Pointer to the panel object
    
    


    初始化

    设置指标的全局变量值并创建其句柄:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set the indicator name and the number of decimal places
       ind_title="Volumes";
       ind_digits=0;
    //--- Create indicator handle
       ResetLastError();
       handle=iVolumes(Symbol(),PERIOD_CURRENT,InpVolume);
       if(handle==INVALID_HANDLE)
         {
          PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
          return INIT_FAILED;
         }
    
    //--- Successful initialization
       return(INIT_SUCCEEDED);
      }
    
    

    如果EA涉及使用仪表板,在此处创建它

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set the indicator name and the number of decimal places
       ind_title="Volumes";
       ind_digits=0;
    //--- Create indicator handle
       ResetLastError();
       handle=iVolumes(Symbol(),PERIOD_CURRENT,InpVolume);
       if(handle==INVALID_HANDLE)
         {
          PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
          return INIT_FAILED;
         }
    
    //--- Dashboard
    //--- Create the panel
       panel=new CDashboard(1,20,20,199,225);
       if(panel==NULL)
         {
          Print("Error. Failed to create panel object");
          return INIT_FAILED;
         }
    //--- Set font parameters
       panel.SetFontParams("Calibri",9);
    //--- Display the panel with the "Symbol, Timeframe description" header text
       panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
    //--- Create a table with ID 0 to display bar data in it
       panel.CreateNewTable(0);
    //--- Draw a table with ID 0 on the panel background
       panel.DrawGrid(0,2,20,6,2,18,97);
    
    //--- Create a table with ID 1 to display indicator data in it
       panel.CreateNewTable(1);
    //--- Get the Y2 table coordinate with ID 0 and
    //--- set the Y1 coordinate for the table with ID 1
       int y1=panel.TableY2(0)+22;
    //--- Draw a table with ID 1 on the panel background
       panel.DrawGrid(1,2,y1,3,2,18,97);
       
    //--- Display tabular data in the journal
       panel.GridPrint(0,2);
       panel.GridPrint(1,2);
    //--- Initialize the variable with the index of the mouse cursor bar
       mouse_bar_index=0;
    //--- Display the data of the current bar on the panel
       DrawData(mouse_bar_index,TimeCurrent());
    
    //--- Successful initialization
       return(INIT_SUCCEEDED);
      }
    
    


    析构

    在EA的 OnDeinit() 处理函数中释放指标句柄:

    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- destroy timer
       EventKillTimer();
       
    //--- Release handle of the indicator
       ResetLastError();
       if(!IndicatorRelease(handle))
          PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
    //--- Clear all comments on the chart
       Comment("");
      }
    
    

    创建的仪表板对象在EA析构时被删除:

    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- destroy timer
       EventKillTimer();
       
    //--- Release handle of the indicator
       ResetLastError();
       if(!IndicatorRelease(handle))
          PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
    //--- Clear all comments on the chart
       Comment("");
       
    //--- If the panel object exists, delete it
       if(panel!=NULL)
          delete panel;
      }
    


    读取数据

    下面提供了通过指标句柄获取数据的一般函数,在关于将振荡指标连接到EA的文章中,已经对这些函数进行了回顾。所提供的函数可以在自定义程序中“按原样”使用:

    //+------------------------------------------------------------------+
    //| Return the indicator data on the specified bar                   |
    //+------------------------------------------------------------------+
    double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
      {
       double array[1]={0};
       ResetLastError();
       if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
         {
          PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
          return EMPTY_VALUE;
         }
       return array[0];
      }
    //+------------------------------------------------------------------+
    //| Return the state of the indicator line                           |
    //+------------------------------------------------------------------+
    ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
      {
    //--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
       const double value0=IndicatorValue(ind_handle,index,  buffer_num);
       const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
       const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
    //--- If at least one of the values could not be obtained, return an undefined value 
       if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
          return LINE_STATE_NONE;
    //--- Line upward reversal (value2>value1 && value0>value1)
       if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
          return LINE_STATE_TURN_UP;
    //--- Line upward direction (value2<=value1 && value0>value1)
       else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
          return LINE_STATE_UP;
    //--- Line upward stop (value2<=value1 && value0==value1)
       else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
          return LINE_STATE_STOP_UP;
    //--- Line downward reversal (value2<value1 && value0<value1)
       if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
          return LINE_STATE_TURN_DOWN;
    //--- Line downward direction (value2>=value1 && value0<value1)
       else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
          return LINE_STATE_DOWN;
    //--- Line downward stop (value2>=value1 && value0==value1)
       else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
          return LINE_STATE_STOP_DOWN;
    //--- Undefined state
       return LINE_STATE_NONE;
      }
    //+------------------------------------------------------------------+
    //| Return the state of the line relative to the specified level     |
    //+------------------------------------------------------------------+
    ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
      {
    //--- Get the values of the indicator line with the shift (0,1) relative to the passed index
       const double value0=IndicatorValue(ind_handle,index,  buffer_num);
       const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
    //--- If at least one of the values could not be obtained, return an undefined value 
       if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
          return LINE_STATE_NONE;
    //--- Define the second level to compare
       double level=(level1==EMPTY_VALUE ? level0 : level1);
    //--- The line is below the level (value1<level && value0<level0)
       if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
          return LINE_STATE_UNDER;
    //--- The line is above the level (value1>level && value0>level0)
       if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
          return LINE_STATE_ABOVE;
    //--- The line crossed the level upwards (value1<=level && value0>level0)
       if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
          return LINE_STATE_CROSS_UP;
    //--- The line crossed the level downwards (value1>=level && value0<level0)
       if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
          return LINE_STATE_CROSS_DOWN;
    //--- The line touched the level from below (value1<level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_TOUCH_BELOW;
    //--- The line touched the level from above (value1>level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_TOUCH_BELOW;
    //--- Line is equal to the level value (value1==level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_EQUALS;
    //--- Undefined state
       return LINE_STATE_NONE;
      }
    //+------------------------------------------------------------------+
    //| Return the indicator line state description                      |
    //+------------------------------------------------------------------+
    string LineStateDescription(const ENUM_LINE_STATE state)
      {
       switch(state)
         {
          case LINE_STATE_UP         :  return "Up";
          case LINE_STATE_STOP_UP    :  return "Stop Up";
          case LINE_STATE_TURN_UP    :  return "Turn Up";
          case LINE_STATE_DOWN       :  return "Down";
          case LINE_STATE_STOP_DOWN  :  return "Stop Down";
          case LINE_STATE_TURN_DOWN  :  return "Turn Down";
          case LINE_STATE_ABOVE      :  return "Above level";
          case LINE_STATE_UNDER      :  return "Under level";
          case LINE_STATE_CROSS_UP   :  return "Crossing Up";
          case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
          case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
          case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
          case LINE_STATE_EQUALS     :  return "Equals";
          default                    :  return "Unknown";
         }
      }
    
    

    使用仪表板时,数据会使用以下函数显示在面板上:

    //+------------------------------------------------------------------+
    //| Display data from the specified timeseries index to the panel    |
    //+------------------------------------------------------------------+
    void DrawData(const int index,const datetime time)
      {
    //--- Declare the variables to receive data in them
       MqlTick  tick={0};
       MqlRates rates[1];
    
    //--- Exit if unable to get the current prices
       if(!SymbolInfoTick(Symbol(),tick))
          return;
    //--- Exit if unable to get the bar data by the specified index
       if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
          return;
    
    //--- Set font parameters for bar and indicator data headers
       int  size=0;
       uint flags=0;
       uint angle=0;
       string name=panel.FontParams(size,flags,angle);
       panel.SetFontParams(name,9,FW_BOLD);
       panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
       panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
    //--- Set font parameters for bar and indicator data
       panel.SetFontParams(name,9);
    
    //--- Display the data of the specified bar in table 0 on the panel
       panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
       panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
       panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
       panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
       panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
       panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);
    
    //--- Display the indicator data from the specified bar on the panel in table 1
       panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
       double value0=IndicatorValue(handle,index,  0);
       double value1=IndicatorValue(handle,index+1,0);
       string value_str=(value0!=EMPTY_VALUE ? DoubleToString(value0,ind_digits) : "");
       color clr=(value0>value1 ? clrGreen : value0<value1 ? clrRed : clrNONE);
       panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,90);
       
    //--- Display a description of the indicator line state
       panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
       ENUM_LINE_STATE state=LineState(handle,index,0);
       panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clr,90);
       
    //--- Redraw the chart to immediately display all changes on the panel
       ChartRedraw(ChartID());
      }
    
    

    面板上状态文本的颜色与光标所在的指标列的颜色相对应。

    此外,当使用仪表板时,面板事件处理程序在OnChartEvent()EA事件处理函数中调用,以及处理用于接收光标下的柱形索引的事件:

    //+------------------------------------------------------------------+
    //| ChartEvent function                                              |
    //+------------------------------------------------------------------+
    void OnChartEvent(const int id,
                      const long &lparam,
                      const double &dparam,
                      const string &sparam)
      {
    //--- Handling the panel
    //--- Call the panel event handler
       panel.OnChartEvent(id,lparam,dparam,sparam);
    
    //--- If the cursor moves or a click is made on the chart
       if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
         {
          //--- Declare the variables to record time and price coordinates in them
          datetime time=0;
          double price=0;
          int wnd=0;
          //--- If the cursor coordinates are converted to date and time
          if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
            {
             //--- write the bar index where the cursor is located to a global variable
             mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
             //--- Display the bar data under the cursor on the panel 
             DrawData(mouse_bar_index,time);
            }
         }
    
    //--- If we received a custom event, display the appropriate message in the journal
       if(id>CHARTEVENT_CUSTOM)
         {
          //--- Here we can implement handling a click on the close button on the panel
          PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
         }
      }
    
    

    编译EA并在图表上启动后,我们可以监控面板上指标值和线的状态:


    您可以在文章附件中查看TestVolumeVolumes.mq5测试EA。


    比尔威廉姆斯指标

    比尔威廉姆斯(Bill Williams)的指标被归入一个单独的组,因为它们是他书中描述的交易系统的一部分。


    加速震荡指标

    价格是最后变化的元素,在价格变化之前,市场驱动力会改变方向,驱动力的加速度必须放缓并达到零。然后它开始加速,直到价格开始改变方向。

    加速震荡指标(Accelerator Oscillator,AC)衡量当前驱动力的加速和减速。该指标将在驱动力发生任何变化之前改变方向,反过来,驱动力将在价格之前改变方向。如果您意识到加速/减速是早期警告的信号,它会给您带来明显的优势。

    零线基本上是驱动力与加速度平衡的地方。如果加速度/减速度高于零,则加速度通常更容易继续向上移动(在低于零的情况下,反之亦然)。与动量振荡指标(AO)不同,零线交叉不是信号。要控制市场并做出决定,唯一需要做的就是观察颜色的变化。为了避免严重的反转,你必须记住:在当前柱为红色时,你不能借助AC指标来买入,在当前柱呈绿色时,你也不能卖出。

    如果你沿着驱动力的方向进入市场(买入时指标高于零,卖出时指标低于零),那么你只需要两个绿色柱就可以买入(卖出两个红色柱)。如果驱动力指向要打开的仓位(买入时指示低于零,卖出时指示高于零),则需要进行确认,因此,需要添加一个柱。在这种情况下,对于空头头寸,指示器在零线上方显示三个红色柱,对于多头头寸,在零线下方显示三个绿色柱。


    参数

    iAC()函数用于创建指标句柄:

    创建加速振荡指标并返回其句柄。只有一个缓冲区。

    int  iAC(
       string           symbol,     // symbol name
       ENUM_TIMEFRAMES  period      // period
       );
    

    symbol

    [in] 其数据将用于计算指标的金融工具的交易品种名称。NULL表示当前交易品种。

    period

    [in] 周期值可以是ENUM_TIMEFRAMES枚举值之一,0表示当前时间框架。

    返回指定技术指标的句柄,如果失败,则返回 INVALID_HANDLE。 要从未使用的指标中释放计算机内存,请使用指标句柄传递到 IndicatorRelease()

    在EA中声明全局变量以创建指标(该指标除了设置升序和降序直方图列的颜色外没有任何输入参数):

    //+------------------------------------------------------------------+
    //|                                               TestWilliamsAC.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    //--- enums
    enum ENUM_LINE_STATE
      {
       LINE_STATE_NONE,        // Undefined state
       LINE_STATE_UP,          // Upward
       LINE_STATE_DOWN,        // Downward
       LINE_STATE_TURN_UP,     // Upward reversal
       LINE_STATE_TURN_DOWN,   // Downward reversal
       LINE_STATE_STOP_UP,     // Upward stop
       LINE_STATE_STOP_DOWN,   // Downward stop
       LINE_STATE_ABOVE,       // Above value
       LINE_STATE_UNDER,       // Below value
       LINE_STATE_CROSS_UP,    // Crossing value upwards
       LINE_STATE_CROSS_DOWN,  // Crossing value downwards
       LINE_STATE_TOUCH_BELOW, // Touching value from below 
       LINE_STATE_TOUCH_ABOVE, // Touch value from above
       LINE_STATE_EQUALS,      // Equal to value
      };
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      ind_digits=0;           // Number of decimal places in the indicator values
    string   ind_title;              // Indicator description
    
    

    在EA中使用仪表板时,声明全局变量包括面板类的文件

    //+------------------------------------------------------------------+
    //|                                               TestWilliamsAC.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    //--- includes
    #include <Dashboard\Dashboard.mqh>
    //--- enums
    enum ENUM_LINE_STATE
      {
       LINE_STATE_NONE,        // Undefined state
       LINE_STATE_UP,          // Upward
       LINE_STATE_DOWN,        // Downward
       LINE_STATE_TURN_UP,     // Upward reversal
       LINE_STATE_TURN_DOWN,   // Downward reversal
       LINE_STATE_STOP_UP,     // Upward stop
       LINE_STATE_STOP_DOWN,   // Downward stop
       LINE_STATE_ABOVE,       // Above value
       LINE_STATE_UNDER,       // Below value
       LINE_STATE_CROSS_UP,    // Crossing value upwards
       LINE_STATE_CROSS_DOWN,  // Crossing value downwards
       LINE_STATE_TOUCH_BELOW, // Touching value from below 
       LINE_STATE_TOUCH_ABOVE, // Touch value from above
       LINE_STATE_EQUALS,      // Equal to value
      };
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      ind_digits=0;           // Number of decimal places in the indicator values
    string   ind_title;              // Indicator description
    //--- variables for the panel
    int      mouse_bar_index;        // Index of the bar the data is taken from
    CDashboard *panel=NULL;          // Pointer to the panel object
    
    


    初始化

    设置指标的全局变量值并创建其句柄:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set the indicator name and the number of decimal places
       ind_title="AC";
       ind_digits=Digits()+2;
    //--- Create indicator handle
       ResetLastError();
       handle=iAC(Symbol(),PERIOD_CURRENT);
       if(handle==INVALID_HANDLE)
         {
          PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
          return INIT_FAILED;
         }
    
    //--- Successful initialization
       return(INIT_SUCCEEDED);
      }
    
    

    如果EA涉及使用仪表板,在此处创建它

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set the indicator name and the number of decimal places
       ind_title="AC";
       ind_digits=Digits()+2;
    //--- Create indicator handle
       ResetLastError();
       handle=iAC(Symbol(),PERIOD_CURRENT);
       if(handle==INVALID_HANDLE)
         {
          PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
          return INIT_FAILED;
         }
    
    //--- Dashboard
    //--- Create the panel
       panel=new CDashboard(1,20,20,199,225);
       if(panel==NULL)
         {
          Print("Error. Failed to create panel object");
          return INIT_FAILED;
         }
    //--- Set font parameters
       panel.SetFontParams("Calibri",9);
    //--- Display the panel with the "Symbol, Timeframe description" header text
       panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
    //--- Create a table with ID 0 to display bar data in it
       panel.CreateNewTable(0);
    //--- Draw a table with ID 0 on the panel background
       panel.DrawGrid(0,2,20,6,2,18,97);
    
    //--- Create a table with ID 1 to display indicator data in it
       panel.CreateNewTable(1);
    //--- Get the Y2 table coordinate with ID 0 and
    //--- set the Y1 coordinate for the table with ID 1
       int y1=panel.TableY2(0)+22;
    //--- Draw a table with ID 1 on the panel background
       panel.DrawGrid(1,2,y1,3,2,18,97);
       
    //--- Display tabular data in the journal
       panel.GridPrint(0,2);
       panel.GridPrint(1,2);
    //--- Initialize the variable with the index of the mouse cursor bar
       mouse_bar_index=0;
    //--- Display the data of the current bar on the panel
       DrawData(mouse_bar_index,TimeCurrent());
    
    //--- Successful initialization
       return(INIT_SUCCEEDED);
      }
    
    


    析构

    在EA的 OnDeinit() 处理函数中释放指标句柄:

    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- destroy timer
       EventKillTimer();
       
    //--- Release handle of the indicator
       ResetLastError();
       if(!IndicatorRelease(handle))
          PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
    //--- Clear all comments on the chart
       Comment("");
      }
    
    

    创建的仪表板对象在EA析构时被删除:

    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- destroy timer
       EventKillTimer();
       
    //--- Release handle of the indicator
       ResetLastError();
       if(!IndicatorRelease(handle))
          PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
    //--- Clear all comments on the chart
       Comment("");
       
    //--- If the panel object exists, delete it
       if(panel!=NULL)
          delete panel;
      }
    


    读取数据

    下面提供了通过指标句柄获取数据的一般函数,在关于将振荡指标连接到EA的文章中,已经对这些函数进行了回顾。所提供的函数可以在自定义程序中“按原样”使用:

    //+------------------------------------------------------------------+
    //| Return the indicator data on the specified bar                   |
    //+------------------------------------------------------------------+
    double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
      {
       double array[1]={0};
       ResetLastError();
       if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
         {
          PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
          return EMPTY_VALUE;
         }
       return array[0];
      }
    //+------------------------------------------------------------------+
    //| Return the state of the indicator line                           |
    //+------------------------------------------------------------------+
    ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
      {
    //--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
       const double value0=IndicatorValue(ind_handle,index,  buffer_num);
       const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
       const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
    //--- If at least one of the values could not be obtained, return an undefined value 
       if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
          return LINE_STATE_NONE;
    //--- Line upward reversal (value2>value1 && value0>value1)
       if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
          return LINE_STATE_TURN_UP;
    //--- Line upward direction (value2<=value1 && value0>value1)
       else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
          return LINE_STATE_UP;
    //--- Line upward stop (value2<=value1 && value0==value1)
       else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
          return LINE_STATE_STOP_UP;
    //--- Line downward reversal (value2<value1 && value0<value1)
       if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
          return LINE_STATE_TURN_DOWN;
    //--- Line downward direction (value2>=value1 && value0<value1)
       else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
          return LINE_STATE_DOWN;
    //--- Line downward stop (value2>=value1 && value0==value1)
       else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
          return LINE_STATE_STOP_DOWN;
    //--- Undefined state
       return LINE_STATE_NONE;
      }
    //+------------------------------------------------------------------+
    //| Return the state of the line relative to the specified level     |
    //+------------------------------------------------------------------+
    ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
      {
    //--- Get the values of the indicator line with the shift (0,1) relative to the passed index
       const double value0=IndicatorValue(ind_handle,index,  buffer_num);
       const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
    //--- If at least one of the values could not be obtained, return an undefined value 
       if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
          return LINE_STATE_NONE;
    //--- Define the second level to compare
       double level=(level1==EMPTY_VALUE ? level0 : level1);
    //--- The line is below the level (value1<level && value0<level0)
       if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
          return LINE_STATE_UNDER;
    //--- The line is above the level (value1>level && value0>level0)
       if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
          return LINE_STATE_ABOVE;
    //--- The line crossed the level upwards (value1<=level && value0>level0)
       if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
          return LINE_STATE_CROSS_UP;
    //--- The line crossed the level downwards (value1>=level && value0<level0)
       if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
          return LINE_STATE_CROSS_DOWN;
    //--- The line touched the level from below (value1<level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_TOUCH_BELOW;
    //--- The line touched the level from above (value1>level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_TOUCH_BELOW;
    //--- Line is equal to the level value (value1==level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_EQUALS;
    //--- Undefined state
       return LINE_STATE_NONE;
      }
    //+------------------------------------------------------------------+
    //| Return the indicator line state description                      |
    //+------------------------------------------------------------------+
    string LineStateDescription(const ENUM_LINE_STATE state)
      {
       switch(state)
         {
          case LINE_STATE_UP         :  return "Up";
          case LINE_STATE_STOP_UP    :  return "Stop Up";
          case LINE_STATE_TURN_UP    :  return "Turn Up";
          case LINE_STATE_DOWN       :  return "Down";
          case LINE_STATE_STOP_DOWN  :  return "Stop Down";
          case LINE_STATE_TURN_DOWN  :  return "Turn Down";
          case LINE_STATE_ABOVE      :  return "Above level";
          case LINE_STATE_UNDER      :  return "Under level";
          case LINE_STATE_CROSS_UP   :  return "Crossing Up";
          case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
          case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
          case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
          case LINE_STATE_EQUALS     :  return "Equals";
          default                    :  return "Unknown";
         }
      }
    
    

    使用仪表板时,数据会使用以下函数显示在面板上:

    //+------------------------------------------------------------------+
    //| Display data from the specified timeseries index to the panel    |
    //+------------------------------------------------------------------+
    void DrawData(const int index,const datetime time)
      {
    //--- Declare the variables to receive data in them
       MqlTick  tick={0};
       MqlRates rates[1];
    
    //--- Exit if unable to get the current prices
       if(!SymbolInfoTick(Symbol(),tick))
          return;
    //--- Exit if unable to get the bar data by the specified index
       if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
          return;
    
    //--- Set font parameters for bar and indicator data headers
       int  size=0;
       uint flags=0;
       uint angle=0;
       string name=panel.FontParams(size,flags,angle);
       panel.SetFontParams(name,9,FW_BOLD);
       panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
       panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
    //--- Set font parameters for bar and indicator data
       panel.SetFontParams(name,9);
    
    //--- Display the data of the specified bar in table 0 on the panel
       panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
       panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
       panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
       panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
       panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
       panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);
    
    //--- Display the indicator data from the specified bar on the panel in table 1
       panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
       double value0=IndicatorValue(handle,index,  0);
       double value1=IndicatorValue(handle,index+1,0);
       string value_str=(value0!=EMPTY_VALUE ? DoubleToString(value0,ind_digits) : "");
       color clr=(value0>value1 ? clrGreen : value0<value1 ? clrRed : clrNONE);
       panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,90);
       
    //--- Display a description of the indicator line state
       panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
       ENUM_LINE_STATE state=LineState(handle,index,0);
       panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clr,90);
       
    //--- Redraw the chart to immediately display all changes on the panel
       ChartRedraw(ChartID());
      }
    
    

    仪表板上指标线状态文本的颜色与光标所在的直方图列的颜色相对应。

    此外,当使用仪表板时,面板事件处理程序在OnChartEvent()EA事件处理函数中调用,以及处理用于接收光标下的柱形索引的事件:

    //+------------------------------------------------------------------+
    //| ChartEvent function                                              |
    //+------------------------------------------------------------------+
    void OnChartEvent(const int id,
                      const long &lparam,
                      const double &dparam,
                      const string &sparam)
      {
    //--- Handling the panel
    //--- Call the panel event handler
       panel.OnChartEvent(id,lparam,dparam,sparam);
    
    //--- If the cursor moves or a click is made on the chart
       if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
         {
          //--- Declare the variables to record time and price coordinates in them
          datetime time=0;
          double price=0;
          int wnd=0;
          //--- If the cursor coordinates are converted to date and time
          if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
            {
             //--- write the bar index where the cursor is located to a global variable
             mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
             //--- Display the bar data under the cursor on the panel 
             DrawData(mouse_bar_index,time);
            }
         }
    
    //--- If we received a custom event, display the appropriate message in the journal
       if(id>CHARTEVENT_CUSTOM)
         {
          //--- Here we can implement handling a click on the close button on the panel
          PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
         }
      }
    
    

    编译EA并在图表上启动后,我们可以监控面板上指标值和线的状态:


    您可以在文章附件中查看TestWilliamS/AC.mq5测试EA。


    鳄鱼指标

    大多数时候,市场保持稳定。只有大约15-30%的时间,市场会产生趋势,而不在交易所的交易员从趋势中获得大部分利润。我爷爷曾经重复说:“如果总是在同一时间喂食,即使是盲鸡也会找到玉米”。我们把这种趋势称为“盲鸡市场”。我们花了很多年的时间,但我们已经制定了一个指标,让我们在进入“盲鸡市场”之前始终保持预备状态。
    Bill Williams

    鳄鱼指标(Alligator)是使用分形几何和非线性动力学的平衡线(移动平均值)的组合。

    • 蓝线(鳄鱼指标的颚线)是用于构建图表的时间段的平衡线(13个周期的平滑移动平均值,向未来移动8个柱);
    • 红线(鳄鱼指标的齿线)是降低一定周期数的平衡线(8周期平滑移动平均值,未来移动5个柱);
    • 绿线(鳄鱼指标的唇线)是再次降低一定周期数的平衡线(5个周期平滑移动平均值,未来移动3个柱)。

    鳄鱼指标的唇线、齿线和颚线展示了不同时期的相互作用。由于市场趋势只能在15-30%的时间内被识别,我们必须遵循趋势,而不是只在特定价格周期内波动的市场。

    当大颚、牙齿和嘴唇闭合或交织在一起时,鳄鱼就要睡觉或已经在睡觉了。当它睡觉时,它的饥饿感会增加——所以它睡得越多,醒来时就会越饿。当它醒来时,它做的第一件事就是张开嘴开始打哈欠。然后它开始闻到食物的气味:牛或熊的肉,并开始寻找它。在吃得足够饱后,鳄鱼开始对食物/价格失去兴趣(平衡线连接在一起)——这是确定利润的时候了。



    参数

    iAlligator()函数用于创建指标句柄:

    返回鳄鱼指标的句柄。

    int  iAlligator(
       string              symbol,            // symbol name
       ENUM_TIMEFRAMES     period,            // period
       int                 jaw_period,        // period for calculating jaws
       int                 jaw_shift,         // horizontal shift of jaws
       int                 teeth_period,      // period for calculating teeth
       int                 teeth_shift,       // horizontal shift of teeth
       int                 lips_period,       // period for calculating lips
       int                 lips_shift,        // horizontal shift of lips
       ENUM_MA_METHOD      ma_method,         // smoothing type
       ENUM_APPLIED_PRICE  applied_price      // price type or handle
       );
    

    symbol

    [in] 其数据将用于计算指标的金融工具的交易品种名称。NULL表示当前交易品种。

    period

    [in] 周期值可以是ENUM_TIMEFRAMES枚举值之一,0表示当前时间框架。

    jaw_period

    [in] 蓝线(鳄鱼指标的颚线)平均周期。

    jaw_shift

    [in] 蓝线相对于价格图表的偏移。

    teeth_period

    [in] 红线(鳄鱼指标的齿线)平均周期。

    teeth_shift

    [in] 红线相对于价格图表的偏移。

    lips_period

    [in] 绿线(鳄鱼指标的唇线)平均周期。

    lips_shift

    [in] 绿线相对于价格图表的偏移。

    ma_method

    [in]  平均方法,可以是 ENUM_MA_METHOD 枚举值中的任意一个。

    applied_price

    [in]  应用的价格,可以是 ENUM_APPLIED_PRICE 价格常数中的任一个或者是另一个指标的句柄。

    返回指定技术指标的句柄,如果失败,则返回 INVALID_HANDLE。 要从未使用的指标中释放计算机内存,请使用指标句柄传递到 IndicatorRelease()

    缓冲区索引: 0 — GATORJAW_LINE, 1 — GATORTEETH_LINE, 2 — GATORLIPS_LINE.


    在EA中声明输入变量和全局变量以创建指标:

    //+------------------------------------------------------------------+
    //|                                        TestWilliamsAlligator.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    //--- enums
    enum ENUM_LINE_STATE
      {
       LINE_STATE_NONE,        // Undefined state
       LINE_STATE_UP,          // Upward
       LINE_STATE_DOWN,        // Downward
       LINE_STATE_TURN_UP,     // Upward reversal
       LINE_STATE_TURN_DOWN,   // Downward reversal
       LINE_STATE_STOP_UP,     // Upward stop
       LINE_STATE_STOP_DOWN,   // Downward stop
       LINE_STATE_ABOVE,       // Above value
       LINE_STATE_UNDER,       // Below value
       LINE_STATE_CROSS_UP,    // Crossing value upwards
       LINE_STATE_CROSS_DOWN,  // Crossing value downwards
       LINE_STATE_TOUCH_BELOW, // Touching value from below 
       LINE_STATE_TOUCH_ABOVE, // Touch value from above
       LINE_STATE_EQUALS,      // Equal to value
      };
    //--- input parameters
    input uint                 InpPeriodJaws  =  13;            /* Jaws Period    */
    input int                  InpShiftJaws   =  8;             /* Jaws Shift     */
    input uint                 InpPeriodTeeth =  8;             /* Teeth Period   */
    input int                  InpShiftTeeth  =  5;             /* Teeth Shift    */
    input uint                 InpPeriodLips  =  5;             /* Lips Period    */
    input int                  InpShiftLips   =  3;             /* Lips Shift     */
    input ENUM_MA_METHOD       InpMethod      =  MODE_SMMA;     /* Smoothed       */
    input ENUM_APPLIED_PRICE   InpAppliedPrice=  PRICE_MEDIAN;  /* Applied Price  */
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      period_jaws=0;          // Jaws line calculation period
    int      period_teeth=0;         // Teeth line calculation period
    int      period_lips=0;          // Lips line calculation period
    int      ind_digits=0;           // Number of decimal places in the indicator values
    string   ind_title;              // Indicator description
    
    

    在EA中使用仪表板时,声明全局变量包括面板类的文件

    //+------------------------------------------------------------------+
    //|                                        TestWilliamsAlligator.mq5 |
    //|                                  Copyright 2023, MetaQuotes Ltd. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Ltd."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    //--- includes
    #include <Dashboard\Dashboard.mqh>
    //--- enums
    enum ENUM_LINE_STATE
      {
       LINE_STATE_NONE,        // Undefined state
       LINE_STATE_UP,          // Upward
       LINE_STATE_DOWN,        // Downward
       LINE_STATE_TURN_UP,     // Upward reversal
       LINE_STATE_TURN_DOWN,   // Downward reversal
       LINE_STATE_STOP_UP,     // Upward stop
       LINE_STATE_STOP_DOWN,   // Downward stop
       LINE_STATE_ABOVE,       // Above value
       LINE_STATE_UNDER,       // Below value
       LINE_STATE_CROSS_UP,    // Crossing value upwards
       LINE_STATE_CROSS_DOWN,  // Crossing value downwards
       LINE_STATE_TOUCH_BELOW, // Touching value from below 
       LINE_STATE_TOUCH_ABOVE, // Touch value from above
       LINE_STATE_EQUALS,      // Equal to value
      };
    //--- input parameters
    input uint                 InpPeriodJaws  =  13;            /* Jaws Period    */
    input int                  InpShiftJaws   =  8;             /* Jaws Shift     */
    input uint                 InpPeriodTeeth =  8;             /* Teeth Period   */
    input int                  InpShiftTeeth  =  5;             /* Teeth Shift    */
    input uint                 InpPeriodLips  =  5;             /* Lips Period    */
    input int                  InpShiftLips   =  3;             /* Lips Shift     */
    input ENUM_MA_METHOD       InpMethod      =  MODE_SMMA;     /* Smoothed       */
    input ENUM_APPLIED_PRICE   InpAppliedPrice=  PRICE_MEDIAN;  /* Applied Price  */
    //--- global variables
    int      handle=INVALID_HANDLE;  // Indicator handle
    int      period_jaws=0;          // Jaws line calculation period
    int      period_teeth=0;         // Teeth line calculation period
    int      period_lips=0;          // Lips line calculation period
    int      ind_digits=0;           // Number of decimal places in the indicator values
    string   ind_title;              // Indicator description
    //--- variables for the panel
    int      mouse_bar_index;        // Index of the bar the data is taken from
    CDashboard *panel=NULL;          // Pointer to the panel object
    
    


    初始化

    设置指标的全局变量值并创建其句柄:

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set and adjust the calculation period if necessary
       period_jaws=int(InpPeriodJaws<1 ? 13 : InpPeriodJaws);
       period_teeth=int(InpPeriodTeeth<1 ? 8 : InpPeriodTeeth);
       period_lips=int(InpPeriodLips<1 ? 5 : InpPeriodLips);
    //--- Set the indicator name and the number of decimal places
       ind_title=StringFormat("Alligator(%lu,%lu,%lu)",period_jaws,period_teeth,period_lips);
       ind_digits=Digits();
    //--- Create indicator handle
       ResetLastError();
       handle=iAlligator(Symbol(),PERIOD_CURRENT,period_jaws,InpShiftJaws,period_teeth,InpShiftTeeth,period_lips,InpShiftLips,InpMethod,InpAppliedPrice);
       if(handle==INVALID_HANDLE)
         {
          PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
          return INIT_FAILED;
         }
    
    //--- Successful initialization
       return(INIT_SUCCEEDED);
      }
    
    

    如果EA涉及使用仪表板,在此处创建它

    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- create timer
       EventSetTimer(60);
    
    //--- Indicator
    //--- Set and adjust the calculation period if necessary
       period_jaws=int(InpPeriodJaws<1 ? 13 : InpPeriodJaws);
       period_teeth=int(InpPeriodTeeth<1 ? 8 : InpPeriodTeeth);
       period_lips=int(InpPeriodLips<1 ? 5 : InpPeriodLips);
    //--- Set the indicator name and the number of decimal places
       ind_title=StringFormat("Alligator(%lu,%lu,%lu)",period_jaws,period_teeth,period_lips);
       ind_digits=Digits();
    //--- Create indicator handle
       ResetLastError();
       handle=iAlligator(Symbol(),PERIOD_CURRENT,period_jaws,InpShiftJaws,period_teeth,InpShiftTeeth,period_lips,InpShiftLips,InpMethod,InpAppliedPrice);
       if(handle==INVALID_HANDLE)
         {
          PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
          return INIT_FAILED;
         }
    
    //--- Dashboard
    //--- Create the panel
       panel=new CDashboard(1,20,20,199,261);
       if(panel==NULL)
         {
          Print("Error. Failed to create panel object");
          return INIT_FAILED;
         }
    //--- Set font parameters
       panel.SetFontParams("Calibri",9);
    //--- Display the panel with the "Symbol, Timeframe description" header text
       panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
    //--- Create a table with ID 0 to display bar data in it
       panel.CreateNewTable(0);
    //--- Draw a table with ID 0 on the panel background
       panel.DrawGrid(0,2,20,6,2,18,97);
    
    //--- Create a table with ID 1 to display indicator data in it
       panel.CreateNewTable(1);
    //--- Get the Y2 table coordinate with ID 0 and
    //--- set the Y1 coordinate for the table with ID 1
       int y1=panel.TableY2(0)+22;
    //--- Draw a table with ID 1 on the panel background
       panel.DrawGrid(1,2,y1,5,2,18,97);
       
    //--- Display tabular data in the journal
       panel.GridPrint(0,2);
       panel.GridPrint(1,2);
    //--- Initialize the variable with the index of the mouse cursor bar
       mouse_bar_index=0;
    //--- Display the data of the current bar on the panel
       DrawData(mouse_bar_index,TimeCurrent());
    
    //--- Successful initialization
       return(INIT_SUCCEEDED);
      }
    
    


    析构

    在EA的 OnDeinit() 处理函数中释放指标句柄:

    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- destroy timer
       EventKillTimer();
       
    //--- Release handle of the indicator
       ResetLastError();
       if(!IndicatorRelease(handle))
          PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
    //--- Clear all comments on the chart
       Comment("");
      }
    
    

    创建的仪表板对象在EA析构时被删除:

    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
      {
    //--- destroy timer
       EventKillTimer();
       
    //--- Release handle of the indicator
       ResetLastError();
       if(!IndicatorRelease(handle))
          PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
    //--- Clear all comments on the chart
       Comment("");
       
    //--- If the panel object exists, delete it
       if(panel!=NULL)
          delete panel;
      }
    


    读取数据

    下面提供了通过指标句柄获取数据的一般函数,在关于将振荡指标连接到EA的文章中,已经对这些函数进行了回顾。所提供的函数可以在自定义程序中“按原样”使用:

    //+------------------------------------------------------------------+
    //| Return the indicator data on the specified bar                   |
    //+------------------------------------------------------------------+
    double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
      {
       double array[1]={0};
       ResetLastError();
       if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
         {
          PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
          return EMPTY_VALUE;
         }
       return array[0];
      }
    //+------------------------------------------------------------------+
    //| Return the state of the indicator line                           |
    //+------------------------------------------------------------------+
    ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
      {
    //--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
       const double value0=IndicatorValue(ind_handle,index,  buffer_num);
       const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
       const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
    //--- If at least one of the values could not be obtained, return an undefined value 
       if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
          return LINE_STATE_NONE;
    //--- Line upward reversal (value2>value1 && value0>value1)
       if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
          return LINE_STATE_TURN_UP;
    //--- Line upward direction (value2<=value1 && value0>value1)
       else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
          return LINE_STATE_UP;
    //--- Line upward stop (value2<=value1 && value0==value1)
       else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
          return LINE_STATE_STOP_UP;
    //--- Line downward reversal (value2<value1 && value0<value1)
       if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
          return LINE_STATE_TURN_DOWN;
    //--- Line downward direction (value2>=value1 && value0<value1)
       else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
          return LINE_STATE_DOWN;
    //--- Line downward stop (value2>=value1 && value0==value1)
       else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
          return LINE_STATE_STOP_DOWN;
    //--- Undefined state
       return LINE_STATE_NONE;
      }
    //+------------------------------------------------------------------+
    //| Return the state of the line relative to the specified level     |
    //+------------------------------------------------------------------+
    ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
      {
    //--- Get the values of the indicator line with the shift (0,1) relative to the passed index
       const double value0=IndicatorValue(ind_handle,index,  buffer_num);
       const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
    //--- If at least one of the values could not be obtained, return an undefined value 
       if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
          return LINE_STATE_NONE;
    //--- Define the second level to compare
       double level=(level1==EMPTY_VALUE ? level0 : level1);
    //--- The line is below the level (value1<level && value0<level0)
       if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
          return LINE_STATE_UNDER;
    //--- The line is above the level (value1>level && value0>level0)
       if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
          return LINE_STATE_ABOVE;
    //--- The line crossed the level upwards (value1<=level && value0>level0)
       if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
          return LINE_STATE_CROSS_UP;
    //--- The line crossed the level downwards (value1>=level && value0<level0)
       if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
          return LINE_STATE_CROSS_DOWN;
    //--- The line touched the level from below (value1<level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_TOUCH_BELOW;
    //--- The line touched the level from above (value1>level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_TOUCH_BELOW;
    //--- Line is equal to the level value (value1==level0 && value0==level0)
       if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
          return LINE_STATE_EQUALS;
    //--- Undefined state
       return LINE_STATE_NONE;
      }
    //+------------------------------------------------------------------+
    //| Return the indicator line state description                      |
    //+------------------------------------------------------------------+
    string LineStateDescription(const ENUM_LINE_STATE state)
      {
       switch(state)
         {
          case LINE_STATE_UP         :  return "Up";
          case LINE_STATE_STOP_UP    :  return "Stop Up";
          case LINE_STATE_TURN_UP    :  return "Turn Up";
          case LINE_STATE_DOWN       :  return "Down";
          case LINE_STATE_STOP_DOWN  :  return "Stop Down";
          case LINE_STATE_TURN_DOWN  :  return "Turn Down";
          case LINE_STATE_ABOVE      :  return "Above level";
          case LINE_STATE_UNDER      :  return "Under level";
          case LINE_STATE_CROSS_UP   :  return "Crossing Up";
          case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
          case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
          case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
          case LINE_STATE_EQUALS     :  return "Equals";
          default                    :  return "Unknown";
         }
      }
    
    

    使用仪表板时,数据会使用以下函数显示在面板上:

    //+------------------------------------------------------------------+
    //| Display data from the specified timeseries index to the panel    |
    //+------------------------------------------------------------------+
    void DrawData(const int index,const datetime time)
      {
    //--- Declare the variables to receive data in them
       MqlTick  tick={0};
       MqlRates rates[1];
    
    //--- Exit if unable to get the current prices
       if(!SymbolInfoTick(Symbol(),tick))
          return;
    //--- Exit if unable to get the bar data by the specified index
       if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
          return;
    
    //--- Set font parameters for bar and indicator data headers
       int  size=0;
       uint flags=0;
       uint angle=0;
       string name=panel.FontParams(size,flags,angle);
       panel.SetFontParams(name,9,FW_BOLD);
       panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
       panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
    //--- Set font parameters for bar and indicator data
       panel.SetFontParams(name,9);
    
    //--- Display the data of the specified bar in table 0 on the panel
       panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
       panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
       panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
       panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
       panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
       panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);
    
    //--- Get the indicator lines data
       double value_jaws=IndicatorValue(handle,index,GATORJAW_LINE);
       double value_teeth=IndicatorValue(handle,index,GATORTEETH_LINE);
       double value_lips=IndicatorValue(handle,index,GATORLIPS_LINE);
       
    //--- Display the Jaws line data from the specified bar on the panel in table 1
       string jaws_str=StringFormat("Jaws(%lu)",period_jaws);
       panel.DrawText(jaws_str, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
       string value_str=(value_jaws!=EMPTY_VALUE ? DoubleToString(value_jaws,ind_digits) : "");
       panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,90);
    //--- Display the Teeth line data from the specified bar on the panel in table 1
       string teeth_str=StringFormat("Teeth(%lu)",period_teeth);
       panel.DrawText(teeth_str, panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
       value_str=(value_teeth!=EMPTY_VALUE ? DoubleToString(value_teeth,ind_digits) : "");
       panel.DrawText(value_str,panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,90);
    //--- Display the Lips line data from the specified bar on the panel in table 1
       string lips_str=StringFormat("Lips(%lu)",period_jaws);
       panel.DrawText(lips_str, panel.CellX(1,2,0)+2, panel.CellY(1,2,0)+2);
       value_str=(value_lips!=EMPTY_VALUE ? DoubleToString(value_lips,ind_digits) : "");
       panel.DrawText(value_str,panel.CellX(1,2,1)+2,panel.CellY(1,2,1)+2,clrNONE,90);
       
    //--- Display a description of the Teeth line state relative to the Jaws line
       panel.DrawText("Teeth vs Jaws", panel.CellX(1,3,0)+2, panel.CellY(1,3,0)+2);
       ENUM_LINE_STATE state_tj=LineStateRelative(handle,index,1,value_jaws,IndicatorValue(handle,index+1,GATORJAW_LINE));
       string state_tj_str=
         (
          state_tj==LINE_STATE_ABOVE        ?  "Teeth > Jaws"  : 
          state_tj==LINE_STATE_UNDER        ?  "Teeth < Jaws"  : 
          state_tj==LINE_STATE_TOUCH_ABOVE  || 
          state_tj==LINE_STATE_TOUCH_BELOW  ?  "Touch"     :
          LineStateDescription(state_tj)
         );
    //--- The label color changes depending on the value of the line relative to the level
       color clr=(state_tj==LINE_STATE_CROSS_UP || state_tj==LINE_STATE_ABOVE  ? clrBlue : state_tj==LINE_STATE_CROSS_DOWN || state_tj==LINE_STATE_UNDER ? clrRed : clrNONE);
       panel.DrawText(state_tj_str,panel.CellX(1,3,1)+2,panel.CellY(1,3,1)+2,clr,90);
       
    //--- Display a description of the Lips line state relative to the Teeth line
       panel.DrawText("Lips vs Teeth", panel.CellX(1,4,0)+2, panel.CellY(1,4,0)+2);
       ENUM_LINE_STATE state_lt=LineStateRelative(handle,index,2,value_teeth,IndicatorValue(handle,index+1,GATORTEETH_LINE));
       string state_lt_str=
         (
          state_lt==LINE_STATE_ABOVE        ?  "Lips > Teeth"  : 
          state_lt==LINE_STATE_UNDER        ?  "Lips < Teeth"  : 
          state_lt==LINE_STATE_TOUCH_ABOVE  || 
          state_lt==LINE_STATE_TOUCH_BELOW  ?  "Touch"     :
          LineStateDescription(state_lt)
         );
    //--- The label color changes depending on the value of the line relative to the level
       clr=(state_lt==LINE_STATE_CROSS_UP || state_lt==LINE_STATE_ABOVE  ? clrBlue : state_lt==LINE_STATE_CROSS_DOWN || state_lt==LINE_STATE_UNDER ? clrRed : clrNONE);
       panel.DrawText(state_lt_str,panel.CellX(1,4,1)+2,panel.CellY(1,4,1)+2,clr,90);
       
    //--- Redraw the chart to immediately display all changes on the panel
       ChartRedraw(ChartID());
      }
    
    

    除了位于光标下方的柱上的指标线的值外,面板还显示“齿-颚”线和“唇-齿”线的比率状态。它们的关系显示在文本中,它们的相对位置由显示的文本的颜色指示。

    此外,当使用仪表板时,面板事件处理程序在OnChartEvent()EA事件处理函数中调用,以及处理用于接收光标下的柱形索引的事件:

    //+------------------------------------------------------------------+
    //| ChartEvent function                                              |
    //+------------------------------------------------------------------+
    void OnChartEvent(const int id,
                      const long &lparam,
                      const double &dparam,
                      const string &sparam)
      {
    //--- Handling the panel
    //--- Call the panel event handler
       panel.OnChartEvent(id,lparam,dparam,sparam);
    
    //--- If the cursor moves or a click is made on the chart
       if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
         {
          //--- Declare the variables to record time and price coordinates in them
          datetime time=0;
          double price=0;
          int wnd=0;
          //--- If the cursor coordinates are converted to date and time
          if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
            {
             //--- write the bar index where the cursor is located to a global variable
             mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
             //--- Display the bar data under the cursor on the panel 
             DrawData(mouse_bar_index,time);
            }
         }
    
    //--- If we received a custom event, display the appropriate message in the journal
       if(id>CHARTEVENT_CUSTOM)
         {
          //--- Here we can implement handling a click on the close button on the panel
          PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
         }
      }
    
    

    编译EA并在图表上启动后,我们可以监控面板上指标值和线的状态:


    您可以在文章附件中查看TestWilliamsAlligator.mq5测试EA。


    动量振荡指标

    比尔威廉姆斯的动量振荡指标(AO,Awesome Oscillator)是一个34周期的简单移动平均线,通过条形图(H+L)/2的中点绘制,从5周期的简单运动平均线中减去,建立在条形图(H+L)/2中心点上。它非常清楚地向我们展示了目前市场驱动力的变化。

    有效的买入信号

    当直方图高于零线时,“碟形”是唯一的买入信号。不要忘记以下几点:
    • 当直方图将其方向从向下反转为向上时,产生碟形信号。第二列比第一列低,颜色为红色。第三列比第二列高,并且颜色为绿色;
    • 对于要生成的碟形信号,直方图应该至少有三个条。

    请记住,对于要使用的碟形信号,所有“动量振荡指标”列都应该在零线上。

    “零线交叉”是当直方图从负值移动到正值时形成的买入信号。请记住:

    • 对于要生成的该信号,仅需要两列;
    • 第一个柱位于零线以下,第二个柱与零线交叉(从负值转换为正值);
    • 同时产生买卖信号是不可能的。

    当直方图值出现在零以下时,“双峰”是唯一可以生成的买入信号。请记住以下内容:

    • 当你有一个向下指向的峰值(最低的低点)在零线以下,然后是另一个向下的峰值,该峰值比前一个向下看的峰值略高(一个绝对值较小的负数,因此更接近零线)时,就会产生信号;
    • 直方图将在两个峰值之间的零线以下。如果柱越过两个尖点之间的零线,那么买入信号就不起作用。然而,会产生一个不同的购买信号——零线交叉;
    • 条形图的每个新的尖峰比先前的尖峰高(更接近零线的较小绝对值的负数);
    • 如果形成了额外的更高的尖峰(即更接近零线),并且柱没有越过零线,则将生成额外的买入信号。

      卖出信号

      动量振荡指标的卖出信号和买入信号等同,源信号被反转为低于零。零线交叉在下降时发生 - 第一个柱高于零线,第二个低于它。双峰信号中高于零线也是反转的。



      参数

      iAO()函数用于创建指标句柄:

      返回动量振荡指标句柄。只有一个缓冲区。

      int  iAO(
         string           symbol,     // symbol name
         ENUM_TIMEFRAMES  period      // period
         );
      

      symbol

      [in] 其数据将用于计算指标的金融工具的交易品种名称。NULL表示当前交易品种。

      period

      [in] 周期值可以是ENUM_TIMEFRAMES枚举值之一,0表示当前时间框架。

      返回指定技术指标的句柄,如果失败,则返回 INVALID_HANDLE。 要从未使用的指标中释放计算机内存,请使用指标句柄传递到 IndicatorRelease()


      在EA中声明输入变量和全局变量以创建指标:

      //+------------------------------------------------------------------+
      //|                                               TestWilliamsAO.mq5 |
      //|                                  Copyright 2023, MetaQuotes Ltd. |
      //|                                             https://www.mql5.com |
      //+------------------------------------------------------------------+
      #property copyright "Copyright 2023, MetaQuotes Ltd."
      #property link      "https://www.mql5.com"
      #property version   "1.00"
      //--- enums
      enum ENUM_LINE_STATE
        {
         LINE_STATE_NONE,        // Undefined state
         LINE_STATE_UP,          // Upward
         LINE_STATE_DOWN,        // Downward
         LINE_STATE_TURN_UP,     // Upward reversal
         LINE_STATE_TURN_DOWN,   // Downward reversal
         LINE_STATE_STOP_UP,     // Upward stop
         LINE_STATE_STOP_DOWN,   // Downward stop
         LINE_STATE_ABOVE,       // Above value
         LINE_STATE_UNDER,       // Below value
         LINE_STATE_CROSS_UP,    // Crossing value upwards
         LINE_STATE_CROSS_DOWN,  // Crossing value downwards
         LINE_STATE_TOUCH_BELOW, // Touching value from below 
         LINE_STATE_TOUCH_ABOVE, // Touch value from above
         LINE_STATE_EQUALS,      // Equal to value
        };
      //--- global variables
      int      handle=INVALID_HANDLE;  // Indicator handle
      int      ind_digits=0;           // Number of decimal places in the indicator values
      string   ind_title;              // Indicator description
      
      

      在EA中使用仪表板时,声明全局变量包括面板类的文件

      //+------------------------------------------------------------------+
      //|                                               TestWilliamsAO.mq5 |
      //|                                  Copyright 2023, MetaQuotes Ltd. |
      //|                                             https://www.mql5.com |
      //+------------------------------------------------------------------+
      #property copyright "Copyright 2023, MetaQuotes Ltd."
      #property link      "https://www.mql5.com"
      #property version   "1.00"
      //--- includes
      #include <Dashboard\Dashboard.mqh>
      //--- enums
      enum ENUM_LINE_STATE
        {
         LINE_STATE_NONE,        // Undefined state
         LINE_STATE_UP,          // Upward
         LINE_STATE_DOWN,        // Downward
         LINE_STATE_TURN_UP,     // Upward reversal
         LINE_STATE_TURN_DOWN,   // Downward reversal
         LINE_STATE_STOP_UP,     // Upward stop
         LINE_STATE_STOP_DOWN,   // Downward stop
         LINE_STATE_ABOVE,       // Above value
         LINE_STATE_UNDER,       // Below value
         LINE_STATE_CROSS_UP,    // Crossing value upwards
         LINE_STATE_CROSS_DOWN,  // Crossing value downwards
         LINE_STATE_TOUCH_BELOW, // Touching value from below 
         LINE_STATE_TOUCH_ABOVE, // Touch value from above
         LINE_STATE_EQUALS,      // Equal to value
        };
      //--- global variables
      int      handle=INVALID_HANDLE;  // Indicator handle
      int      ind_digits=0;           // Number of decimal places in the indicator values
      string   ind_title;              // Indicator description
      //--- variables for the panel
      int      mouse_bar_index;        // Index of the bar the data is taken from
      CDashboard *panel=NULL;          // Pointer to the panel object
      
      


      初始化

      设置指标的全局变量值并创建其句柄:

      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
        {
      //--- create timer
         EventSetTimer(60);
      
      //--- Indicator
      //--- Set the indicator name and the number of decimal places
         ind_title="AO";
         ind_digits=Digits()+1;
      //--- Create indicator handle
         ResetLastError();
         handle=iAO(Symbol(),PERIOD_CURRENT);
         if(handle==INVALID_HANDLE)
           {
            PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
            return INIT_FAILED;
           }
      
      //--- Successful initialization
         return(INIT_SUCCEEDED);
        }
      
      

      如果EA涉及使用仪表板,在此处创建它

      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
        {
      //--- create timer
         EventSetTimer(60);
      
      //--- Indicator
      //--- Set the indicator name and the number of decimal places
         ind_title="AO";
         ind_digits=Digits()+1;
      //--- Create indicator handle
         ResetLastError();
         handle=iAO(Symbol(),PERIOD_CURRENT);
         if(handle==INVALID_HANDLE)
           {
            PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
            return INIT_FAILED;
           }
      
      //--- Dashboard
      //--- Create the panel
         panel=new CDashboard(1,20,20,199,225);
         if(panel==NULL)
           {
            Print("Error. Failed to create panel object");
            return INIT_FAILED;
           }
      //--- Set font parameters
         panel.SetFontParams("Calibri",9);
      //--- Display the panel with the "Symbol, Timeframe description" header text
         panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
      //--- Create a table with ID 0 to display bar data in it
         panel.CreateNewTable(0);
      //--- Draw a table with ID 0 on the panel background
         panel.DrawGrid(0,2,20,6,2,18,97);
      
      //--- Create a table with ID 1 to display indicator data in it
         panel.CreateNewTable(1);
      //--- Get the Y2 table coordinate with ID 0 and
      //--- set the Y1 coordinate for the table with ID 1
         int y1=panel.TableY2(0)+22;
      //--- Draw a table with ID 1 on the panel background
         panel.DrawGrid(1,2,y1,3,2,18,97);
         
      //--- Display tabular data in the journal
         panel.GridPrint(0,2);
         panel.GridPrint(1,2);
      //--- Initialize the variable with the index of the mouse cursor bar
         mouse_bar_index=0;
      //--- Display the data of the current bar on the panel
         DrawData(mouse_bar_index,TimeCurrent());
      
      //--- Successful initialization
         return(INIT_SUCCEEDED);
        }
      
      


      析构

      在EA的 OnDeinit() 处理函数中释放指标句柄:

      //+------------------------------------------------------------------+
      //| Expert deinitialization function                                 |
      //+------------------------------------------------------------------+
      void OnDeinit(const int reason)
        {
      //--- destroy timer
         EventKillTimer();
         
      //--- Release handle of the indicator
         ResetLastError();
         if(!IndicatorRelease(handle))
            PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
      //--- Clear all comments on the chart
         Comment("");
        }
      
      

      创建的仪表板对象在EA析构时被删除:

      //+------------------------------------------------------------------+
      //| Expert deinitialization function                                 |
      //+------------------------------------------------------------------+
      void OnDeinit(const int reason)
        {
      //--- destroy timer
         EventKillTimer();
         
      //--- Release handle of the indicator
         ResetLastError();
         if(!IndicatorRelease(handle))
            PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
      //--- Clear all comments on the chart
         Comment("");
         
      //--- If the panel object exists, delete it
         if(panel!=NULL)
            delete panel;
        }
      


      读取数据

      下面提供了通过指标句柄获取数据的一般函数,在关于将振荡指标连接到EA的文章中,已经对这些函数进行了回顾。所提供的函数可以在自定义程序中“按原样”使用:

      //+------------------------------------------------------------------+
      //| Return the indicator data on the specified bar                   |
      //+------------------------------------------------------------------+
      double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
        {
         double array[1]={0};
         ResetLastError();
         if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
           {
            PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
            return EMPTY_VALUE;
           }
         return array[0];
        }
      //+------------------------------------------------------------------+
      //| Return the state of the indicator line                           |
      //+------------------------------------------------------------------+
      ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
        {
      //--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
         const double value0=IndicatorValue(ind_handle,index,  buffer_num);
         const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
         const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
      //--- If at least one of the values could not be obtained, return an undefined value 
         if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
            return LINE_STATE_NONE;
      //--- Line upward reversal (value2>value1 && value0>value1)
         if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
            return LINE_STATE_TURN_UP;
      //--- Line upward direction (value2<=value1 && value0>value1)
         else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
            return LINE_STATE_UP;
      //--- Line upward stop (value2<=value1 && value0==value1)
         else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
            return LINE_STATE_STOP_UP;
      //--- Line downward reversal (value2<value1 && value0<value1)
         if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
            return LINE_STATE_TURN_DOWN;
      //--- Line downward direction (value2>=value1 && value0<value1)
         else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
            return LINE_STATE_DOWN;
      //--- Line downward stop (value2>=value1 && value0==value1)
         else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
            return LINE_STATE_STOP_DOWN;
      //--- Undefined state
         return LINE_STATE_NONE;
        }
      //+------------------------------------------------------------------+
      //| Return the state of the line relative to the specified level     |
      //+------------------------------------------------------------------+
      ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
        {
      //--- Get the values of the indicator line with the shift (0,1) relative to the passed index
         const double value0=IndicatorValue(ind_handle,index,  buffer_num);
         const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
      //--- If at least one of the values could not be obtained, return an undefined value 
         if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
            return LINE_STATE_NONE;
      //--- Define the second level to compare
         double level=(level1==EMPTY_VALUE ? level0 : level1);
      //--- The line is below the level (value1<level && value0<level0)
         if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
            return LINE_STATE_UNDER;
      //--- The line is above the level (value1>level && value0>level0)
         if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
            return LINE_STATE_ABOVE;
      //--- The line crossed the level upwards (value1<=level && value0>level0)
         if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
            return LINE_STATE_CROSS_UP;
      //--- The line crossed the level downwards (value1>=level && value0<level0)
         if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
            return LINE_STATE_CROSS_DOWN;
      //--- The line touched the level from below (value1<level0 && value0==level0)
         if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
            return LINE_STATE_TOUCH_BELOW;
      //--- The line touched the level from above (value1>level0 && value0==level0)
         if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
            return LINE_STATE_TOUCH_BELOW;
      //--- Line is equal to the level value (value1==level0 && value0==level0)
         if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
            return LINE_STATE_EQUALS;
      //--- Undefined state
         return LINE_STATE_NONE;
        }
      //+------------------------------------------------------------------+
      //| Return the indicator line state description                      |
      //+------------------------------------------------------------------+
      string LineStateDescription(const ENUM_LINE_STATE state)
        {
         switch(state)
           {
            case LINE_STATE_UP         :  return "Up";
            case LINE_STATE_STOP_UP    :  return "Stop Up";
            case LINE_STATE_TURN_UP    :  return "Turn Up";
            case LINE_STATE_DOWN       :  return "Down";
            case LINE_STATE_STOP_DOWN  :  return "Stop Down";
            case LINE_STATE_TURN_DOWN  :  return "Turn Down";
            case LINE_STATE_ABOVE      :  return "Above level";
            case LINE_STATE_UNDER      :  return "Under level";
            case LINE_STATE_CROSS_UP   :  return "Crossing Up";
            case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
            case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
            case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
            case LINE_STATE_EQUALS     :  return "Equals";
            default                    :  return "Unknown";
           }
        }
      
      

      使用仪表板时,数据会使用以下函数显示在面板上:

      //+------------------------------------------------------------------+
      //| Display data from the specified timeseries index to the panel    |
      //+------------------------------------------------------------------+
      void DrawData(const int index,const datetime time)
        {
      //--- Declare the variables to receive data in them
         MqlTick  tick={0};
         MqlRates rates[1];
      
      //--- Exit if unable to get the current prices
         if(!SymbolInfoTick(Symbol(),tick))
            return;
      //--- Exit if unable to get the bar data by the specified index
         if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
            return;
      
      //--- Set font parameters for bar and indicator data headers
         int  size=0;
         uint flags=0;
         uint angle=0;
         string name=panel.FontParams(size,flags,angle);
         panel.SetFontParams(name,9,FW_BOLD);
         panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
         panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
      //--- Set font parameters for bar and indicator data
         panel.SetFontParams(name,9);
      
      //--- Display the data of the specified bar in table 0 on the panel
         panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
         panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
         panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
         panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
         panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
         panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);
      
      //--- Display the indicator data from the specified bar on the panel in table 1
         panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
         double value0=IndicatorValue(handle,index,  0);
         double value1=IndicatorValue(handle,index+1,0);
         string value_str=(value0!=EMPTY_VALUE ? DoubleToString(value0,ind_digits) : "");
         color clr=(value0>value1 ? clrGreen : value0<value1 ? clrRed : clrNONE);
         panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,90);
         
      //--- Display a description of the indicator line state
         panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
         ENUM_LINE_STATE state=LineState(handle,index,0);
         panel.DrawText(LineStateDescription(state),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clr,90);
         
      //--- Display a description of the indicator line state relative to zero
         panel.DrawText("AO vs Zero", panel.CellX(1,2,0)+2, panel.CellY(1,2,0)+2);
         ENUM_LINE_STATE state_zero=LineStateRelative(handle,index,0,0);
         string state_zero_str=
           (
            state_zero==LINE_STATE_ABOVE        ?  "AO > 0"  : 
            state_zero==LINE_STATE_UNDER        ?  "AO < 0"  : 
            state_zero==LINE_STATE_TOUCH_ABOVE  || 
            state_zero==LINE_STATE_TOUCH_BELOW  ?  "Touch"     :
            LineStateDescription(state_zero)
           );
      //--- The label color changes depending on the value of the line relative to the level
         clr=(state_zero==LINE_STATE_CROSS_UP ? clrGreen : state_zero==LINE_STATE_CROSS_DOWN ? clrRed : clrNONE);
         panel.DrawText(state_zero_str,panel.CellX(1,2,1)+2,panel.CellY(1,2,1)+2,clr,90);
         
      //--- Redraw the chart to immediately display all changes on the panel
         ChartRedraw(ChartID());
        }
      
      

      除了描述指标线的状态(其具有位于光标下的直方图列的颜色)之外,面板还显示其相对于零的位置状态。当指标线向上穿过零线时,它用绿色文本标记,而向下的方向用红色文本标记。

      此外,当使用仪表板时,面板事件处理程序在OnChartEvent()EA事件处理函数中调用,以及处理用于接收光标下的柱形索引的事件:

      //+------------------------------------------------------------------+
      //| ChartEvent function                                              |
      //+------------------------------------------------------------------+
      void OnChartEvent(const int id,
                        const long &lparam,
                        const double &dparam,
                        const string &sparam)
        {
      //--- Handling the panel
      //--- Call the panel event handler
         panel.OnChartEvent(id,lparam,dparam,sparam);
      
      //--- If the cursor moves or a click is made on the chart
         if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
           {
            //--- Declare the variables to record time and price coordinates in them
            datetime time=0;
            double price=0;
            int wnd=0;
            //--- If the cursor coordinates are converted to date and time
            if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
              {
               //--- write the bar index where the cursor is located to a global variable
               mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
               //--- Display the bar data under the cursor on the panel 
               DrawData(mouse_bar_index,time);
              }
           }
      
      //--- If we received a custom event, display the appropriate message in the journal
         if(id>CHARTEVENT_CUSTOM)
           {
            //--- Here we can implement handling a click on the close button on the panel
            PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
           }
        }
      
      

      编译EA并在图表上启动后,我们可以监控面板上指标值和线的状态:


      您可以在文章附件中查看TestWilliamsAO.mq5测试EA。


      分形指标

      所有市场的特点都是,在大多数时间里,价格波动不大,只有在短时间内(15-30%)才能看到趋势变化。最能获利的时期通常是市场价格根据特定趋势变化的时候。

      分形指标是Bill Williams交易系统的五个指标之一,可以检测底部或顶部。向上分形的技术定义是一系列至少五个连续的柱,其中在最高最大值之前和之后有两个柱具有较低的最大值。反转集是一系列至少五个连续的柱形图,中间是最低的低点,两边是两个较高的低点。这与卖出分形相关。在图形上,分形的值为最高点和最低点,并由向上或向下箭头表示。

      分形技术指标的信号需要使用鳄鱼技术指标进行过滤。换言之,如果分形比鳄鱼指标的齿线低,则不应结束买入交易;如果分形比鳄鱼指标的齿线高,则不应该结束卖出交易。在分形信号形成并生效后(这是由其在鳄鱼指标颚线之外的位置决定的),它一直是一个信号,直到它被击中,或者直到出现更新的分形信号。



      参数

      iFractals()函数用于创建指标句柄:

      返回分形指标的句柄。

      int  iFractals(
         string           symbol,     // symbol name
         ENUM_TIMEFRAMES  period      // period
         );
      

      symbol

      [in] 其数据将用于计算指标的金融工具的交易品种名称。NULL表示当前交易品种。

      period

      [in] 周期值可以是ENUM_TIMEFRAMES枚举值之一,0表示当前时间框架。

      返回值

      返回指定技术指标的句柄,如果失败,则返回 INVALID_HANDLE。 要从未使用的指标中释放计算机内存,请使用指标句柄传递到 IndicatorRelease()

      缓冲区索引:0 — UPPER_LINE, 1 — LOWER_LINE


      在EA中声明输入变量和全局变量以创建指标:

      //+------------------------------------------------------------------+
      //|                                         TestWilliamsFractals.mq5 |
      //|                                  Copyright 2023, MetaQuotes Ltd. |
      //|                                             https://www.mql5.com |
      //+------------------------------------------------------------------+
      #property copyright "Copyright 2023, MetaQuotes Ltd."
      #property link      "https://www.mql5.com"
      #property version   "1.00"
      //--- global variables
      int      handle=INVALID_HANDLE;  // Indicator handle
      int      ind_digits=0;           // Number of decimal places in the indicator values
      string   ind_title;              // Indicator description
      
      

      在EA中使用仪表板时,声明全局变量包括面板类的文件

      //+------------------------------------------------------------------+
      //|                                         TestWilliamsFractals.mq5 |
      //|                                  Copyright 2023, MetaQuotes Ltd. |
      //|                                             https://www.mql5.com |
      //+------------------------------------------------------------------+
      #property copyright "Copyright 2023, MetaQuotes Ltd."
      #property link      "https://www.mql5.com"
      #property version   "1.00"
      //--- includes
      #include <Dashboard\Dashboard.mqh>
      //--- global variables
      int      handle=INVALID_HANDLE;  // Indicator handle
      int      ind_digits=0;           // Number of decimal places in the indicator values
      string   ind_title;              // Indicator description
      //--- variables for the panel
      int      mouse_bar_index;        // Index of the bar the data is taken from
      CDashboard *panel=NULL;          // Pointer to the panel object
      
      


      初始化

      设置指标的全局变量值并创建其句柄:

      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
        {
      //--- create timer
         EventSetTimer(60);
      
      //--- Indicator
      //--- Set the indicator name and the number of decimal places
         ind_title="Fractals";
         ind_digits=Digits();
      //--- Create indicator handle
         ResetLastError();
         handle=iFractals(Symbol(),PERIOD_CURRENT);
         if(handle==INVALID_HANDLE)
           {
            PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
            return INIT_FAILED;
           }
      
      //--- Successful initialization
         return(INIT_SUCCEEDED);
        }
      
      

      如果EA涉及使用仪表板,在此处创建它

      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
        {
      //--- create timer
         EventSetTimer(60);
      
      //--- Indicator
      //--- Set the indicator name and the number of decimal places
         ind_title="Fractals";
         ind_digits=Digits();
      //--- Create indicator handle
         ResetLastError();
         handle=iFractals(Symbol(),PERIOD_CURRENT);
         if(handle==INVALID_HANDLE)
           {
            PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
            return INIT_FAILED;
           }
      
      //--- Dashboard
      //--- Create the panel
         panel=new CDashboard(1,20,20,199,225);
         if(panel==NULL)
           {
            Print("Error. Failed to create panel object");
            return INIT_FAILED;
           }
      //--- Set font parameters
         panel.SetFontParams("Calibri",9);
      //--- Display the panel with the "Symbol, Timeframe description" header text
         panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
      //--- Create a table with ID 0 to display bar data in it
         panel.CreateNewTable(0);
      //--- Draw a table with ID 0 on the panel background
         panel.DrawGrid(0,2,20,6,2,18,97);
      
      //--- Create a table with ID 1 to display indicator data in it
         panel.CreateNewTable(1);
      //--- Get the Y2 table coordinate with ID 0 and
      //--- set the Y1 coordinate for the table with ID 1
         int y1=panel.TableY2(0)+22;
      //--- Draw a table with ID 1 on the panel background
         panel.DrawGrid(1,2,y1,3,2,18,97);
         
      //--- Display tabular data in the journal
         panel.GridPrint(0,2);
         panel.GridPrint(1,2);
      //--- Initialize the variable with the index of the mouse cursor bar
         mouse_bar_index=0;
      //--- Display the data of the current bar on the panel
         DrawData(mouse_bar_index,TimeCurrent());
      
      //--- Successful initialization
         return(INIT_SUCCEEDED);
        }
      
      


      析构

      在EA的 OnDeinit() 处理函数中释放指标句柄:

      //+------------------------------------------------------------------+
      //| Expert deinitialization function                                 |
      //+------------------------------------------------------------------+
      void OnDeinit(const int reason)
        {
      //--- destroy timer
         EventKillTimer();
         
      //--- Release handle of the indicator
         ResetLastError();
         if(!IndicatorRelease(handle))
            PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
      //--- Clear all comments on the chart
         Comment("");
        }
      
      

      创建的仪表板对象在EA析构时被删除:

      //+------------------------------------------------------------------+
      //| Expert deinitialization function                                 |
      //+------------------------------------------------------------------+
      void OnDeinit(const int reason)
        {
      //--- destroy timer
         EventKillTimer();
         
      //--- Release handle of the indicator
         ResetLastError();
         if(!IndicatorRelease(handle))
            PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
      //--- Clear all comments on the chart
         Comment("");
         
      //--- If the panel object exists, delete it
         if(panel!=NULL)
            delete panel;
        }
      


      读取数据

      下面提供了通过指标句柄获取数据的一般函数,在关于将振荡指标连接到EA的文章中,已经对这些函数进行了回顾。所提供的函数可以在自定义程序中“按原样”使用:

      //+------------------------------------------------------------------+
      //| Return the indicator data on the specified bar                   |
      //+------------------------------------------------------------------+
      double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
        {
         double array[1]={0};
         ResetLastError();
         if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
           {
            PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
            return EMPTY_VALUE;
           }
         return array[0];
        }
      //+------------------------------------------------------------------+
      //| Return the state of the indicator line                           |
      //+------------------------------------------------------------------+
      ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
        {
      //--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
         const double value0=IndicatorValue(ind_handle,index,  buffer_num);
         const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
         const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
      //--- If at least one of the values could not be obtained, return an undefined value 
         if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
            return LINE_STATE_NONE;
      //--- Line upward reversal (value2>value1 && value0>value1)
         if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
            return LINE_STATE_TURN_UP;
      //--- Line upward direction (value2<=value1 && value0>value1)
         else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
            return LINE_STATE_UP;
      //--- Line upward stop (value2<=value1 && value0==value1)
         else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
            return LINE_STATE_STOP_UP;
      //--- Line downward reversal (value2<value1 && value0<value1)
         if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
            return LINE_STATE_TURN_DOWN;
      //--- Line downward direction (value2>=value1 && value0<value1)
         else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
            return LINE_STATE_DOWN;
      //--- Line downward stop (value2>=value1 && value0==value1)
         else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
            return LINE_STATE_STOP_DOWN;
      //--- Undefined state
         return LINE_STATE_NONE;
        }
      //+------------------------------------------------------------------+
      //| Return the state of the line relative to the specified level     |
      //+------------------------------------------------------------------+
      ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
        {
      //--- Get the values of the indicator line with the shift (0,1) relative to the passed index
         const double value0=IndicatorValue(ind_handle,index,  buffer_num);
         const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
      //--- If at least one of the values could not be obtained, return an undefined value 
         if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
            return LINE_STATE_NONE;
      //--- Define the second level to compare
         double level=(level1==EMPTY_VALUE ? level0 : level1);
      //--- The line is below the level (value1<level && value0<level0)
         if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
            return LINE_STATE_UNDER;
      //--- The line is above the level (value1>level && value0>level0)
         if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
            return LINE_STATE_ABOVE;
      //--- The line crossed the level upwards (value1<=level && value0>level0)
         if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
            return LINE_STATE_CROSS_UP;
      //--- The line crossed the level downwards (value1>=level && value0<level0)
         if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
            return LINE_STATE_CROSS_DOWN;
      //--- The line touched the level from below (value1<level0 && value0==level0)
         if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
            return LINE_STATE_TOUCH_BELOW;
      //--- The line touched the level from above (value1>level0 && value0==level0)
         if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
            return LINE_STATE_TOUCH_BELOW;
      //--- Line is equal to the level value (value1==level0 && value0==level0)
         if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
            return LINE_STATE_EQUALS;
      //--- Undefined state
         return LINE_STATE_NONE;
        }
      //+------------------------------------------------------------------+
      //| Return the indicator line state description                      |
      //+------------------------------------------------------------------+
      string LineStateDescription(const ENUM_LINE_STATE state)
        {
         switch(state)
           {
            case LINE_STATE_UP         :  return "Up";
            case LINE_STATE_STOP_UP    :  return "Stop Up";
            case LINE_STATE_TURN_UP    :  return "Turn Up";
            case LINE_STATE_DOWN       :  return "Down";
            case LINE_STATE_STOP_DOWN  :  return "Stop Down";
            case LINE_STATE_TURN_DOWN  :  return "Turn Down";
            case LINE_STATE_ABOVE      :  return "Above level";
            case LINE_STATE_UNDER      :  return "Under level";
            case LINE_STATE_CROSS_UP   :  return "Crossing Up";
            case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
            case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
            case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
            case LINE_STATE_EQUALS     :  return "Equals";
            default                    :  return "Unknown";
           }
        }
      
      

      使用仪表板时,数据会使用以下函数显示在面板上:

      //+------------------------------------------------------------------+
      //| Display data from the specified timeseries index to the panel    |
      //+------------------------------------------------------------------+
      void DrawData(const int index,const datetime time)
        {
      //--- Declare the variables to receive data in them
         MqlTick  tick={0};
         MqlRates rates[1];
      
      //--- Exit if unable to get the current prices
         if(!SymbolInfoTick(Symbol(),tick))
            return;
      //--- Exit if unable to get the bar data by the specified index
         if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
            return;
      
      //--- Set font parameters for bar and indicator data headers
         int  size=0;
         uint flags=0;
         uint angle=0;
         string name=panel.FontParams(size,flags,angle);
         panel.SetFontParams(name,9,FW_BOLD);
         panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
         panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
      //--- Set font parameters for bar and indicator data
         panel.SetFontParams(name,9);
      
      //--- Display the data of the specified bar in table 0 on the panel
         panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
         panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
         panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
         panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
         panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
         panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);
      
      //--- Display the indicator data from the specified bar on the panel in table 1 (upper fractal)
         panel.DrawText(ind_title+" Up", panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
         double value0=IndicatorValue(handle,index,UPPER_LINE);
         string value_str0=(value0!=EMPTY_VALUE ? DoubleToString(value0,ind_digits) : " ");
         panel.DrawText(value_str0,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,90);
      
      //--- Display the indicator data from the specified bar on the panel in table 1 (lower fractal)
         panel.DrawText(ind_title+" Down", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
         double value1=IndicatorValue(handle,index,LOWER_LINE);
         string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,ind_digits) : " ");
         panel.DrawText(value_str1,panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,90);
         
      //--- Redraw the chart to immediately display all changes on the panel
         ChartRedraw(ChartID());
        }
      
      

      此外,当使用仪表板时,面板事件处理程序在OnChartEvent()EA事件处理函数中调用,以及处理用于接收光标下的柱形索引的事件:

      //+------------------------------------------------------------------+
      //| ChartEvent function                                              |
      //+------------------------------------------------------------------+
      void OnChartEvent(const int id,
                        const long &lparam,
                        const double &dparam,
                        const string &sparam)
        {
      //--- Handling the panel
      //--- Call the panel event handler
         panel.OnChartEvent(id,lparam,dparam,sparam);
      
      //--- If the cursor moves or a click is made on the chart
         if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
           {
            //--- Declare the variables to record time and price coordinates in them
            datetime time=0;
            double price=0;
            int wnd=0;
            //--- If the cursor coordinates are converted to date and time
            if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
              {
               //--- write the bar index where the cursor is located to a global variable
               mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
               //--- Display the bar data under the cursor on the panel 
               DrawData(mouse_bar_index,time);
              }
           }
      
      //--- If we received a custom event, display the appropriate message in the journal
         if(id>CHARTEVENT_CUSTOM)
           {
            //--- Here we can implement handling a click on the close button on the panel
            PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
           }
        }
      
      

      在图表上编译并启动EA后,我们可以控制仪表板上的指标缓冲值:


      您可以在文章附件中查看TestWilliamsFractals.mq5测试EA。


      鳄鱼振荡指标

      鳄鱼振荡指标基于鳄鱼指标,并显示其平衡线的收敛/发散程度(平滑移动平均值)。顶部直方图是蓝线和红线值之间的绝对差值。底部直方图是红线和绿线值之间的绝对差值,但带有减号,因为直方图是自上而下绘制的。


      参数

      iGator()函数用于创建指标句柄:

      返回鳄鱼振荡指标的句柄。振荡器显示蓝色和红色鳄鱼指标线之间的差(上部直方图)以及红色和绿色指标线之间的差(下部直方图)。

      int  iGator(
         string              symbol,            // symbol name
         ENUM_TIMEFRAMES     period,            // period
         int                 jaw_period,        // period for calculating jaws
         int                 jaw_shift,         // horizontal shift of jaws
         int                 teeth_period,      // period for calculating teeth
         int                 teeth_shift,       // teeth shift
         int                 lips_period,       // period for calculating lips
         int                 lips_shift,        // horizontal shift of lips 
         ENUM_MA_METHOD      ma_method,         // smoothing type
         ENUM_APPLIED_PRICE  applied_price      // price type or handle
         );
      

      symbol

      [in] 其数据将用于计算指标的金融工具的交易品种名称。NULL表示当前交易品种。

      period

      [in] 周期值可以是ENUM_TIMEFRAMES枚举值之一,0表示当前时间框架。

      jaw_period

      [in] 蓝线(鳄鱼指标的颚线)平均周期。

      jaw_shift

      [in] 鳄鱼指标蓝线相对于价格图的偏移。与指标直方图的视觉偏移没有直接关系。

      teeth_period

      [in] 红线(鳄鱼指标的齿线)平均周期。

      teeth_shift

      [in] 鳄鱼指标红线相对于价格图的偏移。与指标直方图的视觉偏移没有直接关系。

      lips_period

      [in] 绿线(鳄鱼指标的唇线)平均周期。

      lips_shift

      [in] 鳄鱼指标绿线相对于价格图的偏移。与指标直方图的视觉偏移没有直接关系。

      ma_method

      [in]  平均方法,可以是 ENUM_MA_METHOD 枚举中的任意值。

      applied_price

      [in]  应用的价格,可以是 ENUM_APPLIED_PRICE 价格常数中的任一个或者是另一个指标的句柄。

      返回指定技术指标的句柄,如果失败,则返回 INVALID_HANDLE。 要从未使用的指标中释放计算机内存,请使用指标句柄传递到 IndicatorRelease()

      缓冲区索引:0-UPPER_HISTOGRAM,1-上部直方图的彩色缓冲区,2-LOWER_HISTOGRAM,3-下部直方图的彩色缓冲区。


      在EA中声明输入变量和全局变量以创建指标:

      //+------------------------------------------------------------------+
      //|                                            TestWilliamsGator.mq5 |
      //|                                  Copyright 2023, MetaQuotes Ltd. |
      //|                                             https://www.mql5.com |
      //+------------------------------------------------------------------+
      #property copyright "Copyright 2023, MetaQuotes Ltd."
      #property link      "https://www.mql5.com"
      #property version   "1.00"
      //--- enums
      enum ENUM_LINE_STATE
        {
         LINE_STATE_NONE,        // Undefined state
         LINE_STATE_UP,          // Upward
         LINE_STATE_DOWN,        // Downward
         LINE_STATE_TURN_UP,     // Upward reversal
         LINE_STATE_TURN_DOWN,   // Downward reversal
         LINE_STATE_STOP_UP,     // Upward stop
         LINE_STATE_STOP_DOWN,   // Downward stop
         LINE_STATE_ABOVE,       // Above value
         LINE_STATE_UNDER,       // Below value
         LINE_STATE_CROSS_UP,    // Crossing value upwards
         LINE_STATE_CROSS_DOWN,  // Crossing value downwards
         LINE_STATE_TOUCH_BELOW, // Touching value from below 
         LINE_STATE_TOUCH_ABOVE, // Touch value from above
         LINE_STATE_EQUALS,      // Equal to value
        };
      //--- input parameters
      input uint                 InpPeriodJaws  =  13;            /* Jaws Period    */
      input int                  InpShiftJaws   =  8;             /* Jaws Shift     */
      input uint                 InpPeriodTeeth =  8;             /* Teeth Period   */
      input int                  InpShiftTeeth  =  5;             /* Teeth Shift    */
      input uint                 InpPeriodLips  =  5;             /* Lips Period    */
      input int                  InpShiftLips   =  3;             /* Lips Shift     */
      input ENUM_MA_METHOD       InpMethod      =  MODE_SMMA;     /* Smoothed       */
      input ENUM_APPLIED_PRICE   InpAppliedPrice=  PRICE_MEDIAN;  /* Applied Price  */
      //--- global variables
      int      handle=INVALID_HANDLE;  // Indicator handle
      int      period_jaws=0;          // Jaws line calculation period
      int      period_teeth=0;         // Teeth line calculation period
      int      period_lips=0;          // Lips line calculation period
      int      ind_digits=0;           // Number of decimal places in the indicator values
      string   ind_title;              // Indicator description
      
      

      在EA中使用仪表板时,声明全局变量包括面板类的文件

      //+------------------------------------------------------------------+
      //|                                            TestWilliamsGator.mq5 |
      //|                                  Copyright 2023, MetaQuotes Ltd. |
      //|                                             https://www.mql5.com |
      //+------------------------------------------------------------------+
      #property copyright "Copyright 2023, MetaQuotes Ltd."
      #property link      "https://www.mql5.com"
      #property version   "1.00"
      //--- includes
      #include <Dashboard\Dashboard.mqh>
      //--- enums
      enum ENUM_LINE_STATE
        {
         LINE_STATE_NONE,        // Undefined state
         LINE_STATE_UP,          // Upward
         LINE_STATE_DOWN,        // Downward
         LINE_STATE_TURN_UP,     // Upward reversal
         LINE_STATE_TURN_DOWN,   // Downward reversal
         LINE_STATE_STOP_UP,     // Upward stop
         LINE_STATE_STOP_DOWN,   // Downward stop
         LINE_STATE_ABOVE,       // Above value
         LINE_STATE_UNDER,       // Below value
         LINE_STATE_CROSS_UP,    // Crossing value upwards
         LINE_STATE_CROSS_DOWN,  // Crossing value downwards
         LINE_STATE_TOUCH_BELOW, // Touching value from below 
         LINE_STATE_TOUCH_ABOVE, // Touch value from above
         LINE_STATE_EQUALS,      // Equal to value
        };
      //--- input parameters
      input uint                 InpPeriodJaws  =  13;            /* Jaws Period    */
      input int                  InpShiftJaws   =  8;             /* Jaws Shift     */
      input uint                 InpPeriodTeeth =  8;             /* Teeth Period   */
      input int                  InpShiftTeeth  =  5;             /* Teeth Shift    */
      input uint                 InpPeriodLips  =  5;             /* Lips Period    */
      input int                  InpShiftLips   =  3;             /* Lips Shift     */
      input ENUM_MA_METHOD       InpMethod      =  MODE_SMMA;     /* Smoothed       */
      input ENUM_APPLIED_PRICE   InpAppliedPrice=  PRICE_MEDIAN;  /* Applied Price  */
      //--- global variables
      int      handle=INVALID_HANDLE;  // Indicator handle
      int      period_jaws=0;          // Jaws line calculation period
      int      period_teeth=0;         // Teeth line calculation period
      int      period_lips=0;          // Lips line calculation period
      int      ind_digits=0;           // Number of decimal places in the indicator values
      string   ind_title;              // Indicator description
      //--- variables for the panel
      int      mouse_bar_index;        // Index of the bar the data is taken from
      CDashboard *panel=NULL;          // Pointer to the panel object
      
      


      初始化

      设置指标的全局变量值并创建其句柄:

      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
        {
      //--- create timer
         EventSetTimer(60);
      
      //--- Indicator
      //--- Set and adjust the calculation period if necessary
         period_jaws=int(InpPeriodJaws<1 ? 13 : InpPeriodJaws);
         period_teeth=int(InpPeriodTeeth<1 ? 8 : InpPeriodTeeth);
         period_lips=int(InpPeriodLips<1 ? 5 : InpPeriodLips);
      //--- Set the indicator name and the number of decimal places
         ind_title=StringFormat("Gator(%lu,%lu,%lu)",period_jaws,period_teeth,period_lips);
         ind_digits=Digits()+1;
      //--- Create indicator handle
         ResetLastError();
         handle=iGator(Symbol(),PERIOD_CURRENT,period_jaws,InpShiftJaws,period_teeth,InpShiftTeeth,period_lips,InpShiftLips,InpMethod,InpAppliedPrice);
         if(handle==INVALID_HANDLE)
           {
            PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
            return INIT_FAILED;
           }
      
      //--- Successful initialization
         return(INIT_SUCCEEDED);
        }
      
      

      如果EA涉及使用仪表板,在此处创建它

      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
        {
      //--- create timer
         EventSetTimer(60);
      
      //--- Indicator
      //--- Set and adjust the calculation period if necessary
         period_jaws=int(InpPeriodJaws<1 ? 13 : InpPeriodJaws);
         period_teeth=int(InpPeriodTeeth<1 ? 8 : InpPeriodTeeth);
         period_lips=int(InpPeriodLips<1 ? 5 : InpPeriodLips);
      //--- Set the indicator name and the number of decimal places
         ind_title=StringFormat("Gator(%lu,%lu,%lu)",period_jaws,period_teeth,period_lips);
         ind_digits=Digits()+1;
      //--- Create indicator handle
         ResetLastError();
         handle=iGator(Symbol(),PERIOD_CURRENT,period_jaws,InpShiftJaws,period_teeth,InpShiftTeeth,period_lips,InpShiftLips,InpMethod,InpAppliedPrice);
         if(handle==INVALID_HANDLE)
           {
            PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
            return INIT_FAILED;
           }
      
      //--- Dashboard
      //--- Create the panel
         panel=new CDashboard(1,20,20,229,225);
         if(panel==NULL)
           {
            Print("Error. Failed to create panel object");
            return INIT_FAILED;
           }
      //--- Set font parameters
         panel.SetFontParams("Calibri",9);
      //--- Display the panel with the "Symbol, Timeframe description" header text
         panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
      //--- Create a table with ID 0 to display bar data in it
         panel.CreateNewTable(0);
      //--- Draw a table with ID 0 on the panel background
         panel.DrawGrid(0,2,20,6,2,18,112);
      
      //--- Create a table with ID 1 to display indicator data in it
         panel.CreateNewTable(1);
      //--- Get the Y2 table coordinate with ID 0 and
      //--- set the Y1 coordinate for the table with ID 1
         int y1=panel.TableY2(0)+22;
      //--- Draw a table with ID 1 on the panel background
         panel.DrawGrid(1,2,y1,3,2,18,112);
         
      //--- Display tabular data in the journal
         panel.GridPrint(0,2);
         panel.GridPrint(1,2);
      //--- Initialize the variable with the index of the mouse cursor bar
         mouse_bar_index=0;
      //--- Display the data of the current bar on the panel
         DrawData(mouse_bar_index,TimeCurrent());
      
      //--- Successful initialization
         return(INIT_SUCCEEDED);
        }
      
      


      析构

      在EA的 OnDeinit() 处理函数中释放指标句柄:

      //+------------------------------------------------------------------+
      //| Expert deinitialization function                                 |
      //+------------------------------------------------------------------+
      void OnDeinit(const int reason)
        {
      //--- destroy timer
         EventKillTimer();
         
      //--- Release handle of the indicator
         ResetLastError();
         if(!IndicatorRelease(handle))
            PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
      //--- Clear all comments on the chart
         Comment("");
        }
      
      

      创建的仪表板对象在EA析构时被删除:

      //+------------------------------------------------------------------+
      //| Expert deinitialization function                                 |
      //+------------------------------------------------------------------+
      void OnDeinit(const int reason)
        {
      //--- destroy timer
         EventKillTimer();
         
      //--- Release handle of the indicator
         ResetLastError();
         if(!IndicatorRelease(handle))
            PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
      //--- Clear all comments on the chart
         Comment("");
         
      //--- If the panel object exists, delete it
         if(panel!=NULL)
            delete panel;
        }
      


      读取数据

      下面提供了通过指标句柄获取数据的一般函数,在关于将振荡指标连接到EA的文章中,已经对这些函数进行了回顾。所提供的函数可以在自定义程序中“按原样”使用:

      //+------------------------------------------------------------------+
      //| Return the indicator data on the specified bar                   |
      //+------------------------------------------------------------------+
      double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
        {
         double array[1]={0};
         ResetLastError();
         if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
           {
            PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
            return EMPTY_VALUE;
           }
         return array[0];
        }
      //+------------------------------------------------------------------+
      //| Return the state of the indicator line                           |
      //+------------------------------------------------------------------+
      ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
        {
      //--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
         const double value0=IndicatorValue(ind_handle,index,  buffer_num);
         const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
         const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
      //--- If at least one of the values could not be obtained, return an undefined value 
         if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
            return LINE_STATE_NONE;
      //--- Line upward reversal (value2>value1 && value0>value1)
         if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
            return LINE_STATE_TURN_UP;
      //--- Line upward direction (value2<=value1 && value0>value1)
         else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
            return LINE_STATE_UP;
      //--- Line upward stop (value2<=value1 && value0==value1)
         else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
            return LINE_STATE_STOP_UP;
      //--- Line downward reversal (value2<value1 && value0<value1)
         if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
            return LINE_STATE_TURN_DOWN;
      //--- Line downward direction (value2>=value1 && value0<value1)
         else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
            return LINE_STATE_DOWN;
      //--- Line downward stop (value2>=value1 && value0==value1)
         else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
            return LINE_STATE_STOP_DOWN;
      //--- Undefined state
         return LINE_STATE_NONE;
        }
      //+------------------------------------------------------------------+
      //| Return the state of the line relative to the specified level     |
      //+------------------------------------------------------------------+
      ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
        {
      //--- Get the values of the indicator line with the shift (0,1) relative to the passed index
         const double value0=IndicatorValue(ind_handle,index,  buffer_num);
         const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
      //--- If at least one of the values could not be obtained, return an undefined value 
         if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
            return LINE_STATE_NONE;
      //--- Define the second level to compare
         double level=(level1==EMPTY_VALUE ? level0 : level1);
      //--- The line is below the level (value1<level && value0<level0)
         if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
            return LINE_STATE_UNDER;
      //--- The line is above the level (value1>level && value0>level0)
         if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
            return LINE_STATE_ABOVE;
      //--- The line crossed the level upwards (value1<=level && value0>level0)
         if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
            return LINE_STATE_CROSS_UP;
      //--- The line crossed the level downwards (value1>=level && value0<level0)
         if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
            return LINE_STATE_CROSS_DOWN;
      //--- The line touched the level from below (value1<level0 && value0==level0)
         if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
            return LINE_STATE_TOUCH_BELOW;
      //--- The line touched the level from above (value1>level0 && value0==level0)
         if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
            return LINE_STATE_TOUCH_BELOW;
      //--- Line is equal to the level value (value1==level0 && value0==level0)
         if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
            return LINE_STATE_EQUALS;
      //--- Undefined state
         return LINE_STATE_NONE;
        }
      //+------------------------------------------------------------------+
      //| Return the indicator line state description                      |
      //+------------------------------------------------------------------+
      string LineStateDescription(const ENUM_LINE_STATE state)
        {
         switch(state)
           {
            case LINE_STATE_UP         :  return "Up";
            case LINE_STATE_STOP_UP    :  return "Stop Up";
            case LINE_STATE_TURN_UP    :  return "Turn Up";
            case LINE_STATE_DOWN       :  return "Down";
            case LINE_STATE_STOP_DOWN  :  return "Stop Down";
            case LINE_STATE_TURN_DOWN  :  return "Turn Down";
            case LINE_STATE_ABOVE      :  return "Above level";
            case LINE_STATE_UNDER      :  return "Under level";
            case LINE_STATE_CROSS_UP   :  return "Crossing Up";
            case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
            case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
            case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
            case LINE_STATE_EQUALS     :  return "Equals";
            default                    :  return "Unknown";
           }
        }
      
      

      使用仪表板时,数据会使用以下函数显示在面板上:

      //+------------------------------------------------------------------+
      //| Display data from the specified timeseries index to the panel    |
      //+------------------------------------------------------------------+
      void DrawData(const int index,const datetime time)
        {
      //--- Declare the variables to receive data in them
         MqlTick  tick={0};
         MqlRates rates[1];
      
      //--- Exit if unable to get the current prices
         if(!SymbolInfoTick(Symbol(),tick))
            return;
      //--- Exit if unable to get the bar data by the specified index
         if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
            return;
      
      //--- Set font parameters for bar and indicator data headers
         int  size=0;
         uint flags=0;
         uint angle=0;
         string name=panel.FontParams(size,flags,angle);
         panel.SetFontParams(name,9,FW_BOLD);
         panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
         panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
      //--- Set font parameters for bar and indicator data
         panel.SetFontParams(name,9);
      
      //--- Display the data of the specified bar in table 0 on the panel
         panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
         panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
         panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
         panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
         panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
         panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);
      
      //--- Get the indicator buffers data
         double value0=IndicatorValue(handle,index,UPPER_HISTOGRAM); // Upper histogram
         double value1=IndicatorValue(handle,index,1);               // Upper histogram color buffer
         double value2=IndicatorValue(handle,index,LOWER_HISTOGRAM); // Lower histogram
         double value3=IndicatorValue(handle,index,3);               // Lower histogram color buffer
         color clr=clrNONE;
      //--- Display the upper histogram data from the specified bar on the panel in table 1
         panel.DrawText(ind_title+" Up", panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
         string value_str=(value0!=EMPTY_VALUE ? DoubleToString(value0,ind_digits) : "");
         clr=(value1>0 ? clrRed : clrGreen);
         panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clr,100);
         
      //--- Display the lower histogram data from the specified bar on the panel in table 1
         panel.DrawText(ind_title+" Down", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
         value_str=(value2!=EMPTY_VALUE ? DoubleToString(value2,ind_digits) : "");
         clr=(value3>0 ? clrRed : clrGreen);
         panel.DrawText(value_str,panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clr,100);
         
      //--- Redraw the chart to immediately display all changes on the panel
         ChartRedraw(ChartID());
        }
      
      

      描述指标缓冲区的值的文本的颜色具有等于指标直方图的对应列的颜色。

      此外,当使用仪表板时,面板事件处理程序在OnChartEvent()EA事件处理函数中调用,以及处理用于接收光标下的柱形索引的事件:

      //+------------------------------------------------------------------+
      //| ChartEvent function                                              |
      //+------------------------------------------------------------------+
      void OnChartEvent(const int id,
                        const long &lparam,
                        const double &dparam,
                        const string &sparam)
        {
      //--- Handling the panel
      //--- Call the panel event handler
         panel.OnChartEvent(id,lparam,dparam,sparam);
      
      //--- If the cursor moves or a click is made on the chart
         if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
           {
            //--- Declare the variables to record time and price coordinates in them
            datetime time=0;
            double price=0;
            int wnd=0;
            //--- If the cursor coordinates are converted to date and time
            if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
              {
               //--- write the bar index where the cursor is located to a global variable
               mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
               //--- Display the bar data under the cursor on the panel 
               DrawData(mouse_bar_index,time);
              }
           }
      
      //--- If we received a custom event, display the appropriate message in the journal
         if(id>CHARTEVENT_CUSTOM)
           {
            //--- Here we can implement handling a click on the close button on the panel
            PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
           }
        }
      
      

      编译EA并在图表上启动后,我们可以监控面板上指标值和线的状态:



      您可以在文章附件中查看TestWilliamsGator.mq5测试EA。


      市场促进指数

      市场促进指数 (Market Facilitation Index,BW MFI) 是显示分时价格变化的指标。指标的绝对值并没有任何实际意义,只有指标的变化才有意义。Bill Williams 强调MFI和交易量的互换性:

      • 市场促进指数增加,交易量增加——这指出:a)进入市场的参与者数量增加(交易量增加)b)新加入的参与者朝着柱发展的方向开仓,即变化已经开始并加快了速度。
      • 市场促进指数下降,交易量下降。这意味着市场参与者不再感兴趣。
      • 市场促进指数上升,但交易量下降。最有可能的是,市场没有得到交易员的交易量的支持,价格也因场内交易员(经纪人和交易商)的投机而变化。
      • 市场促进指数下降,但交易量增加。牛市和熊市之间有一场战斗,其特点是大量的买卖量,但由于力量相等,价格没有显著变化。其中一方(买方对卖方)最终将赢得这场战斗。通常,价格柱的突破会让你知道这个柱是决定趋势的延续还是取消趋势。比尔·威廉姆斯称这种柱是“屈膝礼”。



      参数

      iBWMFI()函数用于创建指标句柄:

      返回市场促进指数指标的句柄。只有一个缓冲区。

      int  iBWMFI(
         string               symbol,             // symbol name
         ENUM_TIMEFRAMES      period,             // period
         ENUM_APPLIED_VOLUME  applied_volume      // type of volume used for calculations
         );
      

      symbol

      [in] 其数据将用于计算指标的金融工具的交易品种名称。NULL表示当前交易品种。

      period

      [in] 周期值可以是ENUM_TIMEFRAMES枚举值之一,0表示当前时间框架。

      applied_volume

      [in]  使用的交易量. ENUM_APPLIED_VOLUME 枚举值中的任意一个。

      返回指定技术指标的句柄,如果失败,则返回 INVALID_HANDLE。 要从未使用的指标中释放计算机内存,请使用指标句柄传递到 IndicatorRelease()


      在EA中声明输入变量和全局变量以创建指标:

      //+------------------------------------------------------------------+
      //|                                            TestWilliamsBWMFI.mq5 |
      //|                                  Copyright 2023, MetaQuotes Ltd. |
      //|                                             https://www.mql5.com |
      //+------------------------------------------------------------------+
      #property copyright "Copyright 2023, MetaQuotes Ltd."
      #property link      "https://www.mql5.com"
      #property version   "1.00"
      //--- enums
      enum ENUM_LINE_STATE
        {
         LINE_STATE_NONE,        // Undefined state
         LINE_STATE_UP,          // Upward
         LINE_STATE_DOWN,        // Downward
         LINE_STATE_TURN_UP,     // Upward reversal
         LINE_STATE_TURN_DOWN,   // Downward reversal
         LINE_STATE_STOP_UP,     // Upward stop
         LINE_STATE_STOP_DOWN,   // Downward stop
         LINE_STATE_ABOVE,       // Above value
         LINE_STATE_UNDER,       // Below value
         LINE_STATE_CROSS_UP,    // Crossing value upwards
         LINE_STATE_CROSS_DOWN,  // Crossing value downwards
         LINE_STATE_TOUCH_BELOW, // Touching value from below 
         LINE_STATE_TOUCH_ABOVE, // Touch value from above
         LINE_STATE_EQUALS,      // Equal to value
        };
      //--- input parameters
      input ENUM_APPLIED_VOLUME  InpVolume   =  VOLUME_TICK;   /* Applied Volume */
      //--- global variables
      int      handle=INVALID_HANDLE;  // Indicator handle
      int      ind_digits=0;           // Number of decimal places in the indicator values
      string   ind_title;              // Indicator description
      
      

      在EA中使用仪表板时,声明全局变量包括面板类的文件

      //+------------------------------------------------------------------+
      //|                                            TestWilliamsBWMFI.mq5 |
      //|                                  Copyright 2023, MetaQuotes Ltd. |
      //|                                             https://www.mql5.com |
      //+------------------------------------------------------------------+
      #property copyright "Copyright 2023, MetaQuotes Ltd."
      #property link      "https://www.mql5.com"
      #property version   "1.00"
      //--- includes
      #include <Dashboard\Dashboard.mqh>
      //--- enums
      enum ENUM_LINE_STATE
        {
         LINE_STATE_NONE,        // Undefined state
         LINE_STATE_UP,          // Upward
         LINE_STATE_DOWN,        // Downward
         LINE_STATE_TURN_UP,     // Upward reversal
         LINE_STATE_TURN_DOWN,   // Downward reversal
         LINE_STATE_STOP_UP,     // Upward stop
         LINE_STATE_STOP_DOWN,   // Downward stop
         LINE_STATE_ABOVE,       // Above value
         LINE_STATE_UNDER,       // Below value
         LINE_STATE_CROSS_UP,    // Crossing value upwards
         LINE_STATE_CROSS_DOWN,  // Crossing value downwards
         LINE_STATE_TOUCH_BELOW, // Touching value from below 
         LINE_STATE_TOUCH_ABOVE, // Touch value from above
         LINE_STATE_EQUALS,      // Equal to value
        };
      //--- input parameters
      input ENUM_APPLIED_VOLUME  InpVolume   =  VOLUME_TICK;   /* Applied Volume */
      //--- global variables
      int      handle=INVALID_HANDLE;  // Indicator handle
      int      ind_digits=0;           // Number of decimal places in the indicator values
      string   ind_title;              // Indicator description
      //--- variables for the panel
      int      mouse_bar_index;        // Index of the bar the data is taken from
      CDashboard *panel=NULL;          // Pointer to the panel object
      
      


      初始化

      设置指标的全局变量值并创建其句柄:

      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
        {
      //--- create timer
         EventSetTimer(60);
      
      //--- Indicator
      //--- Set the indicator name and the number of decimal places
         ind_title="BW MFI";
         ind_digits=Digits();
      //--- Create indicator handle
         ResetLastError();
         handle=iBWMFI(Symbol(),PERIOD_CURRENT,InpVolume);
         if(handle==INVALID_HANDLE)
           {
            PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
            return INIT_FAILED;
           }
      
      //--- Successful initialization
         return(INIT_SUCCEEDED);
        }
      
      

      如果EA涉及使用仪表板,在此处创建它

      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
        {
      //--- create timer
         EventSetTimer(60);
      
      //--- Indicator
      //--- Set the indicator name and the number of decimal places
         ind_title="BW MFI";
         ind_digits=Digits();
      //--- Create indicator handle
         ResetLastError();
         handle=iBWMFI(Symbol(),PERIOD_CURRENT,InpVolume);
         if(handle==INVALID_HANDLE)
           {
            PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,ind_title,GetLastError());
            return INIT_FAILED;
           }
      
      //--- Dashboard
      //--- Create the panel
         panel=new CDashboard(1,20,20,199,225);
         if(panel==NULL)
           {
            Print("Error. Failed to create panel object");
            return INIT_FAILED;
           }
      //--- Set font parameters
         panel.SetFontParams("Calibri",9);
      //--- Display the panel with the "Symbol, Timeframe description" header text
         panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7));
      //--- Create a table with ID 0 to display bar data in it
         panel.CreateNewTable(0);
      //--- Draw a table with ID 0 on the panel background
         panel.DrawGrid(0,2,20,6,2,18,97);
      
      //--- Create a table with ID 1 to display indicator data in it
         panel.CreateNewTable(1);
      //--- Get the Y2 table coordinate with ID 0 and
      //--- set the Y1 coordinate for the table with ID 1
         int y1=panel.TableY2(0)+22;
      //--- Draw a table with ID 1 on the panel background
         panel.DrawGrid(1,2,y1,3,2,18,97);
         
      //--- Display tabular data in the journal
         panel.GridPrint(0,2);
         panel.GridPrint(1,2);
      //--- Initialize the variable with the index of the mouse cursor bar
         mouse_bar_index=0;
      //--- Display the data of the current bar on the panel
         DrawData(mouse_bar_index,TimeCurrent());
      
      //--- Successful initialization
         return(INIT_SUCCEEDED);
        }
      
      


      析构

      在EA的 OnDeinit() 处理函数中释放指标句柄:

      //+------------------------------------------------------------------+
      //| Expert deinitialization function                                 |
      //+------------------------------------------------------------------+
      void OnDeinit(const int reason)
        {
      //--- destroy timer
         EventKillTimer();
         
      //--- Release handle of the indicator
         ResetLastError();
         if(!IndicatorRelease(handle))
            PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
      //--- Clear all comments on the chart
         Comment("");
        }
      
      

      创建的仪表板对象在EA析构时被删除:

      //+------------------------------------------------------------------+
      //| Expert deinitialization function                                 |
      //+------------------------------------------------------------------+
      void OnDeinit(const int reason)
        {
      //--- destroy timer
         EventKillTimer();
         
      //--- Release handle of the indicator
         ResetLastError();
         if(!IndicatorRelease(handle))
            PrintFormat("%s: IndicatorRelease failed. Error %ld",__FUNCTION__,GetLastError());
      //--- Clear all comments on the chart
         Comment("");
         
      //--- If the panel object exists, delete it
         if(panel!=NULL)
            delete panel;
        }
      


      读取数据

      下面提供了通过指标句柄获取数据的一般函数,在关于将振荡指标连接到EA的文章中,已经对这些函数进行了回顾。所提供的函数可以在自定义程序中“按原样”使用:

      //+------------------------------------------------------------------+
      //| Return the indicator data on the specified bar                   |
      //+------------------------------------------------------------------+
      double IndicatorValue(const int ind_handle,const int index,const int buffer_num)
        {
         double array[1]={0};
         ResetLastError();
         if(CopyBuffer(ind_handle,buffer_num,index,1,array)!=1)
           {
            PrintFormat("%s: CopyBuffer failed. Error %ld",__FUNCTION__,GetLastError());
            return EMPTY_VALUE;
           }
         return array[0];
        }
      //+------------------------------------------------------------------+
      //| Return the state of the indicator line                           |
      //+------------------------------------------------------------------+
      ENUM_LINE_STATE LineState(const int ind_handle,const int index,const int buffer_num)
        {
      //--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index
         const double value0=IndicatorValue(ind_handle,index,  buffer_num);
         const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
         const double value2=IndicatorValue(ind_handle,index+2,buffer_num);
      //--- If at least one of the values could not be obtained, return an undefined value 
         if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE)
            return LINE_STATE_NONE;
      //--- Line upward reversal (value2>value1 && value0>value1)
         if(NormalizeDouble(value2-value1,ind_digits)>0 && NormalizeDouble(value0-value1,ind_digits)>0)
            return LINE_STATE_TURN_UP;
      //--- Line upward direction (value2<=value1 && value0>value1)
         else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)>0)
            return LINE_STATE_UP;
      //--- Line upward stop (value2<=value1 && value0==value1)
         else if(NormalizeDouble(value2-value1,ind_digits)<=0 && NormalizeDouble(value0-value1,ind_digits)==0)
            return LINE_STATE_STOP_UP;
      //--- Line downward reversal (value2<value1 && value0<value1)
         if(NormalizeDouble(value2-value1,ind_digits)<0 && NormalizeDouble(value0-value1,ind_digits)<0)
            return LINE_STATE_TURN_DOWN;
      //--- Line downward direction (value2>=value1 && value0<value1)
         else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)<0)
            return LINE_STATE_DOWN;
      //--- Line downward stop (value2>=value1 && value0==value1)
         else if(NormalizeDouble(value2-value1,ind_digits)>=0 && NormalizeDouble(value0-value1,ind_digits)==0)
            return LINE_STATE_STOP_DOWN;
      //--- Undefined state
         return LINE_STATE_NONE;
        }
      //+------------------------------------------------------------------+
      //| Return the state of the line relative to the specified level     |
      //+------------------------------------------------------------------+
      ENUM_LINE_STATE LineStateRelative(const int ind_handle,const int index,const int buffer_num,const double level0,const double level1=EMPTY_VALUE)
        {
      //--- Get the values of the indicator line with the shift (0,1) relative to the passed index
         const double value0=IndicatorValue(ind_handle,index,  buffer_num);
         const double value1=IndicatorValue(ind_handle,index+1,buffer_num);
      //--- If at least one of the values could not be obtained, return an undefined value 
         if(value0==EMPTY_VALUE || value1==EMPTY_VALUE)
            return LINE_STATE_NONE;
      //--- Define the second level to compare
         double level=(level1==EMPTY_VALUE ? level0 : level1);
      //--- The line is below the level (value1<level && value0<level0)
         if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)<0)
            return LINE_STATE_UNDER;
      //--- The line is above the level (value1>level && value0>level0)
         if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)>0)
            return LINE_STATE_ABOVE;
      //--- The line crossed the level upwards (value1<=level && value0>level0)
         if(NormalizeDouble(value1-level,ind_digits)<=0 && NormalizeDouble(value0-level0,ind_digits)>0)
            return LINE_STATE_CROSS_UP;
      //--- The line crossed the level downwards (value1>=level && value0<level0)
         if(NormalizeDouble(value1-level,ind_digits)>=0 && NormalizeDouble(value0-level0,ind_digits)<0)
            return LINE_STATE_CROSS_DOWN;
      //--- The line touched the level from below (value1<level0 && value0==level0)
         if(NormalizeDouble(value1-level,ind_digits)<0 && NormalizeDouble(value0-level0,ind_digits)==0)
            return LINE_STATE_TOUCH_BELOW;
      //--- The line touched the level from above (value1>level0 && value0==level0)
         if(NormalizeDouble(value1-level,ind_digits)>0 && NormalizeDouble(value0-level0,ind_digits)==0)
            return LINE_STATE_TOUCH_BELOW;
      //--- Line is equal to the level value (value1==level0 && value0==level0)
         if(NormalizeDouble(value1-level,ind_digits)==0 && NormalizeDouble(value0-level0,ind_digits)==0)
            return LINE_STATE_EQUALS;
      //--- Undefined state
         return LINE_STATE_NONE;
        }
      //+------------------------------------------------------------------+
      //| Return the indicator line state description                      |
      //+------------------------------------------------------------------+
      string LineStateDescription(const ENUM_LINE_STATE state)
        {
         switch(state)
           {
            case LINE_STATE_UP         :  return "Up";
            case LINE_STATE_STOP_UP    :  return "Stop Up";
            case LINE_STATE_TURN_UP    :  return "Turn Up";
            case LINE_STATE_DOWN       :  return "Down";
            case LINE_STATE_STOP_DOWN  :  return "Stop Down";
            case LINE_STATE_TURN_DOWN  :  return "Turn Down";
            case LINE_STATE_ABOVE      :  return "Above level";
            case LINE_STATE_UNDER      :  return "Under level";
            case LINE_STATE_CROSS_UP   :  return "Crossing Up";
            case LINE_STATE_CROSS_DOWN :  return "Crossing Down";
            case LINE_STATE_TOUCH_BELOW:  return "Touch from Below";
            case LINE_STATE_TOUCH_ABOVE:  return "Touch from Above";
            case LINE_STATE_EQUALS     :  return "Equals";
            default                    :  return "Unknown";
           }
        }
      
      

      使用仪表板时,数据会使用以下函数显示在面板上:

      //+------------------------------------------------------------------+
      //| Display data from the specified timeseries index to the panel    |
      //+------------------------------------------------------------------+
      void DrawData(const int index,const datetime time)
        {
      //--- Declare the variables to receive data in them
         MqlTick  tick={0};
         MqlRates rates[1];
      
      //--- Exit if unable to get the current prices
         if(!SymbolInfoTick(Symbol(),tick))
            return;
      //--- Exit if unable to get the bar data by the specified index
         if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1)
            return;
      
      //--- Set font parameters for bar and indicator data headers
         int  size=0;
         uint flags=0;
         uint angle=0;
         string name=panel.FontParams(size,flags,angle);
         panel.SetFontParams(name,9,FW_BOLD);
         panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6);
         panel.DrawText("Indicator data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6);
      //--- Set font parameters for bar and indicator data
         panel.SetFontParams(name,9);
      
      //--- Display the data of the specified bar in table 0 on the panel
         panel.DrawText("Date",  panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_DATE),     panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90);
         panel.DrawText("Time",  panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString(  rates[0].time,TIME_MINUTES),  panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90);
         panel.DrawText("Open",  panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()),      panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90);
         panel.DrawText("High",  panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()),      panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90);
         panel.DrawText("Low",   panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()),       panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90);
         panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()),     panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90);
      
      //--- Display the indicator data from the specified bar on the panel in table 1
         panel.DrawText(ind_title, panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2);
         double value=IndicatorValue(handle,index,0);
         string value_str=(value!=EMPTY_VALUE ? DoubleToString(value,ind_digits) : "");
         panel.DrawText(value_str,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,90);
         
      //--- Create Volumes indicator handle
         static bool create=false;
         static int hv=INVALID_HANDLE;
         if(!create)
           {
            ResetLastError();
            hv=iVolumes(Symbol(),PERIOD_CURRENT,InpVolume);
            if(hv==INVALID_HANDLE)
              {
               PrintFormat("%s: Failed to create indicator handle Volumes. Error %ld",__FUNCTION__,GetLastError());
               return;
              }
            create=true;
           }
      
      //--- Get Volumes indicator status
         ENUM_LINE_STATE state_vol=LineState(hv,index,0);
      //--- Display a description of the indicator line state
         panel.DrawText("BW MFI State", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2);
         ENUM_LINE_STATE state=LineState(handle,index,0);
         color clr=clrNONE;
         string state_str=LineStateDescription(state);
         if((state==LINE_STATE_UP || state==LINE_STATE_TURN_UP) && (state_vol==LINE_STATE_UP || state_vol==LINE_STATE_TURN_UP))
           {
            state_str="MFI Up, Vol Up";
            clr=clrGreen;
           }
         if((state==LINE_STATE_DOWN || state==LINE_STATE_TURN_DOWN) && (state_vol==LINE_STATE_DOWN || state_vol==LINE_STATE_TURN_DOWN))
           {
            state_str="MFI Dn, Vol Dn";
            clr=clrSaddleBrown;
           }
         if((state==LINE_STATE_UP || state==LINE_STATE_TURN_UP) && (state_vol==LINE_STATE_DOWN || state_vol==LINE_STATE_TURN_DOWN))
           {
            state_str="MFI Up, Vol Dn";
            clr=clrBlue;
           }
         if((state==LINE_STATE_DOWN || state==LINE_STATE_TURN_DOWN) && (state_vol==LINE_STATE_UP || state_vol==LINE_STATE_TURN_UP))
           {
            state_str="MFI Dn, Vol Up";
            clr=clrLightCoral;
           }
      
      //--- Set font parameters for indicator state data (bold font)
         name=panel.FontParams(size,flags,angle);
         panel.SetFontParams(name,9,FW_BOLD);
         panel.DrawText(state_str,panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clr,90);
      //--- Restore the normal thickness of the panel font
         panel.SetFontParams(name,9);
         
      //--- Redraw the chart to immediately display all changes on the panel
         ChartRedraw(ChartID());
        }
      
      

      通过这里提供的通用函数,可以以通常的方式获得BW MFI指标数据。但是,为了解释指标列的读数,我们需要另一个指标-交易量指标,因为要给直方图列上色,需要比较两个指标-直方图列的值和相对于其先前值的交易量的值。要在函数中获取交易量,创建交易量指标句柄(第一次访问时一次)并比较BW MFI和交易量线的状态。它们相互关系的描述以文本形式显示在面板上。

      此外,当使用仪表板时,面板事件处理程序在OnChartEvent()EA事件处理函数中调用,以及处理用于接收光标下的柱形索引的事件:

      //+------------------------------------------------------------------+
      //| ChartEvent function                                              |
      //+------------------------------------------------------------------+
      void OnChartEvent(const int id,
                        const long &lparam,
                        const double &dparam,
                        const string &sparam)
        {
      //--- Handling the panel
      //--- Call the panel event handler
         panel.OnChartEvent(id,lparam,dparam,sparam);
      
      //--- If the cursor moves or a click is made on the chart
         if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
           {
            //--- Declare the variables to record time and price coordinates in them
            datetime time=0;
            double price=0;
            int wnd=0;
            //--- If the cursor coordinates are converted to date and time
            if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
              {
               //--- write the bar index where the cursor is located to a global variable
               mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time);
               //--- Display the bar data under the cursor on the panel 
               DrawData(mouse_bar_index,time);
              }
           }
      
      //--- If we received a custom event, display the appropriate message in the journal
         if(id>CHARTEVENT_CUSTOM)
           {
            //--- Here we can implement handling a click on the close button on the panel
            PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam);
           }
        }
      
      

      编译EA并在图表上启动后,我们可以监控面板上指标值和线的状态:

      您可以在文章附件中查看TestWilliamsBWMFI.mq5测试EA。


      仪表板类的优化。浏览

      在本系列的测试EA中,我们使用第一篇文章中创建的仪表板。可以在面板中创建一个表格。表格坐标可用于在仪表板上显示数据。现在,仪表板类已经完成——您可以创建任意数量的表来在其中放置数据。我还修复了折叠仪表板、切换时间段并再次扩展仪表板后仪表板数据的临时消失。让我们简要介绍一下所做的更改,以免回到对仪表板类所做更改的主题。

      现在,面板上每个创建的表都可以返回其坐标:X1,Y1——左上角,X2和Y2——右下角。每个板都有自己的ID和名称,您可以通过这些ID和名称访问它们来获取数据。

      CTableData表格数据类现在具有写入和返回这些值的私有变量和公共方法:

      //+------------------------------------------------------------------+
      //| Table data class                                                 |
      //+------------------------------------------------------------------+
      class CTableData : public CObject
        {
      private:
         CArrayObj         m_list_rows;               // List of rows
         uint              m_id;                      // Table ID
         int               m_x1;                      // X1 coordinate
         int               m_y1;                      // Y1 coordinate
         int               m_x2;                      // X2 coordinate
         int               m_y2;                      // Y2 coordinate
         int               m_w;                       // Width
         int               m_h;                       // Height
         string            m_name;                    // Table name
      public:
      //--- Set table name
         void              SetName(const string name) { this.m_name=name;              }
      //--- Return table (1) ID and (2) name
         uint              ID(void)             const { return this.m_id;              }
         string            Name(void)           const { return this.m_name;            }
         
      //--- Set coordinate (1) X1, (2) X2
         void              SetX1(const uint x1)       { this.m_x1=(int)x1;             }
         void              SetX2(const uint x2)       { this.m_x2=(int)x2;             }
      //--- Set coordinate (1) Y1, (2) Y2
         void              SetY1(const uint y1)       { this.m_y1=(int)y1;             }
         void              SetY2(const uint y2)       { this.m_y2=(int)y2;             }
      //--- Set table coordinates
         void              SetCoords(const int x1,const int y1,const int x2,const int y2)
                             {
                              this.SetX1(x1);
                              this.SetY1(y1);
                              this.SetX2(x2);
                              this.SetY2(y2);
                             }
         
      //--- Return coordinate (1) X1, (2) X2
         int               X1(void)             const { return this.m_x1;              }
         int               X2(void)             const { return this.m_x2;              }
      //--- Return coordinate (1) Y1, (2) Y2
         int               Y1(void)             const { return this.m_y1;              }
         int               Y2(void)             const { return this.m_y2;              }
         
      //--- Return (1) width and (2) height
         int               Width(void)          const { return this.m_x2-this.m_x1+1;  }
         int               Height(void)         const { return this.m_y2-this.m_y1+1;  }
         
      //--- Return the list of table rows
      

      添加了一个公有方法,该方法返回指定行中的单元格数:

         int               ColumnsInRow(const int row_index)
                             {
                              //--- If there is no row in the list, return 0
                              if(this.RowsTotal()==0)
                                 return 0;
                              //--- Get a pointer to the specified row and return the number of cells in it
                              CTableRow *row=this.GetRow(row_index);
                              return(row!=NULL ? row.CellsTotal() : 0);
                             }
      //--- Return the total number of cells in the table
      

      添加了一个返回表单元格总数的公有方法:

      //--- Return the total number of cells in the table
         int               CellsTotal(void)
                             {
                              //--- If there is no row in the list, return 0
                              if(this.RowsTotal()==0)
                                 return 0;
                              //---
                              int num=0;
                              int total=this.RowsTotal();
                              for(int i=0;i<total;i++)
                                 num+=this.ColumnsInRow(i);
                              return num;
                             }
      //--- Clear lists of rows and table cells
      

      以前,我们只是返回表第一行中的列数,希望它们在每行中的数量相同。现在我们可以通过表中每行的单元格数量来获得表单元格的总数。我们还可以获取指定的行中的单元格数量。因此,可以创建非晶格表格。由于当前任务缺乏需求,未测试创建行中单元格数不同的表。最有可能的是,还需要进一步的改进。但目前不需要这样的表格。

      该类具有虚拟Compare方法,允许我们按ID(mode=0)或名称(mode!=0)比较表格:

      //--- Virtual method for comparing two objects
         virtual int       Compare(const CObject *node,const int mode=0) const
                             {
                              const CTableData *compared=node;
                              if(mode==0)
                                 return(this.ID()>compared.ID() ? 1 : this.ID()<compared.ID() ? -1 : 0);
                              else
                                 return(this.Name()==compared.Name() ? 0 : this.Name()>compared.Name() ? 1 : -1);
                             }
      

      创建的表的ID现在传递给类的参数构造函数:

      //--- Constructor/destructor
                           CTableData(const uint id) : m_id(id){ this.m_list_rows.Clear(); this.m_name="";  }
                          ~CTableData(void)                    { this.m_list_rows.Clear();                  }
      


      以前表格数据对象实例是在面板类中声明的,而现在我们声明列表中包含指向面板中创建的表的指针。

      //+------------------------------------------------------------------+
      //| Dashboard class                                                  |
      //+------------------------------------------------------------------+
      class CDashboard : public CObject
        {
      private:
         CCanvas           m_canvas;                  // Canvas
         CCanvas           m_workspace;               // Work space
         CArrayObj         m_list_table;              // List of tables
         ENUM_PROGRAM_TYPE m_program_type;            // Program type
         ENUM_MOUSE_STATE  m_mouse_state;             // Mouse button status
      

      声明变量以创建文件名,用于将背景和工作区像素保存到专用部分中的文件:

         string            m_name_gv_m;               // Name of the global terminal variable storing the collapsed panel flag 
      
         string            m_name_gv_u;               // Name of the global terminal variable storing the flag of the pinned panel 
         string            m_filename_bg;             // File name to save background pixels 
         string            m_filename_ws;             // File name for saving work space pixels
         
         uint              m_array_wpx[];             // Array of pixels to save/restore the workspace 
         uint              m_array_ppx[];             // Array of pixels to save/restore the panel background 
      

      添加并改进了使用面板字体以及创建和获取表格及其坐标的方法

      //--- Set default panel font parameters
         void              SetFontParams(const string name,const int size,const uint flags=0,const uint angle=0);
      //--- Return the specified dashboard font parameters
         string            FontParams(int &size,uint &flags,uint &angle);
      //--- Return the specified panel (1) font, (2) size and font flags
         string            FontName(void)    const { return this.m_workspace.FontNameGet();  }
         int               FontSize(void)    const { return this.m_workspace.FontSizeGet();  }
         uint              FontFlags(void)   const { return this.m_workspace.FontFlagsGet(); }
      //--- Display a text message at the specified coordinates
         void              DrawText(const string text,const int x,const int y,const color clr=clrNONE,const int width=WRONG_VALUE,const int height=WRONG_VALUE);
      //--- Create a new table
         bool              CreateNewTable(const int id=WRONG_VALUE);
      //--- Return tabular data object by (1) ID and (2) name
         CTableData       *GetTable(const uint id);
         CTableData       *GetTable(const string name);
      //--- Draw a (1) background grid (2) with automatic cell size
         void              DrawGrid(const uint table_id,const uint x,const uint y,const uint rows,const uint columns,const uint row_size,const uint col_size,const color line_color=clrNONE,bool alternating_color=true);
         void              DrawGridAutoFill(const uint table_id,const uint border,const uint rows,const uint columns,const color line_color=clrNONE,bool alternating_color=true);
      //--- Print grid data (line intersection coordinates)
         void              GridPrint(const uint table_id,const uint indent=0)
                             {
                              CTableData *table=this.GetTable(table_id);
                              if(table==NULL)
                                {
                                 ::PrintFormat("%s: Error. Failed to get table object with id %lu",__FUNCTION__,table_id);
                                 return;
                                }
                              table.Print(indent);
                             }
      //--- Write the X and Y coordinate values of the specified table cell to variables
         void              CellXY(const uint table_id,const uint row,const uint column, int &x, int &y)
                             {
                              CTableData *table=this.GetTable(table_id);
                              if(table==NULL)
                                {
                                 ::PrintFormat("%s: Error. Failed to get table object with id %lu",__FUNCTION__,table_id);
                                 return;
                                }
                              table.CellXY(row,column,x,y);
                             }
      //--- Return the (1) X and (2) Y coordinate of the specified table cell
         int               CellX(const uint table_id,const uint row,const uint column)
                             {
                              CTableData *table=this.GetTable(table_id);
                              if(table==NULL)
                                {
                                 ::PrintFormat("%s: Error. Failed to get table object with id %lu",__FUNCTION__,table_id);
                                 return WRONG_VALUE;
                                }
                              return table.CellX(row,column);
                             }
         int               CellY(const uint table_id,const uint row,const uint column)
                             {
                              CTableData *table=this.GetTable(table_id);
                              if(table==NULL)
                                {
                                 ::PrintFormat("%s: Error. Failed to get table object with id %lu",__FUNCTION__,table_id);
                                 return WRONG_VALUE;
                                }
                              return table.CellY(row,column);
                             }
      //--- Write X1 and Y1, X2 and Y2 coordinate values of the specified table to the variables
         void              TableCoords(const uint table_id,int &x1,int &y1,int &x2,int &y2)
                             {
                              x1=y1=x2=y2=WRONG_VALUE;
                              CTableData *table=this.GetTable(table_id);
                              if(table==NULL)
                                 return;
                              x1=table.X1();
                              y1=table.Y1();
                              x2=table.X2();
                              y2=table.Y2();
                             }
      
      //--- Return the (1) X1, (2) Y1, (3) X2 and (4) Y2 coordinate of the specified table
         int               TableX1(const uint table_id)
                             {
                              CTableData *table=this.GetTable(table_id);
                              return(table!=NULL ? table.X1() : WRONG_VALUE);
                             }
         int               TableY1(const uint table_id)
                             {
                              CTableData *table=this.GetTable(table_id);
                              return(table!=NULL ? table.Y1() : WRONG_VALUE);
                             }
         int               TableX2(const uint table_id)
                             {
                              CTableData *table=this.GetTable(table_id);
                              return(table!=NULL ? table.X2() : WRONG_VALUE);
                             }
         int               TableY2(const uint table_id)
                             {
                              CTableData *table=this.GetTable(table_id);
                              return(table!=NULL ? table.Y2() : WRONG_VALUE);
                             }
      
      //--- Event handler
         void              OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
      //--- Constructor/destructor
                           CDashboard(const uint id,const int x,const int y, const int w,const int h,const int wnd=-1);
                          ~CDashboard();
      


      在类构造函数中,创建文件名以保存背景和工作空间:

      //--- Set the names of global terminal variables to store panel coordinates, collapsed/expanded state and pinning
         this.m_name_gv_x=this.m_program_name+"_id_"+(string)this.m_id+"_"+(string)this.m_chart_id+"_X";
         this.m_name_gv_y=this.m_program_name+"_id_"+(string)this.m_id+"_"+(string)this.m_chart_id+"_Y";
         this.m_name_gv_m=this.m_program_name+"_id_"+(string)this.m_id+"_"+(string)this.m_chart_id+"_Minimize";
         this.m_name_gv_u=this.m_program_name+"_id_"+(string)this.m_id+"_"+(string)this.m_chart_id+"_Unpin";
      
      //--- Set file names for saving background and work space pixels
         this.m_filename_bg=this.m_program_name+"\\Dashboard"+(string)this.m_id+"\\background.bin";
         this.m_filename_ws=this.m_program_name+"\\Dashboard"+(string)this.m_id+"\\workspace.bin";
         
      

      在构造函数的最后,如果面板被折叠,文件中的数据将加载到背景和工作空间像素阵列中:

      //--- If the panel collapse flag is set, load the background and work space pixels from the files into arrays
         if(this.m_minimized)
           {
            if(::FileIsExist(this.m_filename_bg))
               this.FileLoadBackground();
            if(::FileIsExist(this.m_filename_ws))
               this.FileLoadWorkspace();
           }
        }
      

      因此,如果像素以前保存到文件中,并且面板是以最小化的形式创建的,那么面板的外观将从文件中加载,并且面板将以折叠的形式绘制。当它被展开时,它的外观将从文件中填充的像素阵列中获得。

      在析构函数中,如果面板被折叠,那么在删除面板对象之前,我们需要展开它,将像素数据写入文件,然后再次折叠它。之后,我们可以删除面板对象-它的外观已经保存到要在构造函数中进行后续创建时从中恢复的文件中:

      //+------------------------------------------------------------------+
      //| Destructor                                                       |
      //+------------------------------------------------------------------+
      CDashboard::~CDashboard()
        {
      //--- Write the current values to global terminal variables
         ::GlobalVariableSet(this.m_name_gv_x,this.m_x);
         ::GlobalVariableSet(this.m_name_gv_y,this.m_y);
         ::GlobalVariableSet(this.m_name_gv_m,this.m_minimized);
         ::GlobalVariableSet(this.m_name_gv_u,this.m_movable);
      
      //--- If the panel is collapsed,
      //--- expand the panel, save the appearance into pixel arrays and collapse the panel
         if(this.m_minimized)
           {
            this.Expand();
            this.SaveBackground();
            this.SaveWorkspace();
            this.Collapse();
           }
      //--- otherwise, if the panel is expanded,
      //--- save the appearance into pixel arrays
         else
           {
            this.SaveBackground();
            this.SaveWorkspace();
           }
      //--- Save pixel arrays to files
         this.FileSaveBackground();
         this.FileSaveWorkspace();
      
      //--- Delete panel objects
         this.m_canvas.Destroy();
         this.m_workspace.Destroy();
        }
      


      在用于处理点击面板折叠/展开按钮的代码块中,检查标志并且如果面板被展开,则将背景和工作空间保存到像素阵列中

            //--- If the panel collapse/expand button is pressed 
            else if(state==MOUSE_STATE_PRESSED_INSIDE_MINIMIZE)
              {
               //--- Disable chart scrolling, right-click menu and crosshair
               this.SetChartsTool(false);
               //--- If the panel is not collapsed, save the background and work space into pixel arrays 
               if(!this.m_minimized)
                 {
                  this.SaveWorkspace();
                  this.SaveBackground();
                 }
               //--- "flip" the panel collapse flag,
               this.m_minimized=!this.m_minimized;
               //--- redraw the panel taking into account the new state of the flag,
               this.Draw(this.m_title);
               //--- redraw the panel header area 
               this.RedrawHeaderArea();
               //--- If the panel is pinned and expanded, move it to the stored location coordinates
               if(this.m_minimized && !this.m_movable)
                  this.Move(this.m_x_dock,this.m_y_dock);
               //--- Update the canvas with chart redrawing and
               this.m_canvas.Update();
               //--- write the state of the panel expand flag to the global terminal variable
               ::GlobalVariableSet(this.m_name_gv_m,this.m_minimized);
              }
      


      用于保存像素阵列的字符串已从面板折叠方法中删除。现在,只有在按下最小化/展开按钮时才能保存像素:

      //+------------------------------------------------------------------+
      //| Collapse the panel                                               |
      //+------------------------------------------------------------------+
      void CDashboard::Collapse(void)
        {
      //--- Save the pixels of the working space and the panel background into arrays
         this.SaveWorkspace();
         this.SaveBackground();
      //--- Remember the current height of the panel
         int h=this.m_h;
      //--- Change the dimensions (height) of the canvas and working space
         if(!this.SetSizes(this.m_canvas.Width(),this.m_header_h))
            return;
      //--- Draw the header area
         this.DrawHeaderArea(this.m_title);
      //--- Return the saved panel height to the variable
         this.m_h=h;
        }
      


      返回设置的仪表板字体参数的方法的实现:

      //+------------------------------------------------------------------+
      //| Return the specified dashboard font parameters                   |
      //+------------------------------------------------------------------+
      string CDashboard::FontParams(int &size,uint &flags,uint &angle)
        {
         size=this.m_workspace.FontSizeGet();
         flags=this.m_workspace.FontFlagsGet();
         angle=this.m_workspace.FontAngleGet();
         return this.m_workspace.FontNameGet();
        }
      

      该方法返回字体的名称。字体大小、标志和角度被写入链接传递的变量中。

      文本颜色现在也传递给绘图方法。默认值为clrNONE,这意味着之前设置的文本颜色

      //+------------------------------------------------------------------+
      //| Display a text message at the specified coordinates              |
      //+------------------------------------------------------------------+
      void CDashboard::DrawText(const string text,const int x,const int y,const color clr=clrNONE,const int width=WRONG_VALUE,const int height=WRONG_VALUE)
        {
      //--- Declare variables to record the text width and height in them
         int w=width;
         int h=height;
      //--- If the width and height of the text passed to the method have zero values,
      //--- then the entire working space is completely cleared using the transparent color
         if(width==0 && height==0)
            this.m_workspace.Erase(0x00FFFFFF);
      //--- Otherwise
         else
           {
            //--- If the passed width and height have default values (-1), we get its width and height from the text 
            if(width==WRONG_VALUE && height==WRONG_VALUE)
               this.m_workspace.TextSize(text,w,h);
            //--- otherwise,
            else
              {
               //--- if the width passed to the method has the default value (-1) - get the width from the text, or
               //--- if the width passed to the method has a value greater than zero, use the width passed to the method, or
               //--- if the width passed to the method has a zero value, use the value 1 for the width
               w=(width ==WRONG_VALUE ? this.m_workspace.TextWidth(text)  : width>0  ? width  : 1);
               //--- if the height passed to the method has a default value (-1), get the height from the text, or
               //--- if the height passed to the method has a value greater than zero, use the height passed to the method, or
               //--- if the height passed to the method has a zero value, use value 1 for the height
               h=(height==WRONG_VALUE ? this.m_workspace.TextHeight(text) : height>0 ? height : 1);
              }
            //--- Fill the space according to the specified coordinates and the resulting width and height with a transparent color (erase the previous entry)
            this.m_workspace.FillRectangle(x,y,x+w,y+h,0x00FFFFFF);
           }
      //--- Display the text to the space cleared of previous text and update the working space without redrawing the screen
         this.m_workspace.TextOut(x,y,text,::ColorToARGB(clr==clrNONE ? this.m_fore_color : clr));
         this.m_workspace.Update(false);
        }
      


      创建新表和按ID和表名获取表格数据的方法的实现:

      //+------------------------------------------------------------------+
      //| Create a new table                                               |
      //+------------------------------------------------------------------+
      bool CDashboard::CreateNewTable(const int id=WRONG_VALUE)
        {
         uint num=(id>WRONG_VALUE ? id : this.m_list_table.Total());
         CTableData *table=new CTableData(num);
         this.m_list_table.Sort();
         if(this.m_list_table.Search(table)!=WRONG_VALUE)
           {
            PrintFormat("%s: Error. Table with id %lu already exists in the list",__FUNCTION__,num);
            delete table;
            return false;
           }
         if(!this.m_list_table.Add(table))
           {
            PrintFormat("%s: Error. Failed to add table with id %lu to the list",__FUNCTION__,num);
            delete table;
            return false;
           }
         return true;
        }
      //+------------------------------------------------------------------+
      //| Return tabular data object by ID                                 |
      //+------------------------------------------------------------------+
      CTableData *CDashboard::GetTable(const uint id)
        {
         if(this.m_list_table.Total()==0)
           {
            PrintFormat("%s: Error. The list of tables is empty. First you need to create a table using CreateNewTable",__FUNCTION__);
            .return NULL;
           }
         CTableData *table=new CTableData(id);
         if(table==NULL)
           {
            ::PrintFormat("%s: Error. Failed to create table object with id %lu",__FUNCTION__,id);
            .return NULL;
           }
         this.m_list_table.Sort();
         int index=this.m_list_table.Search(table);
         delete table;
         return this.m_list_table.At(index);
        }
      //+------------------------------------------------------------------+
      //| Return tabular data object by name                               |
      //+------------------------------------------------------------------+
      CTableData *CDashboard::GetTable(const string name)
        {
         if(this.m_list_table.Total()==0)
           {
            PrintFormat("%s: Error. The list of tables is empty. First you need to create a table using CreateNewTable",__FUNCTION__);
            .return NULL;
           }
         CTableData *table=new CTableData(0);
         if(table==NULL)
           {
            ::PrintFormat("%s: Error. Failed to create table object");
            .return NULL;
           }
         table.SetName(name);
         this.m_list_table.Sort(1);
         int index=this.m_list_table.Search(table);
         delete table;
         return this.m_list_table.At(index);
        }
      


      表格绘图方法中的更改

      //+------------------------------------------------------------------+
      //| Draw the background grid                                         |
      //+------------------------------------------------------------------+
      void CDashboard::DrawGrid(const uint table_id,
                                const uint x,const uint y,const uint rows,const uint columns,const uint row_size,const uint col_size,
                                const color line_color=clrNONE,bool alternating_color=true)
        {
      //--- Get a table object by ID
         CTableData *table=this.GetTable(table_id);
         if(table==NULL)
           {
            PrintFormat("%s: Error. Failed to get table object with id %lu",__FUNCTION__,table_id);
            return;
           }
      //--- Clear all lists of the tabular data object (remove cells from rows and all rows)
         table.Clear();
      //--- Line height cannot be less than 2
         int row_h=int(row_size<2 ? 2 : row_size);
      //--- Column width cannot be less than 2
         int col_w=int(col_size<2 ? 2 : col_size);
         
      //--- The X1 (left) coordinate of the table cannot be less than 1 (to leave one pixel around the perimeter of the panel for the frame)
         int x1=int(x<1 ? 1 : x);
      //--- Calculate the X2 coordinate (right) depending on the number of columns and their width
         int x2=x1+col_w*int(columns>0 ? columns : 1);
      //--- The Y1 coordinate is located under the panel title area
         int y1=this.m_header_h+(int)y;
      //--- Calculate the Y2 coordinate (bottom) depending on the number of lines and their height
         int y2=y1+row_h*int(rows>0 ? rows : 1);
      //--- Set table coordinates
         table.SetCoords(x1,y1-this.m_header_h,x2,y2-this.m_header_h);
         
      //--- Get the color of the table grid lines, either by default or passed to the method
         color clr=(line_color==clrNONE ? C'200,200,200' : line_color);
      //--- If the initial X coordinate is greater than 1, draw a table frame
      //--- (in case of the coordinate 1, the table frame is the panel frame)
         if(x1>1)
            this.m_canvas.Rectangle(x1,y1,x2,y2,::ColorToARGB(clr,this.m_alpha));
      //--- In the loop by table rows,
         for(int i=0;i<(int)rows;i++)
           {
            //--- calculate the Y coordinate of the next horizontal grid line (Y coordinate of the next table row)
            int row_y=y1+row_h*i;
            //--- if the flag of "alternating" line colors is passed and the line is even
            if(alternating_color && i%2==0)
              {
               //--- lighten the table background color and draw a background rectangle
               color new_color=this.NewColor(clr,45,45,45);
               this.m_canvas.FillRectangle(x1+1,row_y+1,x2-1,row_y+row_h-1,::ColorToARGB(new_color,this.m_alpha));
              }
            //--- Draw a table grid horizontal line
            this.m_canvas.Line(x1,row_y,x2,row_y,::ColorToARGB(clr,this.m_alpha));
            
            //--- Create a new table row object
            CTableRow *row_obj=new CTableRow(i);
            if(row_obj==NULL)
              {
               ::PrintFormat("%s: Failed to create table row object at index %lu",(string)__FUNCTION__,i);
               continue;
              }
            //--- Add it to the list of rows of the tabular data object
            //--- (if adding an object failed, delete the created object)
            if(!table.AddRow(row_obj))
               delete row_obj;
            //--- Set its Y coordinate in the created row object taking into account the offset from the panel title
            row_obj.SetY(row_y-this.m_header_h);
           }
           
      //--- In the loop by table columns,
         for(int i=0;i<(int)columns;i++)
           {
            //--- calculate the X coordinate of the next vertical grid line (X coordinate of the next table row)
            int col_x=x1+col_w*i;
            //--- If the grid line goes beyond the panel, interrupt the loop
            if(x1==1 && col_x>=x1+m_canvas.Width()-2)
               break;
            //--- Draw a vertical line of the table grid
            this.m_canvas.Line(col_x,y1,col_x,y2,::ColorToARGB(clr,this.m_alpha));
            
            //--- Get the number of created rows from the table data object 
            int total=table.RowsTotal();
            //--- In the loop by table rows
            for(int j=0;j<total;j++)
              {
               //--- get the next row
               CTableRow *row=table.GetRow(j);
               if(row==NULL)
                  continue;
               //--- Create a new table cell 
               CTableCell *cell=new CTableCell(row.Row(),i);
               if(cell==NULL)
                 {
                  ::PrintFormat("%s: Failed to create table cell object at index %lu",(string)__FUNCTION__,i);
                  continue;
                 }
               //--- Add the created cell to the row
               //--- (if adding an object failed, delete the created object)
               if(!row.AddCell(cell))
                 {
                  delete cell;
                  continue;
                 }
               //--- In the created cell object, set its X coordinate and the Y coordinate from the row object 
               cell.SetXY(col_x,row.Y());
              }
           }
      //--- Update the canvas without redrawing the chart
         this.m_canvas.Update(false);
        }
      //+------------------------------------------------------------------+
      //| Draws the background grid with automatic cell sizing             |
      //+------------------------------------------------------------------+
      void CDashboard::DrawGridAutoFill(const uint table_id,const uint border,const uint rows,const uint columns,const color line_color=clrNONE,bool alternating_color=true)
        {
      //--- Get a table object by ID
         CTableData *table=this.GetTable(table_id);
         if(table==NULL)
           {
            PrintFormat("%s: Error. Failed to get table object with id %lu",__FUNCTION__,table_id);
            return;
           }
      //--- X1 (left) table coordinate
         int x1=(int)border;
      //--- X2 (right) table coordinate
         int x2=this.m_canvas.Width()-(int)border-1;
      //--- Y1 (upper) table coordinate
         int y1=this.m_header_h+(int)border;
      //--- Y2 (lower) table coordinate
         int y2=this.m_canvas.Height()-(int)border-1;
      //--- Set table coordinates
         table.SetCoords(x1,y1,x2,y2);
      
      //--- Get the color of the table grid lines, either by default or passed to the method
         color clr=(line_color==clrNONE ? C'200,200,200' : line_color);
      //--- If the offset from the edge of the panel is greater than zero, draw a table border,
      //--- otherwise, the panel border is used as the table border
         if(border>0)
            this.m_canvas.Rectangle(x1,y1,x2,y2,::ColorToARGB(clr,this.m_alpha));
      
      //--- Height of the entire table grid
         int greed_h=y2-y1;
      //--- Calculate the row height depending on the table height and the number of rows
         int row_h=(int)::round((double)greed_h/(double)rows);
      //--- In the loop based on the number of rows
         for(int i=0;i<(int)rows;i++)
           {
            //--- calculate the Y coordinate of the next horizontal grid line (Y coordinate of the next table row)
            int row_y=y1+row_h*i;
            //--- if the flag of "alternating" line colors is passed and the line is even
            if(alternating_color && i%2==0)
              {
               //--- lighten the table background color and draw a background rectangle
               color new_color=this.NewColor(clr,45,45,45);
               this.m_canvas.FillRectangle(x1+1,row_y+1,x2-1,row_y+row_h-1,::ColorToARGB(new_color,this.m_alpha));
              }
            //--- Draw a table grid horizontal line
            this.m_canvas.Line(x1,row_y,x2,row_y,::ColorToARGB(clr,this.m_alpha));
            
            //--- Create a new table row object
            CTableRow *row_obj=new CTableRow(i);
            if(row_obj==NULL)
              {
               ::PrintFormat("%s: Failed to create table row object at index %lu",(string)__FUNCTION__,i);
               continue;
              }
            //--- Add it to the list of rows of the tabular data object
            //--- (if adding an object failed, delete the created object)
            if(!table.AddRow(row_obj))
               delete row_obj;
            //--- Set its Y coordinate in the created row object taking into account the offset from the panel title
            row_obj.SetY(row_y-this.m_header_h);
           }
           
      //--- Table grid width
         int greed_w=x2-x1;
      //--- Calculate the column width depending on the table width and the number of columns
         int col_w=(int)::round((double)greed_w/(double)columns);
      //--- In the loop by table columns,
         for(int i=0;i<(int)columns;i++)
           {
            //--- calculate the X coordinate of the next vertical grid line (X coordinate of the next table row)
            int col_x=x1+col_w*i;
            //--- If this is not the very first vertical line, draw it
            //--- (the first vertical line is either the table frame or the panel frame)
            if(i>0)
               this.m_canvas.Line(col_x,y1,col_x,y2,::ColorToARGB(clr,this.m_alpha));
            
            //--- Get the number of created rows from the table data object 
            int total=table.RowsTotal();
            //--- In the loop by table rows
            for(int j=0;j<total;j++)
              {
               //--- get the next row
               CTableRow *row=table.GetRow(j);
               if(row==NULL)
                  continue;
               //--- Create a new table cell 
               CTableCell *cell=new CTableCell(row.Row(),i);
               if(cell==NULL)
                 {
                  ::PrintFormat("%s: Failed to create table cell object at index %lu",(string)__FUNCTION__,i);
                  continue;
                 }
               //--- Add the created cell to the row
               //--- (if adding an object failed, delete the created object)
               if(!row.AddCell(cell))
                 {
                  delete cell;
                  continue;
                 }
               //--- In the created cell object, set its X coordinate and the Y coordinate from the row object 
               cell.SetXY(col_x,row.Y());
              }
           }
      //--- Update the canvas without redrawing the chart
         this.m_canvas.Update(false);
        }
      


      将像素保存到文件/从文件加载像素的方法现在适用于先前在构造函数中创建的文件名

      //+------------------------------------------------------------------+
      //| Save the pixel array of the working space to a file              |
      //+------------------------------------------------------------------+
      bool CDashboard::FileSaveWorkspace(void)
        {
      //--- If the saved array is empty, inform of that and return 'false'
         if(this.m_array_wpx.Size()==0)
           {
            ::PrintFormat("%s: Error. The workspace pixel array is empty.",__FUNCTION__);
            return false;
           }
      //--- If the array could not be saved to a file, report this and return 'false'
         if(!::FileSave(this.m_filename_ws,this.m_array_wpx))
           {
            ::PrintFormat("%s: FileSave '%s' failed. Error %lu",__FUNCTION__,this.m_filename_ws,::GetLastError());
            return false;
           }
      //--- Successful, return 'true'
         return true;
        }
      //+------------------------------------------------------------------+
      //| Save the pixel array of the panel background to a file           |
      //+------------------------------------------------------------------+
      bool CDashboard::FileSaveBackground(void)
        {
      //--- If the saved array is empty, inform of that and return 'false'
         if(this.m_array_ppx.Size()==0)
           {
            ::PrintFormat("%s: Error. The background pixel array is empty.",__FUNCTION__);
            return false;
           }
      //--- If the array could not be saved to a file, report this and return 'false'
         if(!::FileSave(this.m_filename_bg,this.m_array_ppx))
           {
            ::PrintFormat("%s: FileSave '%s' failed. Error %lu",__FUNCTION__,this.m_filename_bg,::GetLastError());
            return false;
           }
      //--- Successful, return 'true'
         return true;
        }
      //+------------------------------------------------------------------+
      //| Upload the array of working space pixels from a file             |
      //+------------------------------------------------------------------+
      bool CDashboard::FileLoadWorkspace(void)
        {
      //--- If failed to upload data from the file into the array, report this and return 'false'
         if(::FileLoad(this.m_filename_ws,this.m_array_wpx)==WRONG_VALUE)
           {
            ::PrintFormat("%s: FileLoad '%s' failed. Error %lu",__FUNCTION__,this.m_filename_ws,::GetLastError());
            return false;
           }
      //--- Successful, return 'true'
         return true;
        }
      //+------------------------------------------------------------------+
      //| Upload the array of panel background pixels from a file          |
      //+------------------------------------------------------------------+
      bool CDashboard::FileLoadBackground(void)
        {
         if(::FileLoad(this.m_filename_bg,this.m_array_ppx)==WRONG_VALUE)
           {
            ::PrintFormat("%s: FileLoad '%s' failed. Error %lu",__FUNCTION__,this.m_filename_bg,::GetLastError());
            return false;
           }
      //--- Successful, return 'true'
         return true;
        }
      


      结论

      在本文中,我们研究了与交易量和比尔威廉姆斯的指标EA的连接。文章中提供的所有代码都可以“按原样”使用,以将它们插入到自定义代码中。接下来,我们将探讨最后一类指标——趋势指标——在EA中的联系和使用。

      所有文件(测试EA和面板类)都可以从下面的文件列表中下载。面板类应位于\MQL5\Include\Dashboard\Dashboard.mqh。


      本文由MetaQuotes Ltd译自俄文
      原文地址: https://www.mql5.com/ru/articles/13277

      附加的文件 |
      TestVolumeAD.mq5 (30.66 KB)
      TestVolumeMFI.mq5 (35.21 KB)
      TestVolumeOBV.mq5 (30.67 KB)
      TestWilliamsAC.mq5 (30.71 KB)
      TestWilliamsAO.mq5 (32.31 KB)
      Dashboard.mqh (217.85 KB)
      配对交易 配对交易
      在这篇文章中,我们将探讨配对交易(pair trading),即它的原理是什么,以及它的实际应用是否有前景。我们还将尝试创建一个配对交易策略。
      开发回放系统 — 市场模拟(第 19 部分):必要的调整 开发回放系统 — 市场模拟(第 19 部分):必要的调整
      在此,我们要做好准备,如此当我们需要往代码里添加新函数时,就能顺滑轻松地发生。当前代码还不能涵盖或处理那些显著推进过程所必需的事情。我们需要将所有东西都结构化,以便能够以最小的工作量实现某些事情。如果我们正确地做好所有事情,我们就能得到一个真正通用的系统,可以轻松地适应任何需要处理的状况。
      MQL5 中的范畴论 (第 13 部分):数据库制程的日历事件 MQL5 中的范畴论 (第 13 部分):数据库制程的日历事件
      本文在 MQL5 中遵循范畴论实现秩序,研究如何在 MQL5 中结合数据库制程进行分类。我们介绍了当辨别交易相关的文本(字符串)信息时,如何把数据库制程概念与范畴论相结合。日历事件是焦点。
      神经网络变得轻松(第四十九部分):软性扮演者-评价者 神经网络变得轻松(第四十九部分):软性扮演者-评价者
      我们继续讨论解决连续动作空间问题的强化学习算法。在本文中,我将讲演软性扮演者-评论者(SAC)算法。SAC 的主要优点是拥有查找最佳策略的能力,不仅令预期回报最大化,而且拥有最大化的动作熵(多样性)。