//+------------------------------------------------------------------+
//|                                                  MACD Sample.mq5 |
//|                   Copyright 2009-2013, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright   "Copyright 2014 Semet Gaetan"
#property link        "http://www.xeberon.net/gaetan"
#property version     "1.0"
#property description "Gaetan Semet Expert Advisor"
#property description "This expert advisor triggers position on MACD and"
#property description "crossing Moving Averages. After a bid is placed"
#property description "it is qualified as 'gaining' or loosing'. If loosing,"
#property description "the stop will triggered to stop losses. If gaining,"
#property description "a trailing stop will be placed that allow a retracement"
#property description "of N percents. Only a small number of retracement is"
#property description "allowed, after a stop loss will be placed."



//---
#include <Trade\Trade.mqh>
#include <Trade\SymbolInfo.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\AccountInfo.mqh>

//---
input double InpLots                      = 0.1; // Size of lots for each position
input int    InpAllowedRetracementPercent = 50;  // Allowed retracement for gaining position (in percent)
input int    IntMaxNumberOfRetracement    = 2;   // Maximum number of allowed retracement before placing a close stop loss.
input int    InpMACDOpenLevel             = 3;   // MACD open level (in pips)
input int    InpMACDCloseLevel            = 2;   // MACD close level (in pips)
input int    InpMA20TrendPeriod           = 20;  // Long MA trend period
input int    InpMA6TrendPeriod            = 6;   // Short MA trend period

//---
int ExtTimeOut=10; // time out in seconds between trade operations

//+------------------------------------------------------------------+
//| CMacdExpert class                                         |
//+------------------------------------------------------------------+
class CMacdExpert
{
    protected:
        double            m_adjusted_point;             // point value adjusted for 3 or 5 points
        CTrade            m_trade;                      // trading object
        CSymbolInfo       m_symbol;                     // symbol info object
        CPositionInfo     m_position;                   // trade position object
        CAccountInfo      m_account;                    // account info wrapper
        
        //--- indicators
        int               m_handle_macd;                // MACD indicator handle
        int               m_handle_ema20;               // long moving average indicator handle
        int               m_handle_ema6;                // short moving average indicator handle
        
        //--- indicator buffers
        double            m_buff_MACD_main[];           // MACD indicator main buffer
        double            m_buff_MACD_signal[];         // MACD indicator signal buffer
        double            m_buff_EMA[];                 // EMA indicator buffer
        
        //--- indicator data for processing
        double            m_macd_current;
        double            m_macd_previous;
        double            m_signal_current;
        double            m_signal_previous;
        double            m_ema_current;
        double            m_ema_previous;
        
        //---
        double            m_macd_open_level;
        double            m_macd_close_level;
        double            m_traling_stop;
        double            m_take_profit;
        
    public:
        CMacdExpert(void);
        ~CMacdExpert(void);
        bool              Init(void);
        void              Deinit(void);
        bool              Processing(void);
    
    protected:
        bool              InitCheckParameters(const int digits_adjust);
        bool              InitIndicators(void);
        bool              LongClosed(void);
        bool              ShortClosed(void);
        bool              LongModified(void);
        bool              ShortModified(void);
        bool              LongOpened(void);
        bool              ShortOpened(void);
};

//--- global expert
CMacdExpert ExtExpert;

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CMacdExpert::CMacdExpert(void) :  m_adjusted_point(0),
                                  m_handle_macd(INVALID_HANDLE),
                                  m_handle_ema20(INVALID_HANDLE),
                                  m_handle_ema6(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)
{
    ArraySetAsSeries(m_buff_MACD_main, true);
    ArraySetAsSeries(m_buff_MACD_signal, true);
    ArraySetAsSeries(m_buff_EMA, true);
}

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CMacdExpert::~CMacdExpert(void)
{
}

//+------------------------------------------------------------------+
//| Initialization and checking for input parameters                 |
//+------------------------------------------------------------------+
bool CMacdExpert::Init(void)
{
    //--- initialize common information
    m_symbol.Name(Symbol());                   // symbol
    m_trade.SetExpertMagicNumber(0xcafebabe);  // magic
    
    //--- tuning for 3 or 5 digits
    int digits_adjust=1;
    if (m_symbol.Digits() == 3 || m_symbol.Digits() == 5)
    {
      digits_adjust = 10;
    }
    m_adjusted_point = m_symbol.Point() * digits_adjust;
    
    //--- set default deviation for trading in adjusted points
    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 default deviation for trading in adjusted points
    m_trade.SetDeviationInPoints(3 * digits_adjust);
    
    if (!InitCheckParameters(digits_adjust))
    {
      return(false);
    }
    if (!InitIndicators())
    {
      return(false);
    }
    
    return(true);
}

//+------------------------------------------------------------------+
//| Checking for input parameters                                    |
//+------------------------------------------------------------------+
bool CMacdExpert::InitCheckParameters(const int digits_adjust)
{
    //--- initial data checks
    if (InpTakeProfit * digits_adjust < m_symbol.StopsLevel())
    {
        printf("Take Profit must be greater than %d", m_symbol.StopsLevel());
        return(false);
    }
    if (InpTrailingStop * digits_adjust < m_symbol.StopsLevel())
    {
        printf("Trailing Stop must be greater than %d", m_symbol.StopsLevel());
        return(false);
    }
    
    //--- check for right lots amount
    if (InpLots < m_symbol.LotsMin() || InpLots > m_symbol.LotsMax())
    {
        printf("Lots amount must be in the range from %f to %f", m_symbol.LotsMin(), m_symbol.LotsMax());
        return(false);
    }
    if (MathAbs(InpLots / m_symbol.LotsStep() - MathRound(InpLots / m_symbol.LotsStep())) > 1.0E - 10)
    {
        printf("Lots amount is not corresponding with lot step %f", m_symbol.LotsStep());
        return(false);
    }
    
    //--- warning
    if (InpTakeProfit <= InpTrailingStop)
    {
        printf("Warning: Trailing Stop must be less than Take Profit");
    }
    
    return(true);
}

//+------------------------------------------------------------------+
//| Initialization of the indicators                                 |
//+------------------------------------------------------------------+
bool CMacdExpert::InitIndicators(void)
{
    //--- create MACD indicator
    if (m_handle_macd == INVALID_HANDLE)
    {
        if ((m_handle_macd = iMACD(NULL, 0, 12, 26, 9, PRICE_CLOSE)) == INVALID_HANDLE)
        {
            printf("Error creating MACD indicator");
            return(false);
        }
    }
    
    //--- create EMA 20 indicator and add it to collection
    if (m_handle_ema20 == INVALID_HANDLE)
    {
        if ((m_handle_ema20 = iMA(NULL, 0, InpMA20TrendPeriod, 0, MODE_EMA, PRICE_CLOSE)) == INVALID_HANDLE)
        {
            printf("Error creating EMA indicator");
            return(false);
        }
    }    
    
    //--- create EMA 6 indicator and add it to collection
    if (m_handle_ema6== INVALID_HANDLE)
    {
        if ((m_handle_ema6 = iMA(NULL, 0, InpMA6TrendPeriod, 0, MODE_EMA, PRICE_CLOSE)) == INVALID_HANDLE)
        {
            printf("Error creating EMA indicator");
            return(false);
        }
    }
    
    return(true);
}

//+------------------------------------------------------------------+
//| Check for long position closing                                  |
//+------------------------------------------------------------------+
bool CMacdExpert::LongClosed(void)
{
    bool res = false;
    
    //--- should it be closed?
    if (m_macd_current > 0)
    {
        if ((m_macd_current < m_signal_current) && (m_macd_previous > m_signal_previous))
        {
            if (m_macd_current > m_macd_close_level)
            {
                //--- close position
                if (m_trade.PositionClose(Symbol()))
                {
                    printf("Long position by %s to be closed",Symbol());
                }
                else
                {
                    printf("Error closing position by %s : '%s'", Symbol(), m_trade.ResultComment());
                }
                //--- processed and cannot be modified
                res = true;
            }
        }
    }
    
    return(res);
}

//+------------------------------------------------------------------+
//| Check for short position closing                                 |
//+------------------------------------------------------------------+
bool CMacdExpert::ShortClosed(void)
{
    bool res=false;
    
    //--- should it be closed?
    if (m_macd_current < 0)
    {
        if ((m_macd_current > m_signal_current) && (m_macd_previous < m_signal_previous))
        {
            if (MathAbs(m_macd_current) > m_macd_close_level)
            {
                //--- close position
                if (m_trade.PositionClose(Symbol()))
                {
                    printf("Short position by %s to be closed",Symbol());
                }
                else
                {
                    printf("Error closing position by %s : '%s'", Symbol(), m_trade.ResultComment());
                }
                //--- processed and cannot be modified
                res = true;
            }
        }
    }
    
    return(res);
}

//+------------------------------------------------------------------+
//| Check for long position modifying                                |
//+------------------------------------------------------------------+
bool CMacdExpert::LongModified(void)
{
    bool res = false;
    
    //--- check for trailing stop    
    if (InpTrailingStop > 0)
    {
        if ((m_symbol.Bid() - m_position.PriceOpen()) > (m_adjusted_point * InpTrailingStop))
        {
            double sl = NormalizeDouble(m_symbol.Bid() - m_traling_stop, m_symbol.Digits());
            double tp = m_position.TakeProfit();

            if ((m_position.StopLoss() < sl) || (m_position.StopLoss() == 0.0))
            {
                //--- modify position
                if (m_trade.PositionModify(Symbol(), sl, tp))
                {
                    printf("Long position by %s to be modified",Symbol());
                }
                else
                {
                    printf("Error modifying position by %s : '%s'", Symbol(), m_trade.ResultComment());
                    printf("Modify parameters : SL=%f,TP=%f", sl, tp);
                }
                //--- modified and must exit from expert
                res = true;
            }
        }
    }
    
    return(res);
}

//+------------------------------------------------------------------+
//| Check for short position modifying                               |
//+------------------------------------------------------------------+
bool CMacdExpert::ShortModified(void)
{
    bool   res = false;
    
    //--- check for trailing stop
    if (InpTrailingStop > 0)
    {
        if ((m_position.PriceOpen() - m_symbol.Ask()) > (m_adjusted_point * InpTrailingStop))
        {
            double sl = NormalizeDouble(m_symbol.Ask() + m_traling_stop, m_symbol.Digits());
            double tp = m_position.TakeProfit();
            
            if ((m_position.StopLoss() > sl) || (m_position.StopLoss() == 0.0))
            {
                //--- modify position
                if (m_trade.PositionModify(Symbol(), sl, tp))
                {
                    printf("Short position by %s to be modified",Symbol());
                }
                else
                {
                    printf("Error modifying position by %s : '%s'",Symbol(), m_trade.ResultComment());
                    printf("Modify parameters : SL=%f,TP=%f", sl, tp);
                }
                //--- modified and must exit from expert
                res=true;
            }
        }
    }
    //--- result
    return(res);
}
//+------------------------------------------------------------------+
//| Check for long position opening                                  |
//+------------------------------------------------------------------+
bool CMacdExpert::LongOpened(void)
{
    bool res = false;
    
    //--- check for long position (BUY) possibility
    if (m_macd_current < 0)
    {
        if (m_macd_current > m_signal_current && m_macd_previous<m_signal_previous)
        {
            if ((MathAbs(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 for free money
                if (m_account.FreeMarginCheck(Symbol(), ORDER_TYPE_BUY, InpLots, price) < 0.0)
                {
                  printf("We have no money. Free Margin = %f",m_account.FreeMargin());
                }
                else
                {
                    //--- open position
                    if (m_trade.PositionOpen(Symbol(), ORDER_TYPE_BUY, InpLots, price, 0.0, tp))
                    printf("Position by %s to be opened", Symbol());
                    else
                    {
                        printf("Error opening BUY position by %s : '%s'", Symbol(), m_trade.ResultComment());
                        printf("Open parameters : price=%f,TP=%f", price, tp);
                    }
                }
                //--- in any case we must exit from expert
                res = true;
            }
        }
    }
    
    return(res);
}

//+------------------------------------------------------------------+
//| Check for short position opening                                 |
//+------------------------------------------------------------------+
bool CMacdExpert::ShortOpened(void)
{
    bool res = false;
    
    //--- check for short position (SELL) possibility
    if (m_macd_current > 0)
    {
        if (m_macd_current < m_signal_current && m_macd_previous > m_signal_previous)
        {
            if (m_macd_current > (m_macd_open_level) && m_ema_current < m_ema_previous)
            {
                double price = m_symbol.Bid();
                double tp    = m_symbol.Ask() - m_take_profit;
                
                //--- check for free money
                if (m_account.FreeMarginCheck(Symbol(), ORDER_TYPE_SELL, InpLots, price) < 0.0)
                {
                    printf("We have no money. Free Margin = %f",m_account.FreeMargin());
                }
                else
                {
                    //--- open position
                    if (m_trade.PositionOpen(Symbol(), ORDER_TYPE_SELL, InpLots, price, 0.0, tp))
                    {
                      printf("Position by %s to be opened",Symbol());
                    }
                    else
                    {
                        printf("Error opening SELL position by %s : '%s'", Symbol(), m_trade.ResultComment());
                        printf("Open parameters : price=%f,TP=%f", price, tp);
                    }
                }
                //--- in any case we must exit from expert
                res = true;
            }
        }
    }
    
    //--- result
    return(res);
}

//+------------------------------------------------------------------+
//| main function returns true if any position processed             |
//+------------------------------------------------------------------+
bool CMacdExpert::Processing(void)
{
    //--- refresh rates
    if (!m_symbol.RefreshRates())
    {
      return(false);
    }
    
    //--- refresh indicators
    if (BarsCalculated(m_handle_macd) < 2 || BarsCalculated(m_handle_ema20) < 2 || BarsCalculated(m_handle_ema6) < 2)
    {
      return(false);
    }
    
    if (CopyBuffer(m_handle_macd, 0, 0, 2, m_buff_MACD_main)  != 2 ||
        CopyBuffer(m_handle_macd, 1, 0, 2, m_buff_MACD_signal)!= 2 ||
        CopyBuffer(m_handle_ema6, 0, 0, 2, m_buff_EMA)        != 2 ||
        CopyBuffer(m_handle_ema20, 0, 0, 2, m_buff_EMA)       != 2)
    {
      return(false);
    }
    
    //   m_indicators.Refresh();
    //--- to simplify the coding and speed up access
    //--- data are put into internal variables
    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];
    
    //--- it is important to enter the market correctly,
    //--- but it is more important to exit it correctly...
    //--- first check if position exists - try to select it
    if (m_position.Select(Symbol()))
    {
        if (m_position.PositionType() == POSITION_TYPE_BUY)
        {
            //--- try to close or modify long position
            if (LongClosed())
            {
              return(true);
            }
            if (LongModified())
            {
              return(true);
            }
        }
        else
        {
            //--- try to close or modify short position
            if (ShortClosed())
            {
              return(true);
            }
            if (ShortModified())
            {
              return(true);
            }
        }
    }
    //--- no opened position identified
    else
    {
        //--- check for long position (BUY) possibility
        if (LongOpened())
        {
          return(true);
        }
        //--- check for short position (SELL) possibility
        if (ShortOpened())
        {
          return(true);
        }
    }
    
    //--- exit without position processing
    return(false);
}
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(void)
{
    //--- create all necessary objects
    if (!ExtExpert.Init())
    {
      return(INIT_FAILED);
    }
    
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert new tick handling function                                |
//+------------------------------------------------------------------+
void OnTick(void)
{
    static datetime limit_time = 0; // last trade processing time + timeout
    
    //--- don't process if timeout
    if (TimeCurrent() >= limit_time)
    {
        //--- check for data
        if (Bars(Symbol(), Period()) > 2 * InpMA20TrendPeriod)
        {
            //--- change limit time by timeout in seconds if processed
            if (ExtExpert.Processing())
            {
              limit_time = TimeCurrent() + ExtTimeOut;
            }
        }
    }
}
//+------------------------------------------------------------------+