| / | Articles |
Articles
MetaTrader 5
Step on New Rails: Custom Indicators in MQL5
To post a new article, please log in or register
|
Step on New Rails: Custom Indicators in MQL5 [ ru ]IntroductionFinally we've got an opportunity to try the new trade terminal - MetaTrader 5. No doubt, it is noteworthy and has many new features as compared to its predecessor. The important advantages of this platform among others are:
I will not list all of the new possibilities and features of the new terminal and language. They are numerous, and some novelties are worth the discussion in a separate article. Also there is no code here, written with object-oriented programming, it is a too serous topic to be simply mentioned in a context as additional advantages for developers. In this article we will consider indicators, their structure,
drawing, types and their programming details, comparing to MQL4. There is nothing complicated in this article, moreover, anything of the considered here can be checked directly in the terminal using the attached files. I hope that this article will be useful both for beginners and experienced developers, maybe some of them will find something new here. 1. THE GENERAL STRUCTURE The general structure of the indicator as compared to MQL4 hasn't changed. As before, there are three functions - for initialization, for data processing, and for the indicator deinitailization. As before, many indicator parameters can be defined by properties (#property keyword). The most of them are designed specifically for indicators. Properties and input parameters as before, are defined in a global context. As example, let's consider the implementation of the custom coloring for RSI indicator. Here is the truncated version, the full one can be found in the file Color_RSI.mq5. Let's consider the parts of the code. //+------------------------------------------------------------------+ //| Color_RSI.mq5 | //+------------------------------------------------------------------+ // information properties #property copyright "TheXpert" #property link "theforexpert@gmail.com" #property version "1.00" // Indicator description -- the total length should not exceed 511 characters with CR symbols #property description "Indicator programming demo in MQL5" #property description "RSI indicator custom painting" The properties specified above are displayed in the indicator information panel (the "Common" tab of the properties). It looks like:
// the indicator properties #property indicator_separate_window // indicator will be displayed in the separate subwindow //#property indicator_minimum 0 //#property indicator_maximum 100 #property indicator_buffers 2 // buffers used #property indicator_plots 1 // buffers displayed #property indicator_color1 DarkSalmon, DeepSkyBlue // we use 2 colors #property indicator_type1 DRAW_COLOR_LINE // and special color style These properties are the indicator properties. The other properties description can be found in the help. //---- buffers double Values[]; // buffer for values double ValuesPainting[]; // buffer for color indexes double BufferForCopy[]; // additional buffer for copying // indicator input parameters input string _1 = "RSI Parameters"; input int RSIPeriod = 5; input ENUM_APPLIED_PRICE AppliedPrice = PRICE_CLOSE; input string _2 = "Color settings"; input color Down = DarkSalmon; input color Up = DeepSkyBlue; // variable to store indicator handle int RSIHandle; Here are the indicator input parameters and global variables (do not confuse with global variables of the client terminal). The indicator input parameters are specified with the input identifier. Now it is possible to set enumeration for the input parameter, sometimes it is useful to avoid the incorrect parameters selection. For example, the AppliedPrice parameter will be displayed in a drop-down list with possible valid values.
So all of the enumerations, including the user defined, will be displayed in the same drop-down list. For example, the following parameter //... enum DayOfWeek { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }; input DayOfWeek Day; //... will be displayed as follows:
int OnInit() { // set the indicator buffers // set Values as a buffer for displaying SetIndexBuffer(0, Values, INDICATOR_DATA); // set ValuesPainting as buffer for colors SetIndexBuffer(1, ValuesPainting, INDICATOR_COLOR_INDEX); // Set beginning of Values buffer drawing PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, RSIPeriod); // Set indicator name IndicatorSetString(INDICATOR_SHORTNAME, "Color RSI(" + string(RSIPeriod) + ")"); // Set empty value for Values buffer PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); // Set colors for the buffer PlotIndexSetInteger(0, PLOT_LINE_COLOR, 0, Down); PlotIndexSetInteger(0, PLOT_LINE_COLOR, 1, Up); RSIHandle = iRSI(NULL, 0, RSIPeriod, AppliedPrice); ArrayResize(BufferForCopy, 3); // Set the indexation order for buffers (as series) ArraySetAsSeries(Values, true); ArraySetAsSeries(ValuesPainting, true); ArraySetAsSeries(BufferForCopy, true); return(0); } OnInit is the indicator initialization function. Here we configure the indicator buffers and their properties, and define the indicator variables which cannot be defined in the properties or must be set dynamically. Also there is an initial data initialization, including the handles assign that are needed for the indicators. // Function for data calculation // note that the function parameters can be renamed int OnCalculate(const int rates_total, const int prev_calculated, const datetime& time[], const double& open[], const double& high[], const double& low[], const double& close[], const long& tick_volume[], const long& volume[], const int& spread[]) { int toCount = (int)MathMin(rates_total, rates_total - prev_calculated + 1); if (prev_calculated < 0 || prev_calculated > rates_total) { toCount = rates_total; } int copied = CopyBuffer(RSIHandle, 0, 0, toCount, Values); if (copied == -1) { Print("Error while data copying. Error №", GetLastError()); return 0; } // Coloring. Yes, now it became so simple for (int i = toCount - 2; i >= 0; --i) { if (Values[i + 1] != EMPTY_VALUE && Values[i] > Values[i + 1]) ValuesPainting[i] = 1; else ValuesPainting[i] = 0; } return rates_total; } OnCalculate is the function for data calculation. This function can be of two kinds. Here is its standard form. The details are below. Function: // the function is not obligatory in a code /* void OnDeinit() {} */ OnDeinit is the indicator deinitialization function. Often it is necessary to release the resources, for example, file handles. For other cases this function is not necessary. 2. TWO CONCEPTS OF INDICATORS The first is standard, the same as we have got used to in MQL4, but in a slightly modified form. The function OnCalculate is used instead of the function Start. int OnCalculate(const int rates_total, // Arrays size const int prev_calculated, // Bars processed on the previous call const datetime& time[], // Data for the current chart and timeframe... const double& open[], const double& high[], const double& low[], const double& close[], const long& tick_volume[], const long& volume[], const int& spread[]) { return rates_total; } In order to reduce the amount of code intended for data copying, the chart data are passed directly as arrays to the parameters of the function. Moreover, the number of available bars is passed as the first parameter of the function, the number of bars processed after last call or 0 (zero) is passed as the second parameter. The 0 (zero) value can be passed at the first indicator call, as well as when loading new or missing data. This parameter is a replacement (alternative or equivalent - it's up to you) for IndicatorCounted(), which is inconvenient for many developers. The second concept is the replacement and expansion of the i<…>OnArray-like functions of MQL4. There is an indicator of such a type in the terminal examples – Custom Moving Average. This type of indicators is intended for data processing dependent on the user's choice, including the custom indicators. The function for data processing for the indicators of such type looks like this: int OnCalculate (const int rates_total, // the size of the price[] array const int prev_calculated, // bars calculated in the previous call const int begin, // where notional data start from const double& price[] // data array for calculation ); { return rates_total; } The last parameter of the function is the data selected by user for processing. If you want to apply an indicator with a lot of buffers, the first indicator buffer will be passed for data processing.
The First Indicator's Data means that the indicator will be applied to the indicator first attached to the selected window of the chart . The Previous Indicator's Data means, that indicator will be applied to the indicator last attached to the selected window of the chart. These indicators can be used to assemble the entire stacks. For example, using the Custom Moving Average indicator it is possible to get the triple smoothing by imposing the first indicator to the necessary data, the second to the first and the third to the second: There are many standard indicators which implement this particular concept. Therefore, when you see the prompt for the function parameter applied_price_or_handle: it indicates that еру indicator is implemented the way that it can be calculated on the user data - the handle of these data must be passed as parameter applied_price_or_handle. Using the same way it is possible to organize data processing directly in the indicator code: { // ... RSIHandle = iRSI(NULL, 0, RSIPeriod, AppliedPrice); SmoothHandle = iMA(NULL, 0, SmoothPeriod, 0, MODE_EMA, RSIHandle); // ... } There is another new application of this concept - the ability to write universal service indicators. An example of such indicator is attached in the file Direction_Brush.mq5. Results are presented on the upper chart. The direction coloring in this case is separated as an independent entity and implemented in the other indicator. Certainly, their universality is limited, since they are applicable only for the zero buffer of the indicator. Nevertheless, I think that indicators of this type might be useful. From the other side, when you write a custom indicator, you should take it into account, because the main information processing in the zero buffer will allow to avoid implementing a multifunctional machine in one indicator. Many of the other actions can be made and executed in the external service indicators. All that you will have to do is attach service indicators with the required functionality to your custom щon. The application range is not so narrow as it might seem at first sight:
The features mentioned above are not the exhaustive list of the concept implementation. I think that many other effective implementations will be found later. 3. ACCESSING DATA The data access principles have changed in MQL5. Now
the work occurs directly in the arrays, and as a result, the calculation speed
has increased significantly. Now it's not necessary to create an array
and call the iCustom function for each value. Instead, it is
possible to get the necessary data count by calling one function, and
then directly use the demanded data copied in a local array specified. Data copying is implemented by using the system function CopyBuffer. You can find the function description in the help. The maximum data count to copy for the indicator and static (with a predefined size) arrays is determined by the size of the array. The size of dynamic array can be changed if the number of copied data exceeds its size. Besides, there are special functions for accessing historical data:
Details can be found in help. This data is passed in only one form of the indicator, for the other they should get their own. Due to the fact that the type of the historical data array is not necessarily double, it is recommended to use only dynamic non-indicator buffers for their storage. Also there is one more undocumented detail - if the copied data count is equal to 0 (zero), the function CopyBuffer will generate an error with code №4003, therefore the data count to copy should be not less than 1 (one) element. 4. THE INDICATOR BUFFERS The buffers count is not limited. Now
you don't have to think how to accommodate the information correctly, how to perform the intermediate calculations efficiently,
working on creation of the cluster indicator. But we should not forget that the buffers storage requires memory. Therefore, if you specify a terminal history depth about 1,000,000 bars and attach the "thick" cluster indicator to the minute chart, do not be surprised when the terminal eats Gb of memory. The essence of the buffer also has undergone some changes. The amount of the buffers used is specified in the in property. #property indicator_buffers 2 // buffers used
This value should correspond to the total buffers count.
The number of displayed buffers is defined by the property: #property indicator_plots 1 // buffers displeyed
Here is some detail. The most of the drawing styles needs only one INDICATOR_DATA buffer for drawing. However, there are some styles that require several indicator buffers for drawing. We mean the following drawing styles:
All of the above types except DRAW_FILLING style (it can't be colored), have colored analogues. All indicator buffers are now divided into 3 types:
int OnInit() { // ... SetIndexBuffer(0, V2, INDICATOR_DATA); SetIndexBuffer(1, V2C,INDICATOR_COLOR_INDEX); SetIndexBuffer(2, V4, INDICATOR_DATA); SetIndexBuffer(3, V4C,INDICATOR_COLOR_INDEX); SetIndexBuffer(4, V1, INDICATOR_CALCULATIONS); SetIndexBuffer(5, V3, INDICATOR_CALCULATIONS); // ... return 0; } There are also some features when referring to indicators via iCustom.
In case of the request to the inaccessible buffer, the error 4806 («The requested data was not found») will be generated. Let's consider the color buffers in details. In MQL4, for each color it was necessary to create a separate buffer, but now using the color styles, you can specify up to 63 colors for a one buffer. In some cases, it will allow to optimize the number of the buffers used, and thus to save the memory usage. It also opens new possibilities for writing indicators, in particular, the use of tone visualization. In addition, this innovation in some cases, greatly simplifies the logic of the application of several colors in comparison with MQL4. The most obvious example - separation of trends by colors. In MQL4 for the very economical (from correct cases) case of implementation it has required three (3) buffers and non-trivial programming. Now it easier than ever. Here are the code samples, the complete implementation can be found in file Color_RSI.mq5. #property indicator_color1 DarkSalmon, DeepSkyBlue // we use 2 colors #property indicator_type1 DRAW_COLOR_LINE // and special color drawing style //---- buffers double Values[]; // buffer for values double ValuesPainting[]; // buffer for color indexes int OnInit() { // Registering buffers // Values as drawing buffer SetIndexBuffer(0, Values, INDICATOR_DATA); // ValuesPainting as color storage buffer SetIndexBuffer(1, ValuesPainting, INDICATOR_COLOR_INDEX); // … return(0); } // function for data calculation int OnCalculate(/*…*/) { int toCount = (int)MathMin(rates_total, rates_total - prev_calculated + 1); int copied = CopyBuffer(RSIHandle, 0, 0, rates_total, Values); if (copied == -1) { Print("Error while data copying. Error №", GetLastError()); return 1; } // Colouring. Yes, now it became so simple for (int i = toCount - 2; i >= 0; --i) { if (Values[i] > Values[i + 1]) ValuesPainting[i] = 1; else ValuesPainting[i] = 0; } return rates_total; } Some more code, and we get the following:
You should note some of details when using color types for drawing. For the color buffers, when using a dynamic color definition scheme, the maximal colors count is limited by the colors count defined in the indicator_colorN property. For example #property indicator_color1 DarkSalmon, DeepSkyBlue // we use 2 colors color scheme of the buffer will contain maximum two colors, even if you will set a larger number of colors dynamically (using the PlotIndexSetInteger). Therefore, the required number of colors should be written in one line - property definition line. Then they can be changed dynamically. The shortest color which I have found - "Red". However, you can always do the following: Instead of #property indicator_color1 Red, Red, Red, Red, Red, Red, Red, Red, //… you may write: #define C Red #property indicator_color1 C, C, C, C, C, C, C, C, C, C, C, C, C, C, //… The maximum number of colors for a buffer is 63. When the color number greater than the maximal (defined by property indicator_colorN) buffer will not be displayed. Here is an example of tone visualization, using the maximum number of colors: In general, the drawing opportunities have significantly increased, and that's great.
5. ARRAYS During the direct reference to the array data by indexes, it is necessary to mean the data ordering type – AsSeries property. It cannot be defined for some of the array types. This flag cannot be set for the multidimensional and for static arrays. For the array types, which are passed to the OnCalculate function it is possible to set such a flag. The data copying using the function CopyBuffer doesn't depend on the AsSeries property, but its implementation is different for different buffers. For indicator buffers it is necessary to copy the entire depth of available history. It must be remembered. For dynamic and static (with a predefined size) arrays the data copying is performed from the present to the past. The function CopyBuffer changes the size of buffers for dynamic (except indicator's) buffers for the necessary size. It is not recommended to use the static arrays for data copying. In general, I advise you always to check, how you copy data and how you address them. The most simple and safe way is:
In addition, I strongly advise to study the help for the CopyBuffer function and for all of the functions connected with AsSeries property. 6. IndicatorCounted Now the debates about the necessity of the IndicatorCounted function will sink into oblivion, because this value is defined directly by us as return value of the previous function call. int OnCalculate(const int rates_total, // array size const int prev_calculated, // bars processed after last call //...) { return rates_total; } More often it is enough to return the rates_total value which contains the bars count of the current function call. However, if the price data were changed since the last OnCalculate() call (for example, history data were loaded or the history data blanks were filled), then the value of the input parameter prev_calculated will be set to 0 (zero) by the client terminal. Also, if OnCalculate function returns zero, then the indicator values are not shown in the DataWindow of the client terminal. Therefore, if you want to see the indicator and perform it sfull recalculation during the history loading process or after a connection failure, return 1 instead of 0. The another useful feature which has been added is determining how many bars has been calculated for the
indicator. It is more useful for Expert Advisors which perform calculations on a
large data. The function is BarsCalculated, its details can be found in the help. This function has another useful application. If the indicator hasn't been loaded, its loading may take some time - the time between the indicator handle creation and its use for the calculations. This time is required for initialization and its initial precalculation. It depends on the calculation speed and indicator details. During this time, the call for the CopyBuffer function generates the error 4806 -- "The requested data was not found". The function BarsCalculated can be used to determine the availability of the indicator data for copying: int WPRHandle = iWPR(NULL, 0, WPRPeriod); int copied = CopyBuffer(WPRHandle, 0, 0, bars, Values); int err = GetLastError(); if (-1 == copied) { if (4806 == err) { for (int i = 0; i < 1000; ++i) { if (BarsCalculated(WPRHandle) > 0) break; } copied = CopyBuffer(WPRHandle, 0, 0, bars, Values); err = GetLastError(); } } if (-1 == copied) { Print("Error when trying to get WPR values, last error is ", err, " bars ", bars); return 0; } //...
SUMMARY In conclusion, I would like to say that just some of the details have been considered in this article. But I hope that the basic aspects are presented here. It would be great if this article were a useful reference on the subject, where you
could always look and find information about the details. If you have found any mistakes in this article, or have discovered something important, please inform me and I will try to correct it and improve the article as soon as possible. I am planning to outine the recent changes, besides that, I hope that some of the useful information will appear in comments. That's the reason to read them carefully. APPENDIX Color.mqh -- include file, copy this file to the folder MQL5/Include. This file is necessary for the Toned_WPR indicator. Color.mq5 -- library, copy this file to the folder MQL5/Libraries. This file is necessary for the Toned_WPR indicator. All of the over files are indicators. ACKNOWLEDGMENTS Again, I would like to thank Mr. Victor Rustamov (granit77) for the manuscript reading, useful discussions and helpful advices. Translated from Russian by MetaQuotes Software Corp. Attachments:
Warning:
All rights to these materials are reserved by MetaQuotes Software Corp.
Copying or reprinting of these materials in whole or in part is prohibited.
MACDtrader wrote:
I am with a broker that uses MT4. When is MT5 coming out and will the forex brokers be offering it ?
from www.BostonTechnologies.com in Boston Massechuesettes. Those are the only places I know of that have it available so far. It requires XP or higher OS, and you may have to replace the PSAPI.DLL in the \Windows\System32 directory. You do this by renaming the original PSAPI.DLL found in the \Windows\System32 directory to PSAPI_OLD.DLL and then install the new(most recently downloaded) PSAPI.DLL file into the \Windows\System32 directory. To download this file go to www.google.comand type in PSAPI.DLL, it will take you to a site to download the file. I'm told that it will take another 6 months to work out the bugs, and for the mql4 programmers to buff up on the new language to convert the various custom indicators that were developed under MT4, so please report any bugs you find. It will probably be readily available and supported by a majority of the current MT4 Brokers in the second/third quarter of 2010. There you have it. Enjoy it...
Rod Harrell +01-510-655-4966 549 - 66th Street, Oakland, CA. 94609-1117, USA
2009.11.05 07:02 RodOaklandCali
Can anyone tell me if the Renko or Point & Figure charts will be available in MQL5. Most platforms are making them available.
2009.11.04 18:51 traderdukeFX
I cannot answer this question. You should ask the terminal developers to get the answer.
2009.11.02 11:33 TheXpert
I am with a broker that uses MT4. When is MT5 coming out and will the forex brokers be offering it ?
2009.10.31 21:32 MACDtrader
5 comments
|