//+------------------------------------------------------------------+
//| MSS Expert Advisor - Basic Template                              |
//+------------------------------------------------------------------+
#property copyright "Lucien"
#property version   "1.00"
#property strict

input double   RiskPercent     = 1.0;
input double   RiskRewardRatio = 2.0;
input ENUM_TIMEFRAMES MSS_TF  = PERIOD_M5;
input int      SwingLength     = 5;
input int      SlBufferPoints  = 10;
input int      ATR_Period      = 14;
input bool     UseATR_SL       = true;
input int      MagicNumber     = 12345;

datetime lastTradeTime = 0;

//+------------------------------------------------------------------+
int OnInit() {
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
void OnTick() {
   static datetime lastMssCheck = 0;
   MqlRates rates[];
   if (CopyRates(_Symbol, MSS_TF, 0, 100, rates) <= 0)
      return;

   if (rates[0].time == lastMssCheck)
      return;
   lastMssCheck = rates[0].time;

   double atr = iATR(_Symbol, MSS_TF, ATR_Period, 0);
   int mssSignal = DetectMSS(rates);

   if (mssSignal == 1 && PositionSelect(_Symbol) == false) // Buy
      OpenTrade(ORDER_TYPE_BUY, atr);
   else if (mssSignal == -1 && PositionSelect(_Symbol) == false) // Sell
      OpenTrade(ORDER_TYPE_SELL, atr);
}

//+------------------------------------------------------------------+
int DetectMSS(const MqlRates &rates[]) {
   int bars = ArraySize(rates);
   if (bars < SwingLength * 2 + 2) return 0;

   // Detect recent swing highs/lows
   double hl = rates[1].low;
   double hh = rates[1].high;

   for (int i = 1; i <= SwingLength; i++) {
      if (rates[i].low > hl) hl = rates[i].low;
      if (rates[i].high < hh) hh = rates[i].high;
   }

   bool bullishMSS = (rates[1].low > rates[2].low && rates[0].close > hh);
   bool bearishMSS = (rates[1].high < rates[2].high && rates[0].close < hl);

   if (bullishMSS) return 1;
   if (bearishMSS) return -1;
   return 0;
}

//+------------------------------------------------------------------+
void OpenTrade(ENUM_ORDER_TYPE type, double atr) {
   double sl, tp, lotSize;
   double entryPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   if (type == ORDER_TYPE_SELL)
      entryPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);

   double slPoints = UseATR_SL ? atr : (10 * _Point);
   slPoints += SlBufferPoints * _Point;
   double stopLoss = type == ORDER_TYPE_BUY ? entryPrice - slPoints : entryPrice + slPoints;
   double takeProfit = type == ORDER_TYPE_BUY ? entryPrice + slPoints * RiskRewardRatio : entryPrice - slPoints * RiskRewardRatio;

   lotSize = CalculateLotSize(slPoints);

   MqlTradeRequest request;
   MqlTradeResult result;
   ZeroMemory(request);
   ZeroMemory(result);

   request.action   = TRADE_ACTION_DEAL;
   request.symbol   = _Symbol;
   request.volume   = lotSize;
   request.type     = type;
   request.price    = entryPrice;
   request.sl       = NormalizeDouble(stopLoss, _Digits);
   request.tp       = NormalizeDouble(takeProfit, _Digits);
   request.deviation= 10;
   request.magic    = MagicNumber;
   request.type_filling = ORDER_FILLING_IOC;

   OrderSend(request, result);
}

//+------------------------------------------------------------------+
double CalculateLotSize(double slPoints) {
   double balance = AccountInfoDouble(ACCOUNT_BALANCE);
   double riskMoney = balance * RiskPercent / 100.0;
   double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
   double tickSize  = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   double contractSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_CONTRACT_SIZE);

   if (tickSize == 0.0) tickSize = _Point;

   double lot = riskMoney / ((slPoints / _Point) * tickValue / tickSize);
   double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);

   lot = MathMax(minLot, MathMin(maxLot, lot));
   lot = MathFloor(lot / lotStep) * lotStep;
   return NormalizeDouble(lot, 2);
}