//+------------------------------------------------------------------+
//|                         	               UniLineBreak v1_2.mq4 |
//+------------------------------------------------------------------+

#include <stdlib.mqh>
#include <stderror.mqh>

extern string     ExpertName                    = "=== UniLineBreak_v1__2 ===";

extern string     GeneralParameters             = "===== GENERAL PARAMETERS =====";
extern int        MagicNumber                   = 456789;      // Manual magic number
extern bool       SpreadProtection              = true;        // Maximum spread protection
extern int        MaxSpreadPermitted            = 2;          // Value of maximum spread allowed
extern int        MaxSlippage                   = 2;          // Maximum slippage allowed
extern bool       EcnBroker                     = true;        // Boolean to define if is ECN broker or not
extern int        NumberOfTry                   = 3;           // Number of tries to modify the orders to the broker.
extern string     CustomComment                 = "Setting 1"; // Allow customize the comment of the EA

extern string     MoneyManagement               = "===== MONEY MANAGEMENT =====";
extern double     Lots                          = 0.01;        // Lot size

//When MM_Risk is disabled: use lot size. 
extern string     MM_Risk                       = "----- Risk Money Management -----";
extern bool       UseMoneyManagementRisk        = false;
extern double     RiskAB                        = 15;          // Risk is % of account balance to risk per sequence

//When MM_Martingale is active: value of initial lot.
extern string     MM_Martingale                 = "----- Martingale -----";
extern bool       UseMartingale_Loss            = false;
extern double     LotsMultiplier_Loss           = 2.0;         // Multiplication factor of lot per each lost operation.

// When Risk and MM_Martingale are both disabled: fixed lot size.
// If MM_Risk is active, never can be active MM_Martingale and viceversa (if MM_Martingale is active, never can be active MM_Risk).

extern string     OrderSettings                 = "===== ORDER SETTINGS =====";
// The order always are stop pending orders
// To the buy orders, it calculates from High of the candle of the signal
// To the sell orders, it calculates from Low of the candle of the signal
// The orders expire when are not executed and the indicator show another new signal

extern int        MaxOrders                     = 1;     // Max Number of orders Pending.
extern double     OrderDistanceBuyPips          = 3;    // Distance in points from: High(Pending)
extern double     OrderDistanceSellPips         = 3;    // Distance in points from: Low(Pending)                                           
                                                             
extern bool       OnlyBuyMode                   = true;  // Only Buy Mode switch
extern bool       OnlySellMode                  = true;  // Only Sell Mode switch

extern string     ExitSettings                  = "===== EXIT SETTINGS =====";
extern bool       CloseOnTrendChange            = false; // Close position by change of indicator signal

// IF THERE IS AN OPEN ACTIVE ORDER AND APPEAR A NEW ONE FROM THE INDICATOR, THE NEW SIGNAL WILL BE PROCESSED WITH THE FOLLOW METHOD:

// When CloseOnTrendChange  = false
// 1- Exist an active order
// 2- The signal don't touch Take Profit or Stop Loss (is still active)
// 3- If there is a new signal,it waits until the price close at Take Profit or Stop Loss
// 4- Once the signal will be closed, the sistem will be waiting for the next signal

// When CloseOnTrendChange  = true
// 1- Exist an active order
// 2- The signal don't touch Take Profit or Stop Loss (is still active)
// 3- If there is a new signal from the indicator, the active order automatically will be closed. It will be closed on the same price 
//of the close candle of the new signal (I mean, when the bar of the new signal closes)
// 4- It will lunch a new pending stop order at the same candle

extern string     ControlBuy                    = "===== CONTROL BUY =====";
extern double     TakeProfitPipsBuy             = 20;
extern double     StopLossPipsBuy               = 20;
extern bool       HideBuyTP_SL                  = false;  // Hidden TakeProfit And StopLoss Mode
extern bool       UseBreakEvenBuy               = false;  // If BreakEven is active, TrailingStop can't be active
extern double     MoveToBreakEveAtPipsBuy       = 12;    // When the price moves to X benefit in pips move SL to BE
extern double     MoveToBreakEvenLockPipsBuy    = 5;     // When the price is BreakEven +/- x pips, move SL to BE
extern bool       UseTrailingStopBuy            = false;  // If the TrailingStop is active, Breakeven can't be active.
extern double     TrailingStopBuy               = 10;
extern double     TrailingStepBuy               = 5;

extern string     ControlSell                   = "===== CONTROL SELL =====";
extern double     TakeProfitPipsSell            = 20;
extern double     StopLossPipsSell              = 20;
extern bool       HideSellTP_SL                 = false;  // Hidden TakeProfit And StopLoss Mode
extern bool       UseBreakEvenSell              = false;  // If BreakEven is active, TrailingStop can't be active
extern double     MoveToBreakEveAtPipsSell      = 12;    // When the price moves to X benefit in pips move SL to BE
extern double     MoveToBreakEvenLockPipsSell   = 5;     // When the price is BreakEven +/- x pips, move SL to BE
extern bool       UseTrailingStopSell           = false;  // If the TrailingStop is active, Breakeven can't be active.
extern double     TrailingStopSell              = 10;
extern double     TrailingStepSell              = 5;

extern string     IndicatorsSettings            = "===== INDICATORS SETTINGS=====";
extern string     __1__                         = "===== UniLineBreak_v2=====";
extern int        TimeFrame                     = 0; // TimeFrame in min
extern int        Price                         = 0; // Applied Price: 0-C,1-O,2-H,3-L,4-Median,5-Typical,6-Weighted
extern int        NumberLines                   = 3; // Number Lines 
extern int        HiLoMode                      = 0; // 0-off,1-on
extern double     MinHeight                     = 0; // in pips
extern int        PreSmooth                     = 1; // Period of Pre-smoothing 
extern int        Pole                          = 1; // 1-EMA(1 pole),2-DEMA(2 poles),3-TEMA(3 poles),4-QEMA(4 poles)...
extern int        Order                         = 1; // Smoothing Order(min. 1)
extern double     WeightFactor                  = 2; // Weight Factor (ex.Wilder=1,EMA=2)   
extern double     DampingFactor                 = 1; // Damping Factor

extern string     Alerts                        = "===== ALERTS =====";
extern bool       ShowMessages                  = true;
extern bool       ShowAlerts                    = true;

extern bool dummyResult;
//Message:(It shows a message on the template that disappears at the moment of fix the error)
//+------------------------------------------------------------------+
//| Local Parameters Definition                                      |
//+------------------------------------------------------------------+
int DecimalPoints;
double point;
bool parameters;
datetime lastCandle;
int signal;
string gbl = "UniLineBreak.";
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
{
  //------------------------------------------------------------------
  DecimalPoints = 1;
  double x = MarketInfo(Symbol(), MODE_LOTSTEP) * 10;
  while (x < 1)
  { x *= 10; DecimalPoints += 1; }
  //------------------------------------------------------------------
  point = Point;
  if (Digits == 3 || Digits == 5)
  {
    point = Point * 10;
    MaxSlippage = MaxSlippage * 10;
  }
  //------------------------------------------------------------------
  parameters = true;
  if (UseMoneyManagementRisk && UseMartingale_Loss)
  {
    Alert("error in parameters\nboth UseMoneyManagementRisk and UseMartingale_Loss inputs can not be true at same time");
    parameters = false;
  }
  if (UseBreakEvenBuy && UseTrailingStopBuy)
  {
    Alert("error in parameters\nboth UseBreakEvenBuy and UseTrailingStopBuy inputs can not be true at same time");
    parameters = false;
  }
  if (UseBreakEvenSell && UseTrailingStopSell)
  {
    Alert("error in parameters\nboth UseBreakEvenSell and UseTrailingStopSell inputs can not be true at same time");
    parameters = false;
  }
  //------------------------------------------------------------------
  lastCandle = 0;
  if (IsTesting()) { gbl = "B." + gbl; GlobalVariablesDeleteAll(gbl); }
  //------------------------------------------------------------------
  return(0);
  //------------------------------------------------------------------
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
{
  //------------------------------------------------------------------
  //------------------------------------------------------------------
  return(0);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
{
  //------------------------------------------------------------------
  if (!parameters) return (0);
  //------------------------------------------------------------------
  if (lastCandle != Time[0])
  {
    lastCandle = Time[0];
    signal = GetSignal(1);
  }
  //------------------------------------------------------------------
  if (!OnlyBuyMode && signal == OP_BUY) signal = -1;
  if (!OnlySellMode && signal == OP_SELL) signal = -1;
  //------------------------------------------------------------------
  if (signal != -1 && TimeCurrent()-Time[0] < 15)
  {
    if (DoesSignalCatched(signal) == -1 && (!SpreadProtection || NormalizeDouble(Ask-Bid-MaxSpreadPermitted*point, Digits)<=0))
    {
      if (IsAnyOrderActive(OP_BUYSTOP) != -1 || IsAnyOrderActive(OP_SELLSTOP) != -1) dummyResult = OrderDelete(OrderTicket());
      if (CloseOnTrendChange && signal == OP_SELL && IsAnyOrderActive(OP_BUY) != -1)
        dummyResult = OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), MaxSlippage, Blue);
      else if (CloseOnTrendChange && signal == OP_BUY && IsAnyOrderActive(OP_SELL) != -1)
        dummyResult = OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), MaxSlippage, Red);
      if (GetOrderCount() < MaxOrders) SetOrder(signal);
    }
  }
  //------------------------------------------------------------------
  ManageSLTP();
  ManageTrade();
  //------------------------------------------------------------------
  return(0);
  //------------------------------------------------------------------
}
//+------------------------------------------------------------------+
int SetOrder(int type)
{
  double price, sl;
  int cmd;
  color col;
  if (type == OP_BUY) { cmd = OP_BUYSTOP; price = High[1]+Ask-Bid+OrderDistanceBuyPips*point; col = Blue; }
  if (type == OP_SELL) { cmd = OP_SELLSTOP; price = Low[1]-OrderDistanceBuyPips*point; col = Red; }
  double lot = Lots;
  if (UseMoneyManagementRisk)
  {
    double PipValue = MarketInfo(Symbol(), MODE_TICKVALUE) / MarketInfo(Symbol(), MODE_TICKSIZE);
    lot = NormalizeDouble(AccountEquity() * RiskAB / (100 * MathAbs(price - sl) * PipValue), DecimalPoints);
  }
  if (UseMartingale_Loss)
  {
    int last = GetLastClosedOrder();
    if (last != -1 && OrderProfit() < 0) lot = NormalizeDouble(LotsMultiplier_Loss * OrderLots(), DecimalPoints);
  }
  ResetLastError();
  int ticket = OrderSend(Symbol(), cmd, lot, NormalizeDouble(price, Digits), MaxSlippage, 0, 0, CustomComment, MagicNumber, 0, col);
  int err = GetLastError();
  if (ticket == -1 && err > 0)
    Print("err=", err, ", type=" + type, ", price=", price, ", lot=", lot, ", ", ErrorDescription(err));
  else if (ticket != -1) ManageSLTP();
  return (ticket);
}
//+------------------------------------------------------------------+
int DoesSignalCatched(int type)
{
  int type2;
  if (type == OP_BUY) type2 = OP_BUYSTOP;
  if (type == OP_SELL) type2 = OP_SELLSTOP;
  for (int i = OrdersTotal() - 1; i >= 0; i--)
    if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      if (OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderOpenTime() >= Time[0] && (OrderType() == type || OrderType() == type2))
        return (OrderTicket());
  for (i = OrdersHistoryTotal() - 1; i >= 0; i--)
    if (OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
      if (OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && (OrderOpenTime() >= Time[0] || OrderCloseTime() >= Time[0]) && (OrderType() == type || OrderType() == type2))
        return (OrderTicket());
  return (-1);
}
//+------------------------------------------------------------------+
int IsAnyOrderActive(int type)
{
  int type2 = -1;
  if (type == OP_BUY) type2 = OP_BUYSTOP;
  if (type == OP_SELL) type2 = OP_SELLSTOP;
  for (int i = OrdersTotal() - 1; i >= 0; i--)
    if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      if (OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && (OrderType() == type || OrderType() == type2))
        return (OrderTicket());
  return (-1);
}
//+------------------------------------------------------------------+
int GetOrderCount(bool all = false)
{
  int count = 0;
  for (int i = OrdersTotal() - 1; i >= 0; i--)
    if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      if ((OrderSymbol() == Symbol() || all == true) && OrderMagicNumber() == MagicNumber)
        count++;
  return (count);
}
//+------------------------------------------------------------------+
int GetLastClosedOrder(int exception = -1)
{
  int ticket = -1; datetime OpenTime = -1;
  for (int i = OrdersHistoryTotal() - 1; i >= 0; i--)
    if (OrderSelect(i, SELECT_BY_POS, MODE_HISTORY))
      if (OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && (OrderType() == OP_BUY || OrderType() == OP_SELL))
        if (OrderTicket() != exception && OrderOpenTime() > OpenTime)
        {
          ticket = OrderTicket(); OpenTime = OrderOpenTime();
        }
  if (ticket != -1) dummyResult = OrderSelect(ticket, SELECT_BY_TICKET);
  return (ticket);
}
//+------------------------------------------------------------------+
void ManageTrade()
{
  double sl, tp;
  for (int i = OrdersTotal() - 1; i >= 0; i--)
    if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      if (OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
      {
        sl = GlobalVariableGet(gbl+OrderTicket()+".SL");
        tp = GlobalVariableGet(gbl+OrderTicket()+".TP");
        if (HideBuyTP_SL && OrderType() == OP_BUY)
        {
          if (tp != 0 && NormalizeDouble(Bid-tp, Digits) >= 0)
          {
            dummyResult = OrderClose(OrderTicket(), OrderLots(), Bid, 0, Blue);
            return;
          }
          else if (sl != 0 && NormalizeDouble(Bid-sl, Digits) <= 0)
          {
            dummyResult = OrderClose(OrderTicket(), OrderLots(), Bid, 0, Blue);
            return;
          }
        }
        if (HideSellTP_SL && OrderType() == OP_SELL)
        {
          if (tp != 0 && NormalizeDouble(tp-Ask, Digits) >= 0)
          {
            dummyResult = OrderClose(OrderTicket(), OrderLots(), Ask, 0, Red);
            return;
          }
          else if (sl != 0 && NormalizeDouble(sl-Ask, Digits) <= 0)
          {
            dummyResult = OrderClose(OrderTicket(), OrderLots(), Ask, 0, Red);
            return;
          }
        }
        int step;
        if (sl != 0 && OrderType() == OP_BUY)
        {
          if (UseBreakEvenBuy && NormalizeDouble(Bid-MoveToBreakEveAtPipsBuy*point, Digits)>0)
            if (NormalizeDouble(sl, Digits)<NormalizeDouble(OrderOpenPrice()+MoveToBreakEvenLockPipsBuy*point, Digits))
            {
              sl = NormalizeDouble(OrderOpenPrice()+MoveToBreakEvenLockPipsBuy*point, Digits);
              if (!HideBuyTP_SL) dummyResult = OrderModify(OrderTicket(), OrderOpenPrice(), sl, OrderTakeProfit(), 0);
              GlobalVariableSet(gbl+OrderTicket()+".SL", sl);
            }
          if (UseTrailingStopBuy && NormalizeDouble(Bid-sl-TrailingStopBuy*point, Digits)>0)
          {
            step = MathFloor((Bid-sl)/(TrailingStepBuy*point));
            if (step >= 1)
            {
              sl = Bid-TrailingStopBuy*point;
              if (!HideBuyTP_SL) dummyResult = OrderModify(OrderTicket(), OrderOpenPrice(), sl, OrderTakeProfit(), 0);
              GlobalVariableSet(gbl+OrderTicket()+".SL", sl);
            }
          }
        }
        if (sl != 0 && OrderType() == OP_SELL)
        {
          if (UseBreakEvenSell && NormalizeDouble(OrderOpenPrice()-Ask-MoveToBreakEveAtPipsSell*point, Digits)>0)
            if (NormalizeDouble(sl, Digits)>NormalizeDouble(OrderOpenPrice()-MoveToBreakEvenLockPipsSell*point, Digits))
            {
              sl = NormalizeDouble(OrderOpenPrice()-MoveToBreakEvenLockPipsSell*point, Digits);
              if (!HideSellTP_SL) dummyResult = OrderModify(OrderTicket(), OrderOpenPrice(), sl, OrderTakeProfit(), 0);
              GlobalVariableSet(gbl+OrderTicket()+".SL", sl);
            }
          if (UseTrailingStopSell && NormalizeDouble(sl-Ask-TrailingStopSell*point, Digits)>0)
          {
            step = MathFloor((sl-Ask)/(TrailingStepSell*point));
            if (step >= 1)
            {
              sl = Ask+TrailingStopSell*point;
              if (!HideSellTP_SL) dummyResult = OrderModify(OrderTicket(), OrderOpenPrice(), sl, OrderTakeProfit(), 0);
              GlobalVariableSet(gbl+OrderTicket()+".SL", sl);
            }
          }
        }
      }
}
//+------------------------------------------------------------------+
void ManageSLTP()
{
  double sl, tp;
  for (int i = OrdersTotal() - 1; i >= 0; i--)
    if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
      if (OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
      {
        sl = GlobalVariableGet(gbl+OrderTicket()+".SL");
        tp = GlobalVariableGet(gbl+OrderTicket()+".TP");
        if (sl == 0 && tp == 0 && (OrderType() == OP_BUY || OrderType() == OP_BUYSTOP))
        {
          if (StopLossPipsBuy != 0) sl = NormalizeDouble(OrderOpenPrice() - StopLossPipsBuy*point, Digits);
          if (TakeProfitPipsBuy != 0) tp = NormalizeDouble(OrderOpenPrice() + TakeProfitPipsBuy*point, Digits);
          if (!HideBuyTP_SL && OrderStopLoss() == 0 && OrderTakeProfit() == 0 && (sl != 0 || tp != 0))
            dummyResult = OrderModify(OrderTicket(), OrderOpenPrice(), sl, tp, 0);
          GlobalVariableSet(gbl+OrderTicket()+".SL", sl);
          GlobalVariableSet(gbl+OrderTicket()+".TP", tp);
        }
        if (sl == 0 && tp == 0 && (OrderType() == OP_SELL || OrderType() == OP_SELLSTOP))
        {
          if (StopLossPipsSell != 0) sl = NormalizeDouble(OrderOpenPrice() + StopLossPipsSell*point, Digits);
          if (TakeProfitPipsSell != 0) tp = NormalizeDouble(OrderOpenPrice() - TakeProfitPipsSell*point, Digits);
          if (!HideSellTP_SL && OrderStopLoss() == 0 && OrderTakeProfit() == 0 && (sl != 0 || tp != 0))
            dummyResult = OrderModify(OrderTicket(), OrderOpenPrice(), sl, tp, 0);
          GlobalVariableSet(gbl+OrderTicket()+".SL", sl);
          GlobalVariableSet(gbl+OrderTicket()+".TP", tp);
        }
      }
}
//+------------------------------------------------------------------+
int GetSignal(int shift)
{
  double x11 = iCustom(NULL, 0, "UniLineBreak_v2 nmc", TimeFrame, Price, NumberLines, HiLoMode, MinHeight, PreSmooth, Pole, Order, WeightFactor, DampingFactor, 0, shift);
  double x12 = iCustom(NULL, 0, "UniLineBreak_v2 nmc", TimeFrame, Price, NumberLines, HiLoMode, MinHeight, PreSmooth, Pole, Order, WeightFactor, DampingFactor, 1, shift);
  double x21 = iCustom(NULL, 0, "UniLineBreak_v2 nmc", TimeFrame, Price, NumberLines, HiLoMode, MinHeight, PreSmooth, Pole, Order, WeightFactor, DampingFactor, 0, shift+1);
  double x22 = iCustom(NULL, 0, "UniLineBreak_v2 nmc", TimeFrame, Price, NumberLines, HiLoMode, MinHeight, PreSmooth, Pole, Order, WeightFactor, DampingFactor, 1, shift+1);
  if (NormalizeDouble(x11-x12, Digits)>0 && NormalizeDouble(x21-x22, Digits)<0) return (OP_BUY);
  if (NormalizeDouble(x11-x12, Digits)<0 && NormalizeDouble(x21-x22, Digits)>0) return (OP_SELL);
  return (-1);
}
//+------------------------------------------------------------------+

