概述
我是一名手工交易者。 我在分析图表时更喜欢不用复杂的公式和指标,只用纯手工(即以图形方式)进行分析。 这有助于我更加灵活地进行交易,关注一些难以形式化的事情,如果我看到波动在增加或放缓,则可以轻松地在时间帧之间切换,并在入场交易之前就知道了可能的价格行为。
基本上,我使用趋势线的不同组合(草叉、扇形、水平、等等)。 为此,我创建了一套便捷的工具,可一键快速绘制趋势线。 现在,我希望与社区共享此工具。
视频演示。 它如何运行
一般概念。 设定任务
故此,我们正在创建一套工具,该工具可帮助您利用键盘快捷键执行最频繁的操作。
可以实现哪些任务? 举例:
- 按下 “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 文件中进行了描述),该线将是向右延伸的射线,或者是两个极值间的短线段。
我们来添加利用键盘扩展线长的可能性。
创建一个控制模块:设置 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,这将有助于手工交易。 我需要来自社区的忠言。 与现有解决方案相比,我不确定是否会应用所有的新想法。 当然,我会自行绘制界面。 不过,所有这些都是为了用于手工交易。 那么,您认为开发这些会有用吗?