MACD Sample - MetaTrader 5 Expert | Forex Indicator Download - MT4/MT5 Resources



The MACD Sample EA included with the MetaTrader 5 client is a sample Moving Average Convergence Divergence indicator for trading using the EA.
The file MACD Sample.mq5 Expert Advisor is located in terminal_data_folder\MQL5\Experts\Examples\MACD\". This Expert Advisor is an example of object-oriented approach in EA development.
Let's consider the structure of the Expert Advisor and how it works.
1 EA properties
1.1. EA properties
//+------------------------------------------------------------------+ //| MACD sample.mq5 | //| Copyright 2009-2013, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #Property Copyright "Copyright 2009-2013, MetaQuotes Software Corp." #Property association "https://www.mql5.com" # Property version "5.20" #PROPERTY DESCRIPTION “It is important to ensure that experts work with normal people” # Property description "Charts and user settings entered without making any errors" #Property description "In our case the variables (lot size, take profit, trailing stop loss)," # Property description "We check take profit on charts exceeding 2*trend_period bars"
The first 5 lines contain comments, the next 7 lines use preprocessor directives to set the properties of the MQL5 program (copyright, link, version, description) #Properties .
When you run the Expert Advisor, they will be displayed in the "Public" tab:

Figure 1 Common parameters of MACD example EA
1.2.Include files
Next, the #include directive tells the compiler to include files containing trade classes of the standard library.
//--- include files #include <Trade\Trade.mqh> #include <Trade\SymbolInfo.mqh> #include <Trade\PositionInfo.mqh> #include <Trade\AccountInfo.mqh>
Instances of the appropriate classes are then used as member variables of the CExpert class (Section 3).
Then there are types, names, default values, and comments. Their functions are shown in the figure. 2.
//--- EA transaction input parameters Enter double InpLots = 0.1 ; // a lot Enter an integer InpTakeProfit = 50 ; // Take profit (in points) Input integer InpTrailingStop = 30 ; // Trailing stop level (in pips) Input integer InpMACD opening price = 3 ; // MACD opening price (in points) Input integer InpMACD closing level = 2 ; // MACD closing price in points Input integer InpMATrendPeriod = 26 ; // MA trend period
Note that the names of the input parameters have the prefix "Inp". Also note that global variables are prefixed with "Ext". This method of naming variables simplifies the use of many different variables.
InpLots - Volume, InpTakeProfit and InpTrailingStop determine take profit and trailing stop levels,
The text in the input parameter line comment and the default value are displayed in the Options tab instead of the name of the input parameter:

Figure 2. Input parameters of the MACD example EA
1.4.Global variables
Then declare the global variable ExtTimeOut. It will be used to control the execution time of trading operations.
integer external timeout = 10 ; // time between trading operations in seconds
After declaring the CSampleExpert class, line 76 declares another global variable: ExtExpert - CSampleExpert class instance:
//--- ExtExpert global variable CSampleExpert ExtExpert; The ExtExpert object (CSampleExpert class example) contains the basic logic of the trading strategy (Section 3).
2. Event handling function
event handler
2.1. OnInit() initialization function
This OnInit() function is called once during the first startup of the Expert Advisor. Usually in the OnInit() event handler, the EA is ready to run: input parameters are checked, indicators and parameters are initialized, etc. In case of a serious error, when further work is pointless, the function will exit with code INIT_FAILED.
//+------------------------------------------------------------------+ //|Expert initialization function | //+------------------------------------------------------------------+ When initialized with integer ( blank ) { //--- initialize and create all required objects if (!ExtExpert.Init()) Return ( initialization failed ); //--- Return upon successful initialization ( initialization successful ); }
In this case, the Init() method of the ExtExpert object is called, which returns true or false depending on the readiness of all objects required for the operation (see Section 3.4). If an error occurs, OnInit() will exit with return code Initialization failed - this is the correct way to complete EA/indicator operation in case of unsuccessful initialization.
2.2. OnTick() function
This OnTick() function will be called every time a new quote is received for the chart symbol on which the EA is running.
//+------------------------------------------------------------------+ //|New Tick Handling Features for Experts | //+------------------------------------------------------------------+ Blank check ( blank ) { Static date and time limit time = 0 ; //Storage last call time + timeout time //--- If the required time has not passed, no operation will be performed if ( time current ()>=limit time) { //--- check data if ( bar ( symbol (), period ()) > 2 *InpMATrendPeriod) { //--- After calling the Processing() method , increase the value of limit_time to ExtTimeOut if (ExtExpert.Processing()) Limit time = time current () + ExtTimeOut; } } }
The OnTick() event handler contains a mechanism to periodically call the ExtExpert.Processing() method for market analysis and trading operations when trading conditions are met.
The time interval between calls is set by the value of the ExtTimeOut input parameter.
2.3. OnDeInit() deinitialization function
OnDeInit() is called when the EA is removed from the chart. If the program places graphical objects during runtime, they can be removed from the chart.
In these examples, the deinitialization function is not used and no action is performed.
3.CSampleExpert class
3.1. CSampleExpert class
//+------------------------------------------------------------------+ //| MACD example EA class example | //+------------------------------------------------------------------+ ClassCSampleExpert { Protected : //--- Protected variable - class member available only inside class methods Double m_adjust_points; // Multiplier for 3/5 digit quotes CTrade m_trade; // CTrade class example CSymbolInfo m_symbol; // CSymbolInfo class example CPositionInfo m_position; // CPositionInfo class example CAccountInfo m_account; // CAccountInfo Class example //--- indicator handle integer m_handle_macd; // MACD indicator handle integer m_handle_ema; // Moving average indicator handle //--- indicator buffer doubled m_buff_MACD_main[]; // MACD indicator main line buffer doubled m_buff_MACD_signal[]; // MACD indicator signal line buffer doubled m_buff_EMA[]; // EMA indicator buffer //--- double the current indicator value m_macd_current; double m_macd_previous; double m_signal_current; Double m_signal_previous; double m_ema_current; double m_ema_previous; //--- m_macd_open_level with double the level (in standard points) ; double m_macd_close_level; double m_traling_stop; Double m_take_profit; people : //--- Constructor CSampleExpert( blank ); //--- destructor ~CSampleExpert( blank ); //--- public methods can be called from outside the class //--- Initialization method boolean initialization( blank ); //--- deinitialization method blank deinitialization( blank ); //--- Handling method boolean processing( blank ); protected : //--- Protected methods can only be accessed inside class methods boolean initcheckParameters( const integer_adjust ); boolean initialization indicator( blank ); boolean longclose( blank ); boolean close short( blank ); boolean longmodify( blank ); boolean shortModify( blank ); boolean longopen( blank ); boolean short position open( blank ); };
The EA class contains declarations of variables (class members) and functions (class methods).
To make working with variables more convenient, all class member variables contain the prefix "m_" (member), which indicates that the variable is a class member. Before declaring a variable or method, specify its type (or the return value of a function).
The visibility of class member variables and methods is defined using access modifiers . In the CSampleExpert class, protected and public modifiers are used. All variables and methods defined in the public section are public and can be accessed from outside. The CSampleExpert class has five such methods:
CSampleExpert class member variables declared with the protected access modifier are only available inside CSampleExpert class methods (and subclasses).
CSampleExpert class method declared using protected access modifier:
3.2. CSampleExpert class constructor
//+------------------------------------------------------------------+ //| CSampleExpert class constructor | //+------------------------------------------------------------------+ CSampleExpert::CSampleExpert( blank ): m_adjustment point( 0 ), m_handle_macd( INVALID_HANDLE ), m_handle_ema( INVALID_HANDLE ), m_macd_current( 0 ), m_macd_previous( 0 ), m_signal_current( 0 ), m_signal_previous( 0 ), m_ema_current( 0 ), m_ema_previous( 0 ), m_macd_open_level( 0 ), m_macd_close_level( 0 ), m_traling_stop( 0 ), m_take_profit( 0 ) { arraySetForSeries (m_buff_MACD_main, true ); arraySetForSeries (m_buff_MACD_Signal, true ); arraySetAsSeries (m_buff_EMA, true ); }
The class constructor is automatically called when creating a class instance object. When called, the default values of the class member variables (in parentheses) and the time series indexing directions are set to m_buff_MACD_main[], m_buff_MACD_signal[], m_buff_EMA[].
3.3. CSampleExpert class destructor
//+------------------------------------------------------------------+ //| CSampleExpert class destructor | //+------------------------------------------------------------------+ CSampleExpert::~CSampleExpert( blank ) { }
The CSampleExpert class destructor contains no code.
3.4. Init method of CSampleExpert class
//+------------------------------------------------------------------+ //|Initialization and verification of input parameters | //+------------------------------------------------------------------+ boolean CSampleExpert::initialize( blank ) { //--- Set public property m_symbol. name( symbol ()); // symbol m_trade.SetExpertMagicNumber( 12345 ); // magic //--- consider 3/5 digit quote integer number_adjustment = 1 ; if ( m_symbol.number ()== 3 || m_symbol.number ()== 5 ) number_adjust = 10 ; m_adjust_point=m_symbol. point ()*numericadjustment; //--- consider the m_adjusted_point modifier when setting level values m_macd_open_level =InpMACDOpenLevel*m_adjusted_point; m_macd_close_level=InpMACDCloseLevel*m_adjusted_point; m_traling_stop =InpTrailingStop*m_adjusted_point; m_take_profit =InpTakeProfit*m_adjusted_point; //--- Set the slippage of 3 points m_trade.SetDeviationInPoints( 3 * digits adjustment); //--- if (!InitCheckParameters(digits_adjust)) return ( false ); if (!InitIndicators()) Return ( false ); //--- Return ( true ) on successful completion; }
In the Init() method, initialize class member variables and verify input parameters.
The call name() method of the m_symbol object ( symbol information class instance) sets the name of the symbol on which the EA is running, and then the method set ExpertMagicNumber() is called; it sets the value of the EA magic number for the m_trade object (which will be used for trading operations). Afterwards, the number() method is used to request the number of digits after the decimal point of the sign and, if necessary, make corrections to the level value.
Next, set point deviation() calls the method of the m_trade object, in which the value of slippage allowed in the trading operation is set.
3.5. InitCheckParameters method of CSampleExpert class
//+------------------------------------------------------------------+ //|Check input parameters | //+------------------------------------------------------------------+ boolean CSampleExpert::InitCheckParameters( const integer number_adjustment) { //--- Check the correctness of the take profit level if (InpTakeProfit*digits_adjustPrint function ( "Take profit must be greater than %d" , m_symbol.StopsLevel()); return ( false ); } //--- Check the correctness of the trailing stop level if (InpTrailingStop*digits_adjust Print function ( "Trailing stop loss must be greater than %d" , m_symbol.StopsLevel()); return ( false ); } //--- Check the correctness of the trading volume if (InpLots m_symbol.LotsMax()) { Print function ( "Lot size must be in the range %f to %f" ,m_symbol.LotsMin(),m_symbol.LotsMax()); return ( false ); } if ( math antibody (InpLots/m_symbol.LotsStep()- math wheel (InpLots/m_symbol.LotsStep()))> 1.0 E-10 ) { Print function ( "Lot size does not correspond to lot step %f" , m_symbol.LotsStep()); return ( false ); } //--- If Take Profit <= Trailing Stop, display a warning if (InpTakeProfit <= InpTrailingStop) print function ( "Warning: Trailing stop must be less than take profit" ); //--- Return ( true ) on successful completion; }
Check the correctness of the EA input parameters in the InitCheckParameters() method. If any argument is invalid, an appropriate message is displayed and the function returns false.
3.6. InitIndicators() method of CSampleExpert class
//+------------------------------------------------------------------+ //|Indicator initialization method | //+------------------------------------------------------------------+ Boolean value CSampleExpert::InitIndicators( blank ) { //--- create MACD indicator if (m_handle_macd== INVALID_HANDLE ) if ((m_handle_macd= Smoothed Moving Average Convergence and Divergence ( invalid , 0 , 12 , 26 , 9 , PRICE_CLOSE ))== INVALID_HANDLE ) { print function ( "Error creating MACD indicator" ); return ( false ); } //--- create EMA indicator if (m_handle_ema== INVALID_HANDLE ) if ((m_handle_ema= ima ( invalid , 0 ,InpMATrendPeriod, 0 , mode_EMA , PRICE_CLOSE ))== INVALID_HANDLE ) { print function ( "Error while creating EMA indicator" ); return ( false ); } //--- Return ( true ) on successful completion ; }
In the InitIndicators() method, check the correctness of the initial values of the m_handle_macd and m_handle_ema variables (they must be equal to INVALID_HANDLE, since they are initialized in the constructor), as well as the technical indicators MACD and MACD creation (using the MACD and IMA functions). If successful, the function will return true and the indicator handle will be saved in the m_handle_macd and m_handle_ema class members.
The handle to the created indicator will be used to check the calculated data volume ( number of calculated bars ) and obtain the numerical value ( copy buffer ) of the indicator in the Processing() method.
3.7. LongClosed() method of CSampleExpert class
//+------------------------------------------------------------------+ //|Check closing conditions| //+------------------------------------------------------------------+ Boolean value CSampleExpert::LongClosed( blank ) { boolean resolution = false ; //--- Should the position be closed? if (m_macd_current > 0 ) if (m_macd_currentm_signal_previous) if (m_macd_current>m_macd_close_level) { //--- Close position if (m_trade.PositionClose( symbol ())) Print function ( "The long position of %s will be closed" , symbol ()); Other printing functions ( "%s error when closing position: '%s'" , symbol (), m_trade.ResultComment()); resolution= true ; } //--- return result return (research); }
The LongClosed() method will return true (and close the long position) if the closing conditions are met:
3.8. ShortClosed() method of CSampleExpert class
//+------------------------------------------------------------------+ //|Check short closing conditions | //+------------------------------------------------------------------+ Boolean value CSampleExpert::ShortClosed( blank ) { boolean resolution = false ; //--- Should the position be closed? if (m_macd_current < 0 ) if (m_macd_current>m_signal_current && m_macd_previousif( m_macd_current )>m_macd_close_level) { //--- Close position if (m_trade.PositionClose( symbol ())) print function ( "The short position of %s will be closed" , symbol ()); Other printing functions ( "%s error when closing position: '%s'" , symbol (), m_trade.ResultComment()); resolution= true ; } //--- return result return (research); }
The ShortClosed() method returns true (and closes the short position) if it is satisfied to close the short position:
3.9. LongModified() method of CSampleExpert class
//+------------------------------------------------------------------+ //|Check conditions for long position modification | //+------------------------------------------------------------------+ Boolean value CSampleExpert::LongModified( blank ) { boolean resolution = false ; //--- check if trailing stop is required if (InpTrailingStop > 0 ) { if (m_symbol.Bid()-m_position.PriceOpen()>m_adjusted_point*InpTrailingStop) { Double sl = normalized double (m_symbol.Bid()-m_traling_stop, m_symbol.Number ()); Double tp=m_position.TakeProfit(); if (m_position.StopLoss()0.0) { //--- Modify the stop loss and profit if the position (m_trade.PositionModify( symbol (),sl,tp)) Print function ( "%s's long position needs to be modified" , symbol ()); other { Print function ( "%s error when modifying position: '%s'" , symbol (), m_trade.ResultComment()); Print function ( "Modify parameters: SL=%f,TP=%f" , sl, tp); } resolution= true ; } } } //--- return result return (research); }
The LongModified() method returns true (and modifies the stop loss value of the position) if the long position modification conditions are met: If the value of the input InpTrailingStop is >0, then the price is checked from the opening price through the InpTrailingStop point in the direction of the position. Next, calculate the value of the new stop loss level and modify the stop loss parameters of the open position.
3.10. ShortModified method of CSampleExpert class
//+------------------------------------------------------------------+ //|Check conditions for long position modification | //+------------------------------------------------------------------+ Boolean value CSampleExpert::ShortModified( blank ) { boolean resolution= false ; //--- check if trailing stop is required if (InpTrailingStop> 0 ) { if ((m_position.PriceOpen()-m_symbol.Ask())>(m_adjusted_point*InpTrailingStop)) { Double sl = normalized double (m_symbol.Ask()+m_traling_stop, m_symbol.number ()); Double tp=m_position.TakeProfit(); if (m_position.StopLoss()>sl || m_position.StopLoss()== 0.0 ) { //--- Modify the stop loss and profit of the position if (m_trade.PositionModify( symbol (),sl,tp)) Print function ( "%s's short position needs to be modified" , symbol ()); other { Print function ( "%s error when modifying position: '%s'" , symbol (), m_trade.ResultComment()); Print function ( "Modify parameters: SL=%f,TP=%f" , sl, tp); } resolution= true ; } } } //--- return result return (research); }
The ShortModified() method will return true (and modify the stop loss value of the position) if the short position modification conditions are met: If the value of the input InpTrailingStop is >0, then it is checked that the price passes the InpTrailingStop point from the opening price in the direction of the position. Next, calculate the value of the new stop loss level and modify the stop loss parameters of the open position.
3.11. LongOpened() method of CSampleExpert class
//+------------------------------------------------------------------+ //|Check long opening | //+------------------------------------------------------------------+ Boolean CSampleExpert::LongOpened( blank ) { boolean resolution = false ; //--- check the conditions for opening a long position if (m_macd_current < 0 ) if (m_macd_current>m_signal_current && m_macd_previousif( m_macd_current )>(m_macd_open_level) && m_ema_current>m_ema_previous) { Double price=m_symbol.Ask(); Double tp =m_symbol.Bid()+m_take_profit; //--- Check free margin if (m_account.FreeMarginCheck( Token (), OrderTypeBuy ,InpLots,Price) < 0.0 ) Print function ( "We have no money. Available margin = %f" ,m_account.FreeMargin()); other { //--- Open a long position if (m_trade.PositionOpen( symbol (), order type buy , InpLots, price, 0.0 , tp)) Print function ( "%s has opened a position" , symbol ()); other { Print function ( "%s Error opening buy position: '%s'" , symbol (), m_trade.ResultComment()); Print function ( "Open parameters: price=%f, TP=%f" , price, tp); } } resolution= true ; } //--- return result return (research); }
The LongOpened() method will return true (and open a long position) if the conditions for opening a position are met:
When all conditions are met, the available margin is checked (Method of AvailableMarginCheck() AccountInformation standard library class) and a long position is opened using the following command Open () method of TradeNet class.
3.12. ShortOpened method of CSampleExpert class
//+------------------------------------------------------------------+ //|Check short positions | //+------------------------------------------------------------------+ Boolean value CSampleExpert::ShortOpened( blank ) { boolean resolution = false ; //--- check the conditions for opening a short position if (m_macd_current > 0 ) if (m_macd_currentm_signal_previous) if (m_macd_current>(m_macd_open_level) && m_ema_current Double price=m_symbol.Bid(); Double tp =m_symbol.Ask()-m_take_profit; //--- Check free margin if (m_account.FreeMarginCheck( Token (), OrderType_Sell , InpLots, Price) < 0.0 ) Print function ( "We have no money. Available margin = %f" ,m_account.FreeMargin()); other { //--- Open short position if (m_trade.PositionOpen( symbol (), order type_sell ,InpLots,price, 0.0 ,tp)) Print function ( "%s has opened a position" , symbol ()); other { Print function ( "%s error when opening a sell position: '%s'" , symbol (), m_trade.ResultComment()); Print function ( "Open parameters: price=%f, TP=%f" , price, tp); } } resolution= true ; } //--- return result return (research); }
The ShortOpened() method will return true (and open a short position) if the conditions for opening a position are met:
When all conditions are met, the available margin is checked ( AvailableMarginCheck() method of the AccountInformation standard library class) and a short position is opened using the Open() method of the TradeNet class.
3.13 Processing() method of CSampleExpert class
//+------------------------------------------------------------------+ //|The main function returns true if any position is processed | //+------------------------------------------------------------------+ boolean CSampleExpert::process( blank ) { //--- Update quotes if (!m_symbol.RefreshRates()) Return ( error ); //--- update indicator value if ( number of calculated bars (m_handle_macd) < 2 || number of calculated bars (m_handle_ema) < 2 ) return ( false ); if ( copy buffer (m_handle_macd, 0 , 0 , 2 ,m_buff_MACD_main) != 2 || Copy buffer (m_handle_macd, 1 , 0 , 2 ,m_buff_MACD_signal)!= 2 || Copy buffer (m_handle_ema, 0 , 0 , 2 ,m_buff_EMA) != 2 ) Returns ( false ); //--- Simplifies indicator work and speeds up access //--- The current value of the indicator is saved in an internal variable (class member) m_macd_current =m_buff_MACD_main[ 0 ]; m_macd_previous =m_buff_MACD_main[ 1 ]; m_signal_current =m_buff_MACD_signal[ 0 ]; m_signal_previous=m_buff_MACD_signal[ 1 ]; m_ema_current =m_buff_EMA[ 0 ]; m_ema_previous =m_buff_EMA[ 1 ]; //--- Correct market entry is important, but correct exit is more important //--- first check if there is an open position if (m_position.select( symbol ())) { if (m_position.PositionType()== POSITION_TYPE_BUY ) { //--- If necessary, we try to close or modify the long position if (longClose()) return ( true ); if (longmodify()) return ( true ); } other { //--- If necessary, we try to close or modify the short position if (short close()) return ( true ); if (shortmodify()) return ( true ); } } //--- no open positions else { //--- Check the condition and open a long position if necessary if (longopen()) return ( true ); //--- Check the condition and open a short position if necessary if (short open()) return ( true ); } //--- Exit without position processing return ( wrong ); }
The Processing() method of the CSampleExpert class is the method of Expert Advisor trading. Call the Processing() method in the OnTick() event handler and monitor the time interval (not less than ExtTimeOut seconds) between consecutive calls to this method (Section 2.2).
By callingthe refresh() method symbol information class quotes have been updated. This Bar Calculation () function is used to request the number of bars for which the indicator Smoothed Convergence and Divergence Moving Average and Moving Average Calculation (Section 3.6) are used; if the number of bars is less than 2, exit the function and return false.
Next, the copy buffer function call requests the last two values of the technical indicator (main and signal MACD lines and moving average values); if the amount of data copied is less than 2, the function is exited. After that, the indicator values in the arrays m_buff_MACD_main[], m_buff_MACD_signal[] and m_buff_EMA[] are copied to the variables m_macd_current, m_macd_previous, m_signal_current, m_signal_previous, m_ema_current and m_ema_previous.
The next step is to carry out position work through the class C position information standard library. If the select() method call has returned true, it means that there is currently an open position whose type is determined using the positionType() method. Further work will be carried out depending on the type of open position.
The optimal values for the parameters of the Strategy Tester MetaTrader 5 terminal can be found using the following methods.
Figure 3 shows the results of the Expert Advisor test in 2013 using default settings.

Figure 3. Backtest results of MACD sample EA trading
The MACD sample Expert Advisor, included in the standard delivery package of the MetaTrader 5 terminal, is an example of the object-oriented approach in EA development.
Attachment download
📎 macd_sample.mq5 (17.98 KB)
Source: MQL5 #2154
💡 Featured Recommendations
✍️ Latest by the author
- •
- •
- •
- •
- •
- •
📌 Popular topics
- •
- •
- •
- •
- •
- •
- •
- •
🔗 You May Be Interested In
- •
- •
- •
- •
- •
- •