/*

Ejemplos para control:

Spread
ECN - Control interno
Filtro Horario con opcion de cierre fin de semana y seleccion horaria.
Varios Money Management en este caso tambien tienen en cuenta la distancia del Stop Loss, para realizar el calculo.
Martingala
Trailing Stop y BreakEven.
Alertas.

*/

#include <stdlib.mqh>
#include <stderror.mqh>

//***********************************************************************************************************************************
//***********************************************************************************************************************************

input string     ExpertName                    = "===  ===";

input string     GeneralParameters             = "===== GENERAL PARAMETERS =====";

input int        Magic                         = 11111; 
input int        MaxSpreadPermitted            = 2;           
input int        MaximumVolumeSize             = 10;
input string     CustomComment                 = "Setting 1";

//***********************************************************************************************************************************
//***********************************************************************************************************************************

input string     MoneyManagement               = "===== MONEY MANAGEMENT =====";

input int        MoneyManagType                = 0;    
input string     MMT_0                         = "MMT = 0, Fixed Volume";
input string     MMT_1                         = "MMT = 1, % Account Balance";
input string     MMT_2                         = "MMT = 2, % Account Equity";

input double     LotSize                       = 0.01;

input double     RiskAccountBalance            = 15;

input double     RiskAccountEquity             = 15;


input string     Martingale                    = "===== MARTINGALE =====";

input int        MartingaleType                = 0;    
input string     MGT_0                         = "MGT = 0, NO Martingale";
input string     MGT_2                         = "MGT = 2, Factor Multiplication";

input double     Martingale_MultiplyOnLoss     = 2.0;

//***********************************************************************************************************************************
//***********************************************************************************************************************************

input string     OrderSettings                 = "===== ORDER SETTINGS =====";

input bool       CloseReverseOperation         = false;           

//***********************************************************************************************************************************
//***********************************************************************************************************************************

input string     Positions                     = "===== POSITIONS SETTINGS =====";
   
input double     TakeProfit                    = 50;
input double     StopLoss                      = 30;
  
input int        TrailingMode                  = 0;
input string     TM_0                          = "TM = 0, Off";
input string     TM_1                          = "TM = 1, -- Mode 1 -- BreakEven & Trailing Stop";
input string     Mode1                         = "----- MODE 1 -----";
input double     BreakEvenStartPips            = 15;
input double     BreakEvenPipsLockIn           = 5;
input double     TrailingStopPips              = 15;
input double     TrailingStopStepPips          = 5;

//***********************************************************************************************************************************
//***********************************************************************************************************************************

input string     FilterTime                    = "===== FILTER TIME =====";

input bool       UseFilterTime                 = false;

input int        ServerOrLocalTime             = 1;
input string     SOLT_0                        = "SOLT = 0, Server Time";
input string     SOLT_1                        = "SOLT = 1, Local Time";

input bool       Sunday                        = false;
input bool       Monday                        = true;
input bool       Tuesday                       = true;
input bool       Wednesday                     = true;
input bool       Thursday                      = true;
input bool       Friday                        = true;
       
input int        StartHour                     = 9; 
input int        StartMinute                   = 0;
//input int        StartSecond                   = 0;

input int        EndHour                       = 23;
input int        EndMinute                     = 0;
//input int        EndSecond                     = 0;

input bool       CloseTradeFriday              = false;
   
input int        CloseHourFriday               = 23;
input int        CloseMinuteFriday             = 30;
//input int        CloseSecondFriday             = 0;

//***********************************************************************************************************************************
//***********************************************************************************************************************************

input string     IndicatorsSettings            = "===== INDICATORS SETTINGS=====";

//***********************************************************************************************************************************
//***********************************************************************************************************************************

input string     Alerts                        = "===== ALERTS =====";

input bool       AlertEnable                   = true;

//***********************************************************************************************************************************
//***********************************************************************************************************************************

int      DecimalPoints, digit, pipMultiplier;
datetime lastCandle;
int      signal;
int      tradeDays[7];
int      startHour[7], startMin[7];
int      endHour[7], endMin[7];
bool     _res;

//***********************************************************************************************************************************

int OnInit()
{
  DecimalPoints = 1;
  double x = MarketInfo(Symbol(), MODE_LOTSTEP) * 10;
  while (x < 1)
  { 
   x *= 10; DecimalPoints += 1; 
  }
  
  digit = _Digits;
  if (digit==2 || digit==4) pipMultiplier = 1;
  if (digit==3 || digit==5) pipMultiplier = 10;
  if (digit==6)             pipMultiplier = 100;
  
  lastCandle = 0; signal = -1;
  InitTime();
 
  return(INIT_SUCCEEDED);
}

//***********************************************************************************************************************************   

void OnDeinit(const int reason)
{
  
}

//***********************************************************************************************************************************

void OnTick()
{

  if (lastCandle != Time[0])
  {
    signal = GetSignal(1);
    lastCandle = Time[0];
  }
 
  if (CloseTradeFriday && DayOfWeek() == 5 && Hour() >= CloseHourFriday && Minute() >= CloseMinuteFriday)
  {
    if (IsAnyOrderActive(-1) != -1)
    {
      if (OrderType() == OP_BUY)
        _res = OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 0, clrBlue);
      if (OrderType() == OP_SELL)
        _res = OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 0, clrRed);
    }
    return;
  }
 
  if (signal != -1 && (MaxSpreadPermitted == 0 || NormalizeDouble(Ask-Bid, _Digits)<=NormalizeDouble(MaxSpreadPermitted*pipMultiplier*_Point, _Digits)))
  {
    if (CloseReverseOperation)
    {
      if (signal == OP_BUY && IsAnyOrderActive(OP_SELL) != -1)
        _res = OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 0, clrBlue);
      if (signal == OP_SELL && IsAnyOrderActive(OP_BUY) != -1)
        _res = OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 0, clrRed);
    }    
    bool allow = !UseFilterTime || CheckTime();
    if (allow && DoesSignalCatched(signal, Time[0]) == -1)
      if (!CloseReverseOperation || ((signal == OP_BUY && IsAnyOrderActive(OP_SELL) == -1) || (signal == OP_SELL && IsAnyOrderActive(OP_BUY) == -1)))
        SetOrder(signal);
  }

  SetSLTP();
  if (TrailingMode == 1) 
  { 
   DoBE(); DoTrail(); 
  }
 
}

//***********************************************************************************************************************************

int SetOrder(int type)
{
  color col;
  double price = 0, lot = 0;
  if (type == OP_BUY) 
  { 
   price = Ask; col = clrBlue; 
  }
  if (type == OP_SELL) 
  { 
   price = Bid; col = clrRed; 
  }
  
  lot = LotSize;
  double PipValue = MarketInfo(Symbol(), MODE_TICKVALUE) / MarketInfo(Symbol(), MODE_TICKSIZE);
  
  if (MoneyManagType == 1)
    lot = NormalizeDouble(AccountBalance()*RiskAccountBalance / (100*PipsToPoints(StopLoss)*PipValue), DecimalPoints);
  
  if (MoneyManagType == 2)
    lot = NormalizeDouble(AccountEquity()*RiskAccountEquity / (100*PipsToPoints(StopLoss)*PipValue), DecimalPoints);
  
  if (MartingaleType != 0)
    lot = MathPow(Martingale_MultiplyOnLoss, GetLossCount())*lot;
  
  if (NormalizeDouble(lot, DecimalPoints)>=NormalizeDouble(MaximumVolumeSize, DecimalPoints)) return (-2);
  
  if (lot < MarketInfo(Symbol(), MODE_MINLOT)) lot = MarketInfo(Symbol(), MODE_MINLOT);
  
  if (lot > MarketInfo(Symbol(), MODE_MAXLOT)) lot = MarketInfo(Symbol(), MODE_MAXLOT);
  int ticket = OrderSend(Symbol(), type, lot, price, 0, 0, 0, CustomComment, Magic, 0);
  int err = GetLastError();
  
  if (ticket == -1 && err > 0)
    Print("err=", IntegerToString(err), ", type=" + 
    IntegerToString(type), ", price=", DoubleToStr(price, _Digits), ", lot=", DoubleToStr(lot, DecimalPoints), ", ", ErrorDescription(err));
  
  else if (ticket != -1) SetSLTP();
  return (ticket);
}

//***********************************************************************************************************************************

void SetSLTP()
{
  double sl, tp;
  for (int i = OrdersTotal() - 1; i >= 0; i--)
    if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      if (OrderSymbol() == Symbol() && OrderMagicNumber() == Magic)
        if (OrderStopLoss() == 0 && OrderTakeProfit() == 0)
        {
          sl = 0; tp = 0;
          if (OrderType() == OP_BUY)
          {
            if (TakeProfit != 0) tp = OrderOpenPrice() + PipsToPoints(TakeProfit);
            if (StopLoss != 0) sl = OrderOpenPrice() - PipsToPoints(StopLoss);
          }
          else if (OrderType() == OP_SELL)
          {
            if (TakeProfit != 0) tp = OrderOpenPrice() - PipsToPoints(TakeProfit);
            if (StopLoss != 0) sl = OrderOpenPrice() + PipsToPoints(StopLoss);
          }
          if (sl != 0 || tp != 0) _res = OrderModify(OrderTicket(), OrderOpenPrice(), sl, tp, 0);
        }
}

//***********************************************************************************************************************************

int IsAnyOrderActive(int type)
{
  int i;
  for (i = OrdersTotal() - 1; i >= 0; i--)
    if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      if (OrderSymbol() == Symbol() && OrderMagicNumber() == Magic && (OrderType() == type || type == -1))
        return (OrderTicket());
  return (-1);
}

//***********************************************************************************************************************************

int DoesSignalCatched(int type, datetime from)
{
  int i;
  for (i = OrdersTotal() - 1; i >= 0; i--)
    if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      if (OrderSymbol()==Symbol() && OrderMagicNumber()==Magic && OrderType()==type && OrderOpenTime()>=from)
        return (OrderTicket());
  for (i = OrdersHistoryTotal() - 1; i >= 0; i--)
    if (OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
      if (OrderSymbol()==Symbol() && OrderMagicNumber()==Magic && OrderType()==type && OrderOpenTime()>=from)
        return (OrderTicket());
  return (-1);
}

//***********************************************************************************************************************************

int GetLossCount()
{
  int count = 0;
  for (int i = OrdersHistoryTotal() - 1; i >= 0; i--)
    if (OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
      if (OrderSymbol() == Symbol() && OrderMagicNumber() == Magic)
      {
        if (OrderProfit() < 0) count++; else break;
      }
  return (count);
}

//***********************************************************************************************************************************

void DoTrail()
{
  double sl;
  int step;
  for (int i = OrdersTotal() - 1; i >= 0; i--)
    if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      if (OrderSymbol() == Symbol() && OrderMagicNumber() == Magic)
      {
        sl = OrderStopLoss();
        if (sl == 0) sl = OrderOpenPrice();
        if (OrderType() == OP_BUY)
        {
          step = (int)(MathFloor((Bid-sl) / PipsToPoints(TrailingStopStepPips)));
          if (step >= 1 && (OrderStopLoss() == 0 || NormalizeDouble(OrderStopLoss(), _Digits)<NormalizeDouble(Bid-PipsToPoints(TrailingStopPips), _Digits)))
            _res = OrderModify(OrderTicket(), OrderOpenPrice(), NormalizeDouble(Bid-PipsToPoints(TrailingStopPips), _Digits), OrderTakeProfit(), 0);
        }
        if (OrderType() == OP_SELL)
        {
          step = (int)(MathFloor((sl-Ask) / PipsToPoints(TrailingStopStepPips)));
          if (step >= 1 && (OrderStopLoss() == 0 || NormalizeDouble(OrderStopLoss(), _Digits)>NormalizeDouble(Ask+PipsToPoints(TrailingStopPips), _Digits)))
            _res = OrderModify(OrderTicket(), OrderOpenPrice(), NormalizeDouble(Ask+PipsToPoints(TrailingStopPips), _Digits), OrderTakeProfit(), 0);
        }
      }
}

//***********************************************************************************************************************************

void DoBE()
{
  double sl;
  for (int i = OrdersTotal() - 1; i >= 0; i--)
    if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      if (OrderSymbol() == Symbol() && OrderMagicNumber() == Magic)
      {
        sl = OrderStopLoss();
        if (sl == 0) sl = OrderOpenPrice();
        if (OrderType() == OP_BUY && NormalizeDouble(Bid-OrderOpenPrice(), _Digits)>=NormalizeDouble(PipsToPoints(BreakEvenStartPips), _Digits))
        {
          if (OrderStopLoss() == 0 || NormalizeDouble(OrderStopLoss(), _Digits)<NormalizeDouble(OrderOpenPrice(), _Digits))
            _res = OrderModify(OrderTicket(), OrderOpenPrice(), NormalizeDouble(OrderOpenPrice()+PipsToPoints(BreakEvenPipsLockIn), _Digits), OrderTakeProfit(), 0);
        }
        if (OrderType() == OP_SELL && NormalizeDouble(OrderOpenPrice()-Ask, _Digits)>=NormalizeDouble(PipsToPoints(BreakEvenStartPips), _Digits))
        {
          if (OrderStopLoss() == 0 || NormalizeDouble(OrderStopLoss(), _Digits)>NormalizeDouble(OrderOpenPrice(), _Digits))
            _res = OrderModify(OrderTicket(), OrderOpenPrice(), NormalizeDouble(OrderOpenPrice()-PipsToPoints(BreakEvenPipsLockIn), _Digits), OrderTakeProfit(), 0);
        }
      }
}

//***********************************************************************************************************************************

int GetSignal(int shift)
{
  // Seņales del o los indicadores
  return (-1);
}

//***********************************************************************************************************************************

void InitTime()
{
   ArrayInitialize(tradeDays, 0);
   if (Sunday == 1)
   {
      tradeDays[0] = 1;
      startHour[0] = StartHour; startMin[0] = StartMinute;
      endHour[0] = EndHour; endMin[0] = EndMinute;
   }
   if (Monday == 1)
   {
      tradeDays[1] = 1;
      startHour[1] = StartHour; startMin[1] = StartMinute;
      endHour[1] = EndHour; endMin[1] = EndMinute;
   }
   if (Tuesday == 1)
   {
      tradeDays[2] = 1;
      startHour[2] = StartHour; startMin[2] = StartMinute;
      endHour[2] = EndHour; endMin[2] = EndMinute;
   }
   if (Wednesday == 1)
   {
      tradeDays[3] = 1;
      startHour[3] = StartHour; startMin[3] = StartMinute;
      endHour[3] = EndHour; endMin[3] = EndMinute;
   }
   if (Thursday == 1)
   {
      tradeDays[4] = 1;
      startHour[4] = StartHour; startMin[4] = StartMinute;
      endHour[4] = EndHour; endMin[4] = EndMinute;
   }
   if (Friday == 1)
   {
      tradeDays[5] = 1;
      startHour[5] = StartHour; startMin[5] = StartMinute;
      endHour[5] = EndHour; endMin[5] = EndMinute;
   }
   
}

//***********************************************************************************************************************************

bool CheckTime()
{
   bool AllowTrade = true;
   MqlDateTime mdt;
   datetime time;
   
   if (ServerOrLocalTime == 1) time = TimeCurrent(); 
   else time = TimeLocal();
   TimeToStruct(time, mdt);
   
   if (tradeDays[mdt.day_of_week] == 0) AllowTrade = false;
   string start, end;
   
   if (startHour[mdt.day_of_week] < 10) start = "0" + IntegerToString(startHour[mdt.day_of_week]);
   else start = IntegerToString(startHour[mdt.day_of_week]);
   
   if (startMin[mdt.day_of_week] < 10) start = start + ":0" + IntegerToString(startMin[mdt.day_of_week]);
   else start = start + ":" + IntegerToString(startMin[mdt.day_of_week]);
   
   if (endHour[mdt.day_of_week] < 10) end = "0" + IntegerToString(endHour[mdt.day_of_week]);
   else end = IntegerToString(endHour[mdt.day_of_week]);
   
   if (endMin[mdt.day_of_week] < 10) end = end + ":0" + IntegerToString(endMin[mdt.day_of_week]);
   else end = end + ":" + IntegerToString(endMin[mdt.day_of_week]);
   
   if (start < end)
      if (TimeToString(time, TIME_MINUTES) < start || TimeToString(time, TIME_MINUTES) > end)
         AllowTrade = false;
   
   if (start > end)
      if (TimeToString(time, TIME_MINUTES) <= start && TimeToString(time, TIME_MINUTES) >= end)
         AllowTrade = false;
         
   return (AllowTrade);
   
}

//***********************************************************************************************************************************

double PipsToPoints(double pips)
{
  return (pips * pipMultiplier * _Point);
}
//***********************************************************************************************************************************
//***********************************************************************************************************************************