//+------------------------------------------------------------------+
//|                                                   MSS_M1_v2.mq5  |
//|            MSS detection for M1 with SwingLength=3              |
//+------------------------------------------------------------------+
#property copyright "StackExchange"
#property version   "1.07"
#property strict

input int SwingLength        = 3;               // Bars left and right for swing pivots
input color BullishColor     = clrGreen;        // Color bullish MSS/lines
input color BearishColor     = clrRed;          // Color bearish MSS/lines
input ENUM_TIMEFRAMES TF     = PERIOD_CURRENT;  // Timeframe to run on (default current)

double prevPivotHigh = 0.0;
datetime prevPivotHighTime = 0;
double prevPivotLow = 0.0;
datetime prevPivotLowTime = 0;

int prevBreakoutType = 0;   // 1= bullish, -1= bearish, 0= none

datetime lastBullMssTime = 0;
double lastBullMssPrice = 0.0;
datetime lastBearMssTime = 0;
double lastBearMssPrice = 0.0;

int OnInit()
  {
   Print("MSS M1 Detector started");
   return(INIT_SUCCEEDED);
  }

void OnDeinit(const int reason)
  {
   string prefixes[] = {"MSS_", "MSS_LABEL_", "MSS_LINE_BULL", "MSS_LINE_BEAR"};
   for(int i=0; i < ArraySize(prefixes); i++)
     {
      int totalObj = ObjectsTotal(0, 0, 0);
      for(int j=totalObj-1; j>=0; j--)
        {
         string name = ObjectName(0, j, 0, 0);
         if(StringFind(name, prefixes[i], 0) == 0)
            ObjectDelete(0, name);
        }
     }
  }

void OnTick()
  {
   DetectMSS();
  }

void DetectMSS()
  {
   int bars = iBars(_Symbol, TF);
   if(bars < SwingLength*2 + 1) return;

   static datetime lastBarTime = 0;
   datetime currentBarTime = iTime(_Symbol, TF, 0);
   if(currentBarTime == lastBarTime) return;
   lastBarTime = currentBarTime;

   int idx = 1; // look at previous fully closed bar

   // Find swing high
   bool foundHigh = false;
   double swingHigh = 0.0;
   datetime swingHighTime = 0;
   for(int i = idx; i < bars - SwingLength; i++)
     {
      if(IsSwingHigh(_Symbol, TF, i, SwingLength))
        {
         swingHigh = iHigh(_Symbol, TF, i);
         swingHighTime = iTime(_Symbol, TF, i);
         foundHigh = true;
         break;
        }
     }

   // Find swing low
   bool foundLow = false;
   double swingLow = 0.0;
   datetime swingLowTime = 0;
   for(int i = idx; i < bars - SwingLength; i++)
     {
      if(IsSwingLow(_Symbol, TF, i, SwingLength))
        {
         swingLow = iLow(_Symbol, TF, i);
         swingLowTime = iTime(_Symbol, TF, i);
         foundLow = true;
         break;
        }
     }

   // Update pivots with debug
   if(foundHigh)
     {
      prevPivotHigh = swingHigh;
      prevPivotHighTime = swingHighTime;
      PrintFormat("Pivot High at %s : %.5f", TimeToString(swingHighTime), swingHigh);
     }
   if(foundLow)
     {
      prevPivotLow = swingLow;
      prevPivotLowTime = swingLowTime;
      PrintFormat("Pivot Low at %s : %.5f", TimeToString(swingLowTime), swingLow);
     }

   double closePrice = iClose(_Symbol, TF, 0);

   // Initialize prevBreakoutType if zero and pivots known
   if(prevBreakoutType == 0 && prevPivotHigh != 0 && prevPivotLow != 0)
     {
      double prevClose = iClose(_Symbol, TF, 1); // close prev bar
      if(prevClose > prevPivotHigh)
        {
         prevBreakoutType = 1;
         Print("Initial breakout set: Bullish");
        }
      else if(prevClose < prevPivotLow)
        {
         prevBreakoutType = -1;
         Print("Initial breakout set: Bearish");
        }
     }

   // Dynamically update breakout type if price crosses pivots
   if(closePrice > prevPivotHigh && prevBreakoutType != 1)
     {
      prevBreakoutType = 1;
      Print("Breakout direction updated to Bullish");
     }
   else if(closePrice < prevPivotLow && prevBreakoutType != -1)
     {
      prevBreakoutType = -1;
      Print("Breakout direction updated to Bearish");
     }

   // MSS detection - only when breakout direction flips and price crosses pivots
   bool bullishMSS = (closePrice > prevPivotHigh && prevBreakoutType == 1 && lastBullMssTime != prevPivotHighTime);
   bool bearishMSS = (closePrice < prevPivotLow && prevBreakoutType == -1 && lastBearMssTime != prevPivotLowTime);

   if(bullishMSS)
     {
      PlotMSS(prevPivotHighTime, prevPivotHigh, true);

      if(lastBullMssTime != 0)
         DrawLine("MSS_LINE_BULL", lastBullMssTime, lastBullMssPrice, prevPivotHighTime, prevPivotHigh, BullishColor);

      lastBullMssTime = prevPivotHighTime;
      lastBullMssPrice = prevPivotHigh;

      PrintFormat("Bullish MSS at %s price %.5f", TimeToString(prevPivotHighTime), prevPivotHigh);
     }
   else if(bearishMSS)
     {
      PlotMSS(prevPivotLowTime, prevPivotLow, false);

      if(lastBearMssTime != 0)
         DrawLine("MSS_LINE_BEAR", lastBearMssTime, lastBearMssPrice, prevPivotLowTime, prevPivotLow, BearishColor);

      lastBearMssTime = prevPivotLowTime;
      lastBearMssPrice = prevPivotLow;

      PrintFormat("Bearish MSS at %s price %.5f", TimeToString(prevPivotLowTime), prevPivotLow);
     }
  }

bool IsSwingHigh(string symbol, ENUM_TIMEFRAMES tf, int index, int len)
  {
   double curHigh = iHigh(symbol, tf, index);
   for(int i=1; i<=len; i++)
     {
      if(iHigh(symbol, tf, index - i) > curHigh) return false;
      if(iHigh(symbol, tf, index + i) > curHigh) return false;
     }
   return true;
  }

bool IsSwingLow(string symbol, ENUM_TIMEFRAMES tf, int index, int len)
  {
   double curLow = iLow(symbol, tf, index);
   for(int i=1; i<=len; i++)
     {
      if(iLow(symbol, tf, index - i) < curLow) return false;
      if(iLow(symbol, tf, index + i) < curLow) return false;
     }
   return true;
  }

void PlotMSS(datetime time, double price, bool bullish)
  {
   string arrowName = "MSS_" + TimeToString(time, TIME_DATE|TIME_SECONDS);
   if(ObjectFind(0, arrowName) >= 0)
      ObjectDelete(0, arrowName);

   string labelName = "MSS_LABEL_" + TimeToString(time, TIME_DATE|TIME_SECONDS);
   if(ObjectFind(0, labelName) >= 0)
      ObjectDelete(0, labelName);

   string text = bullish ? "Bullish MSS" : "Bearish MSS";
   color clr = bullish ? BullishColor : BearishColor;

   ObjectCreate(0, arrowName, OBJ_ARROW, 0, time, price);
   ObjectSetInteger(0, arrowName, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, 2);

   string arrow_char = bullish ? "\xE9" : "\xEA"; // Wingdings up/down arrows
   ObjectSetString(0, arrowName, OBJPROP_SYMBOL, arrow_char);

   ObjectCreate(0, labelName, OBJ_TEXT, 0, time, price);
   ObjectSetString(0, labelName, OBJPROP_TEXT, text);
   ObjectSetInteger(0, labelName, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 11);
   ObjectSetInteger(0, labelName, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, labelName, OBJPROP_SELECTED, false);
   ObjectSetInteger(0, labelName, OBJPROP_BACK, true);
  }

void DrawLine(string baseName, datetime time1, double price1, datetime time2, double price2, color clr)
  {
   string lineName = baseName;

   if(ObjectFind(0, lineName) < 0)
     {
      ObjectCreate(0, lineName, OBJ_TREND, 0, time1, price1, time2, price2);
      ObjectSetInteger(0, lineName, OBJPROP_COLOR, clr);
      ObjectSetInteger(0, lineName, OBJPROP_WIDTH, 2);
      ObjectSetInteger(0, lineName, OBJPROP_RAY_RIGHT, false);
      ObjectSetInteger(0, lineName, OBJPROP_BACK, true);
     }
   else
     {
      ObjectMove(0, lineName, 0, time1, price1);
      ObjectMove(0, lineName, 1, time2, price2);
      ObjectSetInteger(0, lineName, OBJPROP_COLOR, clr);
     }
  }