//+------------------------------------------------------------------+
//|                                                       shakka.mq4 |
//|                        Copyright 2022, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict

#define _Balance         AccountInfoDouble(ACCOUNT_BALANCE)
#define _Equity          AccountInfoDouble(ACCOUNT_EQUITY)
#define _MarginRequired  (double)MarketInfo(_Symbol, MODE_MARGINREQUIRED)
#define _LotStep         SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP)
#define _LotMin          SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN)
#define _LotMax          SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX)
#define _TickValue       SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE)
#define _TickSize        SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE)
#define _Pip             (double)((_Digits==3 || _Digits==5) ? _Point*10 : _Point)
#define _DigitsPip       (int)log10(1/_Pip)
#define _Digitslot       (int)log10(1/_LotStep)
#define _PointValue      (double)_Point/_TickSize*_TickValue
#define _PipValue        (double)((_TickValue*_Pip)/_TickSize)
#define _Ask             SymbolInfoDouble(_Symbol, SYMBOL_ASK)
#define _Bid             SymbolInfoDouble(_Symbol, SYMBOL_BID)
#define _BrokerSpread    (double)(_Ask-_Bid)
//define
#define  EA_NAME    "Exp_Slingshot"
#define  EA_VERSION "Rev_1.00 [2022.03.04]"
string   EA_COMMENT="";

enum ENUM_MONEY_MANAGEMENT {None, FixedLot, ByRisk, ByMargin};

input ENUM_MONEY_MANAGEMENT   InpMMType         = None;     // Select MM Type
input double                  InpLotPercent     = 1.5;      // Lot % for Risk or Margin
input double                  InpLots           = 0.1;      // Fixed Lot
input string                  InpSignalTime     = "16:30";      // Start Time
input double                  InpDistancePips   = 20;       // Distance pips to pending level
input double                  InpRRRatio1       = 2;        // RR First TP
//input double                  InpRRRatio2       = 2;        // RR Second TP
//input double                  InpRRRatio3       = 3;        // RR Third TP
//input double                  InpTakeProfit     = 100.0;    // Take Profit
//input double                  InpStopLoss       = 30.0;    // Stop Loss in Pips
input int                     InpSlippage       = 10;       // Slippage
input double                  InpMaxSpread      = 6.0;      // Max Spread we allowed
input int                     InpMagic          = 234;      // magic number
input bool                    InpUseDefense     = true;      //defense
input double                  InpUseDefFactor   = 2;      // Defense Factor

// V A R I A B L E S
//ulong m_InpSlippage=10;
double   ExtDistancePips=0.0;
double   ExtTakeProfit=0.0;
double   ExtStopLoss=0.0;
int      count_buy, count_sell, count_buystop, count_sellstop, count_all_orders ;
double   buy_op, sell_op, buystop_op, sellstop_op;
double   buy_sl, sell_sl, buystop_sl, sellstop_sl;
double   buy_tp, sell_tp, buystop_tp, sellstop_tp;
double   buy_lot, sell_lot, buystop_lot, sellstop_lot;
double   buy_profit, sell_profit;
int      buy_ticket=-1, buystop_ticket=-1;
int      sell_ticket=-1, sellstop_ticket=-1;
double   NextLot=0, InitialLot=0;
bool     opThisBar = false;
double   LongPrice=0, LongStop=0, LongTake1=0, LongTake2=0, LongTake3=0;
double   ShortPrice=0, ShortStop=0, ShortTake1=0, ShortTake2=0, ShortTake3=0;
datetime StartCycleTime=0;
int      bar_index=0;
double   LastBalanceUsed=0;
string   ObjPrefix  = _Symbol+"_";
string   VLineSignal = ObjPrefix+"SignalTime";
double   signalPrice=0;
datetime ExtSignalTime=0, ExtStartTrade=0;
bool     opAllowed=false;
datetime t0=0;
datetime tRight=0;;
double   totalCycleLoss=0;
int      consCycleLoss=0;
int      CycleLevel=0, tempLevel=0;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
//---
   ExtDistancePips = InpDistancePips * _Pip;
   //ExtTakeProfit   = InpTakeProfit   * _Pip;
   //ExtStopLoss     = InpStopLoss   * _Pip;

   OnInitSetChartVisual();
//---
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---

}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
//---
   OrdersInformation();
   HistoryCycle();
   UpdateParams();

   if (IsNewBar(PERIOD_CURRENT)) opThisBar = false;
   if (opThisBar) return;

   ManageOrder();
}
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{
//---

}
//+------------------------------------------------------------------+
void ManageOrder()
{
   if(InpMaxSpread < _BrokerSpread/_Pip) {
      Print("Spread is too wide");
      return;
   }
   EA_COMMENT = EA_NAME+"_"+_Symbol+"_"+(string)InpMagic;

   LongPrice  = NormalizePrice(LongPrice);
   LongStop   = NormalizePrice(ShortPrice);
   LongTake1  = NormalizePrice(LongPrice+((LongPrice-LongStop)*InpRRRatio1));
   ShortPrice = NormalizePrice(ShortPrice);
   ShortStop  = NormalizePrice(LongPrice);
   ShortTake1 = NormalizePrice(ShortPrice-((ShortStop-ShortPrice)*InpRRRatio1));

   if(LongPrice==0 && ShortPrice==0)return;

   if(count_buy>0) {
      NextLot = nextMartiVolume(buy_sl, ShortStop);
      if(count_sellstop==0) {
         if(InpUseDefense) {
            if(_Bid>ShortPrice) {
               if (OpenOrder(OrderSend(_Symbol, OP_SELLSTOP, NextLot, buy_sl, InpSlippage, ShortStop, ShortTake1, EA_COMMENT, InpMagic, 0, clrNONE))) {
                  opThisBar=true;
                  return;
               }
            }
         }
      }
      else {
         if(!IsVolumeCorrect(NextLot, sellstop_lot)) {
            DeletePendingTrades(sellstop_ticket);
         }
         if(sellstop_op!=ShortPrice || sellstop_sl!=ShortStop) {
            if(ModifyOrder(sellstop_ticket, ShortPrice, ShortStop, ShortTake1)) {
               return;
            }
         }
      }

   }
   else if(count_sell>0) {
      NextLot = nextMartiVolume(sell_sl, LongStop);
      if(count_buystop==0) {
         if(InpUseDefense) {
            if(_Ask<LongPrice) {
               if (OpenOrder(OrderSend(_Symbol, OP_BUYSTOP, NextLot, sell_sl, InpSlippage, LongStop, LongTake1, EA_COMMENT, InpMagic, 0, clrNONE))) {
                  opThisBar=true;
                  return;
               }
            }
         }
      }
      else {
         if(!IsVolumeCorrect(NextLot, buystop_lot)) {
            DeletePendingTrades(buystop_ticket);
         }
         if(buystop_op!=LongPrice || buystop_sl!=LongStop) {
            if(ModifyOrder(buystop_ticket, LongPrice, LongStop, LongTake1)) {
               return;
            }
         }
      }
   }
   else {
      if(count_buy+count_sell==0 && count_buystop+count_sellstop>0) {
         if(CycleDeal()>0) {
            DeleteAllPendingTrades();
            return;
         }
      }
      if(count_all_orders==0) {
         LastBalanceUsed = _Balance;
         StartCycleTime = TimeCurrent();
         tempLevel=0;
         InitialLot = RecalculateLot(LongPrice, LongStop);
         if(count_buystop+count_sellstop==0) {
            if(_Ask<LongPrice && _Bid>ShortPrice) {
               if (OpenOrder(OrderSend(_Symbol, OP_BUYSTOP, InitialLot, LongPrice, InpSlippage, LongStop, LongTake1, EA_COMMENT, InpMagic, 0, clrNONE)) &&
                     OpenOrder(OrderSend(_Symbol, OP_SELLSTOP, InitialLot, ShortPrice, InpSlippage, ShortStop, ShortTake1, EA_COMMENT, InpMagic, 0, clrNONE))
                  ) {
                  opThisBar=true;
                  return;
               }
            }
         }
      }
   }
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OrdersInformation()
{
   /*
   count_all_orders=0;
   count_buy=0;      count_sell=0;     count_buystop=0;     count_sellstop=0;
   buy_lot=0.0;      sell_lot=0.0;     buystop_lot=0.0;     sellstop_lot=0.0;
   buy_op=0.0;       sell_op=0.0;      buystop_op=0.0;      sellstop_op=0.0;
   buy_sl=0.0;       sell_sl=0.0;      buystop_sl=0.0;      sellstop_sl=0.0;
   buy_tp=0.0;       sell_tp=0.0;      buystop_tp=0.0;      sellstop_tp=0.0;

   buy_profit=0.0;   sell_profit=0.0;

   buy_ticket=-1;    buystop_ticket=-1;
   sell_ticket=-1;   sellstop_ticket=-1;

   */
   count_all_orders=0;
   count_buy=0;
   count_sell=0;
   count_buystop=0;
   count_sellstop=0;
   buy_lot=0.0;
   sell_lot=0.0;
   buystop_lot=0.0;
   sellstop_lot=0.0;
   buy_op=0.0;
   sell_op=0.0;
   buystop_op=0.0;
   sellstop_op=0.0;
   buy_sl=0.0;
   sell_sl=0.0;
   buystop_sl=0.0;
   sellstop_sl=0.0;
   buy_tp=0.0;
   sell_tp=0.0;
   buystop_tp=0.0;
   sellstop_tp=0.0;

   buy_profit=0.0;
   sell_profit=0.0;

   buy_ticket=-1;
   buystop_ticket=-1;
   sell_ticket=-1;
   sellstop_ticket=-1;

   for (int c=0; c < OrdersTotal(); c++ ) {
      if (OrderSelect(c, SELECT_BY_POS, MODE_TRADES)) {
         if (OrderSymbol() != Symbol())continue;
         if (OrderMagicNumber() != InpMagic)continue;
         switch (OrderType()) {
         case OP_BUY :
            count_buy++;
            buy_ticket=OrderTicket();
            buy_lot=OrderLots();
            buy_sl = OrderStopLoss();
            buy_op = OrderOpenPrice();
            buy_tp = OrderTakeProfit();
            buy_profit = OrderProfit();
            break;
         case OP_SELL :
            count_sell++;
            sell_ticket=OrderTicket();
            sell_lot=OrderLots();
            sell_op = OrderOpenPrice();
            sell_sl = OrderStopLoss();
            sell_tp = OrderTakeProfit();
            sell_profit = OrderProfit();
            break;
         case OP_BUYSTOP :
            count_buystop++;
            buystop_ticket=OrderTicket();
            buystop_lot=OrderLots();
            buystop_tp = OrderTakeProfit();
            buystop_sl = OrderStopLoss();
            buystop_op = OrderOpenPrice();
            break;
         case OP_SELLSTOP:
            count_sellstop++;
            sellstop_ticket=OrderTicket();
            sellstop_lot=OrderLots();
            sellstop_op = OrderOpenPrice();
            sellstop_sl = OrderStopLoss();
            sellstop_tp = OrderTakeProfit();
            break;
         }
         count_all_orders++;
      }
   }
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double RecalculateLot(double p, double sl)
{
   double volume = 0.0, price=0, stop=0, range=0,
          riskFactor=0, riskForTrade=0,
          risk=0, riskPerLot=0,
          lotByRisk=0, lotByMargin=0;

   riskFactor  = InpLotPercent/100;
   riskForTrade = riskFactor*_Balance;
   range = MathAbs(p-sl)/_Point;

   riskPerLot = range*_PointValue;

   lotByRisk = riskForTrade / riskPerLot;
   lotByMargin = riskForTrade/_MarginRequired;

   volume = (InpMMType==ByRisk) ? MathFloor(lotByRisk/_LotStep)*_LotStep :
            (InpMMType==ByMargin) ? MathFloor(lotByMargin/_LotStep)*_LotStep : InpLots ;

   return NormalizeLot(volume);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double nextMartiVolume(double p, double sl)
{
   double lastVolume=0, nextVolume = 0, lotByRisk=0, riskFactor=0, currPositionRisk=0,
          lotByMargin=0, riskForTrade=0, nextRiskForTrade=0, tempRiskInMoney=0,
          riskToRecover=0, riskPerLot=0, nextRange=0;

   riskFactor  = InpLotPercent/100;
   riskForTrade = riskFactor*LastBalanceUsed;
   nextRange = MathAbs(p-sl)/_Point;
   lastVolume = count_buy>0?buy_lot:sell_lot;

   tempRiskInMoney  = (totalCycleLoss + currentRisk())<0 ? MathAbs(totalCycleLoss + currentRisk()) : 0;
   riskToRecover    = totalCycleLoss<0 ? MathAbs(totalCycleLoss) : 0;
   nextRiskForTrade = riskForTrade+tempRiskInMoney;

   riskPerLot = nextRange*_PointValue;
   lotByRisk  = nextRiskForTrade / riskPerLot;
   //lotByRisk = nextRiskForTrade / (_PipValue*(currentRange()/_Point));
   lotByMargin = (riskForTrade/_MarginRequired) + (riskToRecover / (_PipValue*(nextRange/_Point)));

   nextVolume  = (InpMMType==ByRisk) ? MathFloor(lotByRisk/_LotStep)*_LotStep :
                 (InpMMType==ByMargin) ? MathFloor(lotByMargin/_LotStep)*_LotStep : lastVolume*InpUseDefFactor ;

   return NormalizeLot(nextVolume);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double currentRisk()
{
   double risk=0, currPositionRisk=0, riskPerLot=0,
          tempRiskInMoney=0, totalLoss=0,
          positionLot=0, currentRange=0;

   positionLot  = count_buy>0?buy_lot:sell_lot;
   currentRange = (count_buy>0?buy_sl-buy_op:sell_op-sell_sl)/_Point;
   currPositionRisk = _PointValue*currentRange*positionLot;
   //currPositionRisk = currentRange * (positionLot / _Point) * _TickValue;

   if(currPositionRisk>=0) return(0);
   return (NormalizeDouble(currPositionRisk,2));
}
//+------------------------------------------------------------------+
bool OpenOrder(int tiket)
{
   int c = 10;
   while (!OrderSelect(tiket,SELECT_BY_TICKET)) {
      if (tiket < 1) return(false);
      Sleep(1000);
      c--;
      if (c == 0) return(false);
   }
   return(OrderTicket() == tiket);
}
//+------------------------------------------------------------------+
bool ModifyOrder(int ticket, double price, double stop, double take)
{
   price = NormalizePrice(price);
   stop = NormalizePrice(stop);
   take = NormalizePrice(take);

   for (int cc = OrdersTotal() - 1; cc >= 0; cc--) {
      if (!OrderSelect(cc, SELECT_BY_POS) ) continue;
      if (OrderSymbol() != _Symbol) continue;
      if (OrderMagicNumber() != InpMagic) continue;
      if (OrderModify(ticket, price, stop, take, 0)) {
         return(true);
         break;
      }
   }
   return(false);
}
//+------------------------------------------------------------------+
void DeleteAllPendingTrades()
{
   bool result = false;

   for (int cc = OrdersTotal() - 1; cc >= 0; cc--) {
      if (!OrderSelect(cc, SELECT_BY_POS) ) continue;
      if (OrderSymbol() != _Symbol) continue;
      if (OrderMagicNumber() != InpMagic) continue;
      switch (OrderType()) {
      case OP_BUYSTOP:
      case OP_BUYLIMIT:
      case OP_SELLSTOP:
      case OP_SELLLIMIT:
         result = OrderDelete(OrderTicket(), clrNONE);
      }
      if(result == false) {
         Print("Order ", OrderTicket(), " failed to close. Error:", GetLastError() );
         Sleep(500);
      }
   }
}
//+------------------------------------------------------------------+
void DeletePendingTrades(int ticket)
{
   for (int cc = OrdersTotal() - 1; cc >= 0; cc--) {
      if (!OrderSelect(cc, SELECT_BY_POS) ) continue;
      if (OrderSymbol() != _Symbol) continue;
      if (OrderMagicNumber() != InpMagic) continue;
      if(OrderDelete(ticket, clrNONE))
         Print("Delete tiket #", ticket);
      break;
   }
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool IsNewBar(const ENUM_TIMEFRAMES tf = PERIOD_CURRENT)
{
   datetime bar_time=0;

   if (bar_time < iTime(Symbol(), tf, 0)) {
      bar_time = iTime(Symbol(), tf, 0);
      return (true);
   }
   return (false);
}

//+------------------------------------------------------------------+
double NormalizePrice(double price)
{
   return(MathRound(price/_TickSize) * _TickSize );
}
//+------------------------------------------------------------------+
double NormalizeLot(double lots)
{
   ResetLastError();
   lots = MathMax(MathMin(_LotMax, lots), _LotMin);
   return(MathRound(lots/_LotStep) * _LotStep );
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool IsVolumeCorrect(double correctVol, double currentVol)
{
   if((correctVol<currentVol*0.95) || (correctVol>currentVol*1.05))
      return (false);
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CycleDeal()
{
   int entry_in=0, entry_out=0;

   for (int c=0; c < OrdersTotal(); c++ ) {
      if (OrderSelect(c, SELECT_BY_POS, MODE_TRADES)) {
         if (OrderSymbol() != Symbol())continue;
         if (OrderMagicNumber() != InpMagic)continue;
         if (OrderOpenTime()<StartCycleTime)continue;
         if (OrderType()<=OP_SELL) {
            //level++;
            continue;
         }
      }
   }
   for (int i = 0; i < OrdersHistoryTotal() && !IsStopped(); i++) {
      //for (i = OrdersHistoryTotal() - 1; i >=0; i--) {
      if (!OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) continue;
      if (OrderType() > OP_SELL) continue;
      if (OrderLots() == 0) continue;
      if (OrderOpenTime()<StartCycleTime)continue;
      if (OrderSymbol() != Symbol()) continue;
      //if (StringFind(OrderComment(), "tp") > 0) break;
      if (OrderMagicNumber() == InpMagic) {
         entry_out++;
         continue;
      }
   }
   entry_in = count_buy+count_sell>0 ? 1 : 0;
   return(entry_out+entry_in+tempLevel);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void HistoryCycle()
{
   totalCycleLoss=0;
   consCycleLoss=0;
   CycleLevel=0;
   int i=0;
   for (i = 0; i < OrdersHistoryTotal() && !IsStopped(); i++) {
      //for (i = OrdersHistoryTotal() - 1; i >=0; i--) {
      if (!OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) continue;
      if (OrderType() > OP_SELL) continue;
      if (OrderLots() == 0) continue;
      if (OrderOpenTime()<StartCycleTime)continue;
      if (OrderSymbol() != Symbol()) continue;
      //if (StringFind(OrderComment(), "tp") > 0) break;
      if (OrderMagicNumber() == InpMagic) {
         if(OrderProfit()<0) {
            consCycleLoss++;
            //CycleLevel++;
            totalCycleLoss += OrderProfit()+OrderCommission()+OrderSwap();
            continue;
         }
         else {
            //CycleLevel++;
            totalCycleLoss += OrderProfit()+OrderCommission()+OrderSwap();
            continue;
         }
         if(OrderClosePrice()>=OrderTakeProfit()) {
            DeleteAllPendingTrades();
            break;
         }
      }
   }
}
//+------------------------------------------------------------------+
void UpdateParams()
{
   MqlRates rates[];
   ArraySetAsSeries(rates,true);
   if(CopyRates(Symbol(),0,0,100,rates)<0) {
      return;
   }
   ExtSignalTime = StrToTime(InpSignalTime);
   ExtStartTrade = (datetime)long ((StrToTime(InpSignalTime)) + Period()*60);
   ObjVLine(VLineSignal, ExtSignalTime);
   int barShiftSignal = iBarShift(_Symbol,PERIOD_CURRENT, ExtSignalTime);
   int barShiftTrade  = iBarShift(_Symbol,PERIOD_CURRENT, ExtStartTrade);
   t0          = rates[0].time;
   tRight      = ExtSignalTime + Period()*60;
   signalPrice = t0>ExtSignalTime?rates[barShiftSignal].close:0;


   if(signalPrice!=0) {
      LongPrice  = signalPrice+ExtDistancePips+_BrokerSpread;
      ShortPrice = signalPrice-ExtDistancePips;
   }


   if(t0<ExtSignalTime) {
      if(IsPresentObjects(ObjPrefix)) {
         ObjectsDeleteAll(0, ObjPrefix, 0, OBJ_TEXT);
      }
   }
   if(t0>ExtSignalTime) {
      longGraphics(LongPrice, tRight);
      shortGraphics(ShortPrice, tRight);
   }
   Comment (
      "~~~~~~~~~~~~~~","\n",
      "Shakka_EA","\n",
      " ","\n",
      "Balance :  ", DoubleToString(LastBalanceUsed, 2),"\n",
      "Equity :  ", DoubleToString(_Equity, 2),"\n",
      "~~~~~~~~~~~~~~","\n",
      "Long Price :  ", DoubleToString(LongPrice, _Digits),"\n",
      "Short Price  :  ", DoubleToString(ShortPrice, _Digits),"\n",
      "~~~~~~~~~~~~~~","\n",
      "Next Lot :  ", DoubleToString(NextLot, 2),"\n",
      "Pending Lot :  ", DoubleToString(count_sell>0?buystop_lot:sellstop_lot, 2),"\n",
      "Correct Lot :  ", string(IsVolumeCorrect(NextLot,buystop_lot)),"\n",
      " ","\n",
      "Consec Loss :  ", IntegerToString(consCycleLoss),"\n",
      "~~~~~~~~~~~~~~"
   );
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void longGraphics(double price, datetime tR)
{
   double longLot = count_buy>0?buy_lot:count_buystop>0?buystop_lot:0;
   ObjCMText(ObjPrefix+"LongText", clrAqua, "Long Price ["+DoubleToString(price,_Digits)+"] ["+DoubleToString(longLot,2)+"]", tR, price, ANCHOR_LEFT);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void shortGraphics(double price, datetime tR)
{
   double shortLot = count_sell>0?sell_lot:count_sellstop>0?sellstop_lot:0;
   ObjCMText(ObjPrefix+"ShortText", clrPink, "Short Price ["+DoubleToString(price,_Digits)+"] ["+DoubleToString(shortLot,2)+"]", tR, price, ANCHOR_LEFT);
}
//+------------------------------------------------------------------+
//| Create the vertical line                                         |
//+------------------------------------------------------------------+
bool ObjVLine (const string   name=" ",datetime time=0)
{
   if(ObjectFind(0, name) == -1) {
      if(!ObjectCreate(ChartID(), name, OBJ_VLINE, 0, time, 0)) {
         Print(__FUNCTION__, ": failed to create a vertical line! Error code = ", GetLastError());
         return(false);
      }
   }
   else if(!ObjectMove(ChartID(), name, 0, time, 0)) {
      Print(__FUNCTION__, ": failed to move vertical! Error code = ", GetLastError());
      return(false);
   }
   ObjectSetInteger(ChartID(),name,OBJPROP_COLOR, clrPink);
   ObjectSetInteger(ChartID(),name,OBJPROP_STYLE,STYLE_DOT);
   ObjectSetInteger(ChartID(),name,OBJPROP_WIDTH,1);
   ObjectSetInteger(ChartID(),name,OBJPROP_BACK,false);
   ObjectSetInteger(ChartID(),name,OBJPROP_SELECTABLE,true);
   ObjectSetInteger(ChartID(),name,OBJPROP_SELECTED,false);
   ObjectSetInteger(ChartID(),name,OBJPROP_RAY,true);
   ObjectSetInteger(ChartID(),name,OBJPROP_HIDDEN,false);
   ObjectSetInteger(ChartID(),name,OBJPROP_ZORDER,0);
   return(true);
}
//+------------------------------------------------------------------+
// TrendLine Handling                                                |
//+------------------------------------------------------------------+
bool ObjCMTLine(string    name,   // line name
                color     clr,
                ENUM_LINE_STYLE  style,
                int       width,
                datetime  time1,
                double    price1,
                datetime  time2,
                double    price2
               )
{
   if(ObjectFind(0, name) == -1) {
      if(!ObjectCreate(0, name, OBJ_TREND, 0, time1, price1, time2, price2)) {
         Print(__FUNCTION__, ": failed to create a trend line! Error code = ", GetLastError());
         return(false);
      }
   }
   else if(!ObjectMove(0, name, 0, time1, price1) || !ObjectMove(0, name, 1, time2, price2)) {
      Print(__FUNCTION__, ": failed to move trendline! Error code = ", GetLastError());
      return(false);
   }
   ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, name, OBJPROP_STYLE, style);
   ObjectSetInteger(0, name, OBJPROP_WIDTH, width);
   ObjectSetInteger(0, name, OBJPROP_BACK, true);
   ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, false);
   ObjectSetInteger(0, name, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS);
   return(true);
}
//+------------------------------------------------------------------+
void OnInitSetChartVisual()
{
   ChartSetInteger(ChartID(),CHART_SHIFT,true);
   ChartSetDouble(ChartID(),CHART_SHIFT_SIZE,30);
   ChartSetInteger(ChartID(),CHART_MODE,CHART_CANDLES);
   ChartSetInteger(ChartID(),CHART_SHOW_PERIOD_SEP, false);
   ChartSetInteger(ChartID(),CHART_SHOW_OHLC, true);
   ChartSetInteger(ChartID(),CHART_SHOW_GRID, false);
   ChartSetInteger(ChartID(),CHART_FOREGROUND, true);
   ChartSetInteger(ChartID(),CHART_BRING_TO_TOP,true);
   ChartSetInteger(ChartID(),CHART_SHOW_ASK_LINE,true);
   ChartSetInteger(ChartID(),CHART_AUTOSCROLL,true);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Return the flag of a prefixed object presence                    |
//+------------------------------------------------------------------+
bool IsPresentObjects(const string object_prefix)
{
   for(int i=ObjectsTotal(0, 0)-1; i>=0; i--)
      if(StringFind(ObjectName(i), object_prefix)>WRONG_VALUE)
         return true;
   return false;
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Creating Text object                                             |
//+------------------------------------------------------------------+
bool ObjCMText(const string            name="Text",              // object name
               const color             clr=clrRed,               // color
               const string            text="Text",              // the text itself
               datetime                time=0,                   // anchor point time
               double                  price=0,                  // anchor point price
               const ENUM_ANCHOR_POINT anchor=ANCHOR_LEFT_UPPER // anchor type
              )                // priority for mouse click
{
//--- reset the error value
   ResetLastError();
//--- create Text object
   if(ObjectFind(0, name) == -1) {
      if(!ObjectCreate(ChartID(),name,OBJ_TEXT,0,time,price)) {
         Print(__FUNCTION__,
               ": failed to create \"Text\" object! Error code = ",GetLastError());
         return(false);
      }
   }
   else if(!ObjectMove(ChartID(),name,0,time,price)) {
      Print(__FUNCTION__,
            ": failed to move the anchor point! Error code = ",GetLastError());
      return(false);
   }
   ObjectSetString(ChartID(),name,OBJPROP_TEXT,text);
   ObjectSetString(ChartID(),name,OBJPROP_FONT,"Tahoma");
   ObjectSetInteger(ChartID(),name,OBJPROP_FONTSIZE,9);
   ObjectSetDouble(ChartID(),name,OBJPROP_ANGLE,0.0);
   ObjectSetInteger(ChartID(),name,OBJPROP_ANCHOR,anchor);
   ObjectSetInteger(ChartID(),name,OBJPROP_COLOR,clr);
   ObjectSetInteger(ChartID(),name,OBJPROP_BACK,false);
   ObjectSetInteger(ChartID(),name,OBJPROP_SELECTABLE,false);
   ObjectSetInteger(ChartID(),name,OBJPROP_SELECTED,false);
   ObjectSetInteger(ChartID(),name,OBJPROP_HIDDEN,true);
   ObjectSetInteger(ChartID(),name,OBJPROP_ZORDER,0);
   return(true);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Compares two doubles and returns a value indicating whether one  |
//| is nearly equal to, less than, or greater than the other.        |
//+------------------------------------------------------------------+
int Compare(double a, double b, int digits)
{
// bool res = MathAbs(a - b) < 0.5 * MathPow(10, -digits);
   bool res = NormalizeDouble(a - b, digits) == 0;
//---
   if(res)
      return 0;
   else
      return (a > b) ? 1 : -1;
}
//+------------------------------------------------------------------+
//| Is Equal.                                                        |
//+------------------------------------------------------------------+
bool EQ(double a, double b, int digits = 8)
{
   return Compare(a, b, digits) == 0;
}
//+------------------------------------------------------------------+
//| Is Not Equal.                                                    |
//+------------------------------------------------------------------+
bool NE(double a, double b, int digits = 8)
{
   return Compare(a, b, digits) != 0;
}
//+------------------------------------------------------------------+
//| Is Greater Than.                                                 |
//+------------------------------------------------------------------+
bool GT(double a, double b, int digits = 8)
{
   return Compare(a, b, digits) > 0;
}
//+------------------------------------------------------------------+
//| Is Less Than.                                                    |
//+------------------------------------------------------------------+
bool LT(double a, double b, int digits = 8)
{
   return Compare(a, b, digits) < 0;
}
//+------------------------------------------------------------------+
//| Greater Than or Equal.                                           |
//+------------------------------------------------------------------+
bool GTE(double a, double b, int digits = 8)
{
   return Compare(a, b, digits) >= 0;
}
//+------------------------------------------------------------------+
//| Is Less Than or Equal.                                           |
//+------------------------------------------------------------------+
bool LTE(double a, double b, int digits = 8)
{
   return Compare(a, b, digits) <= 0;
}
//+------------------------------------------------------------------+
