You are on page 1of 35
Step-By-Step Guide to writing an Expert Advisor in MQL5 for Beginners Samuel Olowoyo | 9 June, 2010 Introduction This article is aimed at beginners who wish to learn how to write simple Expert Advisors in the new MQLS language. We will begin first by defining what we want our EA (Expert advisor) to do, and then move on to how we want the EA to doit. 1. Trading Strategy What our EA will do: = It will monitor a particular indicator, and when a certain condition is met (or certain conditions are met), it will place a trade (either a Short/Sell or Long/Buy), depending on the present condition that has been met, The above is called a trading strategy. Before you can write an EA, you must first develop the strategy that you want to automate into the EA. So in this case, let us modify the above statement so that it reflects the strategy we want to develop into an EA. = We will use an indicator called Moving Average with a period of 8 ( You can choose any period, but for the Purpose of our strategy, we will use 8) = We want our EA to place a Long (Buy) trade when the Moving Average-8 (for the sake of our discussion, | will refer to it as MA-8) is increasing upwards and the price is close above it and it will place a Short (Sell) when MA-8 is decreasing downwards and the price is close below it. = We are also going to use another indicator called Average Directional Movement (ADX) with period 8 also to help us determine whether the market is trending or not. We are doing this because we only want to enter the trade when the market is trending and relax when the market is ranging (that is, not trending). To achieve this, we will only place our trade (Buy or Sell) when above conditions are met and the ADX value is, greater that 22. If ADX is greater that 22 but decreasing, or ADX is less than 22, we will not trade, even though the condition B has been met. = We want to also protect ourselves by setting a Stop loss of 30 pips, and for our Profit target; we will target a profit of 100 pips. = We also want our EA to look for Buy/Sell opportunities only when a new bar has been formed and we will also make sure we open a Buy position if the Buy conditions are met and we do not already have one opened, and open a Sell position when the Sell conditions are met and we do not already have one opened. We have now developed our strategy; it is now time to start writing our code. 2. Writing an Expert Advisor 2.1 MQLS5 Wizard Begin by launching the MetaQuotes Language Editor 5. Then press Ctrl+N or click on the New button on the Menu bar File Edit View Tools ee Images BB Incude EB ndicstore Libraries BS Objects ES Presets EB Projects BS Serpts Pra | Figure 1. Starting a new MQLS document In the MQLS Wizard window, select Expert Advisor and click the "Next” as shown on Fig. 2: _ | Welcome to MALS Wizard Floese select what you mould keto crete: @ Emer Adveone (© Castor ndicater O Sei. Olay (© nelude (ng) Figure 2, Selecting program type In the next window, type the Name you want to give to your EA in the Name box. In this case, | typed My_First_EA. You can then type your name in the Author box and also your website address or email address in the Link box (if you have one). =] General properties of the Expert Advisor Please spect genera propettes ofthe Expert Advisor Name: My First £4) z Auther: Copyright 2010, NetaQuctec Safar Cap. Link: tp //mmeB.com Parameter [ane ‘ie Ina value B~| @ (Bacorygne) Boewovee | fet ‘Market Watch: 19:55:26 ey + Symbol Bid Ask + fe euRuso L230 125405 Figure 6, Autotrading is enabled Most of our codes that will implement our trading strategy, developed earlier, will be written within this section Now that we have looked at the various sections of the code for our EA, let us begin adding flesh to the skeleton. 2.2 INPUT PARAMETERS SECTION (Jan input parameters input int Stophoss=30; Uf Stop Loss input int, TakeProfit-100; // Take Profit input int ADX_Period-8; // ADX Period input int MA Feriod=8; W/ Mowing Average Period input iat EA Magic=12245; // ER Magic Number input double Adx_Min=22.07 | // Minimum ADX Value input double Lot=0.1; 77 Yots to Trade JJo-= Other parameters int adxdandle; // handle for our ADX indicator int madandle; // handle for our Moving Average indicator Gouble plsDI[],minDI"],adxVal[]; // Dynamic arzays to hold the values of +DI, -DI and AaDx double maval(1; // Dynamic array to hold the values of Moving Average for each bars Gouble p_close; // Variable to store the close value of a bar int STP, KP; // To be used for Stop Loss & Take Profit values ‘As you can see, we have added more parameters. Before we continue discussing the new parameters, let us discuss something you can see now. The two forward slashes ‘//” allows us to put comments in our codes. With comments, we are able to know what our variables stand for, or what we are doing at that point in time in our code. It also gives a better understanding of our code. There are two basic ways of writing comments: // other Parameters This is a single line comment mn Tais is a multi-line comment ” This is a multi-tine comment. Multi-line comments start with the /* pair of symbols and end with the */ one. The compiler ignores all comments when compiling your code. Using single-line comments for the input parameters is a good way of making our EA users understand what those parameters stands for. On the EA Input properties, our users will not see the parameter itself, but instead they will see the comments as shown below: [Corrman) Inputs Vaile Vike ia Sop Lae 0 ih Tae Pet 100 80x Psod a hon ae PRS a |) £8 Magic Number 12388 [Ninian AD Vaie 220 PA Lasts a Gee Figure 7. Expert Advisor input parameters Now, back to our code... We have decided to add additional parameters for our EA. The EA_Magic is the magic number for all orders by our EA. The minimum ADX value (Adx_Min) is declared as a double data type. A double is used to store floating point constants, which contain an integer part, a decimal point, and a fraction part. Example: Gouble mysum = 123.5678; double b7 = 0.098767 The Lot to trade (Lot) represents the volume of the financial instrument we want to trade. Then we declared other parameters that we will be using: ‘The adxHandle is to be used for storing the ADX indicator handle, while the maHandle will store the handle for the ‘Moving Average indicator. The plsDI[], minDI[], adxVal[] are dynamic arrays that will hold the values of +DI, -DI and main ADX (of the ADX Indicator) for each bar on the chart. The maVall] is a dynamic array that will hold the values of the Moving Average indicator for each bar on the chart. By the way, what are dynamic arrays? A dynamic array is an array declared without a dimension. In other words, no value is specified in the pair of square brackets. A static array, on the other hand has its dimensions defined at the point of declaration. Example: double allbars[20]; // this will take 20 elements p_close is a variable we will use to store the Close price for the bar we are going to monitor for checking of our Buy/Sell trades. STP and TKP are going to be used to store the Stop Loss and the Take Profit values in our EA. 2.3. EAINTIALIZATION SECTION int ontait() ‘ {Jon Get handle for ADK indicator adx#andle=iADX (NULL, 0, ADX_Period) 7 {Jon Get the handle for Moving Average indicator maHandle=iMA(_Synbol, Period,MA Period, 0,MOD= FMA, PRICE CLOS=); (fon What if handle returns Invalid Handle’ if (adxllandle0) // ok, the data has been copied successfully ‘ if(Old_Time!=New_Time(0]) // if old time isn't equal to new bar time ( IsNewSar-true; // if it isn't a first call, the new bar has appeared Af (MOLSTnfoInteger (MOLS_DEBUGGING)) Print ("he have new bar here ",New Time[0]," 0 Old_Time=New Time [0]; 1/ saving bar time , ) else ‘ Alert ("Error in copying historical times daza, error =",GetLastBrror())i ResetLasterror(); return; ue EA should only check for new trade if we have a new bar Lf (IsNewBar==false) ‘ return; ) uo Do we have enough bars te work with int Mybars=3ars(_Symbol, Period) ; if (Mybars<60) //"if total bars is less than 60 bars ‘ Alert ("We have less than 60 bars, FA will now exit!!"); return; ) {Jnr Define sone MOLS Structures we will use for our trade MglTick latest peice; // To be used for getting recent/latest price quotes MgltradeRequest mrequest; // To be used for sending our trade requests MgliradeResult mresult; —// To be used to get our trade results MalRates mrate[]; J M0 be used to store the prices, volumes and spread of each PeroMenory(mrequest); // Initialization of mrequest scructure The Expert Advisor will perform trade operations at the beginning of a new bar, so it's necessary to solve the problem with the new bar identification. In order words, we want to be sure that our EA does not check for Long/Short setups on every tick, we only want our EA to check for Long/Short positions when there is a new bar. We begin by declaring a static datetime variable Old_Time, which will store the Bar time. We declared it as static because we want the value to be retained in memory until the next call of the OnTick function. Then we will be able to compare its value with the New_Time variable (also of datetime data type), which is an array of one element to hold the new(current) bar time. We also declared a bool data type variable IsNewBar and sets its value to false. This is because we want its value to be TRUE only when we have a new bar. We use the CopyTime function to get the time of the current bar. It copies the bar time to the array New_Time with one element; if it is successful, we compare the time of a new bar with the previous bar time. if the times arent equal, it means that we have a new bar, and we set the variable IsNewBar to TRUE and save the value of the current bar time to the variable Old_Time. The IsNewBar variable indicates that we have a new bar. If it's FALSE, we finish the execution of OnTick function. Take a look at the code Lf (MQLSTafoTnteger (M9LS_DEBUGGING)) Print ("We have new bar here ")New Time(0]," old time wi it checks for the debug mode execution, it will print the message about the bar times when debug mode, we will consider it further. The next thing we want to do here is to check if we have enough bars to work with. Why repeat it? We just want to be sure that our EA works correctly. It should be noted that while the Onlnit function is called only once when the EAs attached to a chart, the OnTick function is called every time there is a new tick (price quote). You observe that we have done it again differently here. We decide to store the total bars in history which we obtained from the expression int Mybar! ars( Symbol, Period); in anew variable, Mybars, declared within the OnTick function. This type of variable is a local variable, unlike the variable we declared at the INPUT PARAMETERS section of our code. While the variables, declared at the Input Parameters section of our code, are available to all functions, within our code that may need them, variables declared within a single function is limited and available to that function alone. It can not be used outside of that function. Next, we declared a few variables of MQLS structure types which will be used in this section of our EA. MQLS has quite a number of built in Structures which makes things pretty easy for EA developers. Let's take the Structures one after the other. MalTick ‘This is a structure used for storing the latest prices of symbols. struct MqlMck ‘ datetime time; // Time of the last prices update double bid; // Current Bid price double ask // Current Ask price double last; V/ Price of the last deal (Last) slong volune; // Volume for the current Last price Any variable declared to be of the MalTick type can easily be used to obtain the current values of Ask, Bid, Last and Volume once you call the SymbolinfoTick() function, So we declared latest_price as a MqlTick type so that we could use it to get the Ask and Bid prices MalTradeRequest This structure is used to perform all trade requests for a trade operation. It contains, in its structure, all the fields necessary for performing a trade deal. struct MqlTradeRequest ‘ ENUM_TRADE_AEQUEST_ACTIONS action: // Trade operation type slong, magic: // Bxpert Advisor ID (magic member) long order; Hf Order ticket string symbols 7/ Tzade symbol double volumes // Requested volume for a deal in lots double price; M1 Price double stoplimit; // StopLimit level of the order double sli // Stop Loss level of the order double cpr // Take Profit level of the order along deviation; // Maximal possible deviation from the requ ENUM_ORDER_TYPE. types Wf Order type ENOM_ORDER_TYPE_FIGLING type_filling: execution type ENUM_ORDER_TYPE_TIME type_time; execution tine datetime expiration; expiration time (for the orders o: string comment; 1/ Ozder conment » Any variable declared to be of the MqlTradeRequest type can be used to send orders for our trade operations. Here we declared mrequest as a MqlTradeRequest type. MalTradeResult The result of any trade operation is returned as a special predefined structure of MalTradeResult type. Any variable declared to be of MalTradeResult type will be able to access the trade request results. struct MqlTradeResult ‘ aint retcodey // operation return code slong deal; 77 Deal ticket, if it ls performed slong orders // Order ticket, if it is placed double volume; // Deal volune, confirmed by broker double price; // Deal price, confirmed by broker double bids // Current Bid price double ask; // Current Ask price string comment; // Broker conment to operation (by default: it is filled by u Here we declared mresult as a MalTradeResult type. ‘MalRates ‘The Price (Open, Close, High, Low), the Time, the Volumes of each bar and the spread for a symbol is stored in this structure. Any array declared to be of the MqlRates type can be used to store the price, volumes and spread history for a symbol. struct MqlRates ‘ datetine times // Period start time double — open; 11 Open price Gouble high; // The highest price of the pericd double lows // The lowest price of the period souble clos 1/ Close price long tick volume; // Tick volume iat, spread; 17 Spread long real_volume; // Trade volume u Here we have declared an array mrate[] which will be used to store these information. - Let's make sure our arrays values for the Rates, ADX Values and MA values is store serially similar to the timeseries array “ // the rates arrays ArraySetAsSeries (mrate, true) ; // the ADX DItvalues array ArraySetAsSeries (p1sDI, true); // the ADX DT-values array ArraySetAsSeries (minDI, true) ; 1/ the ADK values arrays ArraySetAsSeries (adxval, true) ; // the MA-8 values arrays ArraySetAsSeries (maVal, true); Next we decide to set all the arrays we will be using to store Bars details as series. This is to ensure that the values that will be copied to the arrays will be indexed like the timeseries, that is, 0, 1, 2, 3, (to correspond with the bars index. So we use the ArraySetAsSeries() function. bool ArraySetasseries( void array{], // array by reference bool set // true denotes reverse order of indexing M It should be noted that this can also be done once at the initialization section of our code. However, | have decided to show it at this point for the sake of our explanation. Mn Get the last price quote using the MQLS MqiTick Structure Lf(!symboltafoTick( symbol, latest_price}) ‘ Alert ("Error getting the latest price quote ~ error:",GetLastError(),"!!1")¢ return; ) We now use the SymbolinfoTick function to obtain the latest price quote. This function takes two arguments - chart symbol and the Mq|Tick structure variable (latest_price). Again, if there is error, we reported it. ue Get the details of the latest 3 bars Lf (CopyRates ( Symbol, Period, 0,3,mzate}<0) ‘ Alert ("Error copying rates/history data ~ erro: return: ) /GetLastBrror (),"!!"); Next we copied the information about the latest three bars into our Mqlrates type array using the CopyRates function. The CopyRates function is used to get history data of MqlRates structure of a specified Symbol-Period in specified quantity into a MqlRates type array. int CopyRates ( etring symbol_name, 71 symbol name ENUM_TIMEFRAMES timeframe, V period int start_pos, U/ start position int, count, // ata count to copy MglRates| rates array!} // target arzay te copy M The symbol name is obtained by using “_symbol’, the current period/timeframe is obtained by using ‘_period’. For the start position, we will start from the current bar, Bar 0 and we will count only three Bars, Bars 0, 1, and 2. The result will be store in our array, mratef]. The mrate[] array now contains all the price, time, volumes and spread information for bars 0 , 1 and 2. Therefore to get the details of any bar, we will use the following: mrate[bar_nurber].bar_property for example, we can have the following information about each bar: mrate[1].time // Bar 1 Start time mrate[1].open // Bar 1 Open price mrate[0].high // Bar 0 (current bar) high price, ete Next we, copied all the indicator values into the dynamic arrays we have declared using the CopyBuffer function. int CopyButter( int indicator_handle, // indicator handle int buffer_num, // indicator buffer number int start pos, // start position int count, 77 amount to copy double buffer{) i} sazget azzay to copy ” The indicator handle is the handle we created in the Oninit section. Concerning buffer numbers, the ADX indicator has three (3) buffers: = 0- MAIN_LINE, = 1 +PLUSDI_LINE, == MINUSDI_LINE. ‘The Moving Average indicator has only one (1) buffer: = 0 MAIN_LINE, We copy from the present bar (0) to the past two bars. So amount of records to copy is 3 (bars 0, 1 and 2). The buffer{] is the target dynamic arrays we had earlier declared - adxVal, plsD1, minDI and maVal. ‘As you can see here again, we try to capture any error that may occur in the copying process. If there fs error, no need to go further. It is important to note that the CopyBuffer() and the CopyRates() function returns the total number of records copied on success while it returns -1 incase of an error. That is why we are checking for a value less than 0 (zero) in the error checking functions here. {Jn-> Copy the new values of our indicators to buffers (arrays) using the handle Lf (CopyBuffer (adx¥andle, 0,0, 3, adxVal)<0 || CopyBuffer(adxHandle, 1, 0,3, plsDI) <0 I] CopyBuffer (adxtandie, 2,0, 3,minDI) <0) ‘ Alert ("Error copying ADX indicator Buffers - returi ) Lf (CopyBuffer (maflandle, 0,0, 3,maVal) <0} ‘ Alert ("Error copying Moving Average indicator buffer - error:",GetLastError()); returi ) ror", GethastError(),"!!" At this point we want to check if we already have a Buy or Sell position opened, in order words, we want to make sure we have only ONE Sell or Buy trade opened at a time. We do not want to open a new Buy if we already have tone, and we do not want to open a new Sell if we already have one opened. To achieve we will first of all declare two bool data type variables (Buy_opened and Sell_opened) which will hold a TRUE value if we already have a position opened for either Buy or Sell. have no errors, so continue we have positions opened alzeady? bool Buy_opened=false; // variable to hold the result of Buy opened position bool Sell_opened=false; // variable to hold the result of sell opened position Af (PositionSelect (_Symbol) ‘ rue) // we have an opened position Af (PositinGettnteger (POSITION_T¥PE} ‘ POSITION TYPE_BUY) Buy_opened = true; //It is a Buy > else if (PositionGet Integer (POSITION TYPE) ‘ Sell_opened = true; // rt is a Sell > We use the trade function PositionSelect to know if we have an open position. This function returns TRUE if we have a position opened already and FALSE if we have none. bool Positionselect ( string symbol 11 Symbo2 name ” It takes, as the major argument/parameter, the symbol (currency pair) we want to check. Here, we use _symbol because we are checking the current symbol(currency-pair). If this expression returns TRUE, then we want to check if the position opened is a Buy or a Sell. We use the PositionGetinteger function for this. it gives us the type of position opened when we use it with the POSITION_TYPE modifier. It returns the Position type identifier which can either be POSITION_TYPE_BUY or POSITION_TYPE_SELL long PositionGetinteger ( 1UM_POSITION PROPERTY property_id // Property identifier In our case, we used it to determine which of the position we already have opened. If itis a Sell, we store a TRUE value in Sell_opened and if it is a Buy, we store a TRUE value in Buy_opened. We will be able to use these two variables later when we are checking for Sell or Buy conditions later in our code. It is now time to store the close price for the bar we will be using for our Buy/Sell setup. Remember we declared a variable for that earlier // Copy the bar close price for the previous bar prior te the current bar, that is Bar 1 pclosesmrate(1].close; // bar 1 close price Having done this, we will now proceed to the next step. ye 1. Check for a long/Buy Setup : MA-8 increasing upwards, previous price close above it, ADK > 22, +DI > -DI ” (Jn Declare bool type variables to hold our Buy Conditions bool Buy Condition 1 = (maVai[O]>maVal(+)) s& (maVal[1]>maVal(2]}; // MA~B Increasing up bool Buy Condition 2 = (p close > maval(i}); {7 previuos price closed above MA~' bool Buy Condition_3 = (adxval[0]>Adx Min); W/ Current ADX value greater than bool Buy Condition 4 = (plsDI(0]>minDI(0}); // 4D1 greater than -DT {Jo-> Putting all together if(Buy Condition 1 4& Buy Condition 2) ‘ if (Buy_Condition_3 6& Buy_Condition_4) ( // any opened Buy position? Af (Bay_opened) Alert ("We already have a Buy Position!!!"); return; // Don't open a new Buy Position ) mrequest.action = TRADE ACTION DEAL; 1 inmediate mrequest.price = Norma‘ izeDouble (1atest_price.ask, Digits // latest asl mrequest.s1 = Normalizebouble(1atest_price.ask ~ S124 Point, Digits); // Stop Los: mrequest.tp = Normalizebouble(latest_price.ask + TKP* Point, Digits}; // Take Pro: mrequest..symbol = symbol; Uf currency pa mrequest.volume = io /f sumer of Lo mrequest.magic = EA Magic; I/ Oxder Magic mrequest.type = ORDER "YPE_BUY; W/ Buy Order mrequest.type_filling = ORDER_FILLING_FOK; 17 order exec nrequest .deviatio /Jas- send order OrderSend (mrequest, mresult) ; 00; 1f Deviation £ It is now time to start checking for a Buy opportunity. Let us analyze the expression above as it represents the strategy we designed earlier. We are declaring a bool type variable for each of our conditions that must be met before an order can be placed. A bool type variable can only contain TRUE or FALSE. So, our Buy strategy has been broken down into four conditions. If any of the conditions is met or satisfied, then a value of TRUE is stored in our bool type variable, otherwise, a value of FALSE will be stored. Let us look at them one by one. ool Buy Condition 1 - (maVal[07>maVal{1]) && (maVal[1]>maval[21); Here we are looking at the MA-8 values on Bars 0, 1 and 2. If value of MA-8 on the current bar is greater than its value on the previous Bar 1 and also the MA-8 value on Bar 1 is greater than its value on Bar 2, it means that MA-8 is increasing upwards. This satisfies one of our conditions for a Buy setup. bool Buy _Condition_2 = (p_close > maVal{1]); This expression is checking to see if Bar 1 Close price is higher than the value of MA-8 at the same period (Bar 1 period). If the price is higher, then our second condition has also been satisfied, then we can check for other conditions. However, if the two conditions we have just considered were not met, then there will be no need to check other conditions. That is why we decide to include the next expressions within these two initial conditions (expressions). bool Buy_Condition_3 = (adxVal(0]>Adx_Min) ; Now we want to check if the current value of ADX (ADX value on Bar 0) is greater than the Minimum ADX value declared in the input parameters. if this expression is true, that fs, the current value of ADX is greater than the Minimum required value; we also want to be sure that the plusDI value is greater than the minusDI value. This is what we achieved in the next expression bool Buy Condition_4 = (plsDI{0]>minb1 (01); If all these conditions are met, that is, if they return true, then we want to be sure that we do not open a new Buy position if we already have one. It is now time to check the value of the Buy_opened variable we declared earlier in our code. 11 any opened Buy position? Af (Buy_opened) ( Alert ("We already have a Buy Position! !!"); return; // Don't open a new Buy Position If Buy_opened fs true, we do not want to open another Buy position, so, we display an alert to inform us and then return so that our EA will now wait for the next Tick. However, if Buy_opened is FALSE, then we prepare our records using the MolTradeRequest type variable (mrequest) which we declared earlier to send our order. + The action here, which is the trade operation type, is TRADE_ACTION_DEAL because we are placing a trade order for an immediate execution. If we are modifying an order, then we will use TRADE_ACTION_MODIFY. To delete an order we will use TRADE_ACTION_REMOVE. We used our MalTick type latest_price to get the latest Ask price. The order Stop loss price is obtained by subtracting our StopLoss in points from the Ask price while the order take profit price is obtained by adding our TakeProfit in points to the Ask price. You will also observe that we used the NormalizeDouble function for the Ask price, the StopLoss and TakeProfit values, itis good practice to always normalize these prices to the number of digits of currency pair before sending it to the trade server. + The symbol is the current symbol (_Symbol or Symbol()). The order type is the type of order we are placing, here we are placing a buy order ORDER_TYPE_BUY. For a Sell order, it will be ORDER_TYPE,_SELL. = The order type_filling is the order execution type; ORDER_FILLING_FOK means that the deal can be executed exclusively with a specified volume at the equal or better price than the order specified price. If there is no sufficient volume of offers on the order symbol, the order will not be executed. The OrderSend() function takes two arguments, the MqlTradeRequest type variable and the MalTradeResult type variable. bool Ordersend( MglTradeRequests request U/ query structure MglTradeResults result, // structure cf the answer M As you can see, we used our MqlTradeRequest type variable and the MqlTradeResult type variable in placing our order using OrderSend. // get the result code if (result retcode==10009 || mresult.retcode=~10008) //Request is completed or or ‘ Alert ("A Buy order has been successfully placed with Ticket# ) else ‘ Alert ("The Buy order request could not be completed ~erro: Resetlastirror (); return; ) *mresult.order, "! Cethastirror())i Having sent our order, we will now use the MqlTradeResult type variable to check the result of our order. If our order is executed successfully, we want to be informed, and if not, we want to know too. With the MqlTradeResult type variable ‘mresult’ we can access the Operation return code and also the order ticket number if the order is placed. The return code 10009 shows that the OrderSend request was completed successfully, while 10008 shows that our order has been placed. That is why we have checked for any of these two return codes. If we have any of them, we are sure that our order has been completed or it has been placed. To check for a Sell Opportunity, we check for the opposite of what we did for Buy Opportunity except for our ADK that must be greater than the Minimum value specified a” 2. Check for a Short/Sell Setup : MA-8 decreasing downwards, previous price close below it, ADK > 22, -DI > +DI ” (Jn-- Declare bool type variables to hold our Sell Conditions bool Sel] Condition 1 = (maVal{O]Adx_Min) ; V7 Current ADX val: bool Sell Condition 4 = (plsDI[0]Adx Min) ; Now we want to check if the current value of ADX (ADX value on Bar 0) is greater than the Minimum ADX value declared in the input parameters. If this expression is true, that fs, the current value of ADX is greater than the ‘Minimum required value; we also want to be sure that the MinusDI value is greater than the plusDI value. This is what we achieved in the next expression bool Sell_Condition 4 = (plspT(0]o) // ck, The data has been copied success: ‘ 3£|Old_Time!=New Time [0]) // s£ oid ‘ IsNewBar=crue; if it isn't a first cell, the new bar hes apr Af (1QLSInfoInteces (MQLS_DEBUGGING)) Print ("We have new bar here ' Old Timentew Time [0]? Vs bo new bar came bar time Figure 10, Setting a breakpoint For our code, we are going to set breakpoint on five different lines. Iwill also label them form 1 to 5 for the sake of explanation. To continue, set breakpoint at the seven code lines as shown in the figure below. Breakpoint 1 is the one we have created above. W7/Dome |O 0 OS @ EH if we have a new bar 03 s¢(TeNevBar==taice) ‘ return; } “ m bara te work 04 inv Mypars-Bars|_symboi, Perio); 5 if(Mybarsceo) // "if total ws 4 Blert ("We have less than 60 bars, FA will now exitt!m); : Define some NOLS Str MgiTick latest price; MgiTradeRequest mrequest; MgiradeResult nresult; MglRates mrave[]: Figure 11. Setting additional breakpoints (Once we have finished setting our breakpoints, we are now set to start debugging our code. Tost the debe pest 5a ce the green button onthe Tob of the metadtar [Dene (OO OS GE ight 2010, MecaQuotes Software Figure 12 Staring the Debugger The first thing the editor does is to compile the code, if there is any error at the point, it will display it and if no error, it will let you know that the code compiled successfully. Description © 'My First EA.mas! @ eror(e),0 warring) Ewors [ FindinFies | Figure 13. Compilation Report Please note that the fact that the code compiled successfully does not mean there may not be errors in your code. Depending on how your code is written, there may be runtime errors. For example, if any of our expressions does not evaluate correctly due to any little oversight, the code will compile correctly but may not run correctly. Too much of the talk, let’s see it in action, Once the debugger has finished compiling the code, it takes you to the trading terminal, and attach the EA to the chart you have specified on the Metaéditor Options settings. At the same time, it shows you the Input parameters section of the EA. Since we are not adjusting anything yet, just click the OK button. ‘Common | pus 7) Ltsto Tae a1 Save So oa Figure 14. Expert Advisor Input Parameters for Debugging Variable Value 22) Sop Loss 30 |i Tae Prot 100 |12) ADX Pod 8 12 Moving Average Period 8 ia} EA Magic Number 12348 38) Minimum ADX Value 20 You will now see the EA clearly on the top-right hand corner of the chart. Once it starts the OnTick(), it will stop as soon as it gets to our breakpoint 1. Gb Gf vo (0 001GGEEe Ee void OnTick() t Ud We wi ‘3 // Be each OnTick execution we wi I) 3 the use the static Old Time variable to L check the current bar time with the saved one. equal ta the sa che bar time. sbavic dacetime 01d Time; catetime New Tame [1 bool IsNewar=false; i copy (Tame 4) ant copied=CopyTime (_Symbol,_Perio3,0,1,lew Time) : 2 if (copied>0) // ok, The dace hes been copi t S£(Old_Time!“tew_Time (01) ‘ IsNewBartrue;// if ic isn's the last b: d successfully 3 cla time isn't equ: hes aopeared Af (MQLSInfoTnteger (MQIS_DEBUGGING)) Print ("We have nev bar here ",New Time Old Time-New Time[0]? a : i . | , Function Line | Expression Value Type LeAmgs OnTick a © Delete (2) Copy Value v Gad 6 v Auto Arrange Figure 15. Debugger stops atthe frst breakpoint You will notice a green arrow at that code line, That tells you that previous code line had been executed; we are now ready to execute the present line. Let me make some explanations before we proceed. If you look at the Editor’s Tool Bar, you will observe that the three buttons with curved arrows which were earlier grayed out are now activated. This is because we are now running the debugger. These buttons/commands are used to step through our code (Step into, Step over or Step out) Omece Figure 16. Step into command The Step Into is used to go from one step of the program execution into the next step, entering into any called functions within that code line. Click on the button or press F11 to invoke the command. (We will use this command in our Step-by-Step debugging of our code.) Slep over, FO Figure 17 Step over command The Step over, on the other hand does not enter into any called function within that code line. Click on the button or press F10 to invoke the command Step out, Shift+ FAL Figure 18. Step out command To execute a program step that is one level higher, you click this button or press Shift+F 11. Also, at the lower part of the Editor, you will see the Toolbox window. The Debug tab in this window has the following headings: = File : This displays the name of the file been called = Function : This displays the present function from the file been called = Line : This displays the number of the code line in the file from which the function is called. = Expression : This is where you can type the name of any expression/variable you are interested in monitoring from our code. + Value : This will display the value of the expression/variable we typed at the Expression area. = Type : This will display the data type of the expression/variable been monitored. Back to our debugging process... The next thing we want to do is now to type in the variables/ expressions from our code that we are interested in monitoring. Make sure you only monitor the variables/expressions that really matters in your code. For our example, we will monitor the following: = Old_Time (old bar time) = New_Time[0] (current bar time) = IsNewBar (flag that indicates the new bar) = Mybars (Total bars in History) - Our EA depends on it You can add other ones like the ADX values, the MA-8 values, etc. To add the expression/variable, double-click under the Expressions area or right-click under the Expressions area and select Add as shown in the figure above. Type the expression/variable to monitor or watch. Fle Function Li. | Expression Value Type © (MQLI\Siperts\My.Firet_EfmeS Ontick 79 | [Oe [iros | Sevnh Debug [Tate | Figure 19. The expressions watching window Type all the necessary variables/expressions... * Fe Function Li | Exoression value Type © \MQLAEperiAMy fit EAmgs _Onick _—_(79._|[9 Old-Time 1970010: 600000 datetime D New Time[0] 'D'1970.01.01 00:00:00° datetime [Newer fase bool [Mvbard Se os [ST Figure 20. Adding expressions or variables to watch If the variable hasn't been declared yet, its type is "Unknown identifier” (except the static variables). Now, lets move on. OE) a-|# -| omic |O O O|G static datetine Old_Time: gavecame New Tame (217 bool isNewsar=raise; // copying the last bar tine to the element Newt: 04 ine copied=copyTime (_Synkol, Feriod,0,1,New Time) 7 @2 ix(copiea>o) // ck, the dave nas been copied : ; Functon Li. | Expression LEAmes —__OnTick © | 6\0ld Time @)New.Timelol ia copie [tend Tmypass Unkrown identifier Figure 21. Step into command in action IsNewBar-crue;// if it den't a fire: call, che af (MQLSInfcInteger (MQLS_DEBUGGING)) Print ("We have new bar here * Old_Time=New Tine[0}: (/ saving bar vine : Type datetime datetime int boot Click the Step into button or press F11 and observe what happens. Keep on pressing this button or F11 until you get to breakpoint no 2, continue until you get to breakpoint no 4 as shown below and observe the expressions watching window. 7|B comic (OO OS @ E/E a! Be) a-\* /--— ER y cheek £ @|3 += (tetessarsaiee) ‘ return; > [f--~ Dow agh bi 4 int mypars=sars(_sympo1,_Perica); O§ if (Mybars<60) // if total bers ‘ Alert ("We have less chan 60 bar: bar new trade if we have a ni a vo work wath less chan 60 bars BA will now exic!!"); revurn; > — Define some Mgrs St es we os Mgiick levest_price; // To be used MgiiradsRequest mrequest; // To be used Function Type EAmaS OnTice datetime - datetime 1 int true boot Figure 22, Watching the expressions or variables GES: /f von (00 ORG e me bar — FA should only check for new trade if we have a ni a 9 Steacturee ve will use for uquiex lavest_prl MgiTradeRequest meq ea ror ¢ ed for si < Function Value Type LEAImg9 ——_Ontick proio6ge16s309° | datetine - 0'2010.06.08 16:53:00" datetime 1 int [inewas en ai Mybats Unkrown dentiftir Figuce 23, Watching the expressions or variables a (TatlewRars=false) t revurn; } [J Do web work a 04 :nt im Pernod) 7 obs bars is less © bars [ XK TaaeceiiNgve Less chan €0 bare, £A wild nay exiet ty) revurn; ne some MOLS Mgitick levest_price: Function Value Type 4 £Amgs OnTicke 2010.06.08 16:53:00" datetime 2010.06 08 16:53:00" datetime 1 int true boot 100000 int Figure 24, Watching the expressions or variables Once there is a new tick, it will return to the fist code line of the OnTick() function. And all the values of our variables/expression will now be reset because this is a new tick except if any of them is declared as a static variable. In our case we have one static variable Old_Time. (/| Expert tick ire + void onTick() 4 if e the static Old Tin he ‘5 a iy 1, rane don't equ scavic devecine old Time; | cecetine New Time bool TeNlewar-falees / copying the last bar time to w = 01 int copied=copyTime (_symkol, Period, 0,1,New_Time); ©2 s£(copiedso) // ck, The cata has been copied succe: t 32 (Old_Tame!=new_time(0]) // if olg came isn't Function tL EAmaS OnTick Type datetime row erter cries Unknow ideiar Dinevesr Cem Divi Unknown ideifer Static local variable Local variables of OnTick function Figure 25. Values of variables on NewTick event To go over the process again, continue pressing the F11 key and keep monitoring the variables at the expressions watching window. You can stop the debugger and then remove all the breakpoints. ‘As we see, in Debug mode it prints the message “We have new bar here.. an View Inset “Chats Toole Window Help Eee ‘see ee fl % -| Mook Woon ar roy Bid Ask Time * 213955 T4530 fee arsio| 143572 173548 | | HI soe asie2/ 1.5510 172550 I euspPy 91099) 1055 171550 ia © USDCAD__L0sri7| Lasra? tiasae symbcs [Tako CSCS cd om o 8 Met Trader = My eB Accounts a | i920 © Gi Indicators 1B Trend sas corwron J Favouries | [too Tamieas Tawa om ieae Times thmfes Samivoe ban 6 e{zoi006.8 08:16:55, My_Fist EA EURUSO,MI) We havenew bar here 2010.05.0817:14.00 old time was 1970.01.01 00:00:00, Figure 26, Expert Advisor prints the message in Debug mode Start the debugging process again; but this time without breakpoints. Keep watching at every tick and if any of our Buy/Sell condition is met, it will place a trade and since we have written our code to tell us if an order is placed successful or otherwise, we will see an alert. Efe View Iroct Ghats Tools Window tly Sor 1B~ B-| @ [Biactedng) Brew owe | it (f) | @ 9 EDL M-+\|1-v4a4a Nova Wat Se eTHEs symbol id Ask Time fe euruso 148517 149s 174749 BB We akendy have a Buy Poaton!!t Hee 2010.06.08 08:4. My Fret EA LAUSD) [We aeadyhave 8 Bu Poston : A 2010.06.08 08:1... My_Fret_EA FLRUSO.M1) A Buy order has bean successfilly placed with Tickat#: 899405 1) ate | auioneas oaaran My Fine €A EURUSO,M) We have new barhere 2000808171720 old tine was 20100608 172600 © 20100605 06603 My Frnt EA EURUSO,NO) A Buy order hasbeen succesfully paced wth Ticket. 695105 © 2100608 66.04 Myf 6A EURUSO.MG) We have new bsrhere 20[00510817600 old tne ws 9100608173500 _~ Trade | Egorure | Hite | News | Mailbox | Alets Experts [ Joural | Figure 27. Expert Advisor places trade curing debussing I think you can leave the EA to work for a few more minutes while you take a coffee. Once you are back and you have made some money (just kidding), then click the STOP (Red) button on the MetaEditor to stop debugging. 1 ae lO OQsee Stop debugging, Shit-F5 topping the debugger Figure 28 What we have actually done here is to see that our EA only checks for a trade at the opening of a new Bar and that our EA actually works. There is still a lot of room for adjustments to our EA code. Let me make it clear, at this point that, the Trading terminal must be connected to the internet, otherwise, debugging will not work because the terminal will not be able to trade. 3.2 TESTING OUR EA STRATEGY At this point we now want to test our EA using the Strategy Tester built into the Trading Terminal. To start the Strategy Tester, press CONTROL+R or click the View menu on the Terminal Menu Bar and click on Strategy Tester as shown below Ince Charts Toole Lengua * a , Statue Bar + Chars Bar , culm ceed 2 cae ee cele symbol ASRS Fallen at Figure 26 Starting the Strategy Testing The Tester (Strategy Tester) is shown at the lower part of the terminal. For you to see all the Tester’s settings, you need to expand/resize it. To do this, move your mouse pointer to the point shown by the red arrow (as shown below) BiLireae [Exposure | History | News | Mailbox | Alerts | Beets | Journal | Spat: (Ny Fist Aes =] EURUSDN + 2 Date: zoooto; G+ 2010052 Be Exooutien: (Noma J Tesingprogess: (f (| [Stat Settings [Tnputs | Agents | Journal | Figure 27. The Strategy Tester window The mouse pointer changes to a double-end arrow, hold down the mouse and drag the line upwards. Stop when you discover that you can see everything on the settings tab. Enpot: (Ny Fit Faas I> =] EuRuso 2I—>- aa Date aun Bane B- Execution: (Norma —S. Depost: 10000 7 Ber USD rowan: [io | BOTS 4 Tesngprars | | [Stat Settings [Tne | Agent | lourral | Figure 28. The Strategy Tester Settings Tab Select the EA you want to test . Select the Currency pair to use for the test . Select the Period/Timeframe to use for the test |. Select Custom Period and set the dates in 5 Set the dates for the custom period to be used for the test 5. Execution is Normal . Select the deposit amount in USD to be used for the test Set Optimization to Disable (We are not optimizing now, we just want to test) . Click this button when you are ready to start test, Before we click the Start button, lets look at the other tabs on the Tester ‘Agents Tab The processor used by the Tester for the Test. Depending on your Computer’s processor type. Mine is only one (1) core processor. Agent Hardware PU Usege status af- leat S x Bi Corei Intel Xeon 3.20GHz, 1023 MB, PRS aa [gl= Remote |) Setings | Inputs |Agents [Journal | Figure 29. The Strategy Tester Agents tab Once the agent, you will see something similar to the figure below =| Agent Hardware CPUUsace Status ‘gf- Local B Coret Intel Xeon 320GHz, 1023 MB, PREG —— ons gR- Remote Settings | Inputs | Graph | Agents [ Joumal | Figure 30, The Strategy Tester Agents tab during a test Journal Tab This is where all the events going on during the test period is displayed “| Time Seurce Message = © 2010-0607 064052 Core deal perform [#350 sel 0.10 EURUSD at 125950] © 2010-0607 054052 Core lol #350 sell 0.10 EURUSD ot 1.25950 done (bazed on order #350] © 210.0607 054052 Core instant sell0.10 EURUSD at 1.25950 s 1.26250 tp: 1.24950 (1.25050 /1.25963 /1. «© 2010.607 054052 Core order performed sel (0 a 26819 [#349 sellO10 EURUSD at 1.25419] © 2010-0607 054052 Core deal performed [#249 cell 9.10 EURUSD at 26419] Hl © 2010-0607 054052 Corel deal #349 sell 0.10 FURUSD at 1.25419 done (based on order #349) a © 2010-0607 06:40:52 Cored stop oss tuiggered buy 00 EURUSD 1.26719 si 1.26448 tp:1.27719 [#349 sel (© 2910.0607 05:40:52 Core ABuy order has been successfully placed with Tieet# 348 M Seting: | Tnpute | Rese | Graph | Agerse | Journal Figure 31. The Strategy Tester Joumal tab showing trade activities Inputs Tab, This is where you can specify the input parameters for the EA. | Variable Value TF StopLess 20 UP Take Proft 300 TF AD Pericd 8 TF Moving Average Peiod a FEA Magic Number 1245 LF Minimum ADx Value 20 [T Letsto Trade oa [) Settngs Linguts [Reais | Graph | Agents | Joureal | Figure 32. The Strategy Tester Inputs tab If we are optimizing our EA, then we will need to set the values in the circled area. = The Start is the values you want the Tester to begin with. = The Step is the increment rate for the value you selected, and = The Stop is the value at which the Tester will stop incrementing the value for that parameter. However, in our case we are not optimizing our EA, so we will not need to touch that for now. Once everything is set, we now go back to the Settings tab and click the Start button. Then the tester begins its work. All you need to do now is to go and take another cup of coffee if you like, or, if you are like me, you may want to monitor every event, then turn to the Journal tab. Graph tab Once you begin to see messages about orders been sent on the Journal Tab, you may then wish to turn to a NEW tab named Graph which has just been created. Once you switch to the Graph tab, you will see the graph keep on increasing or decreasing as the case may be depending on the outcome of your trades. Tie) ee ae ans 10640 Naga inal 10% Tncoe NUOONE “ainize TIOGA BNI OLOOLIO —wINGIS TOOL BNR OOS BRIS Settings | Inputs | Resuts | Graph gents | Journal | Figure 33. The graph result forthe Expert Advisor Test Results tab Once the test is completed, you will see another tab called Results. Switch to the Results tab and you will see the summary of the test we have just carried out. * lina Depost 110 000.00 * Bars 475 Ties 4545073 Total Net Prof 145210 Gross Profit 505250 Gross Loss 2600.40 Profit Factor 140 Expected Payoff 754 | Recovery Factor 3.26 Sharpe Ratio 2643 [Balance Drawdown Ab, 30240 Balance Drawdown M., 38220 G.15%) Balance Drawdown Re. 3.75% (382.20) |= | Equity Drawdown Abs, 317.30 Equity Drawdown Max... 44530 (1.35%) Equity Drawdown Rela... 4.35% (15.30) [Total Tredes 190 ShertPosiions (won %) 116 (B7.07%4) Long Positions (won %) 74 25.68%) Profit Trades (ot tot.. 62 (263%) Loss Trades ("sof total) 128 67.37%) | Largest profittade 10050 loss rede 3060 | | Z| Average profit trade S140 tosetrade m2 al Maximum — consecutive wins ($) 3.00.00) consecutive losses (S) 13 (38220) _ B) Seige | Tnpot [Results [Graph | Agent | Journal | Figure 34. The Strategy Tester Results tab showing test results summary You can see the total Gross Profit, Net Profit, total trades total loss trades and many more. Its really interesting to see that we have about USD 1,450.0 within the period we selected for our test. At least we have some profit. Let me make something very clear to you here. You will discover that the settings for the EA parameters that you see in the Strategy tester is different from the initial settings in the Input parameters of the EA. | have just demonstrated to you that you can change any of those input parameters to get the best out of your EA. Instead of using a period of 8 each for the Moving Average and ADX, | changed it to 10 for Moving Average and 14 for ADX. | also change the Stop Loss from 30 to 35. Last but not the least, I decided to use 2 Hour timeframe. Remember, this is the Strategy Tester. If you want to view a complete report of the test, then right-click on anywhere in the Results tab, you will see a menu. From this menu, Select ‘Save as Report’. * tint Desk xoo0000 Z (Bars 475 Ticks “Total Net Profit 145249 Gross Profit | Deals | Loss 3.60040 Prot actor 140) pect Pov” | —Ondere “Recover Factor 32 Shmmpefetio | oder &.Dels sia maerer 20280 ance DAWEOINM pegon yeeDiandonn Rel 375% 05220 = _Eguty Dawn Ae 3173) Equiy Drawdown" _yDrawdowinRels... 4.35% 449.30) ee 190 Short Positions won) pert | Positions (won % 7425587) Pre Trades (6 BF Openchan eden (eflota) — 129(6737%) Longest profit rade rade 26 | Average profit trade de 2243 Maximum consecutive wine (S) 30000) consecutive losses (6) 13 (38220) | [ (&| senor [insite Lesuts [Graph | Aves [Jounal | Figure 35. Saving the result ofthe test The save dialog window will appear, type a name for your report (if you want, otherwise leave the default name) and click the save button. The whole report will be saved in HTML format for you. To view the chart for the test that was carried out, click Open Chart and you will see the chart displayed FUROR 16a Les 1D LSS, 13170 Fay EA zing wth a Tikereit=10); ADI Pada; MA Paricd=8; EA, Maie=12365; Ad, Nin=22.000000 lot=0 40000 Ms ut Ae Mh 7 -" yf Ms ap ih i sed 128360 ttt 1212s Tay 2010 E Nay 1400 Way 2200 Ti May Onov Te Nay 1400 1) Way 2200 1 May 0600 Te May 1400 13ay 2200 BI Mey oo Figure 36. The chart showing the test That's it, we have successfully written and tested our EA and we now have a result to work with. You can now go back to the strategy tester Settings tab and make the test for other Timeframes/Period. Assignment I want you to carry out the test using different currency pairs, different timeframes, different Stop Loss, different Take profit and see how the EA performs. You can even try new Moving Average and ADX values. As | said earlier, that fs the essence of the Strategy tester. | will also like you to share your results with me. Conclusion In this step by step guide, we have been able to look at the basic steps required in writing a simple Expert Advisor based on a developed trading strategy. We have also looked at how we check our EA for errors using the debugger. We also discussed how to test the performance of our EA using the Strategy Tester. With this, we have been able to see the power and robustness of the new MQLS language. Our EA is not yet perfect or complete as many more adjustments must still be made in order to used it for real trading. There is still more to learn and I want you to read the article over again together with the MQL5 manual, and try everything you have learn in this article, | can assure you that you will be a great EA developer in no distant future. Happy coding,

You might also like