MQL4 Autotrading

  • Read Close 1

    Introduction

    programming in MQL-4 begins with getting acquainted with the editor, in which programs are written to the terminal MetaTrader4.


    Columnist Rosh

    You may download the latest version of the terminal from Alpari’s website - files for downloading. Also on the site developers have a forum where you can learn a lot of useful information - http://www.metaquotes.ru/forum.

    On a note that the distribution is very small, and takes less than 3 Mb, which is sufficiently small for the terminal, providing a powerful tool for writing indicators and advisers. As a derogation to say that the distribution of Omega weighs about 200 Mb, of course, inspires respect, but it is very difficult to defeat, starting with the fact that it requires administrative rights on the computer to its value.

    After installing MetaTrader4 at the right terminal, MetaEditor may be activated by F4. The default language of the interface and help system is in English. To switch to the Russian language, choose the menu "View" - "Languages" - "Russian". Immediately to the Russian language does not change, it is necessary to close the MetaEditor and start it again. The reason is that when you start MetaEditor verify installation of the language and starts with the necessary language file of the interface.

    The easiest way to study the language for me was that I opened some user’s indicator (an algorithm which I understand very well), goes into the composition of the distribution, and tried to understand the code. An additional source of knowledge, as I said, is a developer forum. Open for example, a user’s indicator Alligator.mq4, which is located in the folder c: Program FilesMetaTrader 4expertsindicators (if MetaTrader4 put in a folder by default). This indicator may know or have heard all new to trading. If we fail, and set the cursor to the line 70 on the floor iMa combination of buttons and press Ctrl + F1, then invoke contextual help for that function. This function is a challenge to the values embedded technical indicators Moving Average - moving average, perhaps the most famous and popular technical indicator.

    Next, click "Synchronize" (with the guidance of message "Synchronize dictionary") we will cover "Navigator", in which the list of technical indicators is activated row Moving Average. Another way to cause the panel help - press Ctrl + T. To access the panel "Navigator" needs to press Ctrl + D (Dictionary).

    MetaEditor

    MetaEditor

    Go to article "Object Representation in MetaTrader4".

  • Read Close 2

    Object Representation in MetaTrader4

    It is very difficult to program in MQL-4 without knowing object-oriented programming (OOP). OOP languages are high-level languages, that is why many traders, who wrote indicators and advisers in MQL-2, faced difficulties. Actually, it is not so difficult. The terminal, MetaTrader 4, gives a lot of examples of objects, which help understand this concept. An object is a structure of a programing language, which possesses some definite features. Herewith some objects may contain some other objects. For example, if menu "Window"-"Windows" is chosen in the terminal, a popup window appears with a list of charts. This list displays the opened instruments with specified time-frames. The window with AUDUSD chart in the H4 time-frame goes first. That is, the charts windows are objects.

    If you choose the first window and press button F8 ("Features"), the next window will appear, where features of the chart (object) are given. Features-attributes of the chart-object are given there. We see the color of the background, text, grid, line chart, bar and candlesticks colouring. This is a typical example of object programming, though we can not get access to these features of the chart by means of MQL-4. There as lots of other objects, access to which can be got in the terminal as well as indicators and advisers (and scripts) in MQL-4. Trend lines, vertical and horizontal lines, text marks, texts, patterns, etc are among them. These objects have at least one feature in common, this is the color in which they are displayed in the chart.

    Moreover, standard (inbuilt) indicators and users indicators are also an example of the object. If combination Ctrl+I is pressed in the chart, which contains indicators, the list of indicators, attached to this chart, will appear, that is chart-object contains indicators-objects.

    These indicators can be placed either in the main window of the chart (in the price chart) or in its own separate window, if the indicator does not fit into the price range. The choice of the indicator position (either in the main window or in its own additional window) is also specified by the features of this indicator-object. And again, in case some certain indicator is chosen from the list (with the double click of the mouse), the window with this indicator features appears.

    With the help of tabs we can move along the indicator features and set input parameters of the indicator (the value of the indicator on every bar depends on it), the color of every line and levels of the indicator, and we can also specify the charts where the indicator should be displayed. These features can be modified from the terminal as well as from programs, written in MQL-4. That is why one of the main questions is how to write one's own indicator and from the indicator code get access to the objects features, which are placed in the chart by the user or created by the indicator code.

  • Read Close 3

    MQL-4 executable files (scripts, expert advisers, indicators)

    There are three types of independent executable files in MQL-4. Each file does its own task and is kept in its own directory. Scripts are kept in the folder …MetaTrader 4expertsscripts, indicators are kept in the folder …MetaTrader 4expertsindicators, expert advisers are kept in the folder …MetaTrader 4experts, which contains also folders …scripts and …indicators. It’s easy to define the file type according to the file location, as they have equal file extension *.mq4.

    Each file exercises only its own functions. Thus, different curves may be displayed only from indicators in the simplest and most efficient way. Trading operations may be done only by scripts or expert advisers. Here a digression may be done: the statement about incapability of drawing from scripts or experts seems to be contradicted – it is probable to make and draw graphical objects (vertical, horizontal and trend lines (in the form of a ray), icons of different styles and color) both from scripts and experts. However try to switch over the time-frame of the chart and you’ll have to do it over again, as the graphical objects have conjunction in time and price. In another time-frame all the objects should be deleted and new ones should be drawn.

    Indicators, scripts and expert advisers are also objects. Expert adviser that will call to one or several indicators in its own code may be set in the chart. Script is also unlimited in calling the indicators (user-defined or standard). You may also set an indicator which is built on readings of another indicator, which in its turn calls the third one for calculation. In OOP terms (object oriented programming) it may be characterized as follows: object-chart includes object-adviser, which in its turn includes objects-indicators. Let’s represent it in two ways, standard one and in the form of a diagram. Open EURUSD chart, H1 time-frame. Set expert adviser MACD Sample from the standard supply MetaTrader4 and allow it to trade. Moreover set standard indicator Stochastic Oscillator (5,5,5), standard indicator Fractals and user-defined indicator Zigzag (also from the standard supply MetaTrader4). The picture is the following:

    We have three indicators (two standard and one user-defined). Each of them works independently from the others. Moreover, expert adviser is being executed. Work of each indicator requires machinery time (processor operation life) and place in the random access memory (storage budgets). Let’s consider it now from another point of view. EURUSD chart, H1 represents an object-chart window, where three more object-indicators and one object-expert are placed (these objects are called subsidiary). Object-chart gets computer resources and distributes them between its subsidiary objects. In the expert’s code we may see a standard indicator MACD activation with parameters 12, 26, 9. Flipping from the adviser to the indicator creates one more MACD indicator (12, 29, 9) in the expert. We don’t see this indicator in the chart, but nevertheless it exists. Moreover, there are indicators which also refer to readings of the other indicators (standard or user-defined – doesn’t matter) in their estimation. So, except the objects seen in the chart there are objects unseen for us. These objects also acquire computer recourses (processor operation life and storage budgets). Thus, we do not have four (three indicators and one expert) but five objects – three indicators and one expert containing one indicator. I drew it in the following way:

    Actually, often much more than 3 indicators are drawn on the chart, expert advisers that use calls of more than one indicator are used. All these objects use resources intended for the terminal (MetaTrader4). Moreover, when testing indicators and advisers in one terminal, ten and more charts are opened. Each of them contains indicators and adviser. So, correct writing of executable files and efficient algorithm of advisers, scripts and indicators work are of primary concern. Ultimately, I suggest a screen shot of my terminal on weekends (the market doesn’t work and there are no quotations). 19 charts are opened. Each of them contains 4 indicators.

    Processor operation life is almost not used, but memory uses more than 70 mb for the terminal work.

  • Read Close 4

    Charts

    Indicators, advisers and scripts are written in order to be attached to the charts. That is why it's better to find out what a chart is at once. Let's consider EURUSD chart, time-frame D1. That is, we have a chart of EURUSD, each bar of which reflects this pair behaviour during a day (D1). You can get the name of the instrument in MQL-4 with the help of function Symbol()), time-frame value with the help of Period ().

    These functions can be used in every file of MQL-4. For help refer to the answer wizard in MetaEditor. Function Period() returns the value of the period in minutes, which can be one of the preset constants: 1 minute, 5 minutes, 15 minutes, 30 minutes etc. This is how it is described in the Help:
    Charts periods enumeration

    Chart period. May be one of the following values:
    Constant Figure Description
    PERIOD_M1 1 1 minute.
    PERIOD_M5 5 5 minutes.
    PERIOD_M15 15 15 minutes.
    PERIOD_M30 30 30 minutes.
    PERIOD_H1 60 1 hour.
    PERIOD_H4 240 4 hours.
    PERIOD_D1 1440 Days.
    PERIOD_W1 10080 Weeks.
    PERIOD_MN1 43200 Months.

    In fact, even if there are no indicators in the chart, we have at least one indicator, as even prices can be shown in a chart window in three different ways, candles, bars and lines.

    Thus, it is not so important how a chart is displayed. On what basis it is done is of much greater importance. In this picture a bar chart is given. Unlike the first picture (candles) there are no white or black bodies with the help of which bullish and bearish candles are distinguished visually. Line charts, built by close prices, give even less visual information. Though this additional information is stored in the terminal, but not shown.

    If in terminal MT4 we press Ctrl+S combination (menu item "Save as"), then a dialogue window of EURUSD D1 history saving in *.csv format (the format with delimiters) will appear.

    Let's save the file with the name, suggested by default, EURUSD1440.csv. As we can guess, the file name by default contains the name of the instrument (which may be got with the help of function Symbol()) and period 1440 minutes (the value of function Period() for the daily time-frame is equal to 1440). Then we open the accepted file with Excel.
    The last five lines look like:
    2005.12.19,00:00,1.2029,1.2037,1.1973,1.1999,5166
    2005.12.20,00:00,1.2002,1.2011,1.1839,1.1861,6679
    2005.12.21,00:00,1.1859,1.1910,1.1800,1.1832,6787
    2005.12.22,00:00,1.1830,1.1895,1.1808,1.1871,5284
    2005.12.23,00:00,1.1870,1.1883,1.1827,1.1864,4686

    Let's consider the last line, the rest of them are similar. At first there is a record 2005.12.23, this is a date, December 23, 2005. Then comma separated 00:00, that is 00 o'clock. All in one they mean the beginning of the day 23.12.2005, the time of the daily candlestick opening (Time). Then figures 1.1870 (opening price – Open), 1.1883 (the highest price within a period - High), 1827 (the lowest price within a period - Low), 1.1864 (close price - Close) and 4686 (volume within a period – Volume). If we point the last candlestick in the chart, a prompt message will pop up. Thus, we can draw a conclusion that a chart of every instrument in any time-frame can be presented in the shape of six arrays: Time[], Open[], Low[], High[], Close[] and Volume[]. Only these data are used by indicators and advisers. The first of them contains datetime-type data, the next four arrays contain double-type data, and the last one contains int-type data. Refer to MetaEditor for help. Access to the elements of the array is done by the index, indices always have integer type. The last bar (candlestick) by time always has a zero index. Open[0] means the price of the zero bar opening (the last bar), Time[1] means the date and time of the last but one bar opening, Low[3] is the lowest price within a period for three periods back and so on. It is important to remember that increasing the index we move further into the history up to the earliest bar. To find out the number of bars, given in the chart of an instrument, parameter Bars is necessary. This parameter always contains that number of bars, which can be seen in the chart and it updates automatically while the terminal is working on-line. It should be taken into account that the very first bar in the chart (the most remote one from us by time) will have index Bars-1, as indexation begins from zero.

    And the last thing I want to say is that predefined variable Bars is connected with setup "The maximum number of bars in the window" (that is usually not more than this figure) and does not mean "The maximum number of bars in history" (in the picture 250000). The more the value of the parameter "The maximum number of bars in the window" (in the picture 15000) is, the more memory is consumed by the terminal. This parameter change comes into effect only once MT4 is restarted.

  • Read Close 5

    Script writing

    We already have necessary knowledge to write the simplest program in MQL-4. I chose script rather than indicator or expert adviser, as there would be too much new information. Let’s write a script that operates as the terminal menu bar "File"- "Save as…" (Ctrl+S). For this we start a standard procedure in MetaEditor – "File"- "Create" (Ctrl+N), then a dialog window appears, put a tick in front of the field "User-defined script":

    Click "Next", a new dialog window opens. Fill in fields "Name", "Author" and "Reference". MetaEditor will remember fields "Author" and "Reference" and put their readings when creating a new executable file. Click "Done". The script is ready.

    We have got the simplest code, generated by "Adviser Creation Wizard".

    The code consists of three parts:
    1.Head
    2.Compiler directives
    3.Start() function.
    Head

    is not a code, as it represents five lines of comments. Comment is any line that starts with a double slash. Compiler neglects comments and pays no attention to mistakes. Comments simplify understanding of the programs. There is no reason to spin out on comments, as later you will hardly remember all the details of the program. As we see, "Name", "Author", and "Reference" meanings are shown as comments. It will be useful when the code is published.

    Compiler directives

    don’t actually influence the script work, BUT exactly these particular directives! There are some other directives, which may considerably change the code algorithm. Help in "Compiler directive" suggests a "List of predetermined identifier".

    Start() function

    It is the most important part. Exactly here we set an algorithm that should be executed. Any program in MQL-4 should possess at least one function, this function is called start() by default. The first three lines are comments. For every function it’s necessary to write the minimal comment, which explains the function purpose. It’s better to get used to it at once. It requires just a couple of minutes to be written, but later it will save hours for you and those to whom you’ll give the code. We see that the function is highlighted visually as a block, which allows us to see where one function finishes and the other starts. The function itself may be divided into two parts:

    contains name of the function and type of the returnable reading of this function,

    the body of the function.
    The body of the function is always put in the squiggle brackets and at the end of the function (before the closing squiggle bracket) almost always a return function stays. Help on the operator:

    In this case we see that function start() returns the reading 0 (zero). In this case we won’t use this reading, so the line may be easily commented-out. Our task is to display on the chart readings of Open, Close, Low, High, Time and Volume for each bar (candlestick). We may get access to each bar through the index. Thus, we have to make a run on the bars from the last one to zero. For this we have a cycle for() operator. From the help:

    In the open chart usually not more than Bars bars are located. We have to examine all of them, beginning with the first one in history with index Bars-1, and finishing with the last one with a zero index. Let’s announce a variable quantity of integer type, call it index and write a cycle operator:
    for (int index=Bars-1; index>=0; index-) { // We'll output High, Low, Open, Close for the bar with index index }
    Let’s analyze the cycle:
    int index=Bars-1 we announced a variable quantity index and set for it the initial value Bars-1, i.e. we start with the first bar in the chart
    index>=0 we established condition under which the body of the cycle is done. Thus, as long as the index is above or equal to zero, the cycle body is being done
    index- is equal to index= index-1 . Decrease in a reading by 1 is called decrement, while increase in a reading is called increment. If the index is more or equal to zero, the cycle body is executed, afterwards the index decreases by 1. Thus, some day the index will be below zero and the cycle will break off.
    { // We’ll draw High, Low, Open, Close for a bar with the index index }The function body of the cycle, instead of comments we have to insert the real display function readings. For this we have the Print() function.

    Let’s add Print(High[index],"",Low[index]," ",Open[index]," ",Close[index]," bar=",index) to the function body of the cycle. It displays readings High, Low etc. in the Expert advisers and indicators register. The script is done. Click F5 (Compile) – if there are no mistakes, the script is ready for use.

    Return to the terminal, open folder "Scripts" and sketch our script to the chart EURUSD D1. Open the terminal window "Terminal"(Ctrl+T), switch to the bookmark "Expert advisers" and see the result of the script work. Once the work is finished the script is removed from the chart: 2006.01.07 23:35:31 OutPrint EURUSD,Daily: removed. In the experts’ Register any information may be displayed with the help of operator Print, this information is shown in two columns. The first column contains the date and time of display, while the second one contains the script’s name, instrument’s name, time-frame and colon, after which the result of the script work is displayed. In this simplest script we used two most necessary functions of MQL-4, for() and Print(). For() is used to do the uniform operations (work in the cycle), while Print() is the most convenient function for the experts and indicators’ work logging and code debugging. If the program code doesn’t work or works wrong, we have to log variables of the program to analyze its work. Usually this procedure is enough to find mistakes in the code.

    Ultimately, "Experts" tab logs contain work results of our script and any other adviser or indicator. If we click the right button of the mouse at any line, the context menu will appear.

    Choose "Open", the dialog box will appear:

    Logs of experts (and indicators) are kept in the folder Program FilesMetaTrader 4expertslogs. Every day a new file is created. Our log (where results of script work are kept) is the last by time and date. Let's open it:

    Thus, we fixed our script work, so Print() is very convenient to use for code debugging on-line. The script is here

  • Read Close 6

    Script as a bridge to Microsoft Excel

    MetaTrader4 offers great opportunities for analysis of the financial instruments. Many traders suppose this platform to be the best for the technical analysis. Other opportunities should not be refused from. Data recording to the log file of MT4 is not always convenient, while MQL-4 has functions of work with the file system. Save script OutPrint with a new name WriteFile. It’s necessary to change the algorithm slightly. Insert the opening file function FileOpen() before cycle operator for(), insert the closing file function FileClose() after the cycle, substitute the function Print() with the filing function FileWrite(). It’s necessary to create a handle object-file beforehand for manipulations with the file. The file handle has an int type. FileOpen() has to be given a parameter, file name that has a string type. Files, which are opened in MQL-4 are always kept only in folder C:Program FilesMetaTrader 4expertsfiles (except files used in the tester) for the purpose of safety. Logging to the other directories is forbidden. We’ll log in format CSV, which is understood well by Microsoft Excel. The opened file should be closed at the end of the script work, otherwise other programs won’t get the full access to it. Let’s try to check a simple idea, prices behavior at different weekdays is not coincidental. For this we’ll record the difference between the closing and opening prices in points (Close[i]-Open[i])/Point. Point is a predefined variable that contains the point value (minimum permissible price change) for the current instrument. Moreover, we have to write down a weekday of the current daily bar TimeDayofWeek(Time[i]) and year TimeYear(Time[i]). Let us suppose that we buy on the day opening and sell on the day close. As prices are built according to Bid in MT4 (selling price), while buying is made according to Ask (buying price), we have to take into consideration the spread of the instrument. Spread of the symbol may be got through function MarketInfo(Symbol(),MODE_SPREAD). I remind that execution in operator for() is done in the following way:

    Picture 1

    Picture 1

    At first, the cycle initialization is done. For this initial level of counter index is assumed. I marked the block by S mark. This block is executed once at the beginning. Further on the conditional test is done – block 1. If condition is fulfilled, the cycle body starts fulfilling – block 2. Once the cycle body is fulfilled the counter is changed in block 3 – index--. 3 blocks passing makes one timing period of cycle for(). Further on circle-wise – 1, 2, 3 while conditions of block 1 are fulfilling. Once the condition is violated (in our case counter index is below zero), the cycle will end and control will be given to the functions following function for().

    Our task is to write into the file all the values since 2001 through 2005 (as an example). Cycle for() passes all the bars in our script from the beginning of the history until now. Thus, we do not have to write bars, which refer to years before 2001 and after 2005. For this purpose there are continue and break functions. Let’s modify the script in the following way.

    Picture  2

    Picture 2

    Lets add to the functions a conditional function if(), where we’ll check the terms of the year for each bar. Information about the conditional function is given in MetaEditor, our script just helps understand the logic of the functions use.
    Now the cycle body is fulfilling in another way. Before the function Print() we have two checks. The first one finishes work with the current index and returns work to block 1 of the cycle (in case the bar year is before 2001), the next functions of the cycle body are not fulfilled. The second one fully completes the cycle work (in case the bar year with the current index is after 2005).
    Now we have to add the blocks of opening and closing files and modify output to log for writing into the file. Now our script consists of three operations/blocks.

    Picture  3

    Picture 3

    The output levels will be divided by semicolon in the FileWrite() function, because we indicated ";" as a separator for the file opening. It helps open the file in Microsoft Excel, where each level divided by semicolon will be put into its own column. We should also name the columns to understand figures better. For this we'll add a writing of the heading of the output table. Don’t forget to fill in the year, weekday for each bar, spread and difference between the opening and closing prices in the items.

    Picture  4

    Picture 4

    Work in MetaEditor is finished here. We should compile and execute the script in chart EURUSD D1. Now start Excel and open the file.

    Picture  5

    Picture 5

    Rearrange limits of the columns.

    Picture  6

    Picture 6

    Now we may use the simplest facilities of Excel. Initiate auto filter.

    Picture  7

    Picture 7

    Column titles will be able to filter the data on these columns.

    Picture  8

    Picture 8

    As an example I will take only Fridays. That is, the table will display only the lines which fall on Fridays within 5 years from 2001 till 2005. Let's do the following:

    Picture 9

    Picture 9

    That is in column "Weekday" we have put a condition, equal 5 (5 means Friday in MT4). Now we can count all Fridays, sum up Close-Open figures of Fridays, for it the lowest box in column "Close-Open" should be activated and sign "Sum" should be pressed on the toolbar.

     

    Picture 10

    Picture 10

    Readings for auto-summing are filled in automatically. Click Enter and get a sum in items. The same should be done with column "Spread".

    Picture  11

    Picture 11

    We’ve got the following result: if we bought EURUSD on the day opening every Friday for five years and closed a deal on the day closing, we would take profit of 652 points (1435-783) for those days after spread of 783 points subtracting. The data are nominal, as we didn’t took slippage into consideration. We may check other weekdays in the same way. Filter on the year may be initiated additionally.

    Picture 12

    Picture 12

    The last thing we may build Open, High, Low, Close diagram on the back of our data. For this select the data for the needed period (I've selected a small part of 2003), click "Diagram wizard" and choose type "Stock".

    Picture  13

    >Picture 13

    We have the following diagram. Charts are better in MT4.

    Picture  14

    Picture 14

    The script may be taken here

  • Read Close 7

    Orders in MetaTrader4

    The work of expert or script, that uses trading functions, needs recieving the current data about open and pending orders – opening level, values of Stop Loss and Take Profit, the current profit (or loss) for the specific order, the number of open orders. Sometimes such systems of automatic trading are witten where the length of time of holding an open position is important, and in such cases it is also necessary to know the time of opening the specific position. For diversification purposes such systems are created that trade using similar algorithms, but different instruments, in such cases it is important to know for which instrument the specific order is open. Or two or more mechanical trading systems (MTS), which are also called trading robots, are working with one instrument. In such case it is necessary to differ somehow the orders opened by different MTS for the same instrument. There are MTS in which signals for opening are of different strength of value (and that’s why orders of larger or smaller size of position are opened). Also there are MTS that require opening orders in one direction under several different from each other conditions, for example in case of a break of resistance and a bounce off of a support level. It is also necessary to differ somehow orders opened in one MTS but in different situations, to add comments. Besides there are systems in which pending orders that haven’t worked for a certain period of time after placing, should be deleted, as it is considered that conditions of their (orders) existance don’t correspond the current situation any more. For example we place a pending order on the day when good economic reports are expected, and it is necessary to delete it if it doesn’t work in the first minutes after the news comes. In general there are a lot of reasons for opening, closing, deleting and holding orders and practically all these variants are realized in the trading platform МТ4. Let’s call the tab "Terminal" (Ctrl+T) in the terminal and choose the tab "Trade". In the picture You may see the typical state of the terminal on a trading account on which several MTS are working simultaneously.

    Pic. 1

    Pic. 1

     

    The first column contains the numbers of tickets for each order, the second column shows the time of opening the order, then the column of the order’s type follows (buy or sell), then the column showing the size of the open position, the opening price, the level of Stop Loss, the level of Take Profit, the current market price, at which the order can be closed at the given moment(Ask or Bid depending on the type of the order), total swaps for the given order (positive or negative), the current profit on the given order, and the last column contains commentaries to the order. Below the line cotaining data on the current Balance, Equity, Margin, free margin there is a line with the information about the only pending order with the ticket 3313320. There are no data about swap and current profit for pending orders, other parameters are also known. Besides, MagicNumber of each order isn’t shown in the table. MagicNumber – it is usually a unique whole number which is marked by the expert advisor at the opening of an order, so that this expert advisor can differ its orders from the orders opened by other advisors or by hand (for them MagicNumber=0). In order to see the MagicNumber of a specific order You need to point the mouse cursor at the order needed and a pop-up help with the number of ticket, MagicNumber (id 1005 in the picture) and a comment to the order will appear.

    Pic. 2

    Pic. 2


    Thus MagicNumber for the order with the ticket 3313336 is 1005 and the comment is "sell stop". Let’s create a script which outputs all the attributes of the orders on the current trading account in the file *.csv and in the log journal. In order to do this we need to go through all the orders and request for these attributes. We’ll do it with the help of the cycle for(), operators Print() and FileWrite(). In the picture from the terminal we can see that orders in МТ4 can be presented as a list. We can see that the number of orders is 13 (12 open orders + 1 pending order). The function OrdersTotal() returns the information of the total number of orders. Actually, the list of orders is stored in the terminal, at that it should be remembered that the number of position in the list starts with zero and ends with OrdersTotal()-1. In the given picture the last order in the list will have the number of position 12, not 13. In order to address the order it is necessary at first to select it with the function OrderSelect(). In case of valid trial of selection the function returns value true , otherwise false.

     

    Pic. 3

    Pic. 3


    I launched the script on a demo-account and we can compare the position of orders in the terminal (that we can see) and the position of orders in the memory of the terminal (the result of script’s work).

     

    Pic. 4

    Pic. 4


    It can be seen that orders in the terminal’s memory are arranged not in the way as we see. But if the code is written correctly it doesn’t matter. I added a pending order on USDCAD 0.1 Buy Stop 1.1490 and launched the updated script on the terminal. Please note that the file’s name consists of the number of account on which the script was executed, and after it ends working values of balance, equity, margin and free funds on the account are output in the log journal by the function Print(). Also the number of open sell and buy orders and pending orders of four types is calculated. It is also possible to make a printout of these calculated values.

     

    Pic. 5

    Pic. 5


    So we have such table in Microsoft Excel. The order, placed by hand (USDCAD), has MagicNumber equal to zero. It can be seen that on one of the symbols (EURUSD) orders with different MagicNumber(1002, 1003, 1005, 1006, 1007) are open, and that on two different symbols (EURJPY and GBPJPY) there are orders with the same MagicNumber(1009). МТ4 gives possibility to test a lot of MTS on one trading account МТС, at that each MTS can differ its orders from orders of other MTS and from orders placed by hand or on other symbol.

     

    <#IMG6>
    Ultimately I would like to remeber the most common error in the work with the list of orders– usually processing is started not with the zero position in the list but with the first one, and consequently it is ended beyond the list of orders. Eventually it gives a double mistake – the order with zero position is not processed and an attempt is made to process a nonexistent order on the position OrdersTotal().

    For the script refer to OrderList here

  • Read Close 8

    Closing and deleting orders

    Now we can consider the mechanism of deleting and closing market orders in MT4. We know that with the help of the function OrderSelect (number in the list , SELECT_BY_POS) we choose the order for which we then receive its parameters. The functions OrderClose() and OrderDelete() require such parameter as the number of order ticket, which we’ll recieve with the help of OrderTicket(). Let’s suppose that we need to close all open and delete all pending orders quickly. We’ll create for this purpose a new script StopTrade. mq4 . The simpliest way to do it is to search the list of orders using the cycle for() and depending on the type of order to close or delete each of them. The type of order we’ll get by the function OrderType(). It is sufficient to know the number of ticket to delete a pending order, but it is necessary to indicate the correct price at which we will close orders of the type OP_BUY or OP_SELL to close an order. As in the terminal a lot of orders for different instrument can be opened, and the script will be executed on the chart of one of them, we require the possibility of getting values of Ask and Bid for any symbols. For this purpose there is the function MarketInfo(). The expression MarketInfo("GBPUSD", MODE_ASK) means Ask price for the symbol GBPUSD. The value of the symbol we get from OrderSymbol(). As a prompt I’ve made a table of the list of orders, where the selected line for the order on the 8-th position in the list (highlighted with yellow background) means that this order is chosen by the function OrderSelect(), and below I placed names of functions with the help of which it is possible to receive necessary parameters of the order. In the picture the function OrderTicket() used to get the number of ticket for the chosen order is selected.

    We have all necessary data for writing the script. But the first operation in our script will be verification of the account type on which it is executed. We write this script as a training one, that’s why it is necessary to make protection from an accidental use of the script on a live account. For verification of the account type we use the function isDemo(), which returns true for demo-account, otherwise false.

    Exclamation mark (!) is used for logic expression and makes an “overturn”: expression ! true turns into false, and ! false turns into true. Expression ! isDemo() on a demo account will return value false ( true will turn into false), and on a live account it will return true. In this case the script will terminate the work early, previously having given the warning “The work on a live account is prohibited”. It is preferably to use such protection in all scripts (making trading operation) and expert advisors to avoid unpleasant surprises.
    While searching orders in the cycle we’ll have to verify the type of order, there are 6 types of orders — 2 open (Buy or Sell) and 4 pending. Buy we’ll close at Bid price, and Sell at Ask price for the symbol for which the order is open. It would be preferably to delete the remaining 4 types of pending orders in a like manner irrespectively of buylimit or sellstop order it is. If we look into help we will see that constants 0 and 1 means open orders, while pending ordes have OrderType()>1(2,3,4 and 5).

    Now we can apply deleting OrderDelete() for orders with OrderType()>1, and two other types of orders we can close with the help of OrderClose(). But it isn’t very good to write a lot of comparison operators if(OrderType equals to n) after OrderSelect(), we’ll do it more gracefully. (Expression a==b makes comparison between a and b, and if they are equal it returns true.) For such cases when equality of expression to a limited number of values is verified the operator switch() suits very well. So we’ve got such variant:

    Now we only have to write out closing and deleting. For deleting we simply write OrderDelete(ticket), where =OrderTicket(). For closing open orders we will receive current closing prices with the help of MarketInfo(OrderSymbol(), …). The size of the position that is closed we will receive from OrderLots(),I assigned slippage equal to 3, and I set the red color for the drawn arrow of closing. If you don’t remember what parameters and in what order it is necessary to transfer them to the function, you can get a prompt by pressing the shortcut key. In the picture I fixed the cursor between brackets in OrderClose():

    Now the script is almost ready, we only have to decide how to close and delete orders — from the end of the list or from the beginning. The second common mistake is made in this place.

    The list of orders in the terminal is dynamic, while a pass in the cycle for() will be static — if it was said to pass from 0(zero) to total-1 (the number of orders in the list before deleting minus one), then it will be so. If there were 10 orders and we will begin from the beginning of the list (from zero order in the list) , then after passing from 0 to 4, there will remain 5 orders in the list, and they will be placed on the positions from 0 to 5. And a pass in the cycle from 5 to 9 will fire blank, the remaining orders won’t be closed or deleted. The correct way is to delete orders from the end of the list, then there will be no collapse effect. The order of deleting orders from the end of the list is shown in the picture, the size of the list after each deleting/closing will reduce, but none of the orders will be missed.

    After passing to the zero position there will be no orders in the terminal left. The final variant of the script will be as follows:

    Ultmately let’s assign the shortcut key for our script. For example pressing Alt+A will initiate our script to execute on the active chart. To do this it is necessary to click right mouse button on any script (indicator, advisor) and choose "Set shortcut key".

    Make a double click opposite to our script in the column "Control" and choose Alt from the list. Then we’ll assign the key (for example А).

    The script is ready for applying on a demo-account. We launched it by the shortcut keys on the chart of USDCHF (I did it intentionally, as there are no orders on this instrument at the moment) and 14 open orders were closed and 1 pending order was deleted in 25 seconds — a good result as for the speed.

    Before the execution of the script there were the following orders:

    You can take the script StopTrade here

  • Read Close 9

    Technical indicators calls

    Any trading system consists of signals for opening and closing positions, for trailing protective stop orders. Most expert advisors are written on the basis of standard indicators, included in МТ4, which are called technical indicators in the Help. Let’s consider the simplest system, based on the crossing of two Moving Averages. This indicator has two parameters — the period of averaging (the number of bars used for calculation) and the method of averaging (the type of price). There is the third parameter — shift, but we won’t use it in this example. The situation when a short-term moving average crosses a long-term moving average is usually used for selling, for buying everything is vise versa. We’ll make a script (CrossMASignals. mq4), that will plot buy signals on the chart in the form of blue upward arrows, sell signals in the form of downward red arrows.

    In the picture you can see that on the 21-st of July 2005 the moving average with the period 13 (value 1.2052) crossed from the bottom upwards the moving average with the period 21 (value 1.2050). We will plot the blue arrow at the opening price of the daily bar of 22.07.2005, as we will receive signals on closed bars, this will exclude false signals, when price may several times go up and down during the day. Let’s make the following flowchart :

    Though it is not quite correct to check the intersection of moving averages at the very beginning of the chart (from the bar with index Bars-1), but in this case we won’t have a mistake and we will not analyze so far why the values of averages in the beginning of the chart are equal to zero.

    In our code we need to receive the values of these averages on every bar. We will set two variables maLong and maShort and will assign them corresponding values. From the Help we can see that it is necessary to indicate symbol (NULL), time-frame (0), the values of periods (21 and 13), shift (it will be zero in our case), the type of averaging (simple), the price type (we will apply closing prices) and the index of the bar.

    The fact of intersection of two moving averages can be checked in the following way — on the current bar the short-term moving average is higher than the long-term one, while on the previous bar on the contrary the long-term moving average is higher than the short-term one. That’s why we will set four, not two, variables maLongCur, maLongPrev, maShortCur, maShortPrev.

    I’ve made a picture that shows the connection between the parameters of the indicator on the chart and the parameters of the technical indicator called in the code. For any built-up indicator in MT4 there exists its call in the language MQL-4 in the form of a technical indicator.

    The script is almost ready, we only have to put somehow arrows where it is necessary. We’ll do it with the help of functions for working with graphic objects. In order to create an arrow it is necessary to: 1. create an Object of the type symbol 2. set coordinates of time (X) and price (Y) , to which the object is bound 3. indicate the shape of the symbol and the color with which it will be displayed on the chart.
    All graphic objects in MT4 are created with the help of the function ObjectCreate() . Each object at once is assigned a unique name, there can’t be two objects with the same names. If You try to create the second object with the name that already exists it won’t be created, the mistake won’t be output in the terminal (but it is possible to check the fact of a mistake ) in order not to disturb the user. This is the most common mistake in the work with graphic objects, users got an impression that the program doesn’t work, fruitless attempts are made to find a mistake in the algorithm code, while it doesn’t exist - there is the mistake of creation arrays of graphic objects with the same name. We create an arrow with the function ObjectCreate("unique object name", OBJ_ARROW, handle_window, time, price). The parameter OBJ_ARROW — points that an object of the type arrow (symbol), handle_window — points to the window in which the object will be created (usually it is equal to zero, that points to the "native" window), time and price — coordinates of the arrow. After creating the arrow it is necessary to indicate the code of the symbol — ObjectSet("unique_object_name", OBJPROP_ARROWCODE, symbol_code) . The function ObjectSet() serves for setting properties of a graphic object, for each type of object its own second type of parameter is required, in our case for the object OBJ_ARROW we can set the symbol code with the help of the parameter OBJPROP_ARROWCODE, You can look for symbols codes in the Help menu of MetaEditor. Besides You can use symbols from the font Wingdings, for which Help is also available.

    Symbols with the codes 241 and 242 will do for us. Then we only have to set the last property — the color of the symbol. Again we will use the function ObjectSet() and will set the property OBJPROP_COLOR either Red or Blue according to the situation.

    The script is ready, but there is the last question — uniqueness of the name for each arrow. There is declaration of a string variable arrowName in the code, this name is used every time when a new arrow is created and when attributes of objects-arrows are changed. The simplest way to solve this problem is to use a variable of integer type, for example int arrowCounter, and to increase the counter of arrows by one every time when a new arrow is created with the help of arrowCounter++. The unique name we will get by addition of expression "arrow" and arrowCounter, we will get names of the type arrow1, arrow2 etc.

    The script is almost ready, it works, we launch it on the chart of EURUSD D1 and arrows appear instantly. We press the keys Ctrl+B — the window with the list of objects attached to the chart appears.

    We have got a "graphic" indicator, its advantage is the fact that arrows remain at their places when time-frame is changed, for example when we change it for Weekly. If we press the button "Edit", we will get into the window of properties of the selected object. I chose the arrow (object Arrow) with the name arrow1. On the tabs "General" and "Parameters" we can see the values that were assigned by the script — object name, color, coordinates time and price, and also the symbol code — 242.

    There is only one unaccounted point — if we try to launch the script for the second time — it either won’t work or will work not entirely correctly. Every time when we launch the script objects with the names arrow1, arrow2 etc. are created, but these objects exist already after the first launch of the script. And that’s why if we have to launch it on the same instrument but on another time-frame, we will have to delete previously all the arrows by hand, we can do it with the help of the menu "Charts" — "Objects" — "Delete all icons". This is not the best way, we can delete objects using the function ObjectsDeleteAll (window_number, object_type). It returns the number of objects, that was deleted. Let’s output this values onto the chart with the help of the function Comment(entry_line). This function is convenient to use in indicators and advisors for reflecting the necessary current information. Now we have a script in which everything is included for a proper work.

    The script CrossMASignals may be taken here

  • Read Close 10

    Functions init() and deinit()

    Before we write our first indicator we should complete script CrossMASignals. Let's add to our script a possibility to write buy and sell signals into the file, for it we should place a file opening block at the very beginning of the script, add an operation of signals record to the cycle, at the very end define the file closing. Let's name this script CrossMASignals-2. I should note that bars on the chart are divided into two types, bars with up and down arrows and bars without arrows. Let's write into the file the bar opening time, number, price, at which the up arrow and down arrow are drawn. That is, we will have a csv-bar containing 4 columns: Time[Bar_Index], Bar_Index, Blue_Arrow_Price, Red_Arrow_Price. Bars without arrows will contain zero values in the last two columns. I have added several lines at the beginning of the script:


    and at the end


    and at the very end of the script:



    The ready file you can download here .

    int start()
    {
    double maLongCur, maLongPrev, maShortCur, maShortPrev;


    string arrowName; // here a unique name of the object-arrow will be set
    int arrowCounter=0;
    int deletedArrows;


    string FileName;
    int FileHandle;

    // let's create the file name
    FileName="CrossMA.csv";
    //let's open a file with name FileName (let's create a handle for it)


    FileHandle=FileOpen(FileName,FILE_WRITE | FILE_CSV,";");
    if (FileHandle<1)


    {
    Print("The file has not been opened, error ",GetLastError());
    return;
    }

    // let's write down names of the columns (heading creation)

    FileWrite(FileHandle,"Date","Bar number","Up Arrow","Down Arrow");

    // let's delete arrows if there are any


    deletedArrows=ObjectsDeleteAll(0,OBJ_ARROW);
    Comment("Deleted ",deletedArrows," objects OBJ_ARROW");



    // let's publish utility variables where we will store arrows price coordinates
    double value1,value2;

    //----
    for(int i=Bars-1;i>=0;i--)


    {
    // let's zero utility variables
    value1=0.0;
    value2=0.0;

    maLongCur=iMA(NULL,0,21,0,MODE_SMA,PRICE_CLOSE,i+1);// slow
    // average on the previous bar


    maLongPrev=iMA(NULL,0,21,0,MODE_SMA,PRICE_CLOSE,i+2);// slow average two
    // bars before


    maShortCur=iMA(NULL,0,13,0,MODE_SMA,PRICE_CLOSE,i+1);// quick average
    // two bars before


    maShortPrev=iMA(NULL,0,13,0,MODE_SMA,PRICE_CLOSE,i+2);// quick average on the
    // previous bar




    //If there is a buy signal on the previous bar let's put a blue up arrow
    // at the bar opening price
    if((maShortCur>maLongCur)&&(maShortPrev<maLongPrev))

    {

    // Put arrow

    // object name - arrowName
    // object type - OBJ_ARROW
    // horizontal coordinate - Time[i] time of the bar opening
    // vertical coordinate - Open[i] price of the bar opening
    arrowName="arrow"+arrowCounter;


    ObjectCreate(arrowName,OBJ_ARROW,0,Time[i],Open[i]);



    // let's set the arrow type - 241, up arrow
    ObjectSet(arrowName,OBJPROP_ARROWCODE,241);

    //let's set the arrow color – light blue
    ObjectSet(arrowName,OBJPROP_COLOR,Blue);



    // increase arrow counter
    arrowCounter++;
    value1=Open[i];//remember the price of the light-blue arrow
    }
    //If there is a sell signal on the previous bar put a red down arrow


    // at the price of the bar opening
    if((maShortCur<maLongCur)&&(maShortPrev>maLongPrev))
    {

    // Let's put an arrow

    // object name - arrowName

    // object type - OBJ_ARROW
    // horizontal coordinate - Time[i] bar opening time
    // vertical coordinate - Open[i] bar opening price
    arrowName="arrow"+arrowCounter;

    ObjectCreate(arrowName,OBJ_ARROW,0,Time[i],Open[i]);



    // let's set arrow type - 242 , down arrow
    ObjectSet(arrowName,OBJPROP_ARROWCODE,242);

    // set arrow color - red
    ObjectSet(arrowName,OBJPROP_COLOR,Red);


    // increase arrows counter
    arrowCounter++;
    value2=Open[i];//remember the price of the red arrow
    }


    //write down the data for the current bar into the file


    FileWrite(FileHandle,TimeToStr(Time[i]),i,value1,value2);



    }
    //close file (free the handle, in order we could open the file to
    // edit it with other programs)
    if(FileHandle>0) FileClose(FileHandle);



    return(0);
    }




    We see that functionally the code consists from three parts:
    • Preparation unit, where old marks (arrows) are deleted and a record file opens.
    • Unit of arrows application to the chart and arrows values record into the file.
    • Finishing unit, where the file is closed.
    In MQL-4 there are three predefined procedures executed in some certain order, when the code is run (script, adviser or indicator) at first init() function is executed (if it is declared) where preparatory work, initialization, is convenient. Values of the necessary variables are set there, global arrays are filled out, files are opened etc. In block init() heavy calculating work can not be done as it slows down the work of the code. Then function start() is executed, there the major work is done while the code is working in the terminal memory, this is the main working function. There trading operations (advisors and scripts) are fulfilled, indicators values are calculated, any available values are processed. Function deinit() is the last one to be executed (if it is declared), there the code is finished. This function is executed when we delete an indicator or an advisor from the chart. This function can not be overloaded with calculations as there are no more than 2.5 seconds for the function to be executed. Then it will be compulsory completed. For more detailed information see the inbuilt help "Programs execution" .
    Let's change our script in accordance with these standards and name it CrossMASignals-3. I have added operator Print() at the beginning of each function in order we could see the sequence of these functions execution. Moreover, I have placed variable
    int FileHandle;  // pointer at the file, global variable,
    to the very beginning of the script, beyond functions start, init and deinit. Such variables are called global, they may be run wherever in the program. They say these variables are seen on the global level. We run it in init() when the file pointer is created, in block start() for filing and in block deinit() when file closing. At the same time
    string FileName; // file name, local variable
    was left in block init(), as it is not used anywhere else. This variable is deleted in the terminal memory once block init() is executed and an attempt to run it in the other functions of the script (start or deinit) will call the error notice. Such variables, which exist only within the function where they were declared, are called local. We may declare such variables in other functions and they will not know about existence of each other but it is not recommended as the code becomes too complicated in this case.

    int FileHandle; // file pointer, global variable

    int init()
    {
    string FileName; // file name, local variable


    int deletedArrows;

    // let's create the file name
    Print("init() is executed");
    FileName="CrossMA.csv";
    //let's open the file with name FileName (create a handle)


    FileHandle=FileOpen(FileName,FILE_WRITE | FILE_CSV,";");
    if (FileHandle<1)


    {
    Print("Failed to open the file, error ",GetLastError());
    return;
    }

    // write columns headings (heading creation)

    FileWrite(FileHandle,"Date","Bar number","Up arrow","Down arrow");

    // let's delete arrows if there are any


    deletedArrows=ObjectsDeleteAll(0,OBJ_ARROW);
    Comment("Deleted ",deletedArrows," of objects OBJ_ARROW");



    }
    //----

    int deinit()
    {
    Print("deinit() is executed");

    //let's close the file (free the handle, in order the file could be


    //opened to be edited by other programs)
    if(FileHandle>0) FileClose(FileHandle);


    return(0);


    }
    //----

    int start()
    {

    double maLongCur, maLongPrev, maShortCur, maShortPrev;


    string arrowName; // here a unique name of the object-arrow will be set

    int arrowCounter=0;// arrows counter
    // let's declare utility variables where arrows price coordinates will be stored


    double value1,value2;


    Print("start() is executed");
    for(int i=Bars-1;i>=0;i--)


    {
    // let's zero utility variables
    value1=0.0;
    value2=0.0;

    maLongCur=iMA(NULL,0,21,0,MODE_SMA,PRICE_CLOSE,i+1);// slow average
    // on the previous bar


    maLongPrev=iMA(NULL,0,21,0,MODE_SMA,PRICE_CLOSE,i+2);// slow average
    // two bars before


    maShortCur=iMA(NULL,0,13,0,MODE_SMA,PRICE_CLOSE,i+1);// quick average
    // two bars before


    maShortPrev=iMA(NULL,0,13,0,MODE_SMA,PRICE_CLOSE,i+2);// quick average on
    // the previous bar




    //If there is a buy signal on the previous bar let's put a blue up arrow
    // at the bar opening price
    if((maShortCur>maLongCur)&&(maShortPrev<maLongPrev))

    {

    // Let's put an arrow

    // object name, - arrowName
    // object type, OBJ_ARROW
    // horizontal coordinate, Time[i], bar opening time
    // vertical coordinate, Open[i], bar opening price
    arrowName="arrow"+arrowCounter;


    ObjectCreate(arrowName,OBJ_ARROW,0,Time[i],Open[i]);



    // let's set the arrow type, 241 , up arrow
    ObjectSet(arrowName,OBJPROP_ARROWCODE,241);

    // let's set the arrow color, blue
    ObjectSet(arrowName,OBJPROP_COLOR,Blue);



    // increase the arrow counter
    arrowCounter++;
    value1=Open[i];//let's remember the blue arrow price
    }
    //If there is a sell signal on the previous bar let's put a red down arrow


    // at the bar opening price
    if((maShortCur<maLongCur)&&(maShortPrev>maLongPrev))
    {

    // Let's put an arrow

    // object name - arrowName

    // object type - OBJ_ARROW
    // horizontal coordinate, Time[i], bar opening time
    // vertical coordinate, Open[i], bar opening price
    arrowName="arrow"+arrowCounter;

    ObjectCreate(arrowName,OBJ_ARROW,0,Time[i],Open[i]);



    // let's set the arrow type, 242 , down arrow
    ObjectSet(arrowName,OBJPROP_ARROWCODE,242);

    // let's set the arrow color, red
    ObjectSet(arrowName,OBJPROP_COLOR,Red);


    // let's increase the arrows counter
    arrowCounter++;
    value2=Open[i];//let's remember the red arrow price
    }


    //let's write the data for the current bar into the file


    FileWrite(FileHandle,TimeToStr(Time[i]),i,value1,value2);



    }
    return(0);
    }



    Everything is ready now, though the script's code is not convenient. It has been set that the period of a slow average is equal to 21 and the period of the fast average is equal to 13. Next time when we want to check other periods we will have to change the figures in the code and compile them again. For such cases there are external parameters, parameters of indicators, advisors and scripts. Let's type in new parameters LongPeriod and ShortPeriod, set the initial values and declare parameters with the key word extern (external).



    The value of the average is calculated as follows:


    This is the fourth variant of such a simple script.


    extern int LongPeriod=21; // slow average period


    extern int ShortPeriod=13; // fast average period


    int FileHandle; // file pointer - global variable


    int init()
    {
    string FileName; // file name – local variable

    int deletedArrows;


    // let's generate file name
    Print("Processing init()");
    FileName="CrossMA.csv";
    //let's open the file with name FileName (create a handle)

    FileHandle=FileOpen(FileName,FILE_WRITE | FILE_CSV,";");


    if (FileHandle<1)

    {
    Print("Failed to open the file, error",GetLastError());


    return;
    }

    // write down the title of the columns (heading creation)
    FileWrite(FileHandle,"Date","Bar number","Up arrow","Down arrow");

    // delete arrows if there are any


    deletedArrows=ObjectsDeleteAll(0,OBJ_ARROW);
    Comment("Deleted ",deletedArrows," of objects OBJ_ARROW");



    }
    //----

    int deinit()
    {
    Print("Processing deinit()");

    //close the file (free the handle, in order the file could be


    //opened to edit it by other programs)
    if(FileHandle>0) FileClose(FileHandle);


    return(0);


    }
    //----

    int start()
    {

    double maLongCur, maLongPrev, maShortCur, maShortPrev;


    string arrowName; // here a unique name of the object-arrow will be specified

    int arrowCounter=0;// arrows counter
    // let's declare utility variables where arrows price coordinates will be stored


    double value1,value2;


    Print("start() is executed");
    for(int i=Bars-1;i>=0;i--)


    {
    // let's zero utility variables
    value1=0.0;
    value2=0.0;

    maLongCur=iMA(NULL,0,LongPeriod,0,MODE_SMA,PRICE_CLOSE,i+1);// slow average on the
    // previous bar


    maLongPrev=iMA(NULL,0,LongPeriod,0,MODE_SMA,PRICE_CLOSE,i+2);// slow average two
    // bars before


    maShortCur=iMA(NULL,0,ShortPeriod,0,MODE_SMA,PRICE_CLOSE,i+1);// fast average
    // two bars before


    maShortPrev=iMA(NULL,0,ShortPeriod,0,MODE_SMA,PRICE_CLOSE,i+2);// fast average on the
    // previous bar




    //If there is a signal to buy on the previous bar let's put a blue up arrow
    // at the bar opening price
    if((maShortCur>maLongCur)&&(maShortPrev<maLongPrev))

    {

    // Let's put an arrow

    // object name - arrowName
    // object type - OBJ_ARROW
    // horizontal coordinate, Time[i], bar opening time
    // vertical coordinate, Open[i], bar opening price
    arrowName="arrow"+arrowCounter;


    ObjectCreate(arrowName,OBJ_ARROW,0,Time[i],Open[i]);



    // set the arrow type, 241, up arrow
    ObjectSet(arrowName,OBJPROP_ARROWCODE,241);

    // set the arrow color, blue
    ObjectSet(arrowName,OBJPROP_COLOR,Blue);



    // increase arrows counter
    arrowCounter++;
    value1=Open[i];//let's remember blue arrow price
    }
    //If there is a sell signal on the previous bar let's put a red up arrow


    // at the bar opening price
    if((maShortCur<maLongCur)&&(maShortPrev>maLongPrev))
    {

    // Let's put an arrow

    // object name, arrowName

    // object type, OBJ_ARROW
    // horizontal coordinate, Time[i], bar opening price
    // vertical coordinate, Open[i], bar opening price
    arrowName="arrow"+arrowCounter;

    ObjectCreate(arrowName,OBJ_ARROW,0,Time[i],Open[i]);



    // let's set the arrow type, 242 , down arrow
    ObjectSet(arrowName,OBJPROP_ARROWCODE,242);

    // let's put the arrow color, red
    ObjectSet(arrowName,OBJPROP_COLOR,Red);


    // let's increase the arrows counter
    arrowCounter++;
    value2=Open[i];//let's remember the red arrow price
    }


    //let's write the data for the current bar to the file


    FileWrite(FileHandle,TimeToStr(Time[i]),i,value1,value2);



    }
    return(0);
    }




    When an adviser or indicator is started, a dialog window appears in the charts where input parameters may be set, but there is no such window when a script is started (as in our case). In this case, if it is required, in order the script got input parameters (offered the user to enter them) the following command is used
    #property show_inputs


    Let's add this line and get the fifth final variant. Using it on the chart we will see the following:



    If we just close the dialog window we will see by tab "Experts" that neither of the functions was called, the script was deleted immediately without execution. Let's open file CrossMA.csv , there are four columns there. The last two columns will be necessary to write an indicator.



    Scripts may be downloaded here
  • Read Close 11

    Indicator Creation

    Let's creat a real indicator on crossing of two moving averages (MA). File CrossMA.csv contains columns "Up arrow" and "Down arrow". Bar opening time (Time[]), bar number (which will increase with a new finished bar) and values from two columns/arrays, pointing at the presence of a red or blue arrow or their absence (zero value) correspond univocally to each bar at the current moment of time. Let's creat a custom indicator with the help of "Advisor Creation Wizard".





    Let's specify the name of the indicator (CrossMA), author, link. Close the parameters table and press the "Add" button, a new parameter ExtParam1 (parameter name by default) appears, give a new name, LongPeriod, to this new parameter, as in our previous script, type int remains unchanged, let the initial value be equal to 21.



    Let's add another parameter ShortPeriod, external parameters of our indicator are ready. Move to the "Next". We do not need "Indicator in a separate window" check-box. We will place our arrows in the price chart (that's why check-boxes "Minimum" and "Maximum" are not available for us). Let's click "Add" once again. A red index ("Red") of type "Line" appears in the list of indices in the first line, now change the type to the "Arrow" one and the color to the "Blue".. Let's change symbol type 217 (up arrow) to 241 to make it more similar to the script.



    Let's add the second index and change its data (type, color, symbol).




    #property copyright "MetaQuotes"
    #property link "http://forum.alpari-idc.ru/viewtopic.php?t=48186"


    #property indicator_chart_window
    #property
    indicator_buffers 2

    #property indicator_color1 Blue
    #property indicator_color2 Red

    //---- input parameters
    extern int LongPeriod=21;

    extern int ShortPeriod=13;

    //---- buffers
    double ExtMapBuffer1[];
    double ExtMapBuffer2[];


    //----

    int init()
    {

    SetIndexStyle(0,DRAW_ARROW);
    SetIndexArrow(0,241);

    SetIndexBuffer(0,ExtMapBuffer1);

    SetIndexEmptyValue(0,0.0);
    SetIndexStyle(1,DRAW_ARROW);

    SetIndexArrow(1,242);

    SetIndexBuffer(1,ExtMapBuffer2);
    SetIndexEmptyValue(1,0.0);

    //----
    return(0);

    }

    //----

    int deinit()
    {

    //----

    //----
    return(0);

    }

    //----

    int start()

    {
    int counted_bars=IndicatorCounted();


    return(0);
    }



     

    Advisors creation wizard has done the preparatory work. We see that the indicator will be displayed on the price chart, there are two utility buffers in the indicator, their colors have been specified, arrays were declared

    double ExtMapBuffer1[];

    double ExtMapBuffer2[];
    where the indicators values will be stored. Also two input parameters have been created.



    When the indicator was created function init() was also created. In this function the final setting of our indicator is carried out. Indicators in MT4 are flexible, each indicator can have up to 8 utility (indicator) buffers. The only thing we have to do is to declare these buffers correctly. These buffers are also called indices, a custom indicator may give values for the zero index, first, second etc up to the seventh one. The number of indices is set by the directive
    #property indicator_buffers N

    colors are set beginning with one
    #property indicator_color1 Blue
    #property indicator_color2 Red


    With the help of SetIndexStyle(index_number, mode) we set a necessary style for every index (there may be several indices with different styles of drawing), having specified the Arrow style we also indicate the symbol code (241 and 242) for each index, then it is specified that index 0 is ExtMapBuffer1 array, index 1 is the data from ExtMapBuffer2 array. If we do not join the index and array with function SetIndexBuffer(index_number, buffer_array_name) the indicator won't work. This is the first mistake when an indicator is created. The second one is that a buffer is declared as a type different from the double one, for example, int.


    The SetIndexEmptyValue(index_number, Blank_Value) function is in the last line. Usually there is no “absence of value” notion in the programming languages. That is why some definite value will indicate the absence of it (in our case it will be the lack of an arrow), usually 0.0 value is used for it, though other values are also possible. We should write an algorithm which will fill out the indicator buffers (indices) with the values where arrows will be placed. For it let's refer to the last script CrossMASignals-5, copy everything from the start() block, delete the operations of arrows marking in the chart and a record into the file. I have also added Print() functions into the init() and deinit() blocks. We have almost created an indicator without writing a code. If we put /* at the beginning of the fragment, it will become a comment which is not compiled. It is convenient when mistakes are looked for and when it is necessary to switch off large pieces of a code.


    #property copyright "MetaQuotes"
    #property link "http://forum.alpari-idc.ru/viewtopic.php?t=48186"


    #property indicator_chart_window
    #property
    indicator_buffers 2

    #property indicator_color1 Blue
    #property indicator_color2 Red

    //---- input parameters
    extern int LongPeriod=21;

    extern int ShortPeriod=13;

    //---- buffers
    double ExtMapBuffer1[];
    double ExtMapBuffer2[];


    //----

    int init()

    {
    //---- indicators
    Print("init() is executed");

    SetIndexStyle(0,DRAW_ARROW);

    SetIndexArrow(0,241);

    SetIndexBuffer(0,ExtMapBuffer1);
    SetIndexEmptyValue(0,0.0);

    SetIndexStyle(1,DRAW_ARROW);

    SetIndexArrow(1,242);
    SetIndexBuffer(1,ExtMapBuffer2);

    SetIndexEmptyValue(1,0.0);

    return(0);
    }

    //----


    int deinit()
    {

    Print("deinit() is executed");
    return(0);
    }


    //----

    int start()

    {
    int counted_bars=IndicatorCounted();
    //----
    double maLongCur, maLongPrev, maShortCur, maShortPrev;


    /*
    string arrowName; // here a unique name of the object-arrow will be given
    int arrowCounter=0;// arrow counter

    */

    // let's declare utility variables where arrows price coordinates will be stored
    double value1,value2;

    Print("start() is executed");


    for(int i=Bars-1;i>=0;i--)


    {
    // let's zero utility variables
    value1=0.0;
    value2=0.0;

    maLongCur=iMA(NULL,0,LongPeriod,0,MODE_SMA,PRICE_CLOSE,i+1);// slow average on
    // the previous bar


    maLongPrev=iMA(NULL,0,LongPeriod,0,MODE_SMA,PRICE_CLOSE,i+2);// slow average on the
    // bar two bars before


    maShortCur=iMA(NULL,0,ShortPeriod,0,MODE_SMA,PRICE_CLOSE,i+1);// quick average on
    // the bar two bars before


    maShortPrev=iMA(NULL,0,ShortPeriod,0,MODE_SMA,PRICE_CLOSE,i+2);// quick average
    // on the previous bar



    //If there is a buy signal on the previous bar let's put a blue up arrow
    // at the bar opening price
    if((maShortCur>maLongCur)&&(maShortPrev<maLongPrev))

    {

    /*
    // Let's put an arrow
    // object name - arrowName
    // object type - OBJ_ARROW
    // horizontal coordinate - Time[i] bar opening time
    // vertical coordinate - Open[i] bar opening price
    arrowName="arrow"+arrowCounter;
    ObjectCreate(arrowName,OBJ_ARROW,0,Time[i],Open[i]);

    // let's set the arrow type - 241 , up arrow
    ObjectSet(arrowName,OBJPROP_ARROWCODE,241);

    // let's set the arrow color - blue
    ObjectSet(arrowName,OBJPROP_COLOR,Blue);

    // increase the arrow counter
    arrowCounter++;
    */
    value1=Open[i];//let's remember the price of the blue arrow
    }
    //If there is a sell signal on the previous bar let's put a red down arrow
    // at the bar opening price

    if((maShortCur<maLongCur)&&(maShortPrev>maLongPrev))

    {
    /*
    // Put an arrow
    // object name - arrowName
    // object type - OBJ_ARROW
    // horizontal coordinate - Time[i] bar opening time
    // vertical coordinate - Open[i] bar openig price
    arrowName="arrow"+arrowCounter;
    ObjectCreate(arrowName,OBJ_ARROW,0,Time[i],Open[i]);

    // let's set the arrow type - 242, down arrow
    ObjectSet(arrowName,OBJPROP_ARROWCODE,242);


    // let's set the arrow color - red
    ObjectSet(arrowName,OBJPROP_COLOR,Red);
    // increase the arrow counter
    arrowCounter++;
    */
    value2=Open[i];//remeber the price of the red arrow
    }


    /*
    //let's write down the data for the current bar into the file
    FileWrite(FileHandle,TimeToStr(Time[i]),i,value1,value2);
    */

    }

    return(0);

    }




    The code is compiled very well, but the indicator does not draw anything. For this only two lines should be added:

              
    ExtMapBuffer1[i]=value1;

    ExtMapBuffer2[i]=value2;

    As far as we remember, CrossMA.csv (once script CrossMASignals-5 is executed) contains two columns: "Up arrow" and "Down arrow". Now these columns are in buffers ExtMapBuffer1[] and ExtMapBuffer2[].
    Let's compile the indicator and start it in the chart EURUSD D1 again. There are again two sets of arrows (red and blue) in the chart, but they are of a smaller size and not graphic objects, available for editing. The arrows are an inseparable part of the indicator and they will be redrawn when a time frame is changed. Let's open the bar "Data window" and point the crosses to the last blue up arrow (to get the crosses mode press the mouse wheel or middle button). In front of CrossMA there is 1.1886 value, this is a value of ExtMapBuffer1 on this bar, value2 is a value of ExtMapBuffer2 buffer on this bar, there is no value for it as 0.0 is an empty value ( remember
     SetIndexEmptyValue(0,0.0);


    SetIndexEmptyValue(1,0.0);

    ? )


    As you see, it is quite easy to creat an indicator.
    You can download the indicator here

  • Read Close 12

    Indicator Optimisation

    In spite of its small size the indicator we created has a serious disadvantage as it is resource-intensive. In tab "Experts" (where logs created by MQL-4 programs are seen) a lot of outputs of function Print() are seen (this is referred to the indicator CrossMA-2.mq4):





    As function start() is called on every tick cycle for() is executed with each price change. If you have noticed, "Advisors Creation Wizard" has created a variable counted_bars (counted bars), let's consider its behaviour when the indicator works. I have added a new variable limit, and also printed out.



    The indicator works the same way, the cycle starts counting from i=Bars-1 (limit=Bars-1). The work of the indicator may be improved by watching after the output Print() in block start().



    When the indicator is run on the first call start() counted_bars is equal to zero, Bars is equal to 4413, limit is equal to 4412 (a unite less than Bars). On the second call counted_bars is already equal to 4412 and then it does not change on the current bar. So, the indicator values are calculated for the first time then on each tick they are recalculated uselessly instead of calculating an indicator value only on the zero bar which has not completed yet. Thus, having avoided such a calculation we will offload the computer's processor. Let's check this condition on the first call of counted_bars=0, if it is fulfilled then leave the limit as it is.



    If the indicator has been already calculated we will count only on the zero bar. We get zero as Bars-counted_bars-1 (4413 – 4412 -1).



    A new modernized indicator confirms our suppositions:



    For the first time the indicator is calculated on 4413 bars and further calculations are done only for the zero bar. Let's see what happens when the current bar completes and a new one opens. For the newly created indicator it is better to insert the following line: Print("Bars=",Bars," limit=",limit," counted_bars=",counted_bars); Thus we see the first possible mistake, non-optimized algorithm of the indicator calculation. The second widely-spread mistake (overoptimization) will be considered later.

    #property copyright "MetaQuotes"


    #property link "http://forum.alpari-idc.ru/viewtopic.php?t=48186"


    #property indicator_chart_window
    #property
    indicator_buffers 2
    #property indicator_color1 Blue


    #property indicator_color2 Red

    //---- input parameters
    extern int LongPeriod=21;
    extern int ShortPeriod=13;


    //---- buffers
    double ExtMapBuffer1[];
    double ExtMapBuffer2[];

    //----

    int init()

    {

    Print("init() is executed");


    SetIndexStyle(0,DRAW_ARROW);
    SetIndexArrow(0,241);


    SetIndexBuffer(0,ExtMapBuffer1);

    SetIndexEmptyValue(0,0.0);
    SetIndexStyle(1,DRAW_ARROW);


    SetIndexArrow(1,242);

    SetIndexBuffer(1,ExtMapBuffer2);
    SetIndexEmptyValue(1,0.0);


    return(0);

    }
    //----
    int deinit()
    {
    Print("deinit() is executed");
    return(0);


    }
    //----

    int start()
    {
    int counted_bars=IndicatorCounted();


    int limit; // calculations limiter

    double maLongCur, maLongPrev, maShortCur, maShortPrev;


    // let's declare utility variables where arrows price coordinates will be stored
    double value1,value2;


    if (counted_bars==0) limit=Bars-1; // there are no calculated bars yet,
    // it will calculate from the very beginning



    if (counted_bars>0) limit=Bars-counted_bars-1; // let's subtract from the number of
    // available bars the number of


    //calculated bars and subtract one

    Print("Bars=",Bars," limit=",limit," counted_bars=",counted_bars);



    for(int i=limit;i>=0;i--)
    {


    // let's zero utility variables
    value1=0.0;
    value2=0.0;

    maLongCur=iMA(NULL,0,LongPeriod,0,MODE_SMA,PRICE_CLOSE,i+1);// slow average on
    // the previous bar


    maLongPrev=iMA(NULL,0,LongPeriod,0,MODE_SMA,PRICE_CLOSE,i+2);// slow average two
    // bars before


    maShortCur=iMA(NULL,0,ShortPeriod,0,MODE_SMA,PRICE_CLOSE,i+1);// quick average
    // two bars before


    maShortPrev=iMA(NULL,0,ShortPeriod,0,MODE_SMA,PRICE_CLOSE,i+2);// quick average
    // on the previous bar




    //If there is a buy signal on the previous bar let's put a blue upward arrow
    // at the bar opening price
    if((maShortCur>maLongCur)&&(maShortPrev<maLongPrev))

    {

    value1=Open[i];//let's remember the price of the blue arrow

    }
    //If there is a sell signal on the previous bar let's put a red down arrow
    // at the bar opening price

    if((maShortCur<maLongCur)&&(maShortPrev>maLongPrev))

    {

    value2=Open[i];//let's remember the red arrow price

    }

    ExtMapBuffer1[i]=value1;


    ExtMapBuffer2[i]=value2;

    }


    //----
    return(0);

    }


    It is very important to calculate the customer's indicator optimally. To prove it let's run the simplest expert, which does not open deals and only contains a call of our indicator on each new bar for one time only. I have created two indicators, one of which contains calls of the "correct", optimized indicator, the second one contains the calls of the "incorrect" indicator, the very first draft variant of our indicator. To call the customer's indicators function iCustom(Instrument, Time_frame, Indicator's_name, Customer's_indicator_parameters, Index_number, bar_number) is used. In fact, the first three and the last two parameters are checked, the other ones between them are considered to be the indicator's parameters and transferred to the customer's indicator directly. The compiler does not check the correctness of the transferred parameters as the customer's indicator is called dynamically. The call of our indicator looks the following way:
    blueArrow=iCustom(NULL,0,"CrossMA-3",21,13,0,0);


    redArrow=iCustom(NULL,0,"CrossMA-3",21,13,1,0);



    The customer's indicator call without parameters will be equal to the call of the indicator with parameters by default (in our case 21 and 13), that is why such a record will be like the previous one:
     blueArrow=iCustom(NULL,0,"CrossMA-3",0,0);


    redArrow=iCustom(NULL,0,"CrossMA-3",1,0);




    Parameters of the customer's indicator are also given in the indicator's characteristics:



    I have created two advisors, one of them contains the optimized indicator call (CrossMA-3) with name TestCrossMA, the second one contains non-optimized indicator call (CrossMA-2) , it is named TestCrossMAwrong. Download them here . Store them in the folder /experts, compile and then open the "Strategy tester" in the terminal (Ctrl+R), put the necessary settings and press the "Start" button.



    Note that the customer's indicator should be calculated effectively.

  • Read Close 13

    Moving Average

    We have learnt how to create an indicator that draws symbols/arrows on crossing between two moving averages of different period. Now we’ll write an indicator that plots a simple moving average. A moving average is one of the most important indicators in the technical analysis; on its basis most of other indicators both standard and developed by traders themselves are created. A moving average is characterized by the period of calculation, the type of prices on which it is based and a shift. If we want to calculate a simple 3-period moving average based on the average closing prices, we add together closing prices for each of the last 3 periods (closing price today + closing price yesterday + closing price the day before yesterday) and then divide the total by the number of periods - in this case 3.
    MA(3,Close)=(Close[0]+Close[1]+Close[2])/3.
    We will use again the "Advisor Creation Wizard". We will create the parameters PeriodMA and ShiftMA.


    Next step is to add index; the type and color we won’t change, then we press “Ready”.


    We’ve got a standard template:


    Now we’ll add to the block start() a part of the code from the indicator CrossMA-3, in order not to write again the loop and utility variables.



    We know already how the for() loop works, now we can substitute the for() loop for the while()loop. We factor out the declaration of the variable i (int i=limit;) from for() before the loop, we insert the decrement operation (i--;) into the loop body, in the very end, and then we change the name of the for loop for while:


    As we can see the while() loop has fully substituted the for() loop. Also the validity of the expression in the brackets is checked, and if it is fulfilled, the loop body is executed. It is important not to forget to insert in the loop the decrement operator of the counter i, otherwise we will get an infinite loop. The compiler will not be able to detect such error (as it is not syntactical, it is logical), and if this incorrect indicator (with an infinite loop) is used the terminal will simply hang up and stop to respond to the user’s actions. The only way out of such situation is to close the terminal forcibly from the task manager, open MetaEditor, correct the indicator and launch MetaTrader4 again. But in scripts you may use infinite loops.

    Whether to use the while() loop or the for() loop depends on Your taste, though there are situations when the while() loop is preferable, for example, if it is necessary, moving from the current bar into the depth of history ( the bar index increases) to find a certain bar, which meets some condition. And it is necessary not to forget that the sought after bar may not be found, while the bar index will increase infinitely. And in order to avoid looping it is necessary to add checking if the index excesses of the number Bars. The example of such loop:


    Now we have the loop, we only have to write the algorithm for calculation the sum based on the number of bars PeriodMA, i.e. something like that:

    val=(Close[i]+Close[i+1]+Close[i+2]+… Close[i+n])/PeriodMA

    What will n be equal to, if PeriodMA (the period of our moving average) is known? There are people who can easily calculate the needed formula mentally; I made mistakes for many times, when I tried to do it without the help of a paper and a pen, that’s why it will be safer to follow the induction method. It will take no more than a minute, while the correctness of the result is guaranteed.
    Let’s take a particular case, n=5, as if we want to find the average for 5 bars. Then our formula is as follows:

    Val=(Close[i]+Close[i+1]+Close[i+2]+Close[i+3]+Close[i+4])/5

    or elsewise:

    Val=(Close[i]+Close[i+1]+…+Close[i+PeriodMA-2]+Close[i+PeriodMA-1])/PeriodMA

    Such mechanical addition can be easily inserted into the for() loop.


    In this case we have used both types of loops; we have got a convenient visual partition of the algorithm for searching the bars of the chart and the algorithm for summing up prices. Now we will return to the beginning of the block start(). As we can see in the picture there is no reason to set a limiter of counting limit into the value Bars-1 when the indicator is called for the first time, there are no bars in the depth of history and there is nothing to average. If we for example want to calculate a 5-period moving average, we can start to calculate it only from the bar Bars-5 (See the article "What are charts? ").


    That’s why for the first time when counted_bars is equal to zero it is logically to write:


    The indicator of a simple moving average based on the closing prices is ready, but sometimes a temptation arouses to create a maximally optimized indicator and an attempt is made to improve that part of the block start(), where counted_bars is larger than zero, i.e. the primary calculation of the indicator has been made already. On the ground that after the first run of the indicator Bars hasn’t changed, the calculation of the indicator was made based on the number of bars from Bars-PeriodMA to zero (i.e. counted_bars is supposed to be equal approximately Bars-PeriodMA), we write the following:


    As the aim is to calculate only on the zero bar it is possible to suppose that limit=0 and 0=Bars-counted_bars-PeriodMA. This implies that we presume that during the second launch of the indicator counted_bars+PeriodMA==Bars. In fact this is not so, and this is the second mistake of optimization, I call such indicator over optimized. The function IndicatorCounted() doesn’t return the number of bars, for which the calculation of values was made by this particular indicator, it returns the number of bars that haven’t changed since the last call of the indicator (the zero bar changed, otherwise the tick on which we are executing verification wouldn’t have come). That’s why if Bars=1000, then during the first call of the indicator IndicatorCounted() will return 0 (the indicator hasn’t been calculated yet), while during the second call IndicatorCounted() will return Bars-1. You can check it by yourself, having created an empty indicator (in which no calculations are made at all ) and having inserted in the block start() the call
    Print("Bars=",Bars,"  counted_bars=",counted_bars);
    If we don’t notice this mistake and the indicator remains over optimized then the following will happen (let Bars be equal to 1000, and PeriodMA will be equal to 5):
    • for the first time the indicator is calculated correctly from the bar Bars-PeriodMA to the zero bar
    • on the next tick the checking counted_bars will be greater than zero and the limiter of calculation will become equal to limit=1000-999-5= 1-5=-4
    • a limiter less than zero enters the block while() (or for(), the type of cycle doesn’t matter) and check is performed while(-4>=0) , check fails and recalculation on the zero bar isn’t performed.

    There are two ways to detect such mistake :
    • to launch the indicator on a short-term time-frame and watch its behavior
    • to run the advisor that contains the call of your indicator in the tester. For this indicator the simplest way of verification is the first one. We compile the incorrect indicator and use it on EURUSD M15, and then we wait for half an hour until two new bars appear and we see the following picture.




    Fortunately in this case when we work on-line the mistake in calculations of the indicator becomes evident quickly (the indicator isn’t drawn simply), but in more complicated cases the value of indicator simply drifts and it is not always possible to see it by eye.

    You may download the indicators Simply MA.mq4 and Simply MA wrong.mq4 here.

  • Read Close 14

    Moving Average (ending)

    We have created the indicator of a simple moving average with the period PeriodMA, now we have to involve the parameter ShiftMA, which is responsible for the indicator’s shift. It is often necessary not only to calculate the indicator’s values in the technical analysis, but to shift them by some number of bars forward or backward. This property is called a shift. In MQL-4 a shift of an indicator is achieved very simply, we only have to set to any indicator index the attribute of shift with the help of the function SetIndexShift(), to do this we write this simple construction in the block init() and get the intended effect.



    We apply our new indicator with a zero shift and the second indicator with a shift equal for example to three to a chart. We can see that the indicator that uses a shift is drawn three bars in advance; in case of a negative shift the indicator is drawn tardily. We may improve the indicator. When we look for the values of our indicator in the data window (Ctrl+D), we see that the name of indicator and its value for the given bar are shown. Often an indicator may contain more than one index, usually the zero index is called by the name of the indicator, other indexes are named as Value1 , Value2 etc. In order to give more meaningful names to indexes there is the function SetIndexLabel(), which sets the displayed name for the chosen indicator. We will use as a name SMA(PeriodMA), thus we showed that this is a simple moving average with the period PeriodMA. Usually this function is also used in the block init().



    If we look at the technical indicator Moving Average, we’ll see that we haven’t used the price type to calculate our indicator. Let’s refer to the Help in ME; seven standard price constants are indicated there:



    We will add a new parameter PriceType and we’ll change the blockstart() so that our indicator will be calculated not only on the basis of the opening prices. If we place the necessary type of price instead of prices Close[i+k] in the cycle for(), we won’t need anything else. We’ll introduce a new additional variable double price and we’ll set its values in accordance with the Help in МЕ. We’ll do it for unification of the user and technical indicator, in the block switch(PriceType) we’ll use as keys the predefined price constants.


    Besides we’ll also make the procedure of forming index mark in the block init() more flexible, now we’ll show not only the period (PeriodMA) and the shift (ShiftMA), but also the price type, on which the indicator is based.



    Now the creation of a simple moving average MA.mq4 is fully completed.



    Now let’s consider one more variant of a moving average. There are often such indicators when the current value of the indicator depends on the previous value and the current price. This can be written as follows: F[i]=F[i+1]*a+Price[i]*b, where a and b - are coefficients, which are usually less than 1. Sometimes it is required that a+b=1. Then F[i]=F[i+1]*(1-b)+Price[i]*b . The less the coefficient b is, the less the indicator reacts to the price change and the more it depends on the previous value of the indicator. For an exponential moving average (EMA) the coefficient b=2/(1+PeriodMA). We see that the more the period of EMA is, the smoother the curve of the indicator will be. Let’s create a new indicatorEMA.mq4 based onMA.mq4. We’ll make small changes in the block init()



    But there appears some halt in the block start(). At the very beginning when we calculate the indicator for the first time, we don’t have the previous value of the indicator, so we’ll have to make some assumption. The first variant is to agree that for a bar with the index Bars-1 the value of the indicator is equal to the value of the price on this bar (High, Open, Close or another price constant, for example {High+Low+Close}/3.0 ), and then make calculations following the algorithm. The second variant – for a bar with the index Bars-PeriodMA the value of the indicator is equal to a simple moving average (SMA), and then again make calculations following the algorithm. The first variant is realized in the user indicator Moving Averages.mq4 , which is included in the distribution package of MetaTrader4 and it is situated in the folder MetaTrader 4expertssamplesindicators . That’s why we will follow the second variant in order to compare them.


    We will take the block of calculating a simple moving average (blue color) from the indicator MA.mq4. After a small modification the indicator EMA.mq4 is ready.


    We can see that there are two similar sections/blocks that are very alike in the code. MQL-4 allows to declare user functions for the similar calculations. We can see the Help on functions in the " MQL4 manual" in the section "Functions". We’ll create the function GetPrice(int PriceMode, int index), we’ll transfer all the calculations in it, and set the type of function double.


    Now we will substitute these blocks by a line of calling the functionGetPrice() and the indicator EMA2.mq4.mq4 is ready. We’ll make the same changes with the indicator MA.mq4 and name it MA2.mq4. You can download them here .
  • Read Close 15

    Conversion of the Indicator from MQL-2 to MQL-4

    Let's consider how an indicator, written for MetaTrader3, is converted to MQL-4. Though MQL-2 is much more simple there are some peculiarities that influence creation of difficult indicators. The first difference is that in MQL-2 the indicator could draw no more than two indices, that is why several indicators were to be created when necessary.
    The second restriction is that because of slow work of the language-interpreter the indicator's work (calculations in the indicator or expert's body) ended compulsorily in some definite period of time. That is why utility variable was used to store the index of the latest calculated bar and the cycle kept working from the remembered value. In MQL-4 for optimisation purposes function IndicatorsCounted() was created.
    The third restriction: very few numbers of chart drawing, in MT3 there was a line, a histogram and arrows. The indicators code in MQL-2 is the following



    The interface is commented in green, it is simple. Then in Inputs section input parameters are given. We should only specify how we will declare them in MQL-4. |Variables ppor(100),mpor(-100) are used to draw the second buffer. Indicator buffers in MQL-4 always have type double, let's declare them just in the same manner. Variable per is a period of indicator CCI(), that is why let's declare it as type int. Variable t3_period(8) is used to calculate values of type double, though it should mean some integral-valued period and it can be declared differently, I will declare it with type double. Variable b is clear. Variable x_prd is not used. Let's declare all the variables (Variable), except shift, as type double. Everything is ready to write an indicator. Let's start the procedure:



    The indicator name remained the same as in MT3, though in the code it is RoundPrice.



    Let's copy the variables from mql code, change Inputs into double, declare them in the interface. Note that this part of the code is calculated uselessly every tick though the values of the variables do not change:



    That is why we may place this part of calculation into that part of the indicator only once, after the indicator is run, to block init().
    Operator if() requires minimum correction.



    We ignore operator SetLoopCount(0);. Let's copy the code again and work the syntax out to MQL-4. Let's replace opening operator Begin and closing operator End with the braces:



    We should only convert the rest three marked blocks and everything will be ready. Operators SetIndexValue() and SetIndexValue2() in MQL-2 fill out the indicator arrays №1 and №2. That's why let's write down:



    Technical indicator iCCI() is also simple to convert, let's only add parameters of the instrument and time-frame. In MQL-2 there were two technical indicators: iCCI() and iCCIEx(). The difference between them is that the first one was built at close prices, for the second one we were to set the prices type for calculation. You can refer to MT3 Help. Note the fourth difference of MT3, detailed help in Russian. Let's replace the last thing and the indicator is ready.



    Let's run the indicator in both terminals to compare, in MT3



    and MT4



    The error may be explained by different ways of price drawing. In MT3 the indicator is calculated much slower. Now we can analyse the code of the indicator to improve it, optimizing the mechanism of the indicator calculation. The first question is connected with the cycle where both indicator arrays are zeroed out.



    Moreover we did not use function InditarorCounted(). Let's comment-out the cycle and introduce the calculation limiter.



    Then there are two questions. The first one is why when we count for the first time with counted_bars equal to zero limit is set as equal to Bars-per. Below we find that call of the technical indicator iCCI() with period per is used for calculations. If we draw it into the chart we see that indicator CCI(14)starts forming only from the 15th bar from the end. Before it its values are not specified.



    If they have been calculated already (counted_bars are more than zero) , then Bars-counted_bars are usually equal to one. To optimize the calculations we will try to count the minimum number of bars, decreasing limit by one we comply with both variants. In the first case the result will be limit=Bars-per-1, in the second one limit=0. Here optimisation may be considered to be completed but for one thing. Should we apply our last variant of the indicator to the 15 min chart and wait about half an hour (for other two bars to appear) and then apply the same indicator with the same parameters but with different colors (to distinguish them) we will see that there is a great difference.



    It turns out that our indicator changes its readings with the course of time. In such cases they say that the indicator draws itself. In this case variables values meant to calculate the indicator on the zero bar just flow. For it let's add output of these variables values to the log. Let's save our new indicator with name SmCCI-2.mq4 and start to modify it.



    As we see all the variables flow. Now it is clear what we needed the cycle, where indicator values zeroed on every tick, for.



    We should fix (stabilize) variables e1, e2 ... e6 and our problems will be solved. Calculation of each variable for the current bar depends on the value of this variable on the previous bar. This reminds the algorithm of calculation of the exponential moving average. This method may be applied to store these variables in the indicator buffers. Let's declare e1Buffer[],e2Buffer[]…e6Buffer[] for it.

    Arrays have been declared, now let's replace the variables with the correspondent arrays. Two important moments are marked in the picture. If they are violated it may trigger malfunction of the indicator. These are the most popular errors if the indicator uses more indicator buffers than displayed. In this case the mistake may be looked for in the indicator log.



    Let's run both variants of the indicator in the chart, in two bars let's place another one, the first variant (which is “swimming”), and we will see that the second variant does not change its figures with the course of time unlike the first one.

    Of course, to some extent the indicator under consideration turned out to be simple. If we needed 9 or more indicator buffers we would introduce additional variables where values on the previous bar (something like prev_e1, prev_e2… prev_e6) would be stored or we would just declare arrays. Here conversion of the code from MQL-2 to MQL-4 may be considered completed. Indicators may be downloaded here .
  • Read Close 16

    OrderSend()

    Function OrderSend() in MQL-4 is, I suppose, most frequently used, as with its help pending orders are placed and buy and sell orders are opened. Let’s consider the usage of this function taking as an example a script that will turn round an open position in a particular instrument. Let’s suppose that we have one or several open long positions in EURUSD. Suppose, the price surged and reached the target level (according to our suppositions) where it is necessary to take profit (close out positions), and it is also desirable to open an equal opposite position, i.e. short position. In conditions of fast market when price changes rather quickly it is impossible to perform such operation quickly by hand. We have to:

    • Close successively one or several buy orders.
    • Open one sell order of the equal total volume.
    MQL-4 and MetaTrader4 allow executing all these operation in one action. The matter is that the terminal MT4 allows closing out an open position by the opposite open position, i.e. to close out buy orders by sell orders. Additionally we save a sum of money equal to the spread value. If we have an order to buy 0.1 EURUSD lot at 1.2000 and an order to sell 0.1 EURUSD lot at 1.2500 (and there are no more orders open in EURUSD), then no matter where the price is at the moment, the total current profit for these two orders is equal to 500 points (Point=0.001). Swap points, calculated for these two orders will be as the result negative, but for us it doesn’t matter. MT4 allows to close out a buy order opened at 1.2000, by a sell order at ask price(1.2500). Of course the sizes of the two orders must be the same.

    Suppose, at the moment Bid in EURUSD is equal to 1.2600. If we close each of the two orders independently we will get the following: we close the buy order 1.2000 at 1.2600 (+600 points) and we close the sell order 1.2500 at 1.2603 (Bid + spread 3 points) – in all we have minus 103 points. In all the profit is equal to 600-103=497 points. Suppose the price between two closings out didn’t change. If we closed one order by the other we would get 500 points (1.2500-1.2000). If sizes of opposite orders don’t coincide, then the order of the smaller size is fully closed out, and an order of the size equal to the difference between the opposite orders is left. I.e. in case with 0.2 buy and 0.1 sell, after a partial closing we would get an order to buy 0.1 lot at bid price. This very feature will be used in our turn round script Revers.mq4. We will check availability of only buy or only sell orders, will calculate the total volume of the position open in this instrument and will open an opposite position of doubled size. Thus after the script’s work we will have order(s) of the volume V1 and the opposite order of the volume V2=2*V1. Then we will be able to close out by hand necessary orders and to leave one order of the volume V2-V1=2*V1-V1=V1.

    We will create the script according to the principle of overcoming obstacles. If at some stage conditions are not fulfilled – the script terminates working early. First of all (as usually) we build in protection from a real account:



    If the script is launched on a live account, the warning will appear and the script will terminate working. Next step is to check availability of orders:

    If there are no orders ( OrdersTotal() is equal to zero) – there is no sense to continue working. But even if there are orders, we need to make sure that there are orders in our symbol (the chart of the instrument on which we launch the script), and the orders are open, not pending. We will use the loop to count orders open in our instrument. At the same time we will count the volumes of sell and buy orders separately.

    We can see that if an order is open not in our symbol (OrderSymbol()!=Symbol()), the work with the order terminates and the script proceeds to the order which is next in the list (operator continue;) . After the list of orders is checked, we may check separately availability of buy orders and sell orders, but we may also check both counters by one operation – having added the counters. If the sum is equal to zero there are no open orders, there are only pending orders.

    Such variant doesn’t suit us as well, so we exit. The next check – we need to have only buy orders or only sell orders. We want to have a uniquely defined situation. I checked it by means of multiplying counters, if the product is equal to zero then we have orders of one type only.

    If the script after all these checks hasn’t terminated its work it means that this is high time to open the opposite order of doubled volume. br />

    We haven’t got the doubled order directly, i.e. instead of writing reversLots=2*sellLots (for sell orders) , a complicated construction was used. What for was it done? The size of position is of the type double, and in computer calculations 2.0 multiplied by 2.0 is not always equal to 4.0. This is connected with the mistakes of rounding off not integer variables in the binary numeral system. Though 2 multiplied by 2 is always equal to 4. That’s why at first we will multiply buyLots by 10 (without a decimal point) and then we will double the received integer result. For example, 0.2(lot)*10*2=4. We have got an integer result four. Now we need to receive the size of reversLots(0.4) 10 times smaller accurate to the first decimal place. For this we divide intLots (integer lots) of the type int by 10.0 of the type double (direct your attention to the dot) and then we normalize the result to the accuracy of the first decimal place. As a result we have got the value 0.4 with the necessary accuracy.

    If we divided 4 by 10 (4/10), then, as both numbers are integer (int), the result of division would be calculated according to the rules of integer arithmetic. As 4(integer) isn’t divided by 10 (integer) the result would be zero. This is one of the common mistakes; one mustn’t forget the difference between integer division and division of real numbers in computer calculations.

    So, the size of the opposite order has been calculated, we only have to send the trading instruction to the server. Sometimes prices move so quickly that while the script was making calculations the current prices have become different from the prices at the moment of the script’s launch. It is necessary to have the latest prices always in order to prevent a denial of the server for the reason of invalid prices. For this there is such command as RefreshRates(), I recommend to read the Help on it. When this command is executed the broker’s trading server isn’t addressed, that’s why it may be used as often as it is necessary, not fearing to fall across sanctions from the broker. At last we may send the trading order OrderSend(). Though the Help menu on this function contains all the necessary information, I have to single out two common mistakes in its usage:
    • often traders forget to specify the symbol in which an order is opened. I.e. they simply missed the first parameter of the function (Symbol()), but MQL-4 allows to open orders in "alien" instrument (which is impossible in the tester). The compiler forgives such mistake and doesn’t report anything, and then the user wonders why orders wouldn’t open.
    • when users open an order of the type OP_BUY they mistakenly put Bid as the opening price, and for selling – vice versa. Naturally the order will be hardly opened.
    Besides, it is desirable not to set the parameter Slippage equal to zero, as this increases probability of a denial due to the change of price – during the time needed for your trading order to reach the trading server, the price may change, the zero Slippage parameter will be interpreted by the server as a denial of making a deal at somewhat different price and the order won’t pass.

    In fact, the number of errors, returned by the terminal or trading server is rather big, and these errors help to correct the algorithm. In order to know the code of the returned error there is the GetLastError() function. It is necessary to ask for the error code by means of this function each time we get a mistaken situation. As in the case of a failure OrderSend() returns the value -1, we output this error in case if ticket is less then zero (we could as well check ticket==-1). If the order opens normally, we won’t see the error report. The script is ready, I launch it on the chart of USDCHF, in which an order to buy 0.1 lot was open.

    We can see that the script opened the opposite order doubled in size.

    In the order comment it is indicated "revers order" - out mark. Now at any moment we can click on one of these order, choose the type "Close covered orders", we only have to press the yellow button.

    You can download the script here.
  • Read Close 17

    Expert Advisor

    Now we know almost everything needed for creating an Expert Advisor. From trading functions we haven’t considered only OrderModify() and OrderCloseBy() functions. But before we start writing complicated Expert Advisors it is necessary to understand clearly how an Expert Advisor works on-line, being applied to an instrument’s chart, and how its work is simulated in the tester when it is backtested on the historical data.

    Many traders write indicators and Expert Advisors, not realizing the difference between theory and practice. As after backtesting Expert Advisors in the tester they achieved attractive results they apply these Expert Advisors to online charts and later on they are often surprised at the difference (sometimes striking) between the results achieved online and from backtesting. That’s why first of all we will create an Expert Advisor for which we won’t blush, and which works equally well on any instrument and on any time-frame. This Expert Advisor won’t trade. Its own purpose is to keep a record of the state of a trading account, that’s why I called it BlackBox, by analogy with the equipment for recording flights of airliners.

    Traders think out complicated algorithm for opening positions, even more complicated for trailing stop loss orders, check the indicators’ values on different time-frames, count the number of profitable and loss-making deals in the latest series, track a wide variety of events, that may happen to the price and trading account. But they sometimes forget about two most important events.

    The first event is the coming of a new tick as the start() function of an Expert Advisor and indicator is executed precisely each time when a new tick comes. This event is evident. The second event is the closing of an old bar and formation of a new one. These two events are not equivalent with relation to writing an Expert Advisor. It is evident that the value of an indicator changes in an incomplete bar with each change of the price, that’s why most strategies use values from the latest completed bar, i.e. from the first bar.

    It is necessary to define the moment when the old bar closed and the first tick of the new (zero) bar came. At this moment we estimate the current situation according to the algorithm of the Expert Advisor and take decision whether to execute or not trading operations. This event (formation of a new bar) we will check by a user-defined function isNewBar() of Boolean type, it will return true, if a new bar has formed, and false if this were not the case. If a new bar hasn’t appeared common operations are performed, that can be done on each new tick.

    We create a new Expert Advisor with the help of The Initializing Wisard of the Expert Advisor. It requires no parameters for its work, that’s why we write only the name of the Expert Advisor.


    In the block start() we write several lines of a simple algorithm:

    We can see that on each bar the fact of appearing of a new bar is checked and in case of positive value the EveryBar() function is called. This function is called only once at the beginning of each bar (if isNewBar() is written properly) and during the time of developing of the bar isn’t caleed any more. Besides on each tick the EveryTick() function is called —any actions can be executed in it.
    How can we define the appearing of a new bar? There are two standard ways.
    1. Each bar has such property as Time[] — the time of opening on a new bar, and if each time we ask the value Time[0] and compare it with the value that was on the previous tick, then at the moment of the new bar appearing these values won’t coincide.
    2. The second variant is to remember each time the value of the predefined variable Bars. When a new bar appears this value will also change.

    Both variants are equal, but I will consider the second one. We write the function:



    Pay attention that the variable expertBars (in which we remember the value Bars) is global. Only variables on the global level remember their values between arrivals of each tick and launch of the block start() (from which the isNewBar() function is called). If the expression expertBars!=Bars is true, it means that a new bar appeared, we will set the value res equal to true and we won’t forget to remember the value Bars at this stage — next time in the future we will compare Bars with the current value. Thus the value res returned by the function wil be either true or false.

    Next we think out the EveryBar() function, in order not to think for a long time I decided to print out each new bar in the log order list, as in the script OrderList. mq4 (the article "Orders in MetaTrader4" . ).

    For this from the EveryBar()function a new function — PrintAllOrders() is called.

    We only have to write the EveryTick() function. I decided to count in this function the average profit for all the ticks arrived during the bar. Fro this on each tick the current profit on the account is summarized (AccountProfit()), the number of bars is counts, and when a new bar appears the sum of profits is divided by the number of the ticks arrived, as a result we get the average profit for the bar.



    We will modify slightly the EveryBar() function for calculating the average profit.

    Now when a new bar appears we count the average profit and zeroise the variables SumProfit and ticksBars for further usage from a new leaf on the new bar. We launch it on the chart EURUSD M15 and see the output in the log:

    Now our Expert Advisor each 15 minutes will output the data of orders and average profit for this period.

    I think the logic of this Expert Advisor is clear. You can download the code of the Expert Advisor here .
  • Read Close 18

    The tick indicator in a separate window

    I think most traders saw the tick indicator in the Market Watch window in the terminal MT4. It is also available when orders are placed by hand.

    Many traders appreciated worthily the appearing of this type of chart in MT4 (in МТ3 it wasn’t available). But people get used to good things quickly, and for many traders this is not enough already, there were a lot of requests to add such type of charts in a separate window. We will try to create the tick chart by ourselves. It won’t be convenient to place such chart in the prices window, that’s why we will create it in the subwindow of the main chart. In order to achieve a formal resemblance we will choose the type of display in the form of lines. We launch Expert Advisor Wizard, give the name Ticks, there are no external parameters, the second step is to mark the check box "Indicator in a separate window", add an index of the type Line ( I set the Navy color ) , and press "Ready".

    Our task is extremely simple: when a new tick arrives the start() function is called, in this function in the zero bar we place the value Bid. 100 ticks arrive — we have 100 filled values. The only problem is that it isn’t so easy to do this. If we launch the indicator on a 5 minute time-frame, a new bar will appear only each 5 minutes, but we need the indicator to move left each time when a new tick comes.

    If for standard indicators (both for built-in and custom ones) the terminal itself executes the needed shift, for the tick chart we will have to think out the shift mechanism by ourselves. In actual fact we have an indicator array ExtMapBuffer1[], the values of which we need to move depthward, we transfer the value ExtMapBuffer1[0] in ExtMapBuffer1[1], the value ExtMapBuffer1[1] in ExtMapBuffer1[2], etc., we write the value Bid in the free index ExtMapBuffer1[0].

    Thus we need some function ShiftArray(), after which we write at the zero address Bid.

    We only have to write the ShiftArray() function:

    We had to introduce a new global variable tickCounter, in order to know to what depth it is needed to shift the values of the array. The indicator is almost ready. But we forget that when a new bar appears the indicator array moves automatically. We will add the processing of the event "New bar" and will change the code:

    The isNewBar() function is similar to the one that is used in the Expert Advisor BlackBox, we will simply copy it. But instead of the global variable expertBars I wrote myBars.

    Now the indicator is perfect. It is true we didn’t use optimization possibilities, but how can we optimize this indicator? This is a rare case when there is no use of the built-in function IndicatorCounted() . But we can optimize it. Suppose, we launched the indicator 10 minutes ago, for this period of time 100 arrived, so on each new tick we shift 100 values of the array, and the number of the shifted values is increasing. A day after we will have thousands of such values. And this should bi multiplied by the number of charts to which we will attach this indicator. Perhaps the moment will come when all the computer resources will be used to shift senselessly tens of thousands values in different arrays. In order to avoid this I introduces the external parameter MaxDrawTicks=500 (maximum number of the drawn ticks) and introduced a rule: as soon as the ticks counter tickCounter reaches the value twice a large than this one the array is cut, values from MaxDrawTicks+1 to 2*MaxDrawTicks are filled with zeros. Then everything goes as usual. I proceed from the assumption that 500 values on the chart are enough to estimate the current situation, you may change this number or change the rule of cutting.

    The indicator is written without mistakes, optimization is done, what else is needed for happiness? If one presses Ctrl+Y in the terminal, periods delimeters appear. We could do something like that. And we will see that charts have their own time, that doesn’t coincide with the astronomic. The trend periods, which alternate with a range or consolidation, develops also by law of the internal time. And with the help of delimeters it will be easier to understand how the price behaved inside one or another bar, how ticks changed in the flag or false breach patterns.
    We can draw delimeters only with the help of objects, this object is Vertical line. Now when the "New bar" event happens we draw a vertical line with a unique name. We realize this by means of the SetDelimeter() function. The block start() finally looks like that:

    The SetDelimeter() function will create an object of the type OBJ_VLINE, place it in the indicator window, set the color and style. You can set your own parameters of color and style. The problem of uniqueness of the created names is resolved simply — each new delimiter has the name delimeterDate, which contains the text format of the beginning of a new bar. Thus we always can move the mouse cursor to our delimiter and understand in what period of time the given sequence of ticks developed.

    If you refer to the Help menu in ME, you will understand all the nuances of the functions used for working with the object. There is the last detail left - we created objects, now we need not to forget to move them for the same reason that the indicator array. We will extend the ShiftArray() function, now it shifts not only the indicator array, but all the vertical lines that it will find in the indicator window.

    Having studied the code of this indicator you will be able to create and change easily the properties of vertical lines. At that we can put a period to the writing of the indicator. Though if you wish you may improve it and add some functionality. We launch it on the EURUSD M5 chart and watch it.

    I moved the cursor to the delimiter and saw the name of the vertical line (which is given by Time[] of this period of time ).

    We see that on the tick chart support and resistance lines (1.2644) are seen very well.

    We see how the price broke the support line and returned upward again. 10 minutes after the price broke the support line 1.2620 . We can plot a horizontal line on our indicator.

     

    With the help of our indicator we can study the development of the price at each moment of time, not being physically at the monitor at that moment. I think intraday traders will appreciate worthily this indicator. You may download the indicator here.
  • Read Close 19

    A remarkable function

    At the moment we have:
    • Expert Advisor BlackBox, that can track two events;
    • indicator Ticks, that reflects the tick chart.
    We would like the Expert Advisor to be able to determine the fact of opening and closing an order in an instrument. An order may be opened manually, a pending order may work or it can be opened by another Advisor, it doesn’t matter for us. It can close when it reaches Take Profit, when Stop Loss works out, it can be closed manually or by the Advisor. It would be good to record everything in the log-file or in a distinct file. And it would be great to save the screenshots of such moments.

    A year ago it was possible to record such moments only sitting at the monitor, or having created a complicated construction of several programs. But even is this case the success wasn’t guaranteed. A man cannot track several charts simultaneously for 24 hours a day. While video-grabbers can work only with one monitor, meaning that each chart would require a monitor.

    But now in MT4 there is such function as ScreenShot(), which I called a remarkable function.

    Very often it happened so that a trader opened an order with a set Stop Loss, went away from the terminal for some time and on returning back found out that the price touched the stop loss level and moved away in the needed direction. Or vice versa, the opened order reached some level of the minimum profit, after which the price returned to the opening level, and trader couldn’t venture to close the transaction. Most often in such cases the price developed then in the unfavorable direction, but how to distinguish these two moments? Or the third variant — we have an Expert Advisor that trades online and we would like to improve the algorithm of its exits after having watched transactions. In general in all these cases it would be desirable to save the video sequence, gallery of pictures which could be seen later in the slide show mode.

    It is logic to combine the work of the Advisor BlackBox with recording such pictures, at the same time tracking the status of the indicators we need. We will save the Advisor with the name BlackBox-2 and will start modification. In the Ticks indicator there was one lack — the line Bid wasn’t displayed. We will add it with the help of our Advisor, and at the same time we will learn how to create and move the horizontal line, which we will call bidLine. It is naturally to insert this code in the EveryTick() function:

    This example shows interaction between the Advisor and the indicator window. At first we seek for the line with the name bidLine, if we find it we move it, if not, we create it. You may delete this line and the Advisor will create it again.

    Next, to record screenshots from the terminal we need to add parameters — to start recording a series of screenshots once the order is opened or closed, how many shots should be made for each of these events, how many ticks should be between shots.

    Besides we introduced new global variables: orderShotCounter will decrease after each call of ScreenShot(), while shots will be made each time once tickRateCounter reaches the value 0 (i.e. each 10 ticks in this case). In all NumberShots (100) shots will be recorded for each event.

    It is logic that the record will be kept in the function that is called on each tick. This block will work only in case if orderShotCounter becomes more than zero, that’s why it is very simple to launch a series of shots – we need to determine the moment of opening or closing of the order and set the value orderShotCounter equal to the needed number of shots. Before shot its time and number are displayed with the help of Comment().

    We had to add three new functions. The SymbolOrderTotals() function is simple, it counts the number of opened orders in our symbol. The AddNewOrderLine() and DeleteClosedOrderLine() functions were added for appearance, the first one added in the window of the Ticks indicator a horizontal line at the opening price of the new order, and the second one deletes this line.

    The search of the new order is implemented as follows: we sort out all orders in the loop, for each order in our instrument we look for a horizontal line with the name equal to the order ticket, if such line isn’t found this order is our new order. In such case we create a horizontal line with the name is lineName=DoubleToStr(OrderTicket(),0). We had to use the DoubleToStr() function to modify the number of ticket OrderTicket() in the string type.

    The color of the line depends on the type of orders, buy orders are drawn by the blue color, sell orders by the red color. The search of the last closed order is implemented in reverse order: all horizontal lines on the chart are sorted out, and for the lines that have description "order" the order is sought by the ticket. It is necessary to specify that one can define the closed order only with the help of the OrderCloseTime(), function, that for opened and pending order returns the value 0, i.e. there haven’t been yet the fact of deleting a pending order or closing a market order.

    In practice we used the set of horizontal lines as an array for remembering the list of opened orders. Such method has its advantage: even if one closes the terminal or changes the profile — the list of opened orders will be saved in the form of a set of objects — horizontal lines.

    The last detail — we will count the number of orders in our instrument and will plot the lines of orders (if there is such necessity) when the Advisor is launched for the first time. You may comment all superfluous log file entries by yourself, they are made for convenience of understanding and debugging the program. The logic of the Advisor is not all-purpose, an it won’t be able to track complicated combinations of opening/closing orders.

    In order to make shots when the non-farm payroll statistic was reported I had opened a sell order on the demo account a few minutes before the news. The Advisor found out the new order and shooting began. A series of 100 shots in 800x600 format occupied a little more than 1Mb, but during strong moves the bidLine doesn’t manage to follow the price, and the terminal may hang up for several minutes on computers that are not enough large-powered. That’s why at first it is necessary to test the work of the Advisor under such conditions in order to prevent hindrances for trading on a live account.

     




    You may download the code of the Expert Advisor here .
  • Read Close 20

    Arrays and technical indicators on them

    Array is a data structure consisting of a group of elements that are accessed by indexing. It is often necessary to use arrays in Expert Advisors and indicators. For example if in an indicator it is required to use for calculations more than eight indicator buffers, arrays defined as price series will help. Or it is required for some reasons to insert the logic of an indicator in the Expert Advisor to calculate the values of the indicator without addressing to the iCustom() function.

    What is the difference between a common array and a price series? This difference appears only because additional functions were introduced in MT4 that can calculate values of some technical indicators on arrays. An indicator buffer doesn’t require additional efforts from the programmer, when a new bar appears the size of the indicator buffer as well as the index of all values are autoincremented. The bar that had an index of 6, now has an index of 7. New data (the closing price Close, Volume etc.) are recorded in the new bar that has an index of zero. Indicators on such buffers are calculated from the last index to the zero one. Such behavior of an array is a price series.

    Contrary to a price series a common array behaves conversely, but only in relation to the functions i…onArray():
    • iMAOnArray()
    • iMomentumOnArray()
    • iStdDevOnArray()
    • iEnvelopesOnArray()
    • iBandsOnArray()
    • iCCIOnArray()
    • iRSIOnArray()


    That’s why when you don’t need to use these functions you may not make a difference between an array and a price series. But if you want to calculate a moving average of an array you will feel the difference at once. This is the same as when you are waiting for the train at the station but numbering of coaches for some reason starts from the end of the train. To illustrate the difference the ArraySample.mq4 script is written. The algorithm is simple: an array of 100 elements is filled and a 4 period moving average of this array is calculated with the help of the iMAOnArray() function, at first for a common array, and then an effort is made to turn the array into a price series and make the output once more.

    The sum+=Array[i] expression means the same that sum=sum+Array[i]. Once the script is executed we open the file ArraySample.csv and we see that the declaration of the array as a price series with the help of ArraySetAsSeries() hasn’t helped, the iMAOnArray() function returns the same values. Though the moving average of the array is calculated correctly. We will modify the script, after declaring the array as price series we will fill it over again with values and then will check it.

    We will open the achieved file ArraySample2.csv in Excel. Now the outputs for the array and for the price series are different. It means that it is necessary to declare an array as a price series before the first call. Let’s make manual check of the price series. At first we find the sum of four cells.

    Then we’ll find the moving average for these 4 cells:

    Next we fill the rest of cells with this formula:

    We can see that the calculation of the four period moving average of our array in the script coincides with the manual calculation in Excel.

    The iMAOnArray() function is well suited to cases when it is required to smooth an array of values, suppose some indicator. For example, watching EURUSD H1, we notice that at the end of the American session the indicator ATR(8) starts declining (8 – the duration of the session in hours). In order to achieve smoother values of the indicator we smooth if by a 5 period moving average. For this we drag a standard indicator Moving Average on the window of the ATR indicator and set as starting values "Previous Indicator’s Data".

    If we don’t want to implement these manipulations each time, we can write a custom indicator, in which the values of the ATR indicator will be smoothed. For example the code of the indicator VolATR.mq4:

    The usage of this indicator substitutes two standard indicators, applied one on another.:

    Similarly we consider the iMomentumOnArray() function. It is needed when it is required to calculate the ratio of some elements of the array to other by some period.

    The check file ArraySample3.xls with formulas is enclosed. An example of how this function is used is the indicator of the simplest calculation of the dollar index SumLogSample.mq4.

    And the last function we will consider is iStdDevOnArray(). In MT4 this function characterizes the measure of data spread around the moving average of these data. This measure of spread in statistics is called standard deviation. I.e. at first a moving average of the array data is calculated (the period and type of the moving average are set by the 3-rd and 4-th parameters), and then the spread of data around this average is calculated. I’ll quote the Help on the standard deviation from MS Excel.

    The script is written so that it would be easier to make manual check in Excel.

    In what cases may we need this function? Suppose we need to choose one of two MTS, a criterion is needed. It may be the measure of spread of the MTS testing results, when an array is filled with profits and losses, and the average transaction and the standard deviation are found. It is logically that where the standard deviation in the array of transactions will be less the lesser volatility is expected. Or another variant. After running a backtest, based on the results of the transactions an approximating line (red) is plotted according to the least squares method. And again we calculate the standard deviation from this line. It is easy to make these calculations in the block deinit() of the Expert Advisor.

    The iEnvelopesOnArray() function calculates the moving average of the array (the average isn’t output) and adds (or subtracts) to this average some percent. We achieve the upper or lower percentage band. That’s why it is not difficult to write a script for this function (you may write it yourself for practicing).

    The iBandsOnArray() function also calculates the average of the array and then adds (or subtracts from it) some number of standard deviations. And again we achieve the upper or lower Bollinger Band. As we have already made sure that calculations of the average and standard deviation of an array are made correctly in MT4, it doesn’t make any sense to check these two functions from the script. They are alike to some extent.

    The iCCIOnArray() and iRSIOnArray() functions are more specific. They are rarer used in custom codes, that’s why I didn’t consider them. The main mistakes of using all these functions on arrays:
    • one forgets to declare the type double for arrays
    • one forgets to declare arrays as price series (indicator buffers don’t require this).
    In all the given examples a simple smoothing is used for simplicity of manual check in Excel. You may choose the way of smoothing by yourself.

    The codes of scripts, indicators and files Microsoft Excel you may download here .
  • Read Close 21

    Work with arrays

    There are two pieces of news – bad and good. The bad news – it is impossible to forecast the market.
    The good news – to earn money you don’t need to forecast the market.
    Bruce Babcock

     

    Many of you must have heard this saying in one or another interpretation. There are supporters and opponents of the efficient market hypothesis. Some consider that the process is random and it is impossible to earn money on markets, other think that prices are predictable and this gives opportunities to make a profit from the market’s behavior.

    In each joke there is a grain of joke. But joke continues. From time to time we can hear that one can make a profit on random movement of price, and there is no need in developing a trading system, but only knowledge of mathematic to the extent of the first courses of University is required. The results of trading systems that work on random quotes are provided, the testing results confirm – it is possible to make a profit on a random market in a random way. I created a table in Excel, which models 100 transactions on prices with increments according to the normal distribution law, and created 7 columns, each of which shows total profit out of 20 series of 100 transactions from preset StopLoss(SL) and TakeProfit(TP).

    We can see that profit when TP/SL=3 proved to be maximum (472), and in case of inverse ratio loss is maximum (-492). You may perform your testing series in order to compare your results with mine. For this you should change settings and write 20 times the value of "Total" in the correspondent cells.

    Perhaps your results will be somewhat different from mine, but what if they prove to be close at least by half? What to do in such a case, to start at once trading following the principle of random opening of transaction with ratio TP/SL=3 of at first make sure whether market prices are normally distributed?

    Normal distributions are considered to be the most important among methods of mathematical statistics, I advise you to read the book by S.V. Bulashev " Statistics for Traders". I will try to explain in my own words what normal distribution is. Let us assume that there are two sportsmen shooters, each of which performs 100 shots at a target. We will measure how far from the centre the bullet deviates. For example in centimeters. Having counted all the 100 shots we will get such a table.

    It is seen that the distribution of hits frequency is bell-shaped with the peak near 0. For normal distributions the center coincides with mathematical expectation (arithmetic mean). Besides the bell can have flat edges or steeper edges. It is characterized by dispersion of random value around the average. The criterion of this dispersion is standard deviation (σ - sigma), which we already know how to find. Out of the two shooters in the general case the better will be the one with the smaller standard deviation.

    One of the important features of standard deviation is the fact that in the interval of 3 sigmas from mathematical expectation there are 99,73% values, in the interval +- 2 sigmas - 95,45 %, and in the interval +- sigmas – 68.27 % . We will try to study the distribution of closing prices for any instrument with the help of a script.

    The script will fill in the array CloseArray[] with values Close[i]-Close[i+N], where N I called shift. I.e. we will watch how distribution of increments will change depending on N, with N = from 1 to 10. Each such filling of the array I called selection, each selection will have such characteristics as average value (mathematical expectation), standard deviation (sigma), maximum and minimum value, and percentage of hits in one, two or three sigmas. These characteristics will be output in a *.csv file, so that later we can construct diagrams of distributions using them.

    For writing the main features array Stat[8] is used:

    Thus we can process values Close[i]-Close[i+1] on time frame D1. So to process Close[i]-Close[i+2] we have either to create a new array Stat2[] or to clear the values of array Stat[] and fill them over again. Such a way is not universal, and for such cases multidimensional arrays are used. We’d rather declare array Stat[10][8], where we will save for each shift from 1 to 10 their own characteristics.

    Now we have a two-dimensional array. But there is one more moment – perhaps we will have to get statistics not only for daily bars (D1), but for other time frames as well. We will add universality with the help of one more dimension:

    Arrays in MQL-4 can be no more than four-dimensional, and that is enough for most tasks. Now we can process in the cycle 7 time frames (from PERIOD_M1 to PERIOD_D1) and 10 shifts. As indexes in the first dimension can take on values from 0 to 6, I created a function that returns according to the number of time frame the value of time frame in minutes, so that this can be used in standard functions.

    The formulas themselves are very simple, but the code is a little bit long.




    reference http:www.statsoft.ru/home/portal/glossary/GlossaryTwo/N/NormalDistribution.htm
    #property copyright "MetaQuotes"

    #property link "http://www.alpari-idc.ru/ru/experts/articles/"

    int FileHandle;



    int init()
    {

    string FileName;

    Print("Execute init()");


    // form the name of file, for example ,
    FileName=Symbol()+"CloseStat.csv";
    //open the file FileName (we’ll create a handle at it)

    FileHandle=FileOpen(FileName,FILE_WRITE | FILE_CSV,";");

    if (FileHandle<1)

    {
    Print("Не удалось открыть файл, ошибка ",GetLastError());

    return;
    }
    return(0);

    }

    int deinit()

    {

    Print
    ("Execute deinit()");

    //close the file (release the handle, so that the file can be

    //open for editing by other programs)
    if(FileHandle>0) FileClose(FileHandle);

    return(0);

    }

    int start()
    {

    double CloseArray[];

    double Stat[7,10,8]; // 7 time frames, 10 shifts, 8 characteristics

    // 0 – Number of records for the period and shift
    // 1 – Average in points

    // 2 - Max in points
    // 3 - Min in points
    // 4 – Standard deviation in points
    // 5 – percentage of hits in interval +- sigma

    6 - ------ +- 2 sigma
    7 - ------ +- 3 sigma
    string rangeDescription[14];

    double range[13];

    int freequency[14];
    int curPeriod;

    double Summ,Average,StDev,begin;

    int MaxOnArray,MinOnArray;

    int i_period,shift,m,z,t;

    double sigma,sigma_2,sigma_3;

    int BarsPeriod;

    for (i_period=0;i_period<7;i_period++) // run on time frames


    {
    curPeriod=PeriodNumber(i_period);
    BarsPeriod=iBars(NULL,curPeriod);



    for (shift=1;shift<=10;shift++) // run on shifts
    {


    ArrayResize(CloseArray,BarsPeriod-shift);
    ArraySetAsSeries(CloseArray,true);
    MaxOnArray=-10000;


    MinOnArray=10000;
    Summ=0.0;
    StDev=0.0;
    for (m=0;m<BarsPeriod-shift;m++) // run on bars


    {
    CloseArray[m]=(iClose(NULL,curPeriod,m)-iClose(NULL,curPeriod,m+shift))/Point;


    if (CloseArray[m]>MaxOnArray) MaxOnArray=CloseArray[m];


    if (CloseArray[m]<MinOnArray) MinOnArray=CloseArray[m];


    Summ+=CloseArray[m];
    StDev+=MathPow(CloseArray[m],2);


    } // run on bars

    StDev=StDev/(BarsPeriod-shift); // quadratic mean
    Average=Summ/(BarsPeriod-shift);// simple average


    StDev=MathPow(StDev-MathPow(Average,2),0.5); // standard deviation sigma



    Average=NormalizeDouble(Average,1);
    StDev=NormalizeDouble(StDev,1);


    MaxOnArray=NormalizeDouble(MaxOnArray,1);
    MinOnArray=NormalizeDouble(MinOnArray,1);


    Stat[i_period][shift-1][0]=BarsPeriod-shift;
    Stat[i_period][shift-1][1]=Average;


    Stat[i_period][shift-1][2]=MaxOnArray;
    Stat[i_period][shift-1][3]=MinOnArray;


    Stat[i_period][shift-1][4]=StDev;

    begin=Average-3.0*StDev;


    for(t=0;t<13;t++)
    {
    range[t]=begin;


    begin+=0.5*StDev;
    if (GetLastError()==4002) Print("Имеем выход за пределы массива в
    блоке for (t=1;t<13;t++) range[t]"
    );
    }


    //if (GetLastError()==4002) Print("We have array
    overrun in block for (t=1;t<13;t++) range[t]");

    rangeDescription[0]= "less"
    +DoubleToStr(range
    [
    0],1);


    for (t=1
    ;t<13;t++)
    {
    rangeDescription[t]="от "

    +DoubleToStr(range
    [
    t-1],1
    )+" до "+DoubleToStr

    (range[t],1);

    }

    rangeDescription[13]=
    "more "+DoubleToStr

    (
    range[12],1);

    if (GetLastError
    ()==4002) Print

    ("We have array overrun in block for (t=1;t<13;t++) rangeDescription[t]");

    ArrayInitialize(freequency,0);

    for(m=0;m<BarsPeriod-shift;m++)

    {

    if (CloseArray[m]<=range[0]) freequency[0]++;


    for (z=0;z<12;z++)
    {
    if ((range[z]<CloseArray[m])&&(CloseArray[m]<=range[z+1])) freequency[z+1]++;


    }
    if (range[12]<=CloseArray[m]) freequency[13]++;


    if (GetLastError()==4002) Print("We have an array overrun in block for(m=0;m);
    }
    //Print("Тайм-фрейм ",curPeriod,"M shift ",shift," Average=",Average,


    //" StOff=",StDev," Max=",MaxOnArray," Min=",MinOnArray);

    if (FileWrite(FileHandle,"Тайм-фрейм ",curPeriod,"M shift ",shift," Среднее=",Average,


    " StOff=",StDev," Max=",MaxOnArray," Min=",MinOnArray)<0) Print("Writing in file error ",GetLastError());


    for(z=0;z<14;z++)
    {
    //Print("in range ",rangeDescription[z]," it is found ",freequency[z]," of values");


    FileWrite(FileHandle,z,rangeDescription[z],freequency[z]);
    if (GetLastError()==4002) Print("We have array overrun in block for(z=0;z<14;z++)");
    }



    sigma=freequency[5]+freequency[6]+freequency[7]+freequency[8];

    // number of hits in interval +-1 st.deviation

    sigma_2=sigma+freequency[3]+freequency[4]+freequency[9]+freequency[10];

    // -/- in interval +-2 st. deviations

    sigma_3=sigma_2+freequency[1]+freequency[2]+freequency[11]+freequency[12];

    // -/- in interval +-3 st. deviations

    sigma=NormalizeDouble(100*sigma/(BarsPeriod-shift),1);

    // percentage of hits in the interval from -1 to +1 st. deviations

    sigma_2=NormalizeDouble(100*sigma_2/(BarsPeriod-shift),1);

    // percentage of hits in the interval from -2 to +2

    sigma_3=NormalizeDouble(100*sigma_3/(BarsPeriod-shift),1);

    // percentage of hits in the interval from -3 to +3


    Stat[i_period][shift-1][5]=sigma;
    Stat[i_period][shift-1][6]=sigma_2;


    Stat[i_period][shift-1][7]=sigma_3;

    FileWrite(FileHandle,sigma,"% within the range of standard deviation ");


    FileWrite(FileHandle,sigma_2,"% within the range of two standard deviations ");
    FileWrite(FileHandle,sigma_3,"% within the range of three standard deviations ");



    FileWrite(FileHandle,"---");

    }// run on shifts

    }// run on time frames
    //----
    return(0);


    }


    int PeriodNumber(int number)
    {
    int per_min;


    switch (number)
    {
    case 0: per_min=PERIOD_M1;break;


    case 1: per_min=PERIOD_M5;break;
    case 2: per_min=PERIOD_M15;break;


    case 3: per_min=PERIOD_M30;break;
    case 4: per_min=PERIOD_H1;break;


    case 5: per_min=PERIOD_H4;break;
    default: per_min=PERIOD_D1;break;


    }
    return(per_min);
    }



    I selected blocks for better understanding of the algorithm.

    Please note the numerous usage of the construction.

    The point is that when arrays of dynamic sizes are processed or two arrays of different sizes are processed it easy to make a mistake, which lies in array overrun. For example I declared array
    double Array[100]
    and tried to address to element Array[101](or Array[100]). In this case an error with code 4002 will be generated. It won’t cause a program stoppage but the code of this error will be stored until GetLastError() is called (then the value of the internal variable containing the error code will become equal zero) or until another error takes place. It is this mistake that I have in my script and to find where exactly it takes place I had to trap all the loops. There is no other reliable way to localize the place of mistake.

    After the script is launched file SymbolNameCloseDist.csv will be created. We open it, select necessary data according to distribution frequencies and press "Graph Wizard" button.

    We can see that the check confirms correctness of the script’s work – the sum of selected cells is equal to the number of values with preset time frame and shift.

    We choose the type of diagram and press "Next".

    Everything suits us we press again "Next".

    If you wish – we fill in other fields and press again "Next". The last dialog window.

     

    We press "Ready" and place the diagram window more convenient.

    Now we have the chart of increment distribution for EURUSD M15. We can see that in 10 bars with a probability of 79% the price will be not further than 23 points (1 sigma ) from the current price, with a probability of 94.4% not further than 45 points (2 sigmas) from the current price, and with a probability of 98.2% not further than 67 points (3 sigmas). You should make yourself a conclusion whether this chart corresponds to the normal distribution chart.

    The script is correct, but we can refine it using technical indicators on arrays. We will call the new script CloseDist2.mq4 . Besides I introduced a new function – ArrayCopySeries(). Please note that there is no need to set the size of array temp, in which allegedly closing prices are copied.

    In fact with the help of this function we get access to the time-series of closing prices (MODE_CLOSE), though we address to array temp. In case of address to the data of another time frame or symbol a situation is possible when the history of quotes is absent, in this case mistake 4066 is generated. In the second script processing of error 4006 is added, but you should note that this example is wrong:

    The point is that error 4066 is generated only once when the history is updated, for the second time it may appear only on another time frame or symbol.

    Comparative analysis of slightly different scripts, implementing the same task, will help to understand better the functions that are used. The training scripts themselves I hope will be also useful. You may download scripts and files Microsoft Excel here .

     


  • Read Close 22

    The tester

    … an apparent phenomenon that I've observed many times over the years, but for which I have no hard evidence: The majority of people trade worse than a purely random trader would.
    William Eckhardt (Jack D. Schwager The New Market Wizards)

    We know the main trading functions (except OrderModify()), we have some thoughts about the simplest trading system оn EURUSD, based on the price increment in the normal process simulation, we only have to check it. For this purpose we will write an Expert Advisor (Random Trade.mq4), that will open transactions at random in random direction with rigid parameters StopLoss and TakeProfit.

    We will set the external parameter int StDev, that will mean a standard deviation in points. Our StopLoss will be equal to this parameter with a certain coefficient, which we will change, we will call it SL_K. Consequently TakeProfit will be also calculated as SL_K *StDev . That’s why such parameters as SL_K and TP_K will be also external (changeable at the discretion of the user).

    For modeling a random value in MQL-4 there is such function as MathRand(). In order to get a random value within the interval from 0 to 1, we will have to divide its value by 32767. If the result is more than 0.5 – we buy at market price, if it is less than 0.5 – we sell. In principle the Advisor is ready.

    We start the process of creating an Advisor with the help of "Expert Advisor Creation Wizard"

    We write a simple function GetOrder(), and the Advisor is ready. Please note that the levels StopLoss(SL) and TakePofit(TP) for the order we open are previously normalized. As coefficients for calculating these levels won’t be necessarily integer, which means that the calculated levels themselves may contain more decimal places than Digits, it is better to round these prices to the needed value, otherwise the trading server may disallow the instruction to open an order. Normalization is always required when the levels of prices are calculated and it is desirable when we simply take prices from the chart. Also there is the nonzero parameter Slippage in the order, it won’t influence testing, but for trading online it is better to set it, in order not to receive refusals from the server for the reason of price deterioration in conditions of changing market when you open or close positions.

    And of course we shouldn’t forget that we buy at Ask prices, and sell at Bid prices. As a comment we write a random number Random, on the base of which the decision about the trade direction was made. Partly it was done for an example, partly to check the correctness of the algorithm. We compile the Advisor, open in the terminal the tester panel (Ctrl+R), choose our Advisor, the needed symbol and time frame. We choose the way of testing "Open prices (quick method on completed bars)". It may happen that in the picklist of symbols there won’t be the one we need. Then it is necessary to grab the needed symbol by the mouse and drop it on the tester panel (method “Drag & Drop”).

    But on closer view there is some artificiality, inaccuracy in it. If an order is opened, a new order won’t open until the previous one closes with profit or loss, but once the order closes, a new random order opens. We press the button "Start", after the testing is over we open the tab "Results" and we can see the confirmation:

    In real situations it isn’t so, neither when you trade manually, nor when your use an Expert Advisor. There is also some delay between closing of one order and opening of another. We will improve our model of random trading – we will add lagging between these two events. If this lagging is rigid we won’t realize the random trading model in full, that’s why I suggest making the pause between two orders random as well. Suppose, that there are in average 20 periods between closing of one order and opening of another while trading on hourly time frames.

    Consequently we need to receive somehow information about the time of the last order closing. For this we will declare two new variables : lastTicket (the ticket number of the last order) and lastCloseTime (the time of the last closing). In order not to loose these values in the intervals between calls of the start() function, we will declare them on the global level. Also we will add an external parameter periodTrade, its value will guarantee the maximum time between two orders. Now our Advisor RandomTrade-2 looks like that:

    A lot of commented outputs to log (Print()) are intentionally added in the Advisor, they may be uncommented when needed for better understanding. Now in case of successful opening of an order its ticket is remembered. Later in the GetOrder() function the time interval between the last closing and the current time is compared with the set period in bars periodTrade. The expression (CurTime()-lastCloseTime)/( periodTrade*Period()*60.0) at first will be small, but it will increase with each new bar in the tester, and a random number within the interval from 0 to 1 at first will have the minimum possibility to be less than this expression, but after periodTrade bars the possibility of opening a new order will become equal to 100%.
    We launch the new variant of the Advisor and we can see that the situation has improved. Now a pause between transactions has appeared.

    But if we change the model "Open prices" for the model "All ticks" or "Control points", the picture will change. Here are "Results" for "Control points"

    And here for "All ticks"

    You may run a test on your computer, the number and order of transactions will be different from mine, but the general conclusion will be the same – the more detailed simulation is the more transactions we get. We received a visual proof that this simple Advisor is written incorrectly – when the mode of modeling is changed the test results change considerably. If your Advisor written according to some strategy (unlike mine written based on randomness) behaves in the same way – it means there is a serious defect in the algorithm of the Advisor. In order to achieve results relatively insensitive of the modeling mode, you need to :
    • upload as detailed history as possible for the tested period started from minutes and
    • in an explicit form divide the events EveryBar and EveryTick, as in the Advisor BlackBox.mq4.
    Let these events (functions) will be called and defined differently from mine, but you must distinguish them clearly.

    It is possible to contradict here - "Of course the number of transactions on different models will be different, as the transactions are random and they mustn’t be repeated". In fact our Advisor has the last weak point – it is not really random, though we used the MathRand() function. Each time when the Advisor is tested in the tester one and the same sequence of pseudo-random numbers is generated and that’s why we receive one and the same sequence of transactions regardless of the simulation mode. Check it by yourself.

    In order to achieve a new sequence of random numbers it is necessary to initialize the random number generator with the help of the MathSrand() function. If we use MathSrand(1), we will get one sequence of random number, if we use MathSrand(2) it will be different. But in order to achieve a new series of transactions each time the Advisor is tested, the sequences of transactions must be different. The simplest way out is to use the construction MathSrand(GetTickCount()). We will insert it in the block init() and the problem is solved, as the function GetTickCount() will return a new value each time it is called. We will call the final variant RandomTrade-3.mq4.

    Now we can optimize our Advisor, i.e. search the best parameters for getting profit, or in other words – is the hypothesis confirmed that in case of the ratio TP/SL=3 we will not only receive profit but the profit will be maximum. We press the button "Expert Advisor Properties" , choose the tab "Input parameters" and mark as shown in the picture.

    Don’t forget to make sure that on the tab "Testing" the check-bock "Genetic algorithm" isn’t marked – we don’t need it now. We won’t change the initial deposit - $10000 , buying and selling are allowed (Ppositions: Long&Short).

    We mark the check-box "Optimization", model "Open prices" on the tester panel and get something like that.

    We run a test once again and get a new chart of results optimization.

    We can see that on the same parameters each time we receive a new series of random transactions, i.e. after all we have achieved randomness. But a new question arises – how we can determine – if some parameters are better than other. In this case the optimization of the Advisor doesn’t give an unambiguous answer what to do. If we make 100 runs in the tester for each parameter we will be able to compare somehow the testing results, achieved for different parameters. We will be able to average profit achieved in the result of 100 runs, or the number of transactions, or profitability.

    The optimization results are easy to sort out by in ascending or descending order by pressing on the column name. If we double click the line with the needed parameter of the Advisor we will get again to the tab "Settings", and these parameters will be used when the Advisor is tested.

    We run the Advisor with these parameters and we get the following chart.

    We can see that during testing the Balance curve went up and down, but profit of $3963.66 wasn’t detected. The number of transactions and other testing characteristics don’t coincide as well.

    We wanted random to achieve random transactions – we achieved them. But our hypothesis of possibility to earn on random entries having a certain ratio between TP/SL hasn’t been checked yet. We have the last way to do it - we will add an empty variable (I called it Balk), from which nothing depends in the algorithm and we’ll do optimization. I put this new external variable on the first place in the external parameters section so that the run will start with sorting its values. We will call this variant of the Advisor RandomTrade-4, will compile it and set the optimization parameters.

    The parameter SL_K I excluded in order to accelerate the process (as 3000 runs will be required). When I tested it on hourly time frames it took me 13 minutes.

    We can see that when the value of StDev (consequently of StopLoss as well) is small, the Advisor heavy and stable unprofitability is observed, that practically doesn’t depend on the value TakeProfit. The chart of optimization may be seen in the form of two-dimensional plane, for this we press the right mouse button and set this mode.

    We set values for X axis

    and for Y axis

    Thus it is very convenient to estimate visually the influence of the Advisor parameters on the results of testing. The files of Expert Advisors are here .
  • Read Close 23

    Statistics ( the #include command)

    People believe you can make tons of money with little work. They think you can make 100 percent a year doing a little bit of research on the weekends. That's ridiculous.
    Monroe Trout. Chapter The Best Return That Low Risk Can Buy (Jack D. Schwager The New Market Wizards)


    After an Expert has been tested on history data the tester in MT4 allows to save the Expert testing results in the html format, including the Balance and Equity chart and the list of performed trades. The list contains the date and time of opening and closing of each position, the volume, the type (long or short) and the result (profit or loss) of each transaction. Besides, various calculated parameters for estimation of the Expert testing results are shown in the report. In order to understand better how Expert Advisors work it is recommended to read the following articles on the website of the developers : It is possible to copy the list of transactions from the tab Results into Microsoft Excel and make an additional analysis (to make independent calculation of your results). But even in such case the statistics won’t be full, besides it is impossible to foresee in the tester variants for all occasions. For example we need to receive information about the field Commentary in orders, the lifetime of a position (between opening and closing) in minutes.

    Let’s also output profit in points and in the deposit currency on the basis of 0.1 lot (to avoid Money Management influence). Just to be safe we will output the hour and the day of the week of opening/closing of orders. Additionally we will distinguish orders closed by stop, by take profit and by market. As you see the list is rather big.

    Where shall we perform processing of the history of orders? Of course in the procedure that is executed last, i.e. in the deinit() function. After we have run a backtest of the Expert Advisor we have the history of transactions which we can process in a loop. We will use as a testing ground our Expert Advisor RandomTrade4. We will create two arrays double trades[] and cancells[] (for cancelled orders), in which we will write attributes of each order. We will write line values in the array string commentsTrades[] .

    I didn’t fill the array of cancelled orders; everything is done in a similar manner for them:

    Explanations are required only for the usage of the StringSubstr() function. The point is that the trading server or tester adds "[sl]" to the comment of an order at closing by stop. If you had an order with the comment "test", at closing by stop the comment will turn into "test[sl]". Thus the initial comment is written in the commentsTrades[cnt][0] , in index [1] we write "[sl]", "[tp]" or ""(empty line), if the order was closed by market. In index [2] we write the type of the order – "buy" or "sell". We will call the modified Expert Advisor RandomTrade+Statistika.mq4. Indexes 9, 10 and 12I are not processed in the array trades[]. They may be necessary for us in future.

    Now we only have to decide what we will analyze. Though our Expert doesn’t convey any specific sense, but we will try to extract from it everything we can. Usually an Expert Advisor is written on the basis of some indicator signals and then attempts are made to improve it somehow ( by means of optimization) in order to increase profit at trading on history. We will try the reverse way – we’ll to see the regularity in the indicator readings through the array of random transactions. We’ll take the classic indicator MACD(12,26,9) and we will write in the order’s comment one, if the value of the main line is above zero, or minus one if it is below zero.

    Then we compile the code of the Expert, launch it on EURUSD H1 with the parameter StDev=50 and open a csv-file in Excel. We will try to find the regularity – connection between the value of MACD and the trade direction. After sorting by the type "buy" and field "Comment"=-1 we see that these trades gave us a profit of $1266.

    Now let’s check the reversed situation, we set "Comment"=1 – and get loss of $200.

    We change the order type for "sell" and "Comment" for 1. Again we get loss, though it is small - $250.



    It is interesting that over the last year (on this interval testing was performed) the EURUSD has been declining, while long positions under conditions that StopLoss=50 and TakeProfit=150 points are profitable. Is it possible that we by chance have revealed some regularity in the behaviour of this pair? To check this we will modify slightly the algorithm of our expert. The time of opening positions will be still random, while the direction of opening will be set rigidly. If MACD is above zero we sell, if it is below zero we buy. We will call this variant RandomTtade+Statistika2.mq4 .

    To check our supposition we will make an optimization, if it shows that random transactions with such parameters are profitable in average – it means we are on the right way. The optimization parameters:
    • Balk – from 1 to 100 with an increment equal 1 (100 runs for each combination of value parameters)
    • TP_K – from 1 to 3.
    • StDev – from 10 to 100 with an increment equal 5.
    The optimization took a little less than two hours, I copied the Optimization results and saved them in an Excel file.

    But the most important thing is that on the optimization chart we see that there is a whole series of profitable trades at StDev=65 and TP_K=2. The best results we achieved not when StDev=50, but not far from this value.

    The most interesting thing is that if TakeProfit is three times bigger than stop (TP_K=3) – profit becomes less. This is one more surprise. Let’s set the optimal parameters and run a backtest. And then look at the chart:

    One more run and again we look at the chart:

    One more and again we see profit:

    The testing report for one of the runs in the form of an html-file is attached.

    Can it be so simple? It is hard to believe. We look thoroughly through the trades and notice that as soon as one transaction is the next one is opened. Somehow at such modification of the code we lost the main advantage of our expert – the randomness of trades. Or not? Let’s perform a simple check; at first we will test our expert in the tester with optimal parameters in the LongOnly mode,

    And then in the ShortOnly mode:

    We were taught: the order in which the terms are added does not matter. But here we don’t have profit moreover the number of transactions doesn’t correspond – separately we had approximately 300 long and 300 short positions, in all approximately 600 transactions, but in the Long&Short we had approximately 400 transactions. We may state with confidence a hidden fit of the expert advisor was performed. To disapprove this you need as a minimum to change the code so that a random pause will appear between transactions and as a maximum to teach the expert to distinguish the direction of the opened position.

    Perhaps it will be necessary to make the same analysis for other expert advisors. In such case we can insert a part of the code of this expert in the deinit() function of a new expert. But there is another way – to use the #include directives. The #include directive (include, add) is valid only when the code is compiled. When the compiler meets this command it tries to find the indicated file in the folder "C:Program FilesMetaTrader 4expertsinclude" and insert the whole code from this file into the expert’s code.

    We will demonstrate this with an example. At first we will take out the whole code in the deinit() block in a separate function HistoryCalculate(). Now in the block deinit() there is only the call of this function:

    We save this variant as RandonTrade+Statistika3.mq4. If the compilation is successful we proceed to the creation of the header file Statistika.mqh. We launch Expert Advisor Creation Wizard; choose the type "Header file":

    We set the name of the file, the name of the author and link:

    We have got the template of a header file which contains models of calling the dynamic library dll, the compiled file mq4 – ex4 and simply definitions of the constant (#define).

    We copy the definition of our function HistoryCalculate(), at that we don’t forget to transfer the declarations of variables StatBars and stat_FileHandle, and of the statistics file name TradesFileName:

    In our expert we delete the HistoryCalculate() function and variables StatBars, stat_FileHandle, TradesFileName , and add the line #include :

    We save the expert with the name RandonTrade+Statistika4.mq4 and the header file. The convenience of this approach lies in the fact that if you have debugged some code, which you would like to use repeatedly in you developments, then you can easily insert in in your new code at the compilation stage. That’s why if later you require to change the code of the header file (*.mqh), you mustn’t forget of the necessity of recompiling all the programs that use this file.

    Ultimately we will insert the definition of the unnecessary function Placebo(), which we won’t call, into the file Statistika.mqh.

    We save the file and compile the expert over again. We receive the following message:

    The compiler has determined that this function isn’t used and has deleted it from the executed file. That’s why one header file may contain a big library of various functions for all occasions, it doesn’t influence the size of the final executed file, only necessary functions will be used. The files of the Expert Advisors are here .

     


  • Read Close 24

    Trailing Stop Technique

    First rule of business: protect your investment.
    Etiquette Of The Banker, 1775


    Until now we used in the Expert Advisor only orders with rigidly set StopLoss and TakeProfit levels. Thus having opened a long position we wail until one of the two levels works out – either we get profit, or we get loss. Tertium non datur. But many traders consider the system of exiting a trade to be more important than the system of entering into a position in a trading system. The most basic technique for establishing an appropriate exit point is the (Trailing Stop) technique. In this case the stop-loss order is adjusted continually based on fluctuations in the market price, and in case of a sudden reverse it allows to receive a part of paper (unrealized) profit, instead of getting loss if the initial stop-loss order was executed.

    Before we write the trailing stop function we will get rid of all the lacks of our expert advisor which trades on the basis of random signals. For this we will have to write it practically over again (we will call it RTS.mq4 – Random Trade + Statistika).

    I used empty functions, i.e. functions are declared, but nothing is calculated in them, they return empty values. It can be convenient at writing complicated systems if you have a complete algorithm. The expert advisor in such form is compiled without errors (the isNewBar() function isn’t seen simply). Now we can fill each empty function with a code and compile our expert for checking changes that were made for rude mistakes.

    Schematically I showed it as follows:

    The block number 1 has been realized by means of checking a new bar, if isNewBar() returns true (a new bar appeared) , then at first availability of a signal for opening a long position is checked (SignalExist(OP_BUY)), and if there is such signal, an attempt to open a buy order is made (GetOrder(OP_BUY)). Similarly checking for opening a short position is performed. Now signal for long and short positions are divided and don’t influence each other, we have got rid of the lack in the previous version.

    Please note that I used reserved words from MQL-4 as parameters (OP_BUY and OP_SELL). This makes it easier to understand the code for you and for those who will read it. If I used instead of them concrete values, you would have to open the Help to remember what the 0 parameter means at calling the GetOrder function(0) .

    Besides later the consideration of nuances of opening positions OP_BUY and OP_SELL will allow us to optimize the expert advisor in the LongOnly mode and the ShortOnly mode separately.

    The block number 2 consists of a simple call of the EveryTick() function, which is also empty so far. In the previous advisor in the deinit() function we used the call of the HistoryCalculate() function, I’ve modified the call a little, in order to calculate statistics only in the tester.

    While working on-line this function isn’t called now, but there is also optimization. While performing an optimization we don’t need it as well. We will add the IsOptimization() checking.

    At first we will write the function of receiving a signal, to calculate the time of the last order closing we added a new function – GetLastCloseTime().

    You may see for yourself that now the calculation of the period of time since the last moment of the order closing is performed more correctly, the period of time between Friday’s evening and Monday’s morning doesn’t include the days off.

    Now we write the GetOrder() function on the basis of the previous version. I have only modified the way of filling a commentary so that we can analyze the price movement for the last 3 bars. I.e. we still open positions against the trend, but we want to find a connection between the result of trade and the direction and amplitude of movement just before opening a position. Later you may write in the commentary any information you are interested in for further analysis.

    The advisor is almost ready. In the previous version we checked the availability of open orders.

    and concluded that this was wrong. But if we don’t check the availability of open positions we won’t have the possibility to control the number of these orders. That’s why we will add an external variable MaxOpenOrder and theAllowedOpenOrder() function (allowability of opening new orders of certain type).

    Now we can test once more our hypothesis that when StopLoss=65 points and TakeProfit=130 points it is necessary to open positions against the readings of the indicator MACD. We will save this version as RTS2.mq4. We will launch again testing on EURUSD H1 and will get one of the results.

    There is no profit, the ratio between profitable and losing trades reflects the ratio between SL and TP. But the number of long and short positions doesn’t depend now on the LongOnly mode and the ShortOnly mode. Now we can write the TrailingStop() function.

    The main function of the trailing stop is to protect paper profit by means of adjusting the StopLoss level in the direction of the open position. If we move the stop loss order too quickly an occasional movement against our position may break us out of the trade, after which the price may go in our direction. And we move the stop loss order too slowly then an unfavorable reversal may eat up the most part of our profit. As you see everything depends on the algorithm. That’s why at first we set forth some idea and then we try to program it.

    There are a lot of algorithms of trailing stops, I will consider only two of them. I called them for myself dynamic and static. Dynamic trailing – is moving the protective stop each time the price changes; if the price jumped by 100 points in our direction (suppose upwards for buying), then the protective stop at once will be trailed (upwards) also by 100 points. Static trailing – is moving the stop according to certain criteria that don’t depend on the current price. For example, moving stops on each new bar according certain algorithm. It allows to ignore the price fluctuations inside the bar.

    At first we will realize the dynamic trailing stop.

    Everything is simple in this function and doesn’t require any additional description. If TS=50, the current stop will be at 1.2643.

    We will add the call of trailing in the call for each new bar. In this case modification of orders will be always performed identically (when a new bar appears), independent of the testing mode.

    I have this version of the expert as RTS+TS.mq4 (TS – Trailing Stop). We compile it and launch it on EURUSD H1.

    Now we can consider the variant of static trailing. We will introduce the notion of risk. Risk is a potential loss that is equal to the distance that the price goes from the opening level to the level of the stop. This is of course incorrectly, but that’s not the point. The idea will be as follows: the level of risk must decrease as the square of time. If from the moment of opening the position X amount of time has elapsed and the risk is equal to Y, then in X*2 of time the risk must reduce fourfold. Roughly speaking we are trying to realize the trailing stop by analogy with the Parabolic indicator.

    Many traders try to realize a trailing stop on expert advisors based on the readings of the Parabolic SAR indicator. But this approach has one disadvantage – for the indicator it’s all the same where you entered into a position. That’s why we will write an algorithm which will really adjust the level of the protective stop on the principle of parabola and will be pegged to our point of entry.

    Suppose at the moment of opening of the order we put the StopLoss at a distance of S points. At that we consider that if we have opened the position in the right direction, then in N bars we will be flat, i.e. no profit or loss, (the level of the StopLoss can be moved to the level of the opening of the order). From school physics we remember that in case of parabolic motion the distance S=(a*t^2)/2+V*t+C, where а is acceleration, V- is initial velocity, in our case it is equal to zero, and С – initial displacement, it is equal to the value of the stop with minus sign. Then S= (a*t^2)/2 or a=2*S/t^2.

    If our initial stop is equal to 50 points, while the position must become flat in 5 bars, then a= (2*50)/5^2=100/25=4 points/bar^2. In 1 bar the stop will be 4*1/2-50=-48, i.e. at 48 points from the opening price. In 2 bars the stop will be equal to 4*2^2/2-50=-42 . In three bars the stop will be equal 32 points, in four bar – 18 points, in five bars : 4*5^2/2-50=0 – we may place an order to become flat, if by that time we haven’t been closed by the stop order. We have the algorithm; we only have to write the code.

    We change a little the start() function:

    We compile the expert advisor RTS+TS2.mq4 and test it under the same conditions and with the same parameters. Unfortunately this variant of a trailing stop didn’t give us any improvement of results. Perhaps parabolic trailing isn’t an appropriate variant for this strategy. We can change a little the EveryTick() function.

    Then testing on other models ("All ticks", for example) will cardinally change the behavior of the expert. You may experiment yourself. All the variants of expert advisors are here .
  • Read Close 25

    It catches your eye, but the code can't catch it

    A completed MTS in the form of a program in MQL-4 – is the result of laborious work. At first an idea appears from observations, and then this idea is checked with the help of manual testing on historical data. The entry date and price, and the reason of exit are written down, profits and losses are counted. If manual testing confirmed potential profitability of the trading approach, it’s time to formalize the algorithm of the trading system.

    But not always one manages to formalize entry signals; often it is a very difficult task. A man can estimate visually the current situation in whole, but he finds difficulty in writing the rules in the algorithmic language. Is MT4 helpless here? There is a partial way out of this situation. If we could prepare signals for the expert advisor (pending orders placing, market entry etc.) manually, while with the help of the tester we would find optimal values for the protective stop, the level of Take Profit, sort out different variants of trailing…

    This variant is not bad, but the prospect of typing manually the date, type and direction of a signal in a file may absolutely discourage you. We will try to resolve the first part of the task (creation of the file of signals) with the help of plotting arrows of different shape and color on the chart. Then we will process all these arrows and will write their coordinates in a file. We open the chart and choose the arrow type.

    We plot the arrow in the needed place and set its color, and then we write the description of the signal (sell limit).

    The next arrow must have the "buy limit" signal and be blue. In all there must be at least 50-60 such arrows… Optimism has withered away. Each arrow requires 8-10 mouse clicks (count yourself). But there is a way to make this process easier a bit, for this we will use a script with an infinite loop. We will call it SmartArrow.mq4 (the idea of MetaQuotes). We create with the help of wizard:



    Until we give the instruction to stop the work the script will do something in the infinite loop (see the help on IsStopped()). This script gives the processor no peace. That’s why first of all we will insert the Sleep(1000) function in the loop. This function frees the processor at least for a second.



    The next step is to study how to process some event. We can’t choose by means of the script the arrow type in the menu and stick it to the cursor, but we may track movement of graphical objects on the chart. We insert the ArrowPosChanged() function.

    One may check the change of location of any graphical object with the help of the ObjectGet(ObjectName, PropertyType) function. Object arrow has only two coordinates – first coordinates of time and price. Besides we will change the arrow’s color and type.

    The fact of changing the coordinate may be find out by comparing the value of the coordinate at the current moment with its value at some previous moment. That’s why we will introduce global variables curPrice, prevPrice, сurrTime and prevTime. We’ve got the following code:

    We added checking, if the arrow with the name "SmartArrow" isn’t on the chart the script ends its work. Checking of the first call of the function doesn’t influence the work of the script, makes the algorithm more correct – when the ArrowPosChanged() function is called for the first time there is no false return of the value true. We launch the script for checking:

    Now we will give to the script the arrow with the name "SmartArrow" to work with it. For this we will simply rename any arrow on the chart.

    We launch the script and receive a message (I added output Comment()).

    We save this variant of the script and continue to work on it. First of all at launching the script we will create our arrow "SmartArrow" automatically and will set all attributes for it. For this we will add the init() function.

    We write signals for the expert advisor, the expert advisor’s testing starts no sooner than on the hundredth bar from the end, that’s why we have created our arrow almost at the very end of the chart. We delete our script (it is still looping on the chart, do you remember?), we compile SmartArrow2.mq4 and launch it on the chart

    The launch went off successfully, the tab "Experts" confirms, the block of checking the first call of the function works.

    We scroll the chart to the very end, looking for the green circle, but fail to find it … What is wrong, why doesn’t it work? With the help of the hot keys Ctrl+B we look through the list of objects and find our arrow – it is at the same place where it was!

    Here we remember that almost any function in MT4 returns the error’s code, if we add processing of this error perhaps we will understand what is wrong.

    We compile, launch and look in the tab "Experts": there is no error. The error is in our algorithm, the situation when there is no arrow on the chart at launching the script is processed, while presence of this arrow glitches. We will modify the code once again init():

    Now the code works and we find the green arrow where it should be. We only have to write processing of such event as "Arrow moving". Let’s agree that if the arrow appears to be below the Low bar , we turn it into the arrow "Up"(code 241) of the blue color, if it is above the High bar, then we turn it into the arrow "Down" (code 242) of the red color (we mark the entry of signals limit). You may choose any other code for arrows; they are given in the help menu of ME:

    If the arrow is between High and Low – we simply swear because of the awkward manipulation of the arrow. At the same time we don’t forget to output in comments the coordinates of the arrow in terms of time/price.

    The script works, only sometimes it doesn’t manage to change the parameters of the removed arrow. It occurs in case if at the moment of changing the user holds the arrow. We will add processing of this error (i.e. a situation when parameters of a graphical object must be changed, but they aren’t). You may see the code of this error processing in SmartArrow2.mq4. We perform a compilation and launch the script, we move but don’t release the arrow and watch the output in the tab "Experts".

    We look into the “Error codes” and find out that “Object doesn’t exist”. It means that the object isn’t available for the program as it is used by the user and that’s why such a mistake is returned. The reason is clear, now we have to think out how to resolve such an exception case. We could launch the loop of setting the needed parameter in case the error 4202 is returned, but it is always possible to find a simpler decision. You may see it in SmartArrow3.mq4.

    At last we have overcome visual errors, now we have to get the file of signals. The most natural decision is to use arrays. Each time when a change of the arrow’s coordinates is determined the parameters of the arrow are changed. At this moment we could write the following parameters in an array: time, price and type/code of the arrow (up down). We declare an array Arrows[1000][3]. A thousand of arrows must be sufficient for us. What type of array do we need? The time and code of the arrow are of the int type, while the price is of the double type. We will consider both variants.

    The variant with the double type is simple:

    The variant with the integer array is slightly more complicated. We have to reduce the price with Digits decimal places to an integer. There are two possible variants:

    At last we went through all the points/signals. Now we have to finish the work of script and plot all the stored arrows. The most suitable way is to do it in deinit().

    After the script is deleted all our arrows will be plotted on the chart. The first part – plotting of arrows on the chart – is done. The final variant is saved as SmartArrow5.mq4. All the variants of the scripts are here
  • Read Close 26

    Reading from the file

    Limit losses and let your profit grow


    Why haven’t we at once created the file of signals in our script (at its completion in deinit())? There are two reasons for this:
    • our signals are detected only when the arrow SmartArrow moves on the chart, and as signals can be rather rare, not always we can “jump” from one signal to another while scrolling the chart. That’s why we have to apply the hop, step and jump technique – i.e. to make an intermediate stop (setting an extra signal). If such a function were built in the terminal MT4 we could put marks on the chart with one click.
    • if it is required to plot too many signals for testing, apparently there will be pauses between working sessions with the script. We can create several *.csv files and join them with the help of Excel.
    Let’s create a separate script for recording signals in a file. We adjust the location of arrows on the chart, delete unnecessary (induced extra signals) and launch the script ArrowsToFile.mq4. To write this script I copied ready parts of the code from WriteFile.mq4 , only having changed a little the init() function.

    The code for recording the function in a file isn’t difficult; please note that before recording two barrage checks for the object type and name are performed.

    To test the script we will add a trend line with the name SmartArrow555 on the chart.

    We launch the script on the chart; my 12 arrows were processed.

    We can see that there appeared to be three objects on the chart which were not arrows, also we see that our arrows were processed (sorted) by name, and not by time. In the future we may need the operation of sorting. We open the file in Excel:

    It is so, signals are listed not quite in chronological order. Now we can send our file to a trader we know by email, or upload it onto a forum. But besides, there is another remarkable possibility in MT4 which other programs don’t have. You may explain arbitrarily long in written form or by phone what you want to show somebody, but seeing is believing. MT4 gives such a possibility with the help of templates. We call the menu on the chart with the help the right mouse button and save the chart under a wanted name.

    The saved template we will find in one of the folders in MT4:

    We send this file to another person who places it into the same folder on his computer, opens the template (signals.tpl) and sees in his terminal the full copy of your chart. At that we are over with recording signals in a file, and now we have to learn how to read these data from our file.

    If there is an operation of recording data in a file, there must be the opposite operation – reading from the file. Everything is clear with arrays, we can always get the size of the array and arrange the for() loop to get values of the array elements by the element’s index. But when we open a file we usually don’t know in advance how many lines or values are recorded in it. That’s why some stop-signal is required to break the cycle of reading from the file operations. We refer to the help section and see:

    The FileIsEnding() function is the most suitable for ending reading from the file. We write a simple script to acquire experience; I set rigidly the name of the file for reading signals.

    If it were an indicator or expert advisor this code would cause a hang-up of the terminal! Note: an infinite loop is allowed only in scripts. We have got an infinite loop as we can’t reach the end. An operation of “scrolling” the file is required, the function of reading from a text file FileReadString() is just the one that must provide it. I didn’t know how it worked and decided to check it using the trial and error method.

    The file of signals is small, that’s why we must see everything in the tab "Expert". In Excel I counted 13 lines and 52 values (13*4). I launch the script on any chart and I see the log:

    We can see that the expected values differ from the printed values by one. It means that the script makes a blank cycle before it determines the end of the line and the end of the file. Knowing of this will help us to fill the array of signals in the expert advisor. We will save the script as ReadFile.mq4.

    We won’t write an expert advisor for testing manual signals, we will take RTS+TS3.mg4, will name it CutTheLosses.mq4 and will redefine the init() function. We will make the SignalExist() function empty so far.

    If we know the structure of a text file it is not difficult to read it. Now we have the array of signals and we can proceed to testing of the expert advisor. For this we have to teach the SignalExist() function to retrieve necessary signals from Signals[]. Let’s use a simple algorithm – at each moment of time we run through all the array and compare the current time Time[0] with the time in Signals[i][0], if the values coincide we check the type of the signal and give the answer according to it.

    We will change a little the GetOrder() function: we will delete the random component, we will leave the entry in the order comment untouched – we may need it in the future.

    Please note we insert an additional checking for the order type into the else construction. If sometime we transfer as a parameter OP_BUYLIMIT or OP_SELL_STOP in this function, it will prevent us in the future from a mistaken opening of a sell position. (OP_SELL).

    The advisor is ready, but if we launch it in the tester on EURUSD H1 (from where we took signals), nothing will come of it. We look into the tab "Log" in the tester and we see the following:

    The error code (4103) confirms that it is impossible to open the file. The point is that all file operations in the tester are performed in its own folder, that’s why we will copy our file in the folder /tester/files.

    Now testing of the expert advisor by the signals from the file is performed without mistakes. Moreover testing shows profit! The exotic parabolic trailing stop proves to have right to life in case of certain signals.

    Now even a person who has never written a line of a code (but has read this article), can create his own file of signals, take an advisor of such class, and having sorted different systems of exit (or trailing), check his new system of entries. And if it is difficult to formalize the system of entries, perhaps it will be the only way (and not the worst, in my opinion).
    All the files that were used are available here
  • Read Close 27

    Global Variables

    There is only one disadvantage in our advisor. In block init() there is a procedure of file opening and reading its data. In case of optimisation two problems occur:
    • speed of optimisation decreases as reading from files is much slower than reading from the operative memory;
    • multiple operations of files opening/close are undesirable for the hard disk.
    Programs in MQL-IV have global variables declared in the body of the code out of any function. These values are preserved while the program works and they are available from any point (function) of this program. Some other decision is required for strategies optimiser to preserve variables values between programs launch. In the optimiser we start the same program-advisor several times and sometimes it is required to remember the state of some variables between some separate launches.

    For such purposes there are some special variables, Global Variables. Unlike global variables on the level of a code/program, these variables are global on the level of the terminal. Thus, we can create some global variable in the script and set its value (GlobalVariableSet()), and then check its value in the indicator or advisor (GlobalVariableGet()).

    Unlike other types of variables these global variables are kept within 4 weeks when the terminal is closed. Let's suppose that our advisor should open only a position a day. If the fact of opening a position is kept in some common variable then when the terminal is closed or supply is off the value of this variable (let's call it TradeOpen=true) is not preserved. Then when the terminal is launched again variable TradeOpen will take the default value (false). And the advisor will open a new position automatically, which is forbidden by the algorithm. Usage of a global variable solves this problem.

    Let's create a script to understand how the method of global variables usage works. This script will create a global arrays of values. It is important: global variables can have only type double. There is no possibility to create an array of global variables, though we can solve this problem easily. Functions of global variables check and assignment use the name of this variable, set in the text format. That is why we may set the value for the array by the index as Array_name+index.

    Let's name the script as GlobalArray.mq4. Instead of the global array we will create the array of global variables:

    The code shows a typical work with the global variables, at first we check the presence of the required variable (GlobalVariableCheck(Variable_name)), then we set its value. Let's run the script in any chart, press F3 and see a window with a list of global variables.

    There are three columns: name of the variable, its value and the date of its creation. Now we should adapt it for the purposes of our advisor. Signals from the file should be changed into clear global variables. The simplest way is to code the time of the signal in the name of the global variable and the type of the signal in the value of its variable. Then on each new bar in the advisor we will check the presence of the global variable, containing the bar opening time, if there is such a variable the only thing to do is to check the type of the signal and open a correct position.

    The required functions are given in the Help section of MetaEditor. We do not need function GlobalVariableSetOnCondition() yet, the other functions are clear.

    Let's save the advisor from the previous article as CTL+GV.mq4(Global Variables). Change function init() a bit. At first check the presence of the global variable with name FileOpened (the file has been already open), if there is no such a variable we should open a file, create a number of global variables, which contain signals to open positions.

    We also change a bit the function of the signal detection:



    As you see minimum changes were required. Let's compile the advisor and launch the tester. Once testing is finished press button F3. Newly open global variables appear.



    Now we can optimise our log on, i.e. choose the size of a stop and other parameters of trailing without speeding down of optimisation.

    Important: global variables are the same both for the terminal and the tester. That is why if it is necessary for you to optimise global variables values in the tester when using these global variables in the on-line charts it is better to install two copies of MT4. In one of them testing will be done, the other one will work with your demo or real account.

    There are several variants to use global variables, here are some of them:
    • a) to remember the time of the last signal (of position opening, indicator value);
    • b) to remember the current number of the profitable deals in succession;
    • c) to import some parameters from one advisor to another (e.g. probability of breakout of some important level on the other instrument);
    • d) optimal parameters of the last optimisation in the tester etc.

    The files are available here
  • Read Close 28

    Price Extremums Search

    Some traders use indicator figures, others believe that price is the best indicator. Though it is believed that it is very important to use different levels in trading. Search for the highest and lowest prices within some definite period of time is one of the most popular methods. In MQL IV there are functions Highest() and Lowest() for this purpose.

    Let's suppose that on October 20, 2006 we must find the highest price within the last 26 hours. With this purpose I have added pattern Rectangle to the chart to see the required range of candles.

    Pic. 1

    Pic. 1

    To draw object "Rectangle" in the chart the following should be done: choose Menu "Paste" - "Pattern" - "Rectangle".

    Pic. 2

    Pic. 2

    Mark the upper left corner of the Rectangle with the mouse left button and drag it to the right lower corner of the desirable Rectangle. Moreover, in the object attributes I have set the color of the Rectangle.

    Pic. 3

    Pic. 3

    To get a precise copy of my Rectangle you can set its coordinates:

    Pic. 4

    Pic. 4

    There is no H1 candle with opening time 20.00 of October 19, 2006 in the Rectangle. It was done on purpose to understand the syntax of function Highest(). Help system MetaEditor informs

    Pic. 5

    Pic. 5

    The function corresponds to the requirements of almost every function in MQL IV: at first there is a symbol, to which the function is applied, then there is a period (time frame). Then there is a time series pointer, as we can search not only for the highest High[] but also for the highest Close[] and Low[]. The next parameter, count, specifies the selection where the value is looked for. The last parameter, start, specifies the first bar of the required selection.

    Let's try to write a script which will draw the same object "Rectangle" on the H1 chart of EURUSD. The rectangle covers 26 bars, its high will pass through the maximum price within 26 bars, its bottom will be equal to the minimum price within 26 bars, the right edge will be on the zero bar (bar index is equal to zero), the left edge is on the 26th bar with the index equal to 25 (note it!). With the help of function Highest() we will find the index of the bar with the maximum price High[], for it we write Highest(Symbol(),Period(),MODE_HIGH, 26,0). We have pointed out a search for a bar with the highest High[] within 26 bars from the zero one. Or should I have written 25?

    Let's create script TestHighestLowest.mq4 and have a look at it. At first we will add the required external parameters manually.

    Pic. 6

    Pic. 6

    I have introduced a new variable of type color, with the help of which we will indicate the color of the rectangle, and variable backGround of zero type, we will use it when setting the desirable type of depiction, whether there will be a fill or not. Value firstBar is presented as parameter start for function Highest() and Lowest().

    Let's complete function start():

    Pic. 7

    Pic. 7

    We should find out why the time of the first coordinate time1 is calculated this way and why the opening time firstBar is the time of the second coordinate when a rectangle is created (OBJ_RECTANGLE).

    Moreover, we will also try to analyse creation of a rectangle. In this example we display a massage with an error code in the log.

    The code is ready, let's run it in the chart and change the input parameter a bit:

    Pic. 8

    Pic. 8

    We get the following result:

    Pic. 9

    Pic. 9

    As we see our script shows the highest and the lowest price within the last 26 bars beginning with the zero one. Press Ctrl+B, select our rectangle and see its characteristics:

    Pic. 10

    Pic. 10

    Specially for this example I have chosen a borderline case. Now let's try to execute the script with value countBars=27. Nothing happens when we start it:

    Pic. 11

    Pic. 11

    Error handling is of use, the error code value is given in the Help menu of MetaEditor. Let's delete the rectangle created when the script was run for the first time and run the script again:

    Pic. 12

    Pic. 12

    As we see, price high within 27 bars has increased, which means that we have understood parameters function correctly. Note that if in the required selection there will be two bars with the same extremums, functions Highest() and Lowest() will return the hint to the bar with a larger index, that is will point at the older bar. It may be important for some algorithms.

    There is a disadvantage in our script as we should point out the initial bar (firstBar) as a numeric value. There is a class of tasks for a script to show the index of some necessary bar programmatically. There are special functions for it in MQL-IV PriceOnDropped() and TimeOnDropped():

    Pic. 13

    Pic. 13

    Enter function init() and add variables from the example and also add display of multi-line comment:

    Pic. 14

    Pic. 14

    Combination “slash and n” means line advance due to which multi-line comments are possible. Let's compile the new variant of the script and add it to the chart (the other objects have been deleted):

    Pic. 15

    Pic. 15

    If we execute the script double clicking the mouse, we will get the following:

    Pic. 16

    Pic. 16>

    We can specify how a script is run and coordinates of the point in the chart where it was drawn. Let's use these data and name a new variant of the script as follows TestHighestLowest2.mq4.

    Pic. 17

    Pic. 17

    Let's run a new variant of the script and get the desired result:

    Pic. 18

    Pic. 18

    I have described the four new functions. Scripts are available here

  • Read Close 29

    Some Ways to Build Channels

    A lot of traders use extremums within some definite period of time to create a trading strategy. The most famous ones are Turtles trading system and Price channel. We will consider now how such channels may be created with the help of function iHighest() and iLowest() . Usually such a channel is called PriceChannel, let it be the name of our indicator. Our indicator will show current levels of highs and lows within N period of bars in the shape of lines. Let's call the "Advisors creation wizard" and complete the required fields.

    Pic. 1

    Pic. 1

    In this case I have chosen value 20. This period was used in the Turtles system to trade on breakouts.

    Pic. 2

    Pic. 2

    As we should see the channel levels the indicator will be drawn on the price chart. One line will show the high and the second one – the low.

    Pic. 3

    Pic. 3

    I have completed the code a bit for better understanding and usage. The only thing left is to write the code of buffers filling out with values.

    Pic. 4

    Pic. 4

    Functions iHighest() and iLowest() have replaced the out-dated functions Highest() and Lowest(). The code is very simple, let's compile it and attach it to the chart.

    Pic. 5

    Pic. 5

    In the DataWindow we can see values of any buffer of the indicator for the required date. The only drawback of this indicator (from my point of view) is that there is no visual confirmation of breakout of 20-day range. To correct this drawback we should change the code of the indicator a bit and name it PriceChannel-2.mq4.

    Pic. 6

    Pic. 6

    The new version of the indicator is more vivid.

    Pic. 7

    Pic. 7

    The difference between the two versions is obvious when we compare them.

    Pic. 8

    Pic. 8

    There are a lot of strategies based on breakout of some range.

    Pic. 9

    Pic. 9

    Moreover, trend and counter-trend, based on false breakouts. The drawback of this indicator is the fixed period within which extremums are looked for. There are different ways to adapt indicators of this type for the current market situation. It would be interesting if in the flat period (consolidation) the period of our indicator increased and decreased with the trend developing. Thus, we would be a bit guaranteed against flat false breakout and early exit from the position.

    Indicator ADX() usage is one of such methods. 14-day value is popular for this period. You can notice that in flat its values are small and during a trend they may overcome 40.

    Pic. 10

    Pic. 10

    - Value 150/ADX(14) is used as a variable period for indicator PriceChannel. Let's change the code of our indicator and name it as ADXChannell.mq4.

    Pic. 11

    Pic. 11

    The nature of the price channel has changed.

    Pic. 12

    Pic. 12

    We can make some rules of positions opening by the trend when price touches the opposite side of the channel.
    We have considered only indicators drawing two lines, the channel edges. Sometimes traders add the third line, which is in the mid between the upper and lower edges of the channel. Let's name it the waterline or zero line (or balance line). Let's make the third version of the indicator, PriceChannel3.mq4. We should only add the third buffer and a code to calculate its values.

    Pic. 13

    Pic. 13

    Let's drop the indicator to the chart.

    Pic. 14

    Pic. 14

    In this case we have calculated the middle line of the channel on the basis of its edges. There are indicators built differently: some line is taken (e.g. some moving average) and the edges of the channel are built on its basis following some rules.

    Besides usual averages some difficult algorithms may be used as the mid line, e.g. Kaufman's Adaptive Moving Average, AMA. This indicator is quite familiar to traders. That's why I have decided to take another indicator, FRAMA, as an example. This is not a classical variant of the code, it may be written differently.

    The mid line PriceChannel and FRAMA may be compared.

    Pic. 15

    Pic. 15

    The only advantage is that FRAMA movement is more smooth compared to the mid line PriceChannel. Let's try to build a channel from FRAMA. For this we need a rule following which we will specify the edges of the channel on FRAMA. I think Close price deviation from FRAMA should be used. That is, standard deviation of this distribution.

    So, we have the following:

    • 1. FRAMA indicator value, which we will show
    • 2. Close price deviation from FRAMA, which we will not show
    • 3. the upper edge of the channel, which will be shown
    • 4. the lower edge of the channel, which will be shown. It means that our indicator should depict (draw) three buffers and have one hidden (assisting) buffer for calculation. It is possible in MT4, though you should remember that invisible buffers should be the last ones (have the maximum index).

     

    Pic. 16

    Pic. 16

    To build the edges of the channel at first we should count FRAMA values and deviations, only then in a separate cycle we can calculate the channel edges.

    Pic. 17

    Pic. 17

    As an example I used one of the functions, calculated on the array of values, iStdDevOnArray(). This indicator with values by default looks the following way.

    Pic. 18

    Pic. 18


    The indicators are here .