﻿//+------------------------------------------------------------------+
//|       Waka_Scalper Custom_Grid_Bot V1                            |
//+------------------------------------------------------------------+
#property copyright "Waka_Scalper"
#property link      ""
#property version   "1.00"

#include <Trade\Trade.mqh>

enum ENUM_LOT_METHOD {
    LOT_METHOD_FIXED = 0,
    LOT_METHOD_DYNAMIC = 1,
    LOT_METHOD_DEPOSIT = 2
};

enum ENUM_DRAWDOWN_ACTION {
    DD_ACTION_CLOSE_ALL = 0,
    DD_ACTION_PROHIBIT = 1
};

enum ENUM_TRADE_DIRECTION {
    TRADE_DIR_BOTH = 0,
    TRADE_DIR_BUY = 1,
    TRADE_DIR_SELL = 2
};

enum ENUM_NEWS_ACTION {
    NEWS_ACTION_PROHIBIT = 0,
    NEWS_ACTION_CLOSE = 1
};

enum ENUM_TIMEFRAME_SELECTION {
    TF_CURRENT = 0,
    TF_M1 = 1,
    TF_M5 = 5,
    TF_M15 = 15,
    TF_H1 = 60,
    TF_D1 = 1440
};

input group "=== 1. Money Management & Risk ==="
input bool               AllowNewInitialTrade = true;
input ENUM_LOT_METHOD    LotSizingMethod = LOT_METHOD_FIXED;
input double             FixedLot = 0.01;
input double             DynamicLot = 10000.0;
input double             DepositLoadPercent = 0.25;
input double             MaxLotPerOrder = 100.0;
input bool               AutoSplit = true;
input double             MaxSpreadPips = 10.0;
input double             MaxSlippagePips = 10.0;

input group "=== 2. Portfolio & Hedging Settings ==="
input bool               PortfolioMode = false;
input int                MaxSymbolsAtTime = 2;
input bool               AllowHedging = true;
input ENUM_TRADE_DIRECTION AllowToBuySell = TRADE_DIR_BOTH;
input bool               OneTradePerDay = false;
input bool               OpenOppositeTrade = false;
input bool               ReverseStrategy = false;

input group "=== 3. Drawdown & Protection ==="
input double             MaxOpenLots = 100000.0;
input double             MaxFloatingDrawdownPercent = 100.0;
input double             MaxFloatingDrawdownMoney = 0.0;
input double             MaxDailyDrawdownLimitFTMO = 100.0;
input ENUM_DRAWDOWN_ACTION MaxDrawdownAction = DD_ACTION_CLOSE_ALL;
input bool               HandleMaxDrawdownEventsEveryTick = false;
input bool               CheckMarginForGridLevels = true;

input group "=== 4. Time & Days Filters ==="
input int                HourToStartTrading = 0;
input int                MinutesToStartTrading = 0;
input int                HourToStopTrading = 23;
input int                MinutesToStopTrading = 59;
input bool               TradeOnMonday = true;
input bool               TradeOnTuesday = true;
input bool               TradeOnWednesday = true;
input bool               TradeOnThursday = true;
input bool               TradeOnFriday = true;
input int                RolloverStartHour = 23;
input int                RolloverStartMinutes = 45;
input int                RolloverEndHour = 0;
input int                RolloverEndMinutes = 15;
input bool               SendOrdersDuringRollover = true;

input group "=== 5. Core Strategy & Indicators ==="
input string             SymbolsSeparatedByComma = "AUDCAD";
input ENUM_TIMEFRAME_SELECTION WorkingTF_BB_RSI = TF_M1;
input int                BollingerBandsPeriod = 35;
input int                RSIPeriod = 14;
input double             MaximumRSIValue = 40.0;
input int                DailyEMAPeriod = 0;
input bool               MLPatternRecognition = true;
input double             MLPatternFilterThreshold = 80.0;

input group "=== 6. Take Profit & Stop Loss ==="
input double             TakeProfitInitialTradePips = 5.0;
input bool               WeightedTakeProfit = true;
input double             TakeProfitGridPips = 5.0;
input bool               HideTakeProfit = false;
input bool               SmartTakeProfit = false;
input bool               CoverSwaps = true;
input double             StopLossGridPips = 0.0;
input bool               HideStopLoss = false;
input double             TrailingSLSizePips = 0.0;
input double             TrailingSLStartPips = 10.0;

input group "=== 7. Grid & Martingale System ==="
input double             TradeDistance = 10.0;
input int                PauseBetweenGridTradesMin = 0;
input bool               SmartDistance = true;
input double             Multiplier2ndTrade = 1.0;
input double             Multiplier3rdTo5thTrade = 2.0;
input double             Multiplier6thTrade = 1.6;
input int                MaximumTrades = 9;
input string             CustomMultipliers = "";
input bool               CalculateLevelsFromInitialTrade = true;

input group "=== 8. News & Crash Filters ==="
input bool               NewsFilterEnabled = true;
input ENUM_NEWS_ACTION   NewsFilterAction = NEWS_ACTION_PROHIBIT;
input bool               DisableTradingBankHolidays = false;
input bool               MediumImpactNews = false;
input bool               LowImpactNews = false;
input int                WaitMinutesBeforeEvent = 15;
input int                WaitMinutesAfterEvent = 15;
input bool               StockMarketCrashFilterEnabled = false;
input string             StockMarketSymbol = "US500,SPX500";
input int                CrashFilterPeriod = 48;
input double             MaxHistoricalVolatilityPercent = 40.0;

input group "=== 9. EA & Misc Settings ==="
input string             TradeComment = "Waka_Scalper";
input long               BasicMagicNumber = 84570;
input bool               ShowPanel = true;
input int                FontSize = 6;
input bool               DebugMode = false;

CTrade         trade;
int            rsiHandle;
double         rsiBuffer[];

ENUM_TIMEFRAMES GetIndicatorTimeframe()
  {
   switch(WorkingTF_BB_RSI)
     {
      case TF_M1:  return PERIOD_M1;
      case TF_M5:  return PERIOD_M5;
      case TF_M15: return PERIOD_M15;
      case TF_H1:  return PERIOD_H1;
      case TF_D1:  return PERIOD_D1;
      default:     return PERIOD_CURRENT;
     }
  }

int OnInit()
  {
   trade.SetExpertMagicNumber(BasicMagicNumber);
   
   ENUM_TIMEFRAMES tf = GetIndicatorTimeframe();
   rsiHandle = iRSI(_Symbol, tf, RSIPeriod, PRICE_CLOSE);
   if(rsiHandle == INVALID_HANDLE)
     {
      Print("Failed to create RSI handle");
      return(INIT_FAILED);
     }
     
   ArraySetAsSeries(rsiBuffer, true);
   
   return(INIT_SUCCEEDED);
  }

void OnDeinit(const int reason)
  {
   IndicatorRelease(rsiHandle);
  }

void OnTick()
  {
   if(!MQLInfoInteger(MQL_TRADE_ALLOWED)) return;

   int totalBuy = GetTotalOpenPositions(POSITION_TYPE_BUY);
   int totalSell = GetTotalOpenPositions(POSITION_TYPE_SELL);
   
   double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

   if(totalBuy == 0 && totalSell == 0 && AllowNewInitialTrade)
     {
      CheckInitialEntry(ask, bid);
     }
     
   if(totalBuy > 0 && totalBuy < MaximumTrades)
     {
      if(CheckGridDistance(POSITION_TYPE_BUY, ask))
        {
         double nextLot = CalculateNextLotSize(totalBuy, FixedLot);
         if(trade.Buy(nextLot, _Symbol, ask, 0, 0, TradeComment))
           {
            UpdateBasketTakeProfit(POSITION_TYPE_BUY);
           }
        }
     }
     
   if(totalSell > 0 && totalSell < MaximumTrades)
     {
      if(CheckGridDistance(POSITION_TYPE_SELL, bid))
        {
         double nextLot = CalculateNextLotSize(totalSell, FixedLot);
         if(trade.Sell(nextLot, _Symbol, bid, 0, 0, TradeComment))
           {
            UpdateBasketTakeProfit(POSITION_TYPE_SELL);
           }
        }
     }
  }

void CheckInitialEntry(double ask, double bid)
  {
   if(CopyBuffer(rsiHandle, 0, 0, 2, rsiBuffer) <= 0) return;
   
   double currentRSI = rsiBuffer[0];
   
   if(currentRSI < MaximumRSIValue && (AllowToBuySell == TRADE_DIR_BOTH || AllowToBuySell == TRADE_DIR_BUY))
     {
      double tp = ask + (TakeProfitInitialTradePips * _Point * 10);
      trade.Buy(FixedLot, _Symbol, ask, 0, tp, TradeComment);
     }
   else if(currentRSI > (100 - MaximumRSIValue) && (AllowToBuySell == TRADE_DIR_BOTH || AllowToBuySell == TRADE_DIR_SELL))
     {
      double tp = bid - (TakeProfitInitialTradePips * _Point * 10);
      trade.Sell(FixedLot, _Symbol, bid, 0, tp, TradeComment);
     }
  }

int GetTotalOpenPositions(int type)
  {
   int count = 0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      if(PositionSelectByTicket(PositionGetTicket(i)))
        {
         if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == BasicMagicNumber)
           {
            if(PositionGetInteger(POSITION_TYPE) == type) count++;
           }
        }
     }
   return count;
  }

double CalculateNextLotSize(int positionCount, double initialLot)
  {
   double nextLot = initialLot;
   if(positionCount == 1) nextLot = initialLot * Multiplier2ndTrade;
   else if(positionCount >= 2 && positionCount <= 4) nextLot = initialLot * Multiplier3rdTo5thTrade;
   else if(positionCount >= 5) nextLot = initialLot * Multiplier6thTrade;
   return NormalizeDouble(nextLot, 2);
  }

double GetLastPositionPrice(int type)
  {
   double lastPrice = 0;
   ulong lastTime = 0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      if(PositionSelectByTicket(PositionGetTicket(i)))
        {
         if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == BasicMagicNumber)
           {
            if(PositionGetInteger(POSITION_TYPE) == type)
              {
               ulong time = PositionGetInteger(POSITION_TIME);
               if(time > lastTime)
                 {
                  lastTime = time;
                  lastPrice = PositionGetDouble(POSITION_PRICE_OPEN);
                 }
              }
           }
        }
     }
   return lastPrice;
  }

bool CheckGridDistance(int type, double currentPrice)
  {
   double lastPrice = GetLastPositionPrice(type);
   if(lastPrice == 0) return false;
   
   double distance = 0;
   if(type == POSITION_TYPE_BUY) distance = lastPrice - currentPrice;
   else if(type == POSITION_TYPE_SELL) distance = currentPrice - lastPrice;
      
   if(distance >= (TradeDistance * _Point * 10)) return true;
   return false;
  }

void UpdateBasketTakeProfit(int type)
  {
   int count = 0;
   double totalVolume = 0;
   double weightedPrice = 0;
   
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      if(PositionSelectByTicket(PositionGetTicket(i)))
        {
         if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == BasicMagicNumber)
           {
            if(PositionGetInteger(POSITION_TYPE) == type)
              {
               double vol = PositionGetDouble(POSITION_VOLUME);
               double price = PositionGetDouble(POSITION_PRICE_OPEN);
               totalVolume += vol;
               weightedPrice += price * vol;
               count++;
              }
           }
        }
     }
     
   if(count > 0 && totalVolume > 0)
     {
      double averagePrice = weightedPrice / totalVolume;
      double newTP = 0;
      
      if(type == POSITION_TYPE_BUY) newTP = averagePrice + (TakeProfitGridPips * _Point * 10);
      else if(type == POSITION_TYPE_SELL) newTP = averagePrice - (TakeProfitGridPips * _Point * 10);
         
      newTP = NormalizeDouble(newTP, _Digits);
      
      for(int i = PositionsTotal() - 1; i >= 0; i--)
        {
         if(PositionSelectByTicket(PositionGetTicket(i)))
           {
            if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == BasicMagicNumber)
              {
               if(PositionGetInteger(POSITION_TYPE) == type)
                 {
                  double currentTP = NormalizeDouble(PositionGetDouble(POSITION_TP), _Digits);
                  if(currentTP != newTP)
                    {
                     trade.PositionModify(PositionGetInteger(POSITION_TICKET), PositionGetDouble(POSITION_SL), newTP);
                    }
                 }
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+