手工图表和交易工具包(第一部分)。 准备:结构描述和助手类

29 九月 2020, 09:22
Oleh Fedorov
0
1 562

概述


我是一名手工交易者。 我在分析图表时更喜欢不用复杂的公式和指标,只用纯手工(即以图形方式)进行分析。 这有助于我更加灵活地进行交易,关注一些难以形式化的事情,如果我看到波动在增加或放缓,则可以轻松地在时间帧之间切换,并在入场交易之前就知道了可能的价格行为。

基本上,我使用趋势线的不同组合(草叉、扇形、水平、等等)。 为此,我创建了一套便捷的工具,可一键快速绘制趋势线。 现在,我希望与社区共享此工具。

视频演示。 它如何运行



一般概念。 设定任务


故此,我们正在创建一套工具,该工具可帮助您利用键盘快捷键执行最频繁的操作。

可以实现哪些任务? 举例:

  • 按下 “H” 键(“Horizontal”)绘制一条简单的水平线,而按下 “ i ” 则绘制一条垂直线(仅因为它看起来像一条垂直线)
  • 从图表的任意点开始绘制一定长度(不是无限)的水平线
  • 在距起点一定(任意)距离处绘制垂直线
  • l利用按键切换时间帧,并重新排列图表上的图层
  • 在最近的极限点绘制预设价位的斐波那契扇形
  • 在最近的极限点绘制趋势线;它们的长度应为端点之间距离的倍数;在某些情况下,该趋势线也可以是射线
  • 绘制各种类型的安德鲁草叉(标准,Schiff,反向 Schiff 草叉 - 对于快速趋势)(请参阅 视频)
  • 对于草叉、趋势线和扇形的极值点,应能自定义极值点的顺序(分立左、右两侧的柱线数量)
  • 图形界面,可在不打开设置窗口的情况下自定义所需趋势线和极值点的参数
  • 一组订单管理函数:基于存款百分比开立订单,在开立市价订单或没有设置触发止损价位的挂单时自动设置止损价位,启用按价位部分平仓,尾随止损,诸如此类

当然,大多数任务可以用脚本自动执行。 我完成了其中的几个。 您可在文章的附件里找到它们。 然而,我更喜欢另一种方式。

在智能交易系统或指标里,我们能够创建 OnChartEvent 方法,包含针对任何事件的响应描述:击键、鼠标移动、图形对象的创建或删除。

这就是为什么我决定把所创建的程序作为一个包含文件的原因。 所有函数和变量都分布在若干个类里,从而令其更易于访问。 在这一点上,我只需要类即可方便地对函数进行分组。 这就是为什么在此首个实现中,我们将不使用诸如继承或工厂之类的复杂事物的原因。 这只是一个集合。

甚而,我想创建一个可以在 MQL4 和 MQL5 中均可运行的跨平台类。

程序结构

文件清单

该函数库包含五个相关文件。 所有文件都位于 Include 目录中的 “Shortcuts” 文件夹下。 它们的名称如图所示:GlobalVariables.mqh,Graphics.mqh,Mouse.mqh,Shortcuts.mqh,Utilites.mqh。

函数库主文件(Shortcuts.mqh)

程序的主文件是 "Shortcuts.mqh"。 按键响应逻辑将写入此文件之中。 这是应该连接到智能交易系统的文件。 所有辅助文件也将包括在其中。

//+------------------------------------------------------------------+
//|                                                    Shortcuts.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                            https://www.mql5.com/ru/articles/7468 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://www.mql5.com/en/articles/7468"

#include "GlobalVariables.mqh"
#include "Mouse.mqh"
#include "Utilites.mqh"
#include "Graphics.mqh"

//+------------------------------------------------------------------+
//| The main control class of the program. It should be connected    |
//| to the Expert Advisor                                            |
//+------------------------------------------------------------------+
class CShortcuts
  {
private:
   CGraphics         m_graphics;   // Object for drawing m_graphics
public:
                     CShortcuts();
   void              OnChartEvent(const int id,
                                  const long &lparam,
                                  const double &dparam,
                                  const string &sparam);
  };
//+------------------------------------------------------------------+
//| Default constructor                                              |
//+------------------------------------------------------------------+
CShortcuts::CShortcuts(void)
  {
   ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,true);
  }

//+------------------------------------------------------------------+
//| Event handling function                                          |
//+------------------------------------------------------------------+
void CShortcuts::OnChartEvent(
   const int id,
   const long &lparam,
   const double &dparam,
   const string &sparam
)
  {
//---

   // This will contain the description of the events related to keystrokes
   // and mouse movements

   //  ...

  }
}

CShortcuts shortcuts;

该文件包含 CShortcuts 类描述。

在文件的开头,已连接所有帮助类

该类只有两个方法。 第一个是 OnChartEvent 事件响应程序,该事件响应程序将处理所有按键和鼠标移动事件。 第二个是默认构造函数,可在其中处理鼠标移动。

在类描述之后,将创建一个 shortcuts 变量,当连接函数库时,应在智能交易系统主体的 OnChartEvent 方法中使用该变量。

连接需要两行:

//+------------------------------------------------------------------+
//| The main Expert (file "Shortcuts-Main-Expert.mq5")               |
//+------------------------------------------------------------------+
#include <Shortcuts\Shortcuts.mqh>


// ...

//+------------------------------------------------------------------+
//| The ChartEvent function                                          |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
   shortcuts.OnChartEvent(id,lparam,dparam,sparam);
  }

第一行连接类文件。 第二行则将事件响应控制转移到该类。

之后,智能交易系统就准备就绪,可以进行编译并绘制指标线。

鼠标移动响应类

该类将存储当前光标位置的所有基本参数:坐标 X,Y(以像素和价格/时间为单位),指针所处的柱线序号等 - 所有这些都存储在 “ Mouse.mqh ” 文件当中。

//+------------------------------------------------------------------+
//|                                                        Mouse.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                            https://www.mql5.com/ru/articles/7468 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://www.mql5.com/en/articles/7468"
//+------------------------------------------------------------------+
//| Mouse movement handling class                                    |
//+------------------------------------------------------------------+
class CMouse
  {
   //--- Members
private:
   static int        m_x;           // X
   static int        m_y;           // Y
   static int        m_barNumber;   // Bar number
   static bool       m_below;       // Indication of a cursor above the price
   static bool       m_above;       // Indication of a cursor below the price

   static datetime   m_currentTime; // Current time
   static double     m_currentPrice;// Current price
   //--- Methods
public:

   //--- Remembers the main parameters of the mouse cursor
   static void       SetCurrentParameters(
      const int id,
      const long &lparam,
      const double &dparam,
      const string &sparam
   );
   //--- Returns the X coordinate (in pixels) of the current cursor position
   static int        X(void) {return m_x;}
   //--- Returns the Y coordinate (in pixels) of the current cursor position
   static int        Y(void) {return m_y;}
   //--- Returns the price of the current cursor position
   static double     Price(void) {return m_currentPrice;}
   //--- Returns the time of the current cursor position
   static datetime   Time(void) {return m_currentTime;}
   //--- Returns the bar number of the current cursor position
   static int        Bar(void) {return m_barNumber;}
   //--- Returns a flag showing that the price is below the Low of the current bar
   static bool       Below(void) {return m_below;}
   //--- Returns a flag showing that the price is above the High of the current bar
   static bool       Above(void) {return m_above;}
  };
//---

int CMouse::m_x=0;
int CMouse::m_y=0;
int CMouse::m_barNumber=0;
bool CMouse::m_below=false;
bool CMouse::m_above=false;
datetime CMouse::m_currentTime=0;
double CMouse::m_currentPrice=0;


//+------------------------------------------------------------------+
//| Remembers the main parameters for a mouse movement: coordinates  |
//| of cursor in pixels and price/time, whether the cursor is        |
//| above or below the price.                                        |
//|+-----------------------------------------------------------------+
static void CMouse::SetCurrentParameters(
   const int id,
   const long &lparam,
   const double &dparam,
   const string &sparam
)
  {
//---
   int window = 0;
//---
   ChartXYToTimePrice(
      0,
      (int)lparam,
      (int)dparam,
      window,
      m_currentTime,
      m_currentPrice
   );
   m_x=(int)lparam;
   m_y=(int)dparam;
   m_barNumber=iBarShift(
                  Symbol(),
                  PERIOD_CURRENT,
                  m_currentTime
               );
   m_below=m_currentPrice<iLow(Symbol(),PERIOD_CURRENT,m_barNumber);
   m_above=m_currentPrice>iHigh(Symbol(),PERIOD_CURRENT,m_barNumber);
  }

//+------------------------------------------------------------------+

该类可在程序中的任何位置使用,因为其方法已被声明为静态。 故此无需创建该类的实例即可调用它。

智能交易系统设置模块的描述。 GlobalVariables.mqh 文件


所有提供给用户的设置均存储在 GlobalVariables.mqh 文件当中。

下一篇文章将提供更多设置的说明,因为我们会添加更多图形对象。

以下是当前版本中的设置代码:

//+------------------------------------------------------------------+
//|                                              GlobalVariables.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                            https://www.mql5.com/ru/articles/7468 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://www.mql5.com/en/articles/7468"
//+------------------------------------------------------------------+
//| File describing parameters available to the user                 |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Key settings                                                     |
//+------------------------------------------------------------------+
input string   Keys="=== Key settings ===";
input string   Up_Key="U";            // Switch timeframe up
input string   Down_Key="D";          // Switch timeframe down
input string   Trend_Line_Key="T";              // Trend line
input string   Switch_Trend_Ray_Key="R";        // Indication of a trend line ray
input string   Z_Index_Key="Z";                 // Indication of the chart on top

//+------------------------------------------------------------------+
//| Size settings                                                    |
//+------------------------------------------------------------------+
input string   Dimensions="=== Size settings ===";
input int      Trend_Line_Width=2;        // Trend line width

//+------------------------------------------------------------------+
//| Display styles                                                   |
//+------------------------------------------------------------------+
input string   Styles="=== Display styles ===";
input ENUM_LINE_STYLE      Trend_Line_Style=STYLE_SOLID;       // Trend line style

//+------------------------------------------------------------------+
//| Other parameters                                                 |
//+------------------------------------------------------------------+
input string               Others="=== Other parameters ===";

input bool                 Is_Trend_Ray=false;                      // Trend line - ray
input bool                 Is_Change_Timeframe_On_Create = true;    // Hide objects on higher timeframes?
                                                                    //   (true - hide, false - show)
input bool                 Is_Select_On_Create=true;                // Select upon creation
input bool                 Is_Different_Colors=true;                // Change colors for times

// Number of bars on the left and on the right
// for trend line and fan extreme points
input int                  Fractal_Size_Left=1;                     // Size of the left fractal
input int                  Fractal_Size_Right=1;                    // Size of the right fractal

//+------------------------------------------------------------------+
//| Name prefixes of drawn shapes (can be change only in code,       |
//| not visible in EA parameters)                                    |
//+------------------------------------------------------------------+
// string   Prefixes="=== Prefixes ===";

string   Trend_Line_Prefix="Trend_";                     // Trend line prefix

//+------------------------------------------------------------------+
//| Colors for objects of one timeframe (can be changed only in code,|
//| not visible in EA parameters)                                    |
//+------------------------------------------------------------------+
// string TimeframeColors="=== Time frame colors ===";
color mn1_color=clrCrimson;
color w1_color=clrDarkOrange;
color d1_color=clrGoldenrod;
color h4_color=clrLimeGreen;
color h1_color=clrLime;
color m30_color=clrDeepSkyBlue;
color m15_color=clrBlue;
color m5_color=clrViolet;
color m1_color=clrDarkViolet;
color common_color=clrGray;

//--- Auxiliary constant for displaying error messages
#define DEBUG_MESSAGE_PREFIX "=== ",__FUNCTION__," === "

//--- Constants for describing the main timeframes when drawing
#define PERIOD_LOWER_M5 OBJ_PERIOD_M1|OBJ_PERIOD_M5
#define PERIOD_LOWER_M15 PERIOD_LOWER_M5|OBJ_PERIOD_M15
#define PERIOD_LOWER_M30 PERIOD_LOWER_M15|OBJ_PERIOD_M30
#define PERIOD_LOWER_H1 PERIOD_LOWER_M30|OBJ_PERIOD_H1
#define PERIOD_LOWER_H4 PERIOD_LOWER_H1|OBJ_PERIOD_H4
#define PERIOD_LOWER_D1 PERIOD_LOWER_H4|OBJ_PERIOD_D1
#define PERIOD_LOWER_W1 PERIOD_LOWER_D1|OBJ_PERIOD_W1
//+------------------------------------------------------------------+

辅助函数

该程序拥有许多函数,这些函数与绘图没有直接关系,但是它们可以帮助您找到极端点,切换时间表等。 所有这些函数都在 “Utilites.mqh” 文件中实现。

Utilities.mqh 的文件头部

//+------------------------------------------------------------------+
//|                                                     Utilites.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                            https://www.mql5.com/ru/articles/7468 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://www.mql5.com/en/articles/7468"

//+------------------------------------------------------------------+
//| Class describing auxiliary functions                             |
//+------------------------------------------------------------------+
class CUtilites
  {
public:
   //--- Changes the timeframe to the next one on the toolbar
   static void       ChangeTimeframes(bool isUp);
   //--- Converts string command constants to keycodes
   static int        GetCurrentOperationChar(string keyString);
   //--- Switches layers in charts (the chart is on top of all objects)
   static void       ChangeChartZIndex(void);
   //--- Returns the number of the nearest extreme bar
   static int        GetNearestExtremumBarNumber(
      int starting_number=0,
      bool is_search_right=false,
      bool is_up=false,
      int left_side_bars=1,
      int right_side_bars=1,
      string symbol=NULL,
      ENUM_TIMEFRAMES timeframe=PERIOD_CURRENT
   );

   //--- Returns the color for the current timeframe
   static color      GetTimeFrameColor(long allDownPeriodsValue);
   //--- Returns a list of all timeframes lower than the current one (including the current one)
   static long       GetAllLowerTimeframes(int NeededTimeframe=PERIOD_CURRENT);
   //--- Coordinates of the straight line. Writes numbers of extreme bars to points p1 and p2
   static void       SetExtremumsBarsNumbers(bool _is_up,int &p1, int &p2);
   //--- Converts a string to an array of floating point numbers
   static void       StringToDoubleArray(
      string _haystack,
      double &_result[],
      const string _delimiter=","
   );
   //--- Determines the name of the current object
   static string            GetCurrentObjectName(
      const string _prefix,
      const ENUM_OBJECT _type=OBJ_TREND,
      int _number = -1
   );
   //--- Obtains the number of the next object
   static int               GetNextObjectNumber(
      const string _prefix,
      const ENUM_OBJECT _object_type,
      bool true
   );
   //--- Returns the distance, in screen pixels, between adjacent bars
   static int               GetBarsPixelDistance(void);
   //--- Converts a numeric value of the timeframe to its string name
   static string            GetTimeframeSymbolName(
      ENUM_TIMEFRAMES _timeframe=PERIOD_CURRENT  // Desired timeframe
   );
  };

该函数顺序更改图表的周期

此文件中的第一个函数很简单。 例如,顺序更改当前图形周期的函数如下所示:

//+------------------------------------------------------------------+
//| Sequentially changes the current chart period                    |
//|                                                                  |
//| In this implementation, only the timeframes shown in the         |
//| standard panel are used.                                         |
//|                                                                  |
//| Parameters:                                                      |
//|    _isUp - timeframe switch direction: up (true)                 |
//|            or down (false)                                       |
//+------------------------------------------------------------------+
static void CUtilites::ChangeTimeframes(bool _isUp)
  {
   ENUM_TIMEFRAMES timeframes[] =
     {
      PERIOD_CURRENT,
      PERIOD_M1,
      PERIOD_M5,
      PERIOD_M15,
      PERIOD_M30,
      PERIOD_H1,
      PERIOD_H4,
      PERIOD_D1,
      PERIOD_W1,
      PERIOD_MN1
     };
   int period = Period();
   int shift = ArrayBsearch(timeframes,period);
   if(_isUp && shift < ArraySize(timeframes)-1)
     {
      ChartSetSymbolPeriod(0,NULL,timeframes[++shift]);
     }
   else
      if(!_isUp && shift > 1)
        {
         ChartSetSymbolPeriod(0,NULL,timeframes[--shift]);
        }
  } 

首先,在函数中,于默认工具栏里指定所有时间帧的数组。 如果您希望在 MetaTrader 5 的所有可用时间帧之间切换,则应将响应的常量写入数组。 不过,在这种情况下,可能会丢失兼容性,并且该函数库在 MQL4 里可能会停止运行。

接下来,我们利用标准函数获取当前周期,并在列表中查找它

然后,利用标准 ChartSetSymbolPeriod 函数切换图表时间帧,当前时间帧之后下一个时间帧应传递给函数。

代码中用到的其他函数应当很清晰,故无须解释。 这些代码很简单。

一些简单的函数

//+------------------------------------------------------------------+
//| Converts string command constants to keycodes                    |
//+------------------------------------------------------------------+
static int CUtilites::GetCurrentOperationChar(string keyString)
  {
   string keyValue = keyString;
   StringToUpper(keyValue);
   return(StringGetCharacter(keyValue,0));
  }
//+------------------------------------------------------------------+
//| Switch chart position to display on top of other objects         |
//+------------------------------------------------------------------+
static void CUtilites::ChangeChartZIndex(void)
  {
   ChartSetInteger(
      0,
      CHART_FOREGROUND,
      !(bool)ChartGetInteger(0,CHART_FOREGROUND)
   );
   ChartRedraw(0);
  } 
//+------------------------------------------------------------------+
//| Returns a string name for any standard timeframe                 |
//| Parameters:                                                      |
//|    _timeframe - ENUM_TIMEFRAMES numeric value for which we need  |
//|                 to find a string name                            |
//| Return value:                                                    |
//|   string name of the required timeframe                          |
//+------------------------------------------------------------------+
static string CUtilites::GetTimeframeSymbolName(
   ENUM_TIMEFRAMES _timeframe=PERIOD_CURRENT  // Desired timeframe
)
  {
   ENUM_TIMEFRAMES current_timeframe; // current timeframe
   string result = "";
//---
   if(_timeframe == PERIOD_CURRENT)
     {
      current_timeframe = Period();
     }
   else
     {
      current_timeframe = _timeframe;
     }
//---
   switch(current_timeframe)
     {
      case PERIOD_M1:
         return "M1";
      case PERIOD_M2:
         return "M2";
      case PERIOD_M3:
         return "M3";
      case PERIOD_M4:
         return "M4";
      case PERIOD_M5:
         return "M5";
      case PERIOD_M6:
         return "M6";
      case PERIOD_M10:
         return "M10";
      case PERIOD_M12:
         return "M12";
      case PERIOD_M15:
         return "M15";
      case PERIOD_M20:
         return "M20";
      case PERIOD_M30:
         return "M30";
      case PERIOD_H1:
         return "H1";
      case PERIOD_H2:
         return "M1";
      case PERIOD_H3:
         return "H3";
      case PERIOD_H4:
         return "H4";
      case PERIOD_H6:
         return "H6";
      case PERIOD_H8:
         return "H8";
      case PERIOD_D1:
         return "D1";
      case PERIOD_W1:
         return "W1";
      case PERIOD_MN1:
         return "MN1";
      default:
         return "Unknown";
     }
  }
//+------------------------------------------------------------------+
//| Returns the standard color for the current timeframe             |
//+------------------------------------------------------------------+
static color CUtilites::GetTimeFrameColor(long _all_down_periods_value)
  {
   if(Is_Different_Colors)
     {
      switch((int)_all_down_periods_value)
        {
         case OBJ_PERIOD_M1:
            return (m1_color);
         case PERIOD_LOWER_M5:
            return (m5_color);
         case PERIOD_LOWER_M15:
            return (m15_color);
         case PERIOD_LOWER_M30:
            return (m30_color);
         case PERIOD_LOWER_H1:
            return (h1_color);
         case PERIOD_LOWER_H4:
            return (h4_color);
         case PERIOD_LOWER_D1:
            return (d1_color);
         case PERIOD_LOWER_W1:
            return (w1_color);
         case OBJ_ALL_PERIODS:
            return (mn1_color);
         default:
            return (common_color);
        }
     }
   else
     {
      return (common_color);
     }
  }

极值搜索函数及其应用

下一个函数有助于找到极值点。 根据参数,极值点可以位于鼠标指针的右侧或左侧。 另外,您可以在极值的左侧和右侧指定所需的柱线数量。

//+------------------------------------------------------------------+
//| Returns the number of the nearest fractal in the selected        |
//|   direction                                                      |
//| Parameters:                                                      |
//|   starting_number - bar number at which the search starts        |
//|   is_search_right - search to the right (true) or left (false)   |
//|   is_up - if "true", search by High levels, otherwise - Low      |
//|   left_side_bars - number of bars on the left                    |
//|   right_side_bars - number of bars on the right                  |
//|   symbol - symbol name for search                                |
//|   timeframe - period for search                                  |
//| Return value:                                                    |
//|   the number of the extremum closest to starting_number,         |
//|   matching the specified parameters                              |                 |
//+------------------------------------------------------------------+
static int CUtilites::GetNearestExtremumBarNumber(
   int starting_number=0,              // Initial bar number
   const bool is_search_right=false,   // Direction to the right
   const bool is_up=false,             // Search by Highs
   const int left_side_bars=1,         // Number of bars to the left
   const int right_side_bars=1,        // Number of bars to the right
   const string symbol=NULL,           // The required symbol
   const ENUM_TIMEFRAMES timeframe=PERIOD_CURRENT // The required timeframe
)
  {
//---
   int   i,
         nextExtremum,
         sign = is_search_right ? -1 : 1;

//--- If the starting bar is specified incorrectly
//--- (is beyond the current chart borders)
//--- and search - towards the border...
   if((starting_number-right_side_bars<0
       && is_search_right)
      || (starting_number+left_side_bars>iBars(symbol,timeframe)
          && !is_search_right)
     )
     { //--- ...it is necessary to display an error message
      if(Print_Warning_Messages)
        {
         Print(DEBUG_MESSAGE_PREFIX,
               "Can't find extremum: ",
               "wrong direction");
         Print("left_side_bars = ",left_side_bars,"; ",
               "right_side_bars = ",right_side_bars);
        }
      return (-2);
     }
   else
     {
      //--- otherwise - the direction allows you to select the correct bar.
      //---   check all bars in the required direction,
      //---   as long as we are beyond the known chart borders
      while((starting_number-right_side_bars<0
             && !is_search_right)
            || (starting_number+left_side_bars>iBars(symbol,timeframe)
                && is_search_right)
           )
        {
         starting_number +=sign;
        }
     }
//---
   i=starting_number;
 
   //--- Preparation is complete. Proceed to search
   while(i-right_side_bars>=0
         && i+left_side_bars<iBars(symbol,timeframe)
        )
     {
      //--- Depending on the level, check the required extremum
      if(is_up)
        { //--- either the upper one
         nextExtremum = iHighest(
                           Symbol(),
                           Period(),
                           MODE_HIGH,
                           left_side_bars+right_side_bars+1,
                           i-right_side_bars
                        );
        }
      else
        {  //--- or the lower one
         nextExtremum = iLowest(
                           Symbol(),
                           Period(),
                           MODE_LOW,
                           left_side_bars+right_side_bars+1,
                           i-right_side_bars
                        );
        }
      if(nextExtremum == i) // If the current bar is an extremum,
        {
         return nextExtremum;  // the problem is solved
        }
      else // Otherwise - continue to shift the counter of the checked candlestick to the desired direction
         if(is_search_right)
           {
            if(nextExtremum<i)
              {
               i=nextExtremum;
              }
            else
              {
               i--;
              }
           }
         else
           {
            if(nextExtremum>i)
              {
               i=nextExtremum;
              }
            else
              {
               i++;
              }
           }
     }
//--- If the edge is reached but no extremum has been found,
   if(Print_Warning_Messages)
     {
      //--- show an error message.
      Print(DEBUG_MESSAGE_PREFIX,
            "Can't find extremum: ",
            "an incorrect starting point or wrong border conditions.");
      Print("left_side_bars = ",left_side_bars,"; ",
            "right_side_bars = ",right_side_bars);
     }
   return (-1);
  } 

为了绘制趋势线,我们需要一个函数来找到鼠标右边两个最近的极值点。 此函数可以使用前一个函数:

//+------------------------------------------------------------------+
//| Finds 2 nearest extreme points to the right of the current       |
//| mouse pointer position                                           |
//| Parameters:                                                      |
//|    _is_up - search by High (true) or Low (false)                 |
//|    int &_p1 - bar number of the first point                      |
//|    int &_p2 - bar number of the second point                     |
//+------------------------------------------------------------------+
static void CUtilites::SetExtremumsBarsNumbers(
   bool _is_up, // search by High (true) or by Low (false)
   int &_p1,    // bar number of the first point
   int &_p2     // bar number of second point
)
  {
   int dropped_bar_number=CMouse::Bar();
//---
   _p1=CUtilites::GetNearestExtremumBarNumber(
          dropped_bar_number,
          true,
          _is_up,
          Fractal_Size_Left,
          Fractal_Size_Right
       );
   _p2=CUtilites::GetNearestExtremumBarNumber(
          _p1-1, // Bar to the left of the previous found extremum
          true,
          _is_up,
          Fractal_Size_Left,
          Fractal_Size_Right
       );
   if(_p2<0)
     {
      _p2=0;
     }
  } 

产生对象名称

为了能够绘制一系列相同对象,这些对象的名称必须具有唯一性。 最有效的方法是用与此对象类型相对应的前缀,并为其添加唯一的数字。 GlobalVariables.mqh 中列出了不同对象类型的前缀。

数字则由相应的函数生成。

//+------------------------------------------------------------------+
//| Returns the number of the next object in the series              |
//| Parameters:                                                      |
//|   prefix - name prefix for this group of objects.                |
//|   object_type - the type of objects to search in.                |
//|   only_prefixed - if "false", search in all objects              |
//|                      of this type, "true" - only the objects     |
//|                      with the specified prefix                   |
//| Return value:                                                    |
//|   number of the next object in series. If search by prefix,      |
//|      and the existing numbering has a gap, the next number       |
//|      will be at gap beginning.                                   |
//+------------------------------------------------------------------+
int               CUtilites::GetNextObjectNumber(
   const string prefix,
   const ENUM_OBJECT object_type,
   bool true
)
  {
   int count = ObjectsTotal(0,0,object_type),
       i,
       current_element_number,
       total_elements = 0;
   string current_element_name = "",
          comment_text = "";
//---
   if(only_prefixed)
     {
      for(i=0; i<count; i++)
        {
         current_element_name=ObjectName(0,i,0,object_type);
         if(StringSubstr(current_element_name,0,StringLen(prefix))==prefix)
           {
            current_element_number=
               (int)StringToInteger(
                  StringSubstr(current_element_name,
                               StringLen(prefix),
                               -1)
               );
            if(current_element_number!=total_elements)
              {
               break;
              }
            total_elements++;
           }
        }
     }
   else
     {
      total_elements = ObjectsTotal(0,-1,object_type);
      do
        {
         current_element_name = GetCurrentObjectName(
                                   prefix,
                                   object_type,
                                   total_elements
                                );
         if(ObjectFind(0,current_element_name)>=0)
           {
            total_elements++;
           }
        }
      while(ObjectFind(0,current_element_name)>=0);
     }
//---
   return(total_elements);
  } 

代码中实现了两种搜索算法。 第一个算法(该函数库的主要算法)检查与该类型相对应并具有指定前缀的所有对象。 找到可用编号之后,算法会将其返回给用户。 这样就可以填满编号中的“空隙”。

不过,此算法不适用于可能存在多个相同编号但对象后缀不同的情况。 在早前版本中,当我用脚本绘制对象时,我就针对草叉集合使用了这样的命名。 

因此,该函数库还有第二个搜索方法。 该算法会获取此类型对象的总数,并检查是否存在以相同前缀开头且具有相同索引的名称。 若有,则将数字加 1,直到找到可用值。

当有编号时(或可以使用函数轻松获得编号时),就可轻松创建名称。

//+------------------------------------------------------------------+
//| Generates the current element name                               |
//| Parameters:                                                      |
//|    _prefix - name prefix for this group of objects.              |
//|    _type - the type of objects to search in.                     |
//|    _number - the number of the current object if it is ready.    |
//| Return value:                                                    |
//|    current object name string.                                   |
//+------------------------------------------------------------------+
string            CUtilites::GetCurrentObjectName(
   string _prefix,
   ENUM_OBJECT _type=OBJ_TREND,
   int _number = -1
)
  {
   int Current_Line_Number;

//--- Addition to the prefix - current timeframe
   string Current_Line_Name=IntegerToString(PeriodSeconds()/60)+"_"+_prefix;

//--- Get the element number
   if(_number<0)
     {
      Current_Line_Number = GetNextObjectNumber(Current_Line_Name,_type);
     }
   else
     {
      Current_Line_Number = _number;
     }

//--- Generate the name
   Current_Line_Name +=IntegerToString(Current_Line_Number,4,StringGetCharacter("0",0));

//---
   return (Current_Line_Name);
  } 

相邻柱线之间的距离(以像素为单位)

有时必须要计算距将来某个点的距离。 最可靠的方法之一是计算两个相邻柱线之间的像素距离,然后将其乘以所需的系数(缩进所需的柱线数量)。

相邻柱线之间的距离可用以下函数计算:

//+------------------------------------------------------------------+
//| Calculates a distance in pixels between two adjacent bars        |
//+------------------------------------------------------------------+
int        CUtilites::GetBarsPixelDistance(void)
  {
   double price; // arbitrary price on the chart (used for
                 // standard functions calculating coordinates
   datetime time1,time2;  // the time of the current and adjacent candlesticks
   int x1,x2,y1,y2;       // screen coordinates of two points
                          //   on adjacent candlesticks
   int deltha;            // result - the desired distance

//--- Initial settings
   price = iHigh(Symbol(),PERIOD_CURRENT,CMouse::Bar());
   time1 = CMouse::Time();

//--- Get the time of the current candlestick
   if(CMouse::Bar()<Bars(Symbol(),PERIOD_CURRENT)){ // if at the middle of the chart,
      time2 = time1+PeriodSeconds();                // take the candlestick on the left
   }
   else {                                           // otherwise
      time2 = time1;
      time1 = time1-PeriodSeconds();                // take the candlestick on the right
   }

//--- Convert coordinates form price/time values to screen pixels
   ChartTimePriceToXY(0,0,time1,price,x1,y1);
   ChartTimePriceToXY(0,0,time2,price,x2,y2);

//--- Calculate the distance
   deltha = MathAbs(x2-x1);
//---
   return (deltha);
  } 

函数将字符串转换为双精度数组

利用 EA 参数设置斐波那契级别的最便捷方法是用由逗号分隔数值组成的字符串。 不过,MQL 需要使用双精度数值来设置级别。

以下函数可以帮助从字符串中提取数字。

//+------------------------------------------------------------------+
//| Converts a string with separators to an array of double          |
//|    numbers                                                       |
//| Parameters:                                                      |
//|    _haystack - source string for conversion                      |
//|    _result[] - resulting array                                   |
//|    _delimiter - separator character                              |
//+------------------------------------------------------------------+
static void CUtilites::StringToDoubleArray(
   string _haystack,             // source string
   double &_result[],            // array of results
   const string _delimiter=","   // separator
)
  {
//---
   string haystack_pieces[]; // Array of string fragments
   int pieces_count,         // Number of fragments
       i;                    // Counter
   string current_number=""; // The current string fragment (estimated number)

//--- Split the string into fragments
   pieces_count=StringSplit(_haystack,StringGetCharacter(_delimiter,0),haystack_pieces);
//--- Convert
   if(pieces_count>0)
     {
      ArrayResize(_result,pieces_count);
      for(i=0; i<pieces_count; i++)
        {
         StringTrimLeft(haystack_pieces[i]);
         StringTrimRight(haystack_pieces[i]);
         _result[i]=StringToDouble(haystack_pieces[i]);
        }
     }
   else
     {
      ArrayResize(_result,1);
      _result[0]=0;
     }
  } 

绘图类:使用实用程序函数的示例

这篇文章有些冗长,这就是为什么大多数绘图函数将在下一篇文章里讲述的原因。 不过,为了测试某些已创建函数,我将在此处添加绘制简单直线的代码(基于两个最近的极值)。

Graphics.mqh 文件头部

//+------------------------------------------------------------------+
//|                                                     Graphics.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                            https://www.mql5.com/ru/articles/7468 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://www.mql5.com/en/articles/7468"

//+------------------------------------------------------------------+
//| Class for plotting graphic objects                               |
//+------------------------------------------------------------------+
class CGraphics
  {
   //--- Fields
private:
   bool              m_Is_Trend_Ray;
   bool              m_Is_Change_Timeframe_On_Create;
   //--- Methods
private:
   //--- Sets general parameters for any newly created object
   void              CurrentObjectDecorate(
      const string _name,
      const color _color=clrNONE,
      const int _width = 1,
      const ENUM_LINE_STYLE _style = STYLE_SOLID
   );
public:
   //--- Default constructor
                     CGraphics();
   //--- Universal function for creating trend lines with specified parameters
   bool              TrendCreate(
      const long            chart_ID=0,        // chart ID
      const string          name="TrendLine",  // line name
      const int             sub_window=0,      // subwindow number
      datetime              time1=0,           // time of the first point
      double                price1=0,          // price of the first point
      datetime              time2=0,           // time of the second point
      double                price2=0,          // price of the second point
      const color           clr=clrRed,        // line color
      const ENUM_LINE_STYLE style=STYLE_SOLID, // line style
      const int             width=1,           // line width
      const bool            back=false,        // in the background
      const bool            selection=true,    // if the line is selected
      const bool            ray_right=false,   // ray to the right
      const bool            hidden=true,       // hide in the list of objects
      const long            z_order=0          // Z-Index
   );
   //--- Plots a trend line based on the two nearest (to the right of the mouse) extreme points
   void              DrawTrendLine(void);
   //--- Checks if the straight line is a ray
   bool              IsRay() {return m_Is_Trend_Ray;}
   //--- Sets the ray indication (whether the line should be extended to the right)
   void              IsRay(bool _is_ray) {m_Is_Trend_Ray = _is_ray;}
   //--- Checks if the display of objects created by the program should be changed on higher timeframes
   bool              IsChangeTimeframe(void) {return m_Is_Change_Timeframe_On_Create;}
   //--- Sets flags for the display of objects created by the program on higher timeframes
   void              IsChangeTimeframe(bool _is_tf_change) {m_Is_Change_Timeframe_On_Create = _is_tf_change;}
  };
  

函数可为任何新创建的对象设置常规参数

//+------------------------------------------------------------------+
//| Sets general parameters for all new objects                      |
//| Parameters:                                                      |
//|    _name - the name of the object being modified                 |
//|    _color - the color of the object being modified. If not set,  |
//|             standard color of the current period is used         |
//|    _width - object line width                                    |
//|    _style - object line type                                     |
//+------------------------------------------------------------------+
void CGraphics::CurrentObjectDecorate(
   const string _name,            // the name of the object being modified
   const color _color=clrNONE,    // the color of the object being modified
   const int _width = 1,          // object line width
   const ENUM_LINE_STYLE _style = STYLE_SOLID  // object line style
)
  {
   long timeframes;         // timeframes on which the object will be displayed
   color currentColor;      // object color
//--- Specify timeframes on which the object will be displayed
   if(Is_Change_Timeframe_On_Create)
     {
      timeframes = CUtilites::GetAllLowerTimeframes();
     }
   else
     {
      timeframes = OBJ_ALL_PERIODS;
     }
//--- Specify the object color
   if(_color != clrNONE)
     {
      currentColor = _color;
     }
   else
     {
      currentColor = CUtilites::GetTimeFrameColor(timeframes);
     }
//--- Set attributes
   ObjectSetInteger(0,_name,OBJPROP_COLOR,currentColor);            // Color
   ObjectSetInteger(0,_name,OBJPROP_TIMEFRAMES,timeframes);         // Periods
   ObjectSetInteger(0,_name,OBJPROP_HIDDEN,true);                   // Hide in the list of objects
   ObjectSetInteger(0,_name,OBJPROP_SELECTABLE,true);               // Ability to select
   ObjectSetInteger(0,_name,OBJPROP_SELECTED,Is_Select_On_Create);  // Stay selected after creation?
   ObjectSetInteger(0,_name,OBJPROP_WIDTH,_width);                  // Line width
   ObjectSetInteger(0,_name,OBJPROP_STYLE,_style);                  // Line style
  } 

直线绘制函数

//+------------------------------------------------------------------+
//| Universal function for creating trend lines with specified       |
//|    parameters                                                    |
//| Parameters:                                                      |
//|    chart_ID - chart ID                                           |
//|    name - line name                                              |
//|    sub_window - subwindow number                                 |
//|    time1 - time of the first point                               |
//|    price1 - price of the first point                             |
//|    time2 - time of the second point                              |
//|    price2 - price of the second point                            |
//|    clr - line color                                              |
//|    style - line style                                            |
//|    width - line width                                            |
//|    back - in the background                                      |
//|    selection - if the line is selected                           |
//|    ray_right - ray to the right                                  |
//|    hidden - hide in the list of objects                          |
//|    z_order - priority of mouse clicks (Z-Index)                  |
//| Return value:                                                    |
//|    indication of success. If line drawing failed,                |
//|    false is returned, otherwise - true                           |
//+------------------------------------------------------------------+
bool              CGraphics::TrendCreate(
      const long            chart_ID=0,        // chart ID
      const string          name="TrendLine",  // line name
      const int             sub_window=0,      // subwindow number
      datetime              time1=0,           // time of the first point
      double                price1=0,          // price of the first point
      datetime              time2=0,           // time of the second point
      double                price2=0,          // price of the second point
      const color           clr=clrRed,        // line color
      const ENUM_LINE_STYLE style=STYLE_SOLID, // line style
      const int             width=1,           // line width
      const bool            back=false,        // in the background
      const bool            selection=true,    // if the line is selected
      const bool            ray_right=false,   // ray to the right
      const bool            hidden=true,       // hide in the list of objects
      const long            z_order=0          // Z-Index
)
  {

//--- Reset the last error message
   ResetLastError();
//--- Create a line
   if(!ObjectCreate(chart_ID,name,OBJ_TREND,sub_window,time1,price1,time2,price2))
     {
      if(Print_Warning_Messages) // if failed, show an error message
        {
         Print(__FUNCTION__,
               ": Can't create trend line! Error code = ",GetLastError());
        }
      return(false);
     }

//--- Set additional attributes
   CurrentObjectDecorate(name,clr,width,style);

//--- line in the foreground (false) or in the background (true)
   ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back);
//--- ray to the right (true) or exact borders (false)
   ObjectSetInteger(chart_ID,name,OBJPROP_RAY_RIGHT,ray_right);
//--- mouse click priority (Z-index)
   ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order);
//--- Update the chart image
   ChartRedraw(0);
//--- Everything is good. The line has been drawn successfully.
   return(true);
  }

我们以该公开函数为基础,并创建另一个函数,该函数通过两个相邻的极值绘制一条直线。

//+------------------------------------------------------------------+
//| Draws a trend line by nearest extreme points in the right        |
//+------------------------------------------------------------------+
void              CGraphics::DrawTrendLine(void)
  {
   int dropped_bar_number=CMouse::Bar(); // candlestick number under the mouse
   int p1=0,p2=0;                        // numbers of the first and seconds points
   string trend_name =                   // trend line name
      CUtilites::GetCurrentObjectName(Trend_Line_Prefix,OBJ_TREND);
   double
      price1=0,   // price of the first point
      price2=0,   // price of the second point
      tmp_price;  // variable for temporary storing of the price
   datetime
      time1=0,    // time of the first point
      time2=0,    // time of the second point
      tmp_time;
   int x1,x2,y1,y2;   // Point coordinates
   int window=0;      // Subwindow number

//--- Setting initial parameters
   if(CMouse::Below()) // If a mouse cursor is below the candlestick Low
     {
      //--- Find two extreme points below
      CUtilites::SetExtremumsBarsNumbers(false,p1,p2);

      //--- Determine point coordinates
      time1=iTime(Symbol(),PERIOD_CURRENT,p1);
      price1=iLow(Symbol(),PERIOD_CURRENT,p1);
      time2=iTime(Symbol(),PERIOD_CURRENT,p2);
      price2=iLow(Symbol(),PERIOD_CURRENT,p2);
     }
   else // otherwise
      if(CMouse::Above()) // If a mouse cursor is below the candlestick High
        {
         //--- Find two extreme points above
         CUtilites::SetExtremumsBarsNumbers(true,p1,p2);
        
         //--- Determine point coordinates
         time1=iTime(Symbol(),PERIOD_CURRENT,p1);
         price1=iHigh(Symbol(),PERIOD_CURRENT,p1);
         time2=iTime(Symbol(),PERIOD_CURRENT,p2);
         price2=iHigh(Symbol(),PERIOD_CURRENT,p2);

        }
//--- Draw the line
   TrendCreate(0,trend_name,0,
               time1,price1,time2,price2,
               CUtilites::GetTimeFrameColor(CUtilites::GetAllLowerTimeframes()),
               0,Trend_Line_Width,false,true,m_Is_Trend_Ray
              );

//--- Redraw the chart
   ChartRedraw(0);
  } 

请注意 CUtilites::SetExtremumsBarsNumbers 函数调用,该函数获取点 1 和 2 间的柱线数量。 其代码已在前面讲过了。 其余的似乎很清晰,因此无需赘言

最终函数根据两个点画一条简单的直线。 取决于 Is_Trend_Ray 全局参数(在 GlobalVariables.mqh 文件中进行了描述),该线将是向右延伸的射线,或者是两个极值间的短线段。

 Is_Trend_Ray = true  Is_Trend_Ray = false

我们来添加利用键盘扩展线长的可能性。


创建一个控制模块:设置 OnChartEvent 方法

现在基本函数已经准备就绪,我们可以自定义键盘快捷键了。

Shortcuts.mqh 里,编写 CShortcuts::OnChartEvent 方法。

//+------------------------------------------------------------------+
//| Event handling function                                          |
//+------------------------------------------------------------------+
void CShortcuts::OnChartEvent(
   const int id,
   const long &lparam,
   const double &dparam,
   const string &sparam
)
  {
//---
   switch(id)
     {
      //--- Save the coordinates of the mouse cursor
      case CHARTEVENT_MOUSE_MOVE:
         CMouse::SetCurrentParameters(id,lparam,dparam,sparam);
         break;

      //--- Handle keystrokes
      case CHARTEVENT_KEYDOWN:
         //--- Change the timeframe 1 level up
         if(CUtilites::GetCurrentOperationChar(Up_Key) == lparam)
           {
            CUtilites::ChangeTimeframes(true);
           };
         //--- Change the timeframe 1 level down
         if(CUtilites::GetCurrentOperationChar(Down_Key) == lparam)
           {
            CUtilites::ChangeTimeframes(false);
           };
         //--- Change the Z Index of the chart (chart on top of all objects)
         if(CUtilites::GetCurrentOperationChar(Z_Index_Key) == lparam)
           {
            CUtilites::ChangeChartZIndex();
           }
         //--- Draw a trend line
         if(CUtilites::GetCurrentOperationChar(Trend_Line_Key) == lparam)
           {
            m_graphics.DrawTrendLine();
           }
         //--- Switch the Is_Trend_Ray parameter
         if(CUtilites::GetCurrentOperationChar(Switch_Trend_Ray_Key) == lparam)
           {
            m_graphics.IsRay(!m_graphics.IsRay());
           }

         break;
         //---

     }
  } 

当前函数库实现的按键

动作
 按键 含义
 依据主要的TFs (来自 TFs 面板) 向上改变时间帧 U  Up
 向下改变时间帧  D  Down
 改变图表 Z 级别 (图表是否位于所有对象之上)  Z  Z order
 基于最接近鼠标的两个单向极端点绘制一条坡度趋势线  T  Trend line
 切换新线的射线模式
 R 键  Ray

结束语

附件包含当前版本的函数库。 附件里还包括三个脚本。

  • 第一个是 Del-All-Graphics。 它从当前窗口里删除所有图形对象。 在我的终端里,为此脚本设置 Ctrl+A 作为快捷键(全部)。
  • 第二个脚本是 Del-All-Prefixed。 它能够删除所有带前缀的对象(例如,所有趋势线,或以 H1 开头的对象)。 我用 Alt+R (删除) 来调用它。
  • 最后,第三个脚本( DeselectAllObjects )能够取消选择当前窗口中的所有对象。 我的键盘快捷键是 Ctrl+D (在 Photoshop 里作为取消选择)。

最好将函数库连接到智能交易系统,而不是指标,因为如果连接到指标并尝试将此指标与其他某些智能交易系统一起使用,则可能会导致严重的性能下降。 至少在我的情况里如此。 当然,这可能是有其他错误。

进一步的实现是什么。

该函数库的第二个版本将讲述如何实现视频中显示的有用对象。 一些对象是基元(如垂直线或水平线),而其他对象(如特定长度的线)则需要付出更多的努力。 由于“输出错误”或某些其他原因,其中一些仍然不能正常工作。 我将讲述我的决定,当然,欢迎您提供反馈。

第三个版本将包含用于配置参数的图形界面。

第四版(如果有的话)将提供成熟的助理 EA,这将有助于手工交易。 我需要来自社区的忠言。 与现有解决方案相比,我不确定是否会应用所有的新想法。 当然,我会自行绘制界面。 不过,所有这些都是为了用于手工交易。 那么,您认为开发这些会有用吗?

本文译自 MetaQuotes Software Corp. 撰写的俄文原文
原文地址: https://www.mql5.com/ru/articles/7468

附加的文件 |
DoEasy 函数库中的时间序列(第四十四部分):指标缓冲区对象类集合 DoEasy 函数库中的时间序列(第四十四部分):指标缓冲区对象类集合

本文介绍如何创建指标缓冲区对象类的集合。 我计划测试为指标创建和操控任意数量缓冲区的能力(在 MQL 指标中可以创建的最大缓冲区数量为 512)。

原生推特(Twitter)客户端:第二部 原生推特(Twitter)客户端:第二部

一款以 MQL 类实现的推特(Twitter)客户端,允许您发送带照片的推文。 您只需要包含一个独立的包含文件,之后您即可将所有出色的图表和信号发作推文。

神经网络在交易中的实际应用 神经网络在交易中的实际应用

在本文中,我们将研究神经网络与交易终端集成的主要方面,从而创建功能齐全的交易机器人。

神经网络在交易中的实际应用。 是时候进行实践了 神经网络在交易中的实际应用。 是时候进行实践了

本文提供了在 Matlab 平台上实际运用神经网络模块的讲述和指南。 它还涵盖了运用神经网络模块创建交易系统的主要方面。 为了能够在一篇文章中厘清复杂内容,我必须对其进行修改,从而在一个程序中组合若干个神经网络模块函数。