我是一名手工交易者。 我在分析图表时更喜欢不用复杂的公式和指标,只用纯手工(即以图形方式)进行分析。 这有助于我更加灵活地进行交易,关注一些难以形式化的事情,如果我看到波动在增加或放缓,则可以轻松地在时间帧之间切换,并在入场交易之前就知道了可能的价格行为。
基本上,我使用趋势线的不同组合(草叉、扇形、水平、等等)。 为此,我创建了一套便捷的工具,可一键快速绘制趋势线。 现在,我希望与社区共享此工具。
视频演示。 它如何运行
一般概念。 设定任务
故此,我们正在创建一套工具,该工具可帮助您利用键盘快捷键执行最频繁的操作。
可以实现哪些任务? 举例:
- 按下 “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); }