MQL4 Expert Advisor Example
Learn in this complete MQL4 Expert Advisor Example article how to code a fully functioning MA Expert Advisor to help you program your trading EA.
The few manuals and guides that discuss the building of an expert advisor tend to use the moving average cross (MACross) as an example. The reason for this is that it is the most popular indicator based strategy out there, and it so much easier to teach new coding concepts using a trading concept most people are already familiar with.
Following this familiar path, I submit a basic expert advisor based on a simple moving cross (20-200):
// Section 1:
// Preprocessor Directives, External & Internal Variables
#property copyright “Copyright © 2008-2010, Excel Markets”
#property link “/article/”
extern string EAName = “MACross”;
extern double MagicNumber = 59483;
extern double Lots =0.1;
extern double LotDigits =2;
extern int Slippage = 5;
extern double StopLoss = 80;
extern double TakeProfit =0;
extern bool OppositeClose = true;
extern bool EnterOpenBar = true;
extern int FastMATime = 0;
extern int FastMAPeriod = 2;
extern int FastMAType = 0; //0:SMA 1:EMA 2:SMMA 3:LWMA
extern int FastMAPrice = 0;
extern int FastMAShift = 0;
extern int SlowMATime = 0;
extern int SlowMAPeriod = 30;
extern int SlowMAType = 1; //0:SMA 1:EMA 2:SMMA 3:LWMA
extern int SlowMAPrice = 0;
extern int SlowMAShift = 0;
// Global Variables
int Counter, vSlippage;
double ticket, number, vPoint;
double
FastMACurrent,
FastMAPrevious,
SlowMACurrent,
SlowMAPrevious;
int init(){
if(Digits==3 || Digits==5)
{ vPoint=Point*10; vSlippage=Slippage*10; }
else{ vPoint=Point; vSlippage=Slippage; }
return(0); }
//——————————————————-
// Section 3: Start
int start()
{
if(Bars<100) { Print(“Bars less than 100”);
return(0); }
//——————————————————–
// Section 3A: Define ShortCuts to Common Functions
int Total, OType=-1, Ticket;
double Price, SL, TP, Lot;
bool CloseBuy=false, CloseSell=false, OpenBuy=false, OpenSell=false;
for(int Counter=1; Counter<=OrdersTotal(); Counter++)
{
if (OrderSelect(Counter-1,SELECT_BY_POS)==true)
if (OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
Ticket=OrderTicket();
OType =OrderType();
Price =OrderOpenPrice();
SL =OrderStopLoss();
TP =OrderTakeProfit();
Lot =OrderLots();
}
}
//—————————————————-
// Section 3B: Indicator Calling
int Current = 0;
FastMACurrent = iMA(NULL, FastMATime, FastMAPeriod, FastMAShift, FastMAType, FastMAPrice, Current + 0);
FastMAPrevious = iMA(NULL, FastMATime, FastMAPeriod, FastMAShift, FastMAType, FastMAPrice, Current + 1);
SlowMACurrent = iMA(NULL, SlowMATime, SlowMAPeriod, SlowMAShift, SlowMAType, SlowMAPrice, Current + 0);
SlowMAPrevious = iMA(NULL, SlowMATime, SlowMAPeriod, SlowMAShift, SlowMAType, SlowMAPrice, Current + 1);
//————————————————
// Section 3C: Entry Conditions
bool OpenBar=true;
if(EnterOpenBar) if(iVolume(NULL,0,0)>1) OpenBar=false;
if (FastMACurrent > SlowMACurrent&& FastMAPrevious < SlowMAPrevious
&& OpenBar){
OpenBuy=true;
if (OppositeClose) CloseSell=true;
}
if (FastMACurrent<slowmacurrent&&> < SlowMACurrent&& FastMAPrevious > SlowMAPrevious
&& OpenBar){
OpenSell=true;
if (OppositeClose) CloseBuy=true;
}
//————————————————-
// Section 3D: Close Conditions
while(true)
{
if (OType==0 && CloseBuy==true)
{
close (OP_BUY); // Close Buy
return;
}
if (OType==1 && CloseSell==true)
{
close (OP_SELL); // Close Sell
return;
}
break;
}
//————————————————–
// Section 3E: Order Placement
while(true)
{
if (OrdersTotalMagicOpen()==0 && OpenBuy==true)
{
if(StopLoss>0){SL=Bid – StopLoss*vPoint;}else{SL=0;} if(TakeProfit>0){TP=Bid+TakeProfit*vPoint;}else{TP=0;}
ticket=0;number=0;
while(ticket<=0 && number<100){
RefreshRates();
ticket = OrderSend(Symbol(),OP_BUY,NormalizeDouble(Lots,LotDigits), Ask,vSlippage,SL,TP,EAName, MagicNumber, 0, Green);
return (ticket);
}}
if (OrdersTotalMagicOpen()==0 && OpenSell==true)
{
if(StopLoss>0){SL=Ask + StopLoss*vPoint;}else{SL=0;} if(TakeProfit>0){TP=Ask-TakeProfit*vPoint;}else{TP=0;}
ticket=0;number=0;
while(ticket<=0 && number<100){
RefreshRates();
ticket= OrderSend(Symbol(),OP_SELL, NormalizeDouble(Lots,LotDigits), Bid,vSlippage,SL,TP, EAName, MagicNumber, 0, Red);
return (ticket);
}}
break;
}
//———————————————————
return; // End of start()
}
void close(int type){
if(OrdersTotal()>0){
for(Counter=OrdersTotal()-1;Counter>=0;Counter–){
OrderSelect(Counter,SELECT_BY_POS,MODE_TRADES);
if(type==OP_BUY && OrderType()==OP_BUY){
if(OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber) {
RefreshRates();
OrderClose(OrderTicket(),OrderLots(),NormalizeDouble(Bid,Digits), vSlippage);
} }
if(type==OP_SELL && OrderType()==OP_SELL){
if(OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber) {
RefreshRates(); OrderClose(OrderTicket(),OrderLots(),NormalizeDouble(Ask,Digits),vSlippage);
}}
}}}
// Section 4B: OrdersTotalMagicOpen Function
int OrdersTotalMagicOpen() {
int l_count_0 = 0;
for (int l_pos_4 = OrdersTotal() – 1; l_pos_4 >= 0; l_pos_4–) {
OrderSelect(l_pos_4, SELECT_BY_POS, MODE_TRADES);
if (OrderSymbol() != Symbol() || OrderMagicNumber() != MagicNumber) continue;
if (OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
if (OrderType() == OP_SELL || OrderType() == OP_BUY) l_count_0++;
}
return (l_count_0);
}
</slowmacurrent&&>
It you have no prior programming experience, the above code might look a bit arcane and intimidating. One way to get over the intimidation factor is to worry less about the little details and focus on the big picture.
Try not to figure out exactly how each and every bit of the language works and wondering what is happening behind the scenes, at the processor level, and just accept that it is working. You don’t need to get caught up in the details of the language and it’s format. You don’t need to be concerned with the nuts and bolts in order to understand and construct an EA. At this point in time you just need to know how the pieces of the puzzle fit together, and what are the most important pieces that can be manipulated in order to develop new strategies.
I will assist you in putting the puzzle together, and point you in the direction of the most important pieces. I have numbered and labeled each section in the EA to enable easier cross-referencing as I guide you through the understanding of each of the parts.
Section1: Preprocessor Directives, external and internal Variables
First, a word on Comments. You might notice some comments that I have included after the //.
Any line that begins with // is free text and ignored by the program.
We include comments even though the computer ignores them in order to help explain in plain English the meaning of our programming statements. Yes, programming language can be hard to understand at first glance, and adding comments can be useful as you write your code to make your life easier.
Next, a word on Preprocessor Directives. Each directive begins with a pound sign (#). There are many advanced forms of directives, such as the #import and #include, but we are just using the simplest of them all, the #property copyright preprocessor directive that identifies the code as ours. Other than that, it is not that important and does nothing fancy.
The external variables are next, and they are important. In the previous article, I have explained how a variable is like a small box where can store things for later use. The external variable (which has the word extern preceding it) is important because it displays its parameters outside the program in the Expert Dialog box for the user to easily manipulate.
Many of the external variables you see in the basic EA above are self-explanatory. We will discuss EAName, MagicNumber, Lotsize, TakeProfit and Stoploss when you later take up the syntax of the OrderSend function, found in Section 3E, OrderPlacement. These variables refer mostly to that function.
The interesting external variables in this section are: moving average parameter variables (particularly MAPeriod), OppositeClose, and EnterOpenBar.
Moving Average Parameter Variables.
Notice that I have placed all the moving average parameter values as external variables. I did not have to do this. I could have just just made the most important variable, the MAPeriod, an external variable, leaving the rest of the parameters in their defaulted values within the indicators when I call them in Section 3B, Indicator Calling. I have declared almost all the parameters as external variables just in case I want to optimize any at a later point. For now I’ll probably end up just optimizing the MAPeriod, but it could be useful to optimize some of the others in the future. We will discuss more about these parameters when we tackle Section 3B, Indicator Calling.
My True/False Bool (Boolean) Variables: OppositeClose, and EnterOpenBar
When you see a bool in a variable, it is a type used for values of truth. The type bool comes from Boole, the last name of the inventor of the logical calculus. Let’s examine the bool OppositeClose.
The external bool for this variable allows me to switch on and off the oppositeclose condition. Whenever oppositeclose is referenced in the code, it will be defaulted as true, meaning I want it to be switched on. If set to false, it will be swtiched off. Optionally, instead of using true or false, you can use 0 for false and 1 for true.
The OppositeClose bool refers to the idea of being able to close an order on an opposite signal. What does this mean? If set to true, and I am currently in a long position, and a short entry order is triggered, the short entry order closes out my current long before putting on a short trade. The short entry signal is the opposite signal that closes the current long (and vice versa). I have defaulted oppositeclose as true because I definately want it activated. If I had chosen false, that is, decativated the oppositeclose, the short entry signal would not close out my prior long trade, and my long trade would remain open until it was closed by hitting the stoploss or takeprofit. In general it is a good idea to have the oppositeclose set to true and activated. We will discuss the coding of oppositeclose in Section 3D, Close Conditions, as well as its related function found in Section 4A, Close function.
The EnterOpenBar bool refers to the idea of entering only at the open of each new bar, instead of interbar or close. When making new strategies based on indicators I prefer to default the EnterOpenBar as true in order to quickly see how the strategy backtests. The strategytester has three types of backtest modes in its drop down menu: everytick, control points, and open prices only. Every tick is more accurate but slower than the others. Open prices are less accurate but faster than the others. Control points sits in the middle of the two in both accuracy and speed. However, if EnterOpenBar is set to true, then you can safely backtest on the open prices only mode, vastly increasing your speed of backtesting, while at the same time having the very similar accuracy and results to the everytick mode. Besides the speed of backtesting, I have also noticed that when enteronopenbar is set to true, it improves the overall performance and reliability of the system, particularly if it is based on common indicators. I encourage you to experiment with turning the enteronopenbar to true and false in order to see the differences in results. The coding behind EnterOpenBar can be found in Section 3C, Entry Logic.
Lastingly, in this section I have declared a few internal variables (sometimes called Global Variables), such as
Notice how I do not declare a particular value for each identifier. Without a declared value, each indentifier is defaulted at 0, waiting to be later determined. When it is finished with its determination, it reverts back to 0. Also notice that I am listing the identifiers after double, one after another, seperated by commas, till I end the statement with the semicolon. This can be done because none have a globally distinct value. I could have declared these from within the start() function, instead of here, but having them here in this section allows me to reference them globally, from within any function of the code. That is very handy and saves needless repetition.
Section 2: Initialization
As you can see, there is not much to this section.
What I have included in this section is the code for setting the point value relative to your broker’s currency digits (brokers are set up with either a 4 digit or 5 digit quoting system):
{ vPoint=Point*10; vSlippage=Slippage*10; }
else{ vPoint=Point; vSlippage=Slippage; }
Plain English Translation: if your currency pair is quoted in digits of 3 or 5, then point value will equal Point*10 , and if not (such as 2 or 4), point value will remain as point value without a multiple.
Inserting code to automatically detect and adjust for fractional 3 or 5 digit brokers is a useful item, and I explain it more in its own article, Auto Detect Slippage and Point Values.
Learn Syntax, Language and Structure. Notice how the if condition is put in parenthesis () and the statements are put in braces {}. That is the common structure of an if condition followed by its statements. In this case, the if condition is if(Digits==3 || Digits==5), keeping in mind that the double equal sign (==) stands for equals and the double vertical lines (||) stand for “or”. Yes, you have to be aware of how your diction gets translated into machine language: while it would be convenient if we could just say “and” or “or,” the program will not understand you if you use these words. Instead, you have to use the double vertical lines (||) for “or” and the double ampersand (&&) for “and”.
Lastly, the first statement that falls in brackets{ vPoint=Point*10; vSlippage=Slippage*10; } has actually two statements separated by a semicolon: one statement defining what vPoint means and another statement defining what vSlippage means. When the condition is not met, there is the interlocking else function that points to an alternative compound statement in brackets { vPoint=Point; vSlippage=Slippage; }.
Section 3: The Start () Function
This section is the most important and longest, and it is best to divide this section into separate chuncks, which I have lettered as 3A, 3B etc.
Within the beginning of this start() function I have included the following lines:
return(0); }
Translation: If Bars are less than 100, do not trade, and print on the screen that bars are less than 100. This is useful code to include in order to prevent a trade from occurring with insufficient bars loaded onto the chart.
Learn Syntax, Language and Structure. Here is another if condition (Bars < 100) set within parenthesis after “if”. Now note that the expression that follows the if condition must be set within braces {} if it contains two or more compound statements, and each statement within the braces must be separated by a semicolon. In this example, we have two statements that follow the if condition. In the first statement, Print is a resident function that needs the have a description within quotes and surrounded by parenthesis. It will print that description on the screen when the condition is met. The semicolon completes that expression. In the second statement, return (0) means that no trades will occur, if there is less than 100 bars.
Section 3A: Define Short Tags to Common Trading Functions
Here I have defined a number of short tags that represent common trading functions and outfitted these tags to work with MagicNumbers.
Why would I want my trading functions to work with MagicNumbers?
The MagicNumber is the fingerprint of your EA, allowing the program to differentiate this EA from other EAs (or trades) operating on the same currency and timeframe. For instance, if I would like the program to track my open buy positions for this EA only, and not the open buy positions of the platform itself. Thus, when I reference any of my trade information functions, I would like to have them affiliated with the MagicNumber.
For a complete list and definitions of these trade information functions, click here: http://docs.mql4.com/trading
I make my trading functions work with magicnumbers by placing them under the OrderSelect() Function:
{ if (OrderSelect(Counter-1,SELECT_BY_POS, MODE_TRADES)==true)
if (OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
tag names = common trading functions;
}
}}
Translation: If there is any open or pending trades that have my MagicNumber, the following tag names will stand for common trading functions
Lean Syntax, Language and Structure. You will see some variant of the OrderSelect function quite often in different EAs, often within the first lines of any custom function (I have used it in my own two functions, 4A and 4B). The OrderSelect function selects an order for further processing, returning true if the function succeeds and false if it fails.
Because the OrderSelect() function is so important, I have creating its own article which you can reference called Retrieving Order Information with OrderSelect
For my purposes, I am using the OrderSelect function to select by trades, MODE_TRADES (which means open and pending orders) and MagicNumber. In other words, I want the trading functions that I have subsumed under this function to work with open and pending orders that are of my magicnumber. The third line, particularly the part that says OrderMagicNumber() == MagicNumber, represents the condition for subsuming the trading functions within the MagicNumber. Of all the trading functions I have subsumed under this function, the one that I use later on in the code is OType=OrderType(), which I use in section 3D, Close Functions.
I should also mention the relevance of the first line in that block of code:
This is called a for operator, and it used to loop through a block of code a predetermined number of times. The first expression, int = Counter =1, initializes our Counter variable with a value of 1. The second expression, Counter <=OrdersTotal(), is the condition, if true, will execute the code within braces (if there were 3 open orders, it will execute the loop three time). The third expression, Counter++, means “increment the value of Counter by one.” Every time the loop completes, the counter is incremented by 1 in this example, until eventually all the open orders are accounted for.
Section 3B: Indicator Calling
Here I declare four different moving averages:
FastMAPrevious = iMA(NULL, FastMATime, FastMAPeriod, FastMAShift, FastMAType, FastMAPrice, Current + 1);
SlowMACurrent = iMA(NULL, SlowMATime, SlowMAPeriod, SlowMAShift, SlowMAType, SlowMAPrice, Current + 0);
SlowMAPrevious = iMA(NULL, SlowMATime, SlowMAPeriod, SlowMAShift, SlowMAType, SlowMAPrice, Current + 1);
Each one refers to the moving average indicator that is native to MT4 and which has its own particular syntax:
I like to imagine the structure in parenthesis that follows the iMA indentifier as a bus with a number of designated seats. Each seat in the bus is separated by a comma and is called a parameter. The iMA indicator has seven parameters. Each parameter in turn holds a default value that can be customized (or personalized, to keep with the bus metaphor). It is useful to know what the function of each parameter, the default values taking up each parameter, how they can be customized, and what parameters really drive the bus.
Below is table of the Describing each of the Parameters of the Moving Average:
MA Parameters | Description |
---|---|
Symbol | Symbol for trading, such as EURUSD. Symbol() represents the currency chart’s pair |
TimeFrame | The time period of the chart to apply moving average, usually set to 0, meaning the symbol of the chart EA is attached to. |
MAPeriod | The look-back period of the moving average. This is the most important variable. |
MAShift | The forward shift of the moving average line, in bars, usually set to 0. |
MAMethod | The calculation method of the moving average, with choices including simple, exponential, smoothed or linear weighted. The second most important variable. |
MAPrice | The price array to use when calculating the moving average, either close, open, high, low or some type of average. Usually the default of 0, or close, is used. |
Shift | The backward shift of the bar to return the calculation for. A value of 0 returns the indicator value of the current bar, and a value of 3 will return the indicator value from 3 bars ago. This is the third most important variable, as we shall see. |
A handy quick reference to MA parameters (as well as the parameters to all 20 native indicators) can be found here: http://docs.mql4.com/indicators/iMA
For our immediate purposes, we will be working with the default parameter values, and the most important parameter for our purposes is the MAPeriod, the length of the moving average, which I have defaulted as 2 for the FastMAPeriod and 30 for the SlowMAPeriod. It is the MAPeriod that drives the bus, because it differentiates the fast from the slow moving average.
MAMethod is also important, particularly the Simple (Integer=0) and Exponential (Integer=1). For the fast moving average, I am defaulted with 0 or Simple, and for the slow moving average, I am defaulted with 1 or Exponential. I will thus want the 30 period exponential moving average to cross the 2 period simple moving average in order to trigger a buy signal.
MAShift and MAPrice are usually left at 0, and changing these parameters has little effect.
The last parameter, Shift, bears no relationship to the fourth parameter, MAShift, so do not confuse the two. Actually, this last parameter an important parameter for locating the moving average in time. It especially becomes important for differentiating the previous bar from the current bar, which is integral to our entry and exit logic.
Remember, all these parameters have been placed as external variables in order to easily modify or optimize them at a later stage.
Quick Question: If I am using just a fast and slow moving average for the Dual MA Crossover, why did I have to declare four moving averages?
When I want to make a dual MA Crossover, it is necessary to indicate what takes place before and after the crossover. As we shall see, the instance of the buy crossover is when the current fast MA is over the current slow MA while the previous fast MA was under the previous slow MA. That makes four moving averages that must be defined: FastMACurrent, FastMAPrevious, SlowMACurrent, SlowMAPrevious. What differentiates a current from a previous moving average? The only thing that differentiates the Current from the Previous moving averages is the last parameter, the Shift parameter: Shift is at 0 for current, and 1 for previous.
Section 3C: Entry Logic:
In the first part of the entry logic, we will be coding up the EnterOpenBar logic that we alluded to earlier. Interestingly, this is a short but important piece of code often overlooked by many expert advisors. The code looks like this:
if(EnterOpenBar) if(iVolume(NULL,0,0)>1) openbar=false;
How does the program find the open of new bar? It has to find the first tick, which occurs on the new bar. Thus, in the above code, I am checking for volume and delaying the entry of the trade till it detects the first tick of the new bar found. You can see that I have two if conditional statements back to back.
The first one, “if (enteronopenbar),” refers to the bool variable, which I have earlier defaulted as true. When true it passes on to the next if conditional statement, “if (iVolume(NULL,0,0)>1).” This second if condition checks to see if the volume is 1, in which case openbar becomes true (anything greater than 1 is false) because it has found the first tick of the new bar. Since this checking for openbar is a simple but crucial component for any new system, I have discusses it a bit more in length in its own article Enter on Open Bar.
Next, we move on to the brains of the EA, the strategy conditions for entry and exit.
Here are the buy and sell conditions that I intend to code:
Buy Condition1:
if 3 period moving average crosses above 30 period moving average, buy at market (also close open sell position);
Sell Condition1:
if 3 period moving average crosses under 30 period moving average, sell at market (also close open buy position).
How are these two conditions converted into MQL4 code?
There are many possible ways to code up crossover conditions, but for teaching purposes, we will go with the simplest. MT4 does not have a built in crossover function so we are going to build a common two step work around. We are going to indicate the buy crossover condition by watching if previous bar’s fast moving average was previously below the slow moving average and now the current bar bar’s moving average is above the slow moving average. Ergo, it has crossed over. Perhaps you can now better understand the following code.
if (
FastMACurrent > SlowMACurrent &&
FastMAPrevious < SlowMAPrevious
&& openbar)
{
OpenBuy=true;
if (OppositeClose) CloseSell=true;
}
if (
FastMACurrent FastMAPrevious > SlowMAPrevious && openbar)
{
OpenSell=true;
if (OppositeClose) CloseBuy=true;
}
In terms of syntax, when you declare an if condition, you must put the logic within braces{}, particularly if contains two or more statements. In order to suggest that the FastMACurrent must be greater than SlowMACurrent, I use the > sign, which is called an operator. We are basically going to say, if the previous bar’s 20 period moving average was below the 200 period moving average, and if the current bar’s 20 period moving average is now above the current bar’s 200 period moving average, then buy at market. Since operators are so important to trading conditions, I wrote a brief article on them Simple Logical Operators
After the if condition, there are two statements enclosed in braces {} and separated by semicolons: the one statement sets OpenBuy as true if the moving average conditions have been met, and it is simple enough to understand. The second statement is a bit more nuanced. It sets CloseSell as true if the preceding moving average condition has been met, and the extern bool OppositeClose is also true. Notice how it requests its own internal if condition, in this case the OppositeClose bool = true or false, before it can activate its CloseSell or CloseBuy bools. I like to imagine these internal if bool conditions as key and lock mechanisms, enabling the end user to easily turn on and off mechanism (in this case, OppositeClose) from within the expert properties tab.
Section 3D: Close Conditions
We start off this section with the while operator. It is a simple method of looping in MQL4, similar to the for loop discussed above, but more appropriate if you are unsure of the number of iterations.
Our while loop basically looks like this:
// loop code
}
Next we place our conditions to close our buy and sell orders:
{
close (OP_BUY); // Close Buy
return;
}
OType is a variable that stands for trade information function called OderType(), and the each of the trade order types have a corresponding integer. OrderType == 0 refers to OP_BUY, which is buy position, while OrderType = 1 refers to OP_SELL, a sell position.
Here are various order types and their corresponding integer values:
OrderType | Integer | Description |
---|---|---|
OP_BUY | 0 | Buy Position |
OP_SELL | 1 | Sell Position |
OP_BUYLIMIT | 3 | Buy Limit Pending |
OP_BUYSTOP | 4 | Buy Stop Pending |
OP_SELLLIMIT | 5 | Sell Limit Pending |
OP_SELLSTOP | 6 | Sell Stop Pending |
If there is a current buy position (OType==1) and the bool CloseBuy is (==) true, then my custom close function, close (OP_BUY) can be executed. To learn about my custom close function, click here.
I end this section with a break operator. The operator ‘break’ stops the execution of the nearest external operator of ‘while’, ‘for’ or ‘switch’ type. The execution of the operator ‘break’ consists in passing the control outside the compound operator of ‘while’, ‘for’ or ‘switch’ type to the nearest following operator.
Section 3E: OrderPlacement
After the break operator of the previous section, I begin this section with another while operator. The break of the previous section has passed the control or flow over to this second while loop.
Here in this section the program is going to be placing its buy and sell orders.
It starts with the following condition:
OrdersTotalMagicOpen() is a custom program that we will visit shortly that counts the numbers of Open Orders subsumed within the magic number of the EA. If the order total is 0 (==0), then we can proceed. And (&&) if the bool OpenBuy is true, we can proceed.
Next comes the code for determining the values of the stop losses and profit targets:
Here we are using two if-else sequenced statements back to back. Let us go over them a bit. Keep in mind that an else condition evaluates an alternative condition, providing that the previous if statements false. We are combining else and if to create alternative conditions that will only be executed if true. In this case, we are saying that if StopLoss > 0, then we can determine the value of the StopLoss (Bid-StopLoss *vPoint). If it is instead 0 that is our StopLoss, we do not determine the value of the StopLoss and this alternative scenario is that the StopLoss remains as 0. The same logic is duplicate for the TakeProfit. The values that are determined from these if-else conditions are what turn up in the StopLoss and TakeProfit parameters of the OrderSend() function.
Next we are wanting to continue to refresh rates and push our orders through until they are filled:
while(ticket<=0 && number<100){
RefreshRates();
We are saying that if ticket is 0 (order not yet filled), and if the number of attempts to fill is less than 100, then we will continue to refresh rates and attempt a fill. This is a useful code to insert if there are requotes in a quickly moving market and you want to be filled regardless. Again, you see the while operator in action, this time looping through the tickets and number of attempts to place the order.
Next comes the OrderSend() function. It is such a multifaceted function in its own right that I have wrote a brief article for it here Market Orders with OrderSend
Section 4A: Close Function
see the article on Closing Orders
Section 4B: OrdersTotalMagicOpen Function
see the article on Order Counting Functions