//+------------------------------------------------------------------+
//|                                                 CandleSimple.mq5 |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Trade\TerminalInfo.mqh>
#include <Trade\AccountInfo.mqh>
#include <Trade\SymbolInfo.mqh>
//#include <Trade\OrderInfo.mqh>
//#include <Trade\HistoryOrderInfo.mqh>
//#include <Trade\PositionInfo.mqh>
//#include <Trade\DealInfo.mqh>
#include <Trade\Trade.mqh>

CAccountInfo      myAccount;
CSymbolInfo       mySymbol;      // symbol info object
COrderInfo        myOrder;       // pending orders object
CHistoryOrderInfo myHistOrder;
CPositionInfo     myPosition;    // trade position object
CDealInfo         myDeal;
CTrade            myTrade;       // trading object

#define IsVisualMode    (bool)MQLInfoInteger(MQL_VISUAL_MODE)
#define IsTesting       (bool)MQLInfoInteger(MQL_TESTER)
#define IsConnected        (bool)TerminalInfoInteger(TERMINAL_CONNECTED)
#define IsTradeAllowed     (bool)MQLInfoInteger(MQL_TRADE_ALLOWED)
#define  NL    "\n"
#define _Balance        AccountInfoDouble(ACCOUNT_BALANCE)
#define _Margin         AccountInfoDouble(ACCOUNT_MARGIN)
#define _FreeMargin     AccountInfoDouble(ACCOUNT_MARGIN_FREE)
#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 _Ask             SymbolInfoDouble(_Symbol, SYMBOL_ASK)
#define _Bid             SymbolInfoDouble(_Symbol, SYMBOL_BID)
#define _PointValue     _TickValue*_Point / _TickSize
#define _Pip            (double)((_Digits==3 || _Digits==5) ? _Point*10 : _Point)
#define _PipValue       _TickValue*_Pip / _TickSize
#define _BrokerSpread   (SymbolInfoDouble(_Symbol, SYMBOL_ASK)-SymbolInfoDouble(_Symbol, SYMBOL_BID))
#define _DigitsPip      (int)log10(1/_Pip)
#define _Digitslot      (int)log10(1/_LotStep)

enum ENUM_SIGNALS {
   NoSignal   = 0,
   signalBuy  = 1,
   signalSell = 2
};
// input variables
input group    "Money Management" //**** Input for MM *****
input double   LotSize              = 0.1;
input double   TradeRisk            = 2.0;

input group    "Trade"
input bool     UseATRSL             = true;
input int      StopLossPips         = 20;
input int      TakeProfitPips       = 40;
input int      MagicNumber          = 12345;
input int      Slippage             = 10;
input double   MaxSpread            = 6.0;            // Max Spread we allowed
input int      MaxPosition          = 10;
input bool     CloseOnOpposite      = false;

input group    "ATR"
input int      ATRPeriods           = 20;
input int      ATRtimeframe         = 0;
input double   ATRMultiplier        = 2;
input int      CandleCount          = 1;
input bool     UseDistanceFiltering = false;
input int      MinDistancePips      = 10;

int      handle_iATR;                       // variable for storing the handle of the iMA indicator

//global variables
int      count_buy, count_sell;
double   buy_op, sell_op;
double   buy_sl, sell_sl;
double   buy_tp, sell_tp;
double   buy_lot, sell_lot;
double   buy_profit, sell_profit;
int      buy_ticket=-1,  sell_ticket=-1;
bool     opThisBar = false;
double   ExtStopLoss, ExtTakeProfit, ExtDistance;
string   Gap, ScreenMessage;
double   CurrentATR, ActualSL;
datetime m_prev_bars                = 0;        // "0" -> D'1970.01.01 00:00';
double positionOpenPrice=0;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
//---
   if(!mySymbol.Name(Symbol())) // sets symbol name
      return(INIT_FAILED);
//---
   myTrade.SetExpertMagicNumber(MagicNumber);
   myTrade.SetMarginMode();
   //myTrade.SetTypeFillingBySymbol(mySymbol.Name());
   myTrade.SetDeviationInPoints(Slippage);
   if(IsFillingTypeAllowed(_Symbol, ORDER_FILLING_FOK))
      myTrade.SetTypeFilling(ORDER_FILLING_FOK);
   else
      return(INIT_PARAMETERS_INCORRECT);

   ExtDistance   = MinDistancePips * _Pip;
   ExtStopLoss   = StopLossPips    * _Pip;
   ExtTakeProfit = TakeProfitPips  * _Pip;

   handle_iATR=iATR(_Symbol, _Period, ATRPeriods);
//--- if the handle is not created
   if(handle_iATR==INVALID_HANDLE) {
      //--- tell about the failure and output the error code
      PrintFormat("Failed to create handle of the iATR indicator for the symbol %s/%s, error code %d",
                  Symbol(),
                  EnumToString(Period()),
                  GetLastError());
      //--- the indicator is stopped early
      return(INIT_FAILED);
   }
   manageupperstatus("Candle Color Trader - CR 2013");
   OnInitTestVisual();
//---
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
   Comment("");
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   OrdersInformation();

//--- we work only at the time of the birth of new bar
   datetime time_0=iTime(_Symbol,_Period,0);
   if(time_0==m_prev_bars)
      return;
   m_prev_bars=time_0;

   if(!CheckPermission())return;
   if(count_buy+count_sell>=MaxPosition) return;

   //if(GetSignal()==1) {
   if(GetSignal()==1) {
      if(count_sell>0 && CloseOnOpposite) {
         ClosePositions(POSITION_TYPE_SELL);
         return;
      }
      OpenPosition(POSITION_TYPE_BUY, CalculateLot(), correctStopLoss(), correctTakeProfit());
      return;
   }
   //if(GetSignal()==2) {
   if(GetSignal()==2) {
      if(count_buy>0 && CloseOnOpposite) {
         ClosePositions(POSITION_TYPE_BUY);
         return;
      }
      OpenPosition(POSITION_TYPE_SELL, CalculateLot(), correctStopLoss(), correctTakeProfit());
      return;
   }

   DisplayUserFeedback();
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int GetSignal()
{
   if(iClose(_Symbol,PERIOD_CURRENT,1) > iOpen(_Symbol,PERIOD_CURRENT,1))
      return signalBuy;
   if(iClose(_Symbol,PERIOD_CURRENT,1) < iOpen(_Symbol,PERIOD_CURRENT,1))
      return signalSell;

   return NoSignal;
}
//+------------------------------------------------------------------+
void OrdersInformation()
{
   count_buy=0;
   count_sell=0;
   buy_lot=0.0;
   sell_lot=0.0;
   buy_op=0.0;
   sell_op=0.0;
   buy_sl=0.0;
   sell_sl=0.0;
   buy_tp=0.0;
   sell_tp=0.0;
   buy_profit=0.0;
   sell_profit=0.0;
   buy_ticket=ULONG_MAX;
   sell_ticket=ULONG_MAX;

   for(int i=PositionsTotal()-1; i>=0; i--) {
      if(myPosition.SelectByIndex(i)) { // selects the position by index for further access to its properties
         if(myPosition.Symbol()==mySymbol.Name() && myPosition.Magic()==MagicNumber) {
            positionOpenPrice = myPosition.PriceOpen();
            if(myPosition.PositionType()==POSITION_TYPE_BUY) {
               count_buy++;
               buy_ticket = myPosition.Ticket();
               buy_op = myPosition.PriceOpen();
               buy_sl = myPosition.StopLoss();
               buy_tp = myPosition.TakeProfit();
               buy_lot = myPosition.Volume();
               buy_profit = myPosition.Profit();
            }
            if(myPosition.PositionType()==POSITION_TYPE_SELL) {
               count_sell++;
               sell_ticket = myPosition.Ticket();
               sell_op = myPosition.PriceOpen();
               sell_sl = myPosition.StopLoss();
               sell_tp = myPosition.TakeProfit();
               sell_lot = myPosition.Volume();
               sell_profit = myPosition.Profit();
            }
         }
      }
   }
}
//+------------------------------------------------------------------+
//| Open Buy position                                                |
//+------------------------------------------------------------------+
bool OpenPosition(ENUM_POSITION_TYPE pos_type, double volume, double sl, double tp)
{
   string EA_COMMENT = MQLInfoString(MQL_PROGRAM_NAME);

   sl=mySymbol.NormalizePrice(sl);
   tp=mySymbol.NormalizePrice(tp);

   if(pos_type==POSITION_TYPE_BUY) {
      if(!myTrade.Buy(volume, mySymbol.Name(), mySymbol.Ask(), sl, tp, EA_COMMENT)) {
         return(false);
      }
   }
   if(pos_type==POSITION_TYPE_SELL) {
      if(!myTrade.Sell(volume, mySymbol.Name(), mySymbol.Bid(), sl, tp, EA_COMMENT)) {
         return(false);
      }
   }
   myTrade.PrintResult();
   return(true);
}
//+------------------------------------------------------------------+
//| Close positions                                                  |
//+------------------------------------------------------------------+
bool ClosePositions(const ENUM_POSITION_TYPE pos_type)
{
   for(int i=PositionsTotal()-1; i>=0; i--) {
      if(myPosition.SelectByIndex(i)) {
         if(myPosition.Symbol()!=mySymbol.Name())continue;
         if(myPosition.Magic()!=MagicNumber) continue;
         if(!CheckSpreadForTrade())continue;
         if(myPosition.PositionType()==pos_type) {
            if(!myTrade.PositionClose(myPosition.Ticket()))
               return(false);
         }
      }
   }
   myTrade.PrintResult();
   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
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 (false);
   }
   return (true);
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double CalculateLot()
{
   double sl=0;

   CurrentATR  = iATRGet(1)*ATRMultiplier;

   //ActualSL = (UseATRSL) ? iATRGet(1)/_Pip : StopLossPips;
   sl = (UseATRSL) ? CurrentATR : ExtStopLoss;

   double RiskInMoney = _Balance*(TradeRisk/100);
   double slRangeInPoints = GetSignal()==1 ? (_Ask-ActualSL)/_Point : (ActualSL-_Bid)/_Point;

   double lot = RiskInMoney / (slRangeInPoints*_PointValue);
   if (LotSize>0) lot=LotSize;
   if (lot < _LotMin) lot = _LotMin;

   return(NormalizeLot(lot));
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double correctStopLoss()
{
   CurrentATR  = iATRGet(1)*ATRMultiplier;
   double temp=0, sl=0;
   temp = UseATRSL ? CurrentATR : StopLossPips>0 ? ExtStopLoss : 0;

   if(temp==0) sl=0;
   else sl = GetSignal()==1 ? _Ask-temp : _Bid+temp+_BrokerSpread;

   return(NormalizePrice(sl));
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double correctTakeProfit()
{
   double tp=0;

   if(TakeProfitPips==0) tp=0;
   else tp = GetSignal()==1 ? _Ask+ExtTakeProfit : _Bid-ExtTakeProfit;

   return(NormalizePrice(tp));
}

//+------------------------------------------------------------------+
//| Normalize price                                                  |
//+------------------------------------------------------------------+
double NormalizePrice(double price)
{
   if(_TickSize!=0)
      return(NormalizeDouble(MathRound(price/_TickSize)*_TickSize,_Digits));
//---
   return(NormalizeDouble(price,_Digits));
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double NormalizeLot(double thelot)
{
   double lots = _LotStep * NormalizeDouble(thelot / _LotStep, 0);
   lots = MathMax(MathMin(_LotMax, lots), _LotMin);
   return (lots);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void DisplayUserFeedback()
{
   CurrentATR = iATRGet(1)*ATRMultiplier;
   double swapLong  = SymbolInfoDouble(_Symbol, SYMBOL_SWAP_LONG);
   double swapShort = SymbolInfoDouble(_Symbol, SYMBOL_SWAP_SHORT);
   string CommentString = "";

   CommentString+="\n" + "Candle Color Trading EA" + "\n";
   CommentString+="local  time: "+TimeToString(TimeLocal() + TIME_DATE|TIME_MINUTES|TIME_SECONDS) + "\n";
   CommentString+="server time: "+TimeToString(TimeCurrent() + TIME_DATE|TIME_MINUTES|TIME_SECONDS) + "\n";
   CommentString+="+---------+ general information and settings:" + "\n";
   CommentString+="Current ATR with multiplier: " + DoubleToString(CurrentATR/_Pip,0) + " (multiplier: " + DoubleToString(ATRMultiplier,2) + ")" + "\n";
   CommentString+="Current stop loss: " + DoubleToString(ActualSL/_Pip,0) + "\n";
   CommentString+="Current lot size: " + DoubleToString(CalculateLot(),_Digitslot) + "\n";
   CommentString+="Magic number: " + (string)MagicNumber + "\n";
   CommentString+=" " + "\n";
   CommentString+="open trades: " + string(count_buy+count_sell) + " | types: "+string(count_buy ? "buy" : "sell") + "\n";
   CommentString+=" " + "\n";
   CommentString+="Spread = " + DoubleToString(_BrokerSpread, 1) + " | LongSwap " + DoubleToString(swapLong, 2) + ": ShortSwap " + DoubleToString(swapShort, 2) + "\n";
   CommentString+="---------------------------------------------\n";
   Comment(CommentString);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void manageupperstatus(string addstatus)
{
   ObjectDelete(0,"Upper_Status");
   if(addstatus!="") {
      ObjectCreate(0, "Upper_Status", OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0,"Upper_Status", OBJPROP_CORNER,1);
      ObjectSetInteger(0,"Upper_Status", OBJPROP_XDISTANCE, 2);
      ObjectSetInteger(0,"Upper_Status", OBJPROP_YDISTANCE, 13);
      ObjectSetString (0, "Upper_Status", OBJPROP_TEXT, addstatus);
      ObjectSetString (0, "Upper_Status", OBJPROP_FONT, "Verdana");
      ObjectSetInteger(0, "Upper_Status", OBJPROP_FONTSIZE, 9);
      ObjectSetInteger(0, "Upper_Status", OBJPROP_COLOR, clrWhite);
   }
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
void OnInitTestVisual()
{
   if (IsVisualMode || (!IsTesting)) {
      long hwnd=ChartID();
      if(hwnd>0) { // If it succeeded, additionally customize
         //--- Set the indent of the right border of the chart
         ChartSetInteger(hwnd,CHART_SHIFT,true);
         //--- Display as candlesticks
         ChartSetInteger(hwnd,CHART_MODE,CHART_CANDLES);
         ChartSetInteger(hwnd,CHART_SHOW_PERIOD_SEP, false);
         ChartSetInteger(hwnd,CHART_SHOW_GRID, false);
         ChartSetInteger(hwnd,CHART_FOREGROUND, false);
         ChartSetInteger(hwnd,CHART_BRING_TO_TOP,true);
         ChartSetInteger(hwnd,CHART_SHOW_ASK_LINE,true);
         ChartSetInteger(hwnd,CHART_SHIFT,true);
         ChartSetInteger(hwnd,CHART_AUTOSCROLL,true);
         ChartSetDouble(hwnd,CHART_SHIFT_SIZE,20);
         //--- Set the tick volume display mode
         //ChartSetInteger(handle,CHART_SHOW_VOLUMES,CHART_VOLUME_TICK);
      }
   }
}
//+------------------------------------------------------------------+
bool CheckPermission()
{
   if(IsTesting) {
      return (true);
   }

   if (!IsTradeAllowed ||
         !IsConnected ||
         IsStopped() ||
         !CheckFilterForTrade() ||
         !CheckSpreadForTrade()
      )
      return (false);

   return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CheckFilterForTrade()
{
   if(UseDistanceFiltering)
      if(MathAbs(positionOpenPrice-_Bid) < ExtDistance)
         return(false);
   return(true);
}
//+------------------------------------------------------------------+
//|                        CheckSpreadForTrade                       |
//+------------------------------------------------------------------+
bool CheckSpreadForTrade()
{
//-- if Broker Spread > Max Spread we are allowed
   if(MaxSpread < _BrokerSpread/_Pip) {
      string msgTemp = "Broker spread "+(string)_BrokerSpread+" at "+(string)_Symbol+
                       " has larger than max spread = "+(string)MaxSpread+" we allowed" + "\n";
      msgTemp += "We wait until broker spread reduced.";
      return(false);
   }
//--- checking successful
   return(true);
}
//+------------------------------------------------------------------+
//| Get value of buffers for the iRSI                                |
//+------------------------------------------------------------------+
double iATRGet(const int index)
{
   double ATR[1];
//--- reset error code
   ResetLastError();
//--- fill a part of the iATR array with values from the indicator buffer that has 0 index
   if(CopyBuffer(handle_iATR,0,index,1,ATR)<0) {
      //--- if the copying fails, tell the error code
      PrintFormat("Failed to copy data from the iRSI indicator, error code %d",GetLastError());
      //--- quit with zero result - it means that the indicator is considered as not calculated
      return(EMPTY_VALUE);
   }
   return(ATR[0]);
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Checks if the specified filling mode is allowed                  |
//+------------------------------------------------------------------+
bool IsFillingTypeAllowed(string symbol,int fill_type)
{
//--- Obtain the value of the property that describes allowed filling modes
   int filling=(int)SymbolInfoInteger(symbol,SYMBOL_FILLING_MODE);
//--- Return true, if mode fill_type is allowed
   return((filling &fill_type)==fill_type);
}
//+------------------------------------------------------------------+
//| Get value of buffers                                             |
//+------------------------------------------------------------------+
double iGetArray(const int handle, const int buffer, const int start_pos, const int count, double &arr_buffer[])
{
   bool result=true;
   if(!ArrayIsDynamic(arr_buffer)) {
      Print("This a no dynamic array!");
      return(false);
   }
   ArrayFree(arr_buffer);
//--- reset error code
   ResetLastError();
//--- fill a part of the iBands array with values from the indicator buffer
   int copied=CopyBuffer(handle, buffer, start_pos, count, arr_buffer);
   if(copied!=count) {
      //--- if the copying fails, tell the error code
      PrintFormat("Failed to copy data from the indicator, error code %d", GetLastError());
      //--- quit with zero result - it means that the indicator is considered as not calculated
      return(false);
   }
   return(result);
}
//+------------------------------------------------------------------+
