KaanForex
29-11-2018
277

Basic Expert Advisor: MA Cross

The best way to illustrate how to code up an expert advisor is by example. 

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, ForexRazor.Com" 
#property link "http://www.forexrazor.com/"

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;

// Section 2: Initialization

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&& 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()
}


// Section 4A: Close Function

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);
}


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. 
 

extern bool OpositeClose=true


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: everytickcontrol 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
 

double ticket, number, vPoint;


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. 
 

Razor Tip!Remember, any time you come across specific indentifiers and it is hard to see what part of the code they refer to, there is a fast way to find their matching counterparts. Just copy and paste the indentifier (ex: ExpertName) into the find field (Cnt+F) in order to quickly jump down to the matching identifier residing in other parts of the code. Unless you like playing ISPY with words, you probably will find yourself doing this often to match up the different parts of the code.

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): 
 

if(Digits==3 || Digits==5)
{ 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/Define Slippage and Point Values for 5 Digit Brokers

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".  
 

NoteWhile it is easy to type in the double ampersand (&&) for "and," it is hard to type the double vertical lines (||) for "or" so a quick short cut for this is just to copy and paste it.


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: 
 

if(Bars<100) { Print("Bars less than 100"); 
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.
 

NoteEvery left brace must have a matching right bracket, or it will not compile, so we close the two statements with a right brace.

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: 

Đăng ký tài khoản



Log In Your Account