//+------------------------------------------------------------------+
//|                                                    Jonestown.mq4 |
//|                                           Copyright 2023, Clemmo |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Clemmo"
#property link      "https://www.forexfactory.com/thread/post/14330452#post14330452"
#property version   "1.00"
#property strict

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
input double startBalance=5000;
input double Starting_lots=0.01;
input double delta=5000;
input int    levelNumber=50;
input double riskAmount=5000;  //Risk amount per lot ($)
input int    MN=11181978; //Magic Number
input int    X=20;
input int    Y=3;
input int    Z=50; // Z(%)
input ENUM_MA_METHOD method=MODE_SMA;
input bool useProtectMode=false; //profit protect mode on/off.


input string First_StarTime="00:00";
input string First_EndTime="0:00";

input string Second_StarTime="0:00";
input string Second_EndTime="23:59";

input string Third_StarTime="00:00";
input string Third_EndTime="0:00";

string globalName="MaxEquity";

int startFirst,endFirst, startSecond, endSecond, startThird,endThird;
double Uplevels[];
double Dnlevels[];
int    trend=0; //0:up  1:dn
double openEquity=0.0;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int GetMins_FromDate(datetime startD)
  {
   return TimeHour(startD)*60+TimeMinute(startD);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int GetMins_FromDateText(string endTime)
  {
   datetime endD=StringToTime(endTime);
   return GetMins_FromDate(endD);
  }

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   ArrayResize(Uplevels,levelNumber);
   ArrayResize(Dnlevels,levelNumber);
//---
   startFirst=GetMins_FromDateText(First_StarTime);
   endFirst=GetMins_FromDateText(First_EndTime);

   startSecond=GetMins_FromDateText(Second_StarTime);
   endSecond=GetMins_FromDateText(Second_EndTime);

   startThird=GetMins_FromDateText(Third_StarTime);
   endThird=GetMins_FromDateText(Third_EndTime);


   if(!GlobalVariableCheck(globalName) || IsTesting())
      GlobalVariableSet(globalName,0.0);


   for(int i=0; i<levelNumber; i++)
      Uplevels[i]=startBalance+i*delta;

   for(int i=levelNumber-1; i>=1; i--)
     {
      if(i==levelNumber-1)
         Dnlevels[i]=Uplevels[i]-(Uplevels[i]-Uplevels[i-1])*Z/100;
      else
         Dnlevels[i]=Dnlevels[i+1]-(Dnlevels[i+1]-Uplevels[i-1])*Z/100;
     }
   Dnlevels[0]=0;


//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   bool isDD= MaxDDProcess();
   if(isDD)
      return;
   bool isTradingTime=IsTradingTime();
   if(!isTradingTime)
      return;
   double MA1=iMA(_Symbol,_Period,X,0,method,PRICE_CLOSE,1);
   double MA2=iMA(_Symbol,_Period,X,0,method,PRICE_CLOSE,Y+1);

   bool isBuy=MA1>MA2 && Close[1]<Close[Y+1] && Close[1]>Close[X+Y+1];
   bool isSell=MA1<MA2 && Close[1]>Close[Y+1] && Close[1]<Close[X+Y+1];

   static datetime OrderT;
   if(isBuy && CountMarketOrders(_Symbol,OP_BUY,MN)==0 && OrderT!=Time[0])
     {
      int ticket;     
      CloseOrders(_Symbol,OP_SELL,MN);
      double lot=openEquity<AccountEquity()? GetLot():GetLot_Decrease();

      if(SendOrder(_Symbol,OP_BUY,lot,0,0,MN,ticket))
        {
         openEquity=AccountEquity();
         OrderT=Time[0];
        }
     }
   if(isSell && CountMarketOrders(_Symbol,OP_SELL,MN)==0 &&OrderT!=Time[0])
     {
      int ticket;
      CloseOrders(_Symbol,OP_BUY,MN);
      double lot=openEquity<AccountEquity()? GetLot():GetLot_Decrease();

      if(SendOrder(_Symbol,OP_SELL,lot,0,0,MN,ticket))
        {
         OrderT=Time[0];
         openEquity=AccountEquity();
        }
     }


  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double GetLot_Decrease()
  {

   double result=Starting_lots;
   for(int i=levelNumber-1; i>=1; i--)
     {
      double equity=AccountEquity();
      if(equity>=Dnlevels[i])
         return (i+1)*Starting_lots;
     }
   return result;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double GetLot()
  {
   double result=Starting_lots;
   for(int i=1; i<levelNumber; i++)
     {
      double equity=AccountEquity();
      if(equity<Uplevels[i])
         return i*Starting_lots;
     }
   result=levelNumber*Starting_lots;

   return result;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool IsTradingTime()
  {
   bool result=false;

   int now=GetMins_FromDate(TimeCurrent());

   if((now>=startFirst && now<=endFirst) ||
      (now>=startSecond && now<=endSecond) ||
      (now>=startThird && now<=endThird))
      result=true;

   return result;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CountMarketOrders(string   orderSymbol,int type,
                      int      orderMagicNumber
                     )
  {
   int count = 0;
   for(int i = OrdersTotal() - 1; i >= 0; i--)
     {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
         if(OrderSymbol() == orderSymbol &&
            OrderMagicNumber() == orderMagicNumber &&
            (OrderType()==type))
           {
            count++;
           }
        }
     }
   return(count);
  }
//+------------------------------------------------------------------+
bool  MaxDDProcess()
  {
   bool isDD=false;
   double maxEquity=GlobalVariableGet(globalName);
   double equity=AccountEquity();

   if(equity>maxEquity)
     {
      GlobalVariableSet(globalName,equity);
      //Print("MAX Equity is Updated  ",TimeCurrent(), " : ",equity);
      return false;
     }
   else
     {
      double DD=(maxEquity-equity);
      double curLot=getLastLot(_Symbol,MN);
      if(DD>=curLot*riskAmount && curLot!=0)
        {
         Print("!!! Max DD is reached !!! maxEquity:",maxEquity, " curDD:",DD,"($)");
         CloseOrders(_Symbol,OP_BUY,MN);
         CloseOrders(_Symbol,OP_SELL,MN);
         GlobalVariableSet(globalName,0);
         isDD=true;
        }
     }
   return isDD;
  }
//+------------------------------------------------------------------+
double getLastLot(string sym,int magic)
  {
   int cpt, total = OrdersTotal();

   for(cpt=total-1; cpt>=0; cpt--)
     {
      double bid=SymbolInfoDouble(sym,SYMBOL_BID);
      double ask=SymbolInfoDouble(sym,SYMBOL_ASK);
      if(OrderSelect(cpt,SELECT_BY_POS,MODE_TRADES)==false)
         continue;
      if(OrderSymbol() == sym  && OrderMagicNumber()==magic)
        {
         return OrderLots();
        }

     }
   return 0;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CloseOrders(string sym, int orderType, int magic)
  {
   int cpt, total = OrdersTotal();

   for(cpt=total-1; cpt>=0; cpt--)
     {
      double bid=SymbolInfoDouble(sym,SYMBOL_BID);
      double ask=SymbolInfoDouble(sym,SYMBOL_ASK);
      if(OrderSelect(cpt,SELECT_BY_POS,MODE_TRADES)==false)
         continue;
      if(OrderSymbol() == sym && OrderType()==orderType && OrderMagicNumber()==magic)
        {
         bool result;
         if(orderType==0)
            result=OrderClose(OrderTicket(), OrderLots(), bid, 1000, Blue);
         if(orderType==1)
            result=OrderClose(OrderTicket(), OrderLots(), ask, 1000, Red);
         if(orderType>1)
            result=OrderDelete(OrderTicket());
        }

     }
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool SendOrder(string sym, int _type,double Lot,double StopLoss,double TakeProfit, int MagicNumber,int& ticket,string comment="")
  {
   double Min_Lot=MarketInfo(sym,MODE_MINLOT);
   int slipage=(int)(2*MarketInfo(sym,MODE_SPREAD));
   double bid=SymbolInfoDouble(sym,SYMBOL_BID);
   double ask=SymbolInfoDouble(sym,SYMBOL_ASK);
   double point=SymbolInfoDouble(sym,SYMBOL_POINT);
   int    digits=(int)SymbolInfoInteger(sym,SYMBOL_DIGITS);
   int minstoplevel=(int)MarketInfo(sym,MODE_STOPLEVEL);//----



   double m_point = point;
   if(digits == 3 || digits == 5)
      m_point *= 10;
   while(IsTradeContextBusy());

   double SL = 0, TP = 0;

   if(Lot<Min_Lot)
     {
      Print(" Lot is smaller than minimum lotsize: ", Lot," lots");
      return false;
     }
   string desc = "";
   if(!CheckVolumeValue(sym,Lot, desc))
      return(false);

//----
   if(_type == OP_BUY)
     {
      if(CheckMoneyForTrade(sym,Lot,OP_BUY)==false)
        {
         Alert(" Not enough money for ", Lot," lots");
         return false;
        }
      RefreshRates();

      if(StopLoss > 0)
        {
         SL = NormalizeDouble(ask - StopLoss * m_point,digits);
         if(SL> NormalizeDouble(bid-minstoplevel*point,digits))
           {PrintFormat("Invalid Input StopLoss(%f).",StopLoss); return false;}
        }
      if(TakeProfit > 0)
        {
         TP = NormalizeDouble(ask + TakeProfit * m_point, digits);
         if(TP < NormalizeDouble(ask+minstoplevel*point,digits))
           {Print("Invalid Input TakeProfit.",TakeProfit); return false;}
        }

      RefreshRates();
      ticket = OrderSend(sym, OP_BUY, Lot, ask, 10000, SL, TP, comment, MagicNumber, 0,Blue);
      if(ticket>0)
        {
         return true;
        }
     }
//----
   if(_type == OP_SELL)
     {
      if(CheckMoneyForTrade(sym,Lot,OP_SELL)==false)
        {
         Alert(" Not enough money for ", Lot," lots");
         return false;
        }
      RefreshRates();

      if(StopLoss > 0)
        {
         SL = NormalizeDouble(bid + StopLoss * m_point, digits);
         if(SL < NormalizeDouble(ask+minstoplevel*point,digits))
           {Print("Invalid Input StopLoss.",StopLoss); return false;}
        }
      if(TakeProfit > 0)
        {
         TP = NormalizeDouble(bid - TakeProfit * m_point, digits);
         if(TP >  NormalizeDouble(bid-minstoplevel*point,digits))
           {Print("Invalid Input TakeProfit.",TakeProfit); return false;}
        }

      RefreshRates();
      ticket = OrderSend(sym, OP_SELL, Lot, bid, 10000, SL, TP, comment, MagicNumber, 0,Red);
      if(ticket>0)
        {

         return true;
        }
     }
//----
   if(ticket < 0)
     {
      Print("OrderSend failed with error code #", GetLastError());
      // Alert("OrderSend  failed with error code #", GetLastError());
      return(false);
     }
   return(true);
  }



//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CheckVolumeValue(string sym,double volume,string &description)
  {
//--- minimal allowed volume for trade operations
   double min_volume=SymbolInfoDouble(sym,SYMBOL_VOLUME_MIN);
   if(volume<min_volume)
     {
      description=StringFormat("Volume is less than the minimum allowed SYMBOL_VOLUME_MIN=%.2f, %.2f,(%s)",min_volume, volume,sym);
      Alert(description);
      return(false);
     }

//--- maximal allowed volume of trade operations
   double max_volume=SymbolInfoDouble(sym,SYMBOL_VOLUME_MAX);
   if(volume>max_volume)
     {
      description=StringFormat("Volume is greater than the maximum allowed SYMBOL_VOLUME_MAX=%.2f,(%s)",max_volume,sym);
      Alert(description);
      return(false);
     }

//--- get minimal step of volume changing

   description="Correct volume value";
   return(true);
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CheckMoneyForTrade(string symb, double lots,int type)
  {
   double free_margin=AccountFreeMarginCheck(symb,type, lots);
//-- if there is not enough money
   if(free_margin<0)
     {
      string oper=(type==OP_BUY)? "Buy":"Sell";
      Print("Not enough money for ", oper," ",lots, " ", symb, " Error code=",GetLastError());
      return(false);
     }
//--- checking successful
   return(true);
  }
//+------------------------------------------------------------------+
