//+------------------------------------------------------------------+
//|                                      Future Swing Projection.mq5 |
//|                                             Abioye Israel Pelumi |
//|                                              https://Algoyin.com |
//+------------------------------------------------------------------+
#property copyright "Abioye Israel Pelumi"
#property link      "https://Algoyin.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots   0

#define PFX "FSO_"

input int    InpLen         = 30;    // Swing Detection Length

datetime last_bar_time; // Stores the last processed candle time

//--- Stores the distance between consecutive swing points and the calculated average swing movement
double s1_s2_interval; // distance between Swing 1 and Swing 2
double s2_s3_interval; // distance between Swing 2 and Swing 3
double s3_s4_interval; // distance between Swing 3 and Swing 4
double s4_s5_interval; // distance between Swing 4 and Swing 5
double s5_s6_interval; // distance between Swing 5 and Swing 6
double avg_price;      // average distance of the five completed swing legs

int atr_handle;
double atr_buffer[];

//+------------------------------------------------------------------+
//| Create or update a horizontal line                               |
//+------------------------------------------------------------------+
void DrawTrend(const string name,
               datetime x1, double y,
               datetime x2, double y2,
               color col, int w,
               int style)
  {
//--- Create only if missing
   if(ObjectFind(0, name) < 0)
     {
      ObjectCreate(0, name, OBJ_TREND, 0, x1, y, x2, y2); // create trend line object at start/end coordinates
      ChartRedraw(0);
     }

//--- Update properties
   ObjectSetInteger(0, name, OBJPROP_COLOR, col);
   ObjectSetInteger(0, name, OBJPROP_WIDTH, w);
   ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, false);  // stop the line from projecting infinitely to the right
   ObjectSetInteger(0, name, OBJPROP_BACK, true);        // draw behind chart candles
   ObjectSetInteger(0, name, OBJPROP_STYLE, style);
   ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false); // prevent user from accidentally dragging it
   ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);      // hide from the object list

//--- Update position
   ObjectMove(0, name, 0, x1, y);                        // move anchor point 0 (start)
   ObjectMove(0, name, 1, x2, y2);                       // move anchor point 1 (end)

   ChartRedraw(0);
  }

//+------------------------------------------------------------------+
//| Create or update a rectangle                                     |
//+------------------------------------------------------------------+
void DrawBox(const string name,
             datetime x1, double yTop,
             datetime x2, double yBot,
             color fillCol)
  {
//--- Create object only if it does not exist
   if(ObjectFind(0, name) < 0)
     {
      ObjectCreate(0, name, OBJ_RECTANGLE, 0, x1, yTop, x2, yBot); // create rectangle spanning the price zone
      ChartRedraw(0);
     }

//--- Update object properties
   ObjectSetInteger(0, name, OBJPROP_COLOR, fillCol);
   ObjectSetInteger(0, name, OBJPROP_BGCOLOR, fillCol);           // fill color of the rectangle body
   ObjectSetInteger(0, name, OBJPROP_FILL, true);                 // enable solid fill instead of outline only
   ObjectSetInteger(0, name, OBJPROP_BACK, true);
   ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);

//--- Update position
   ObjectMove(0, name, 0, x1, yTop);                             // top-left corner
   ObjectMove(0, name, 1, x2, yBot);                             // bottom-right corner

   ChartRedraw(0);
  }

//+------------------------------------------------------------------+
//| Create or update text                                            |
//+------------------------------------------------------------------+
void CreateTxt(const string name,
               datetime x, double y,
               string txt, color fillCol)
  {
//--- Create object only if it does not exist
   if(ObjectFind(0, name) < 0)
     {
      ObjectCreate(0, name, OBJ_TEXT, 0, x, y, 0, 0);  // create text label at given time/price
      ChartRedraw(0);
     }

//--- Update object properties
   ObjectSetInteger(0, name, OBJPROP_COLOR, fillCol);
   ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
   ObjectSetString(0,name,OBJPROP_TEXT,txt);          // set the displayed text string

//--- Update position
   ObjectMove(0, name, 0, x, y);
   ChartRedraw(0);
  }

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   atr_handle = iATR(_Symbol,PERIOD_CURRENT,200); // ATR(200) handle used to size the projection boxes
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ObjectsDeleteAll(0,PFX); // remove every object created by this indicator (matched by prefix)
   ChartRedraw(0);

  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int32_t rates_total,
                const int32_t prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int32_t &spread[])
  {
//--- Ensure enough historical candles are available for swing detection
   if(rates_total <= InpLen)
      return(0);

//--- Get the opening time of the current bar
   datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);

//--- Execute the analysis only once per newly formed bar
   if(currentBarTime != last_bar_time)
     {
      bool isGup = false; // true = potential upward market movement, false = potential downward market movement

      //--- Scan historical candles to determine the current market direction
      for(int i = rates_total - 2; i > InpLen; i--)
        {
         double H = high[i];
         double L = low[i];

         //--- Check whether the latest confirmed swing is a swing high
         if(i + 1 <= rates_total - 2 && IsSwingHigh(high, i, InpLen) && H > high[i + 1])
           {
            isGup = false; // Latest swing is a high -> potential downward market movement
            break;
           }
         //--- Otherwise, check whether the latest confirmed swing is a swing low
         else
            if(i + 1 <= rates_total - 2 && IsSwingLow(low, i, InpLen) && L < low[i + 1])
              {
               isGup = true; // Latest swing is a low -> potential upward market movement
               break;
              }
        }

      //--- Potential upward market structure
      if(isGup == true)
        {
         for(int i = rates_total - 2; i > InpLen; i--)
           {
            //--- Search for the first swing low as the starting point
            if(i + 1 <= rates_total - 2 && IsSwingLow(low,i,InpLen) && low[i] < low[i + 1])
              {
               //--- i = Swing 1 (Low)
               for(int j = i; j >= InpLen; j--)
                 {
                  //--- Search for the next swing high
                  if(IsSwingHigh(high,j,InpLen) && high[j] > high[j + 1])
                    {
                     s1_s2_interval = high[j] - low[i]; // Leg size: Swing 1 (Low) to Swing 2 (High)

                     //--- j = Swing 2 (High)
                     for(int k = j; k >= InpLen; k--)
                       {
                        //--- Search for the next swing low
                        if(IsSwingLow(low,k,InpLen) && low[k] < low[k + 1])
                          {
                           s2_s3_interval = high[j] - low[k]; // Leg size: Swing 2 (High) to Swing 3 (Low)

                           //--- k = Swing 3 (Low)
                           for(int l = k; l >= InpLen; l--)
                             {
                              //--- Search for the next swing high
                              if(IsSwingHigh(high,l,InpLen) && high[l] > high[l + 1])
                                {
                                 s3_s4_interval = high[l] - low[k]; // Leg size: Swing 3 (Low) to Swing 4 (High)

                                 //--- l = Swing 4 (High)
                                 for(int m = l; m >= InpLen; m--)
                                   {
                                    //--- Search for the next swing low
                                    if(IsSwingLow(low,m,InpLen) && low[m] < low[m + 1])
                                      {
                                       s4_s5_interval = high[l] - low[m]; // Leg size: Swing 4 (High) to Swing 5 (Low)

                                       //--- m = Swing 5 (Low)
                                       for(int n = m; n >= InpLen; n--)
                                         {
                                          //--- Search for the final swing high
                                          if(IsSwingHigh(high,n,InpLen) && high[n] > high[n + 1])
                                            {
                                             //--- n = Swing 6 (High) - six swing points identified, draw structure
                                             s5_s6_interval = high[n] - low[m]; // leg size: swing5 (low) to swing6 (high)

                                             avg_price = (s1_s2_interval + s2_s3_interval + s3_s4_interval + s4_s5_interval + s5_s6_interval) / 5; // average of the 5 measured legs

                                             //--- Draw lines connecting the six identified swing points to visualize the market structure
                                             DrawTrend(PFX + "S12",time[i], low[i], time[j], high[j], clrGreen, 2, STYLE_SOLID);         // Connect Swing 1 (Low) to Swing 2 (High)
                                             DrawTrend(PFX + "S23",time[j], high[j], time[k], low[k], clrLightSeaGreen, 2, STYLE_SOLID); // Connect Swing 2 (High) to Swing 3 (Low)
                                             DrawTrend(PFX + "S34",time[k], low[k], time[l], high[l], clrGoldenrod, 2, STYLE_SOLID);     // Connect Swing 3 (Low) to Swing 4 (High)
                                             DrawTrend(PFX + "S45",time[l], high[l], time[m], low[m], clrDarkOrange, 2, STYLE_SOLID);    // Connect Swing 4 (High) to Swing 5 (Low)
                                             DrawTrend(PFX + "S56",time[m], low[m], time[n], high[n], clrDeepSkyBlue, 2, STYLE_SOLID);   // Connect Swing 5 (Low) to Swing 6 (High)

                                             datetime fs_time = iTime(_Symbol,PERIOD_CURRENT,0) + (PeriodSeconds(_Period) * 5);         // projection point 5 bars into the future
                                             DrawTrend(PFX + "FS",time[i],low[i],fs_time, low[i] + avg_price,clrBlue,2,STYLE_DASH);     // projected swing line from the average leg size

                                             CopyBuffer(atr_handle, 0,0,1,atr_buffer); // pull latest ATR value into atr_buffer[0]
                                             DrawBox(PFX + "S",time[i],low[i],fs_time,low[i] - atr_buffer[0],clrLightSeaGreen); // support zone sized by ATR
                                             DrawBox(PFX + "R",time[j],high[j],fs_time,high[j] + atr_buffer[0],clrOrange); // resistance zone sized by ATR
                                             CreateTxt(PFX + "TXT",fs_time,low[i] + avg_price,DoubleToString(low[i] + avg_price,_Digits),clrBlue); // label the projected price level

                                             break;
                                            }
                                         }
                                       break;
                                      }
                                   }
                                 break;
                                }
                             }
                           break;
                          }
                       }
                     break;
                    }
                 }

               break;
              }
           }
        }

      //--- Potential downward market structure
      if(isGup == false)
        {
         for(int i = rates_total - 2; i > InpLen; i--)
           {
            //--- Search for the first swing high as the starting point
            if(i + 1 <= rates_total - 2 && IsSwingHigh(high,i,InpLen) && high[i] > high[i + 1])
              {
               //--- i = Swing 1 (High)
               for(int j = i; j >= InpLen; j--)
                 {

                  //--- Search for the next swing low
                  if(IsSwingLow(low,j,InpLen) && low[j] < low[j + 1])
                    {
                     s1_s2_interval = high[i] - low[j]; // Leg size: Swing 1 (High) to Swing 2 (Low)

                     //--- j = Swing 2 (Low)
                     for(int k = j; k >= InpLen; k--)
                       {

                        //--- Search for the next swing high
                        if(IsSwingHigh(high,k,InpLen) && high[k] > high[k + 1])
                          {
                           s2_s3_interval = high[k] - low[j]; // Leg size: Swing 2 (Low) to Swing 3 (High)

                           //--- k = Swing 3 (High)
                           for(int l = k; l >= InpLen; l--)
                             {

                              //--- Search for the next swing low
                              if(IsSwingLow(low,l,InpLen) && low[l] < low[l + 1])
                                {
                                 s3_s4_interval = high[k] - low[l]; // Leg size: Swing 3 (High) to Swing 4 (Low)

                                 //--- l = Swing 4 (Low)
                                 for(int m = l; m >= InpLen; m--)
                                   {

                                    //--- Search for the next swing high
                                    if(IsSwingHigh(high,m,InpLen) && high[m] > high[m + 1])
                                      {
                                       s4_s5_interval = high[m] - low[l]; // Leg size: Swing 4 (Low) to Swing 5 (High)

                                       //--- m = Swing 5 (High)
                                       for(int n = m; n >= InpLen; n--)
                                         {

                                          //--- Search for the final swing low
                                          if(IsSwingLow(low,n,InpLen) && low[n] < low[n + 1])
                                            {
                                             //--- n = Swing 6 (Low) - six swing points identified, draw structure

                                             DrawTrend(PFX + "S12",time[i],high[i],time[j], low[j],clrGreen,2,STYLE_SOLID);
                                             DrawTrend(PFX + "S23",time[j],low[j],time[k], high[k],clrLightSeaGreen,2,STYLE_SOLID);
                                             DrawTrend(PFX + "S34",time[k],high[k],time[l], low[l],clrGoldenrod,2,STYLE_SOLID);
                                             DrawTrend(PFX + "S45",time[l],low[l],time[m], high[m],clrDarkOrange,2,STYLE_SOLID);
                                             DrawTrend(PFX + "S56",time[m],high[m],time[n], low[n],clrDeepSkyBlue,2,STYLE_SOLID);

                                             s5_s6_interval = high[m] - low[n]; // leg size: swing5 (high) to swing6 (low)

                                             avg_price = (s1_s2_interval + s2_s3_interval + s3_s4_interval + s4_s5_interval + s5_s6_interval) / 5; // average of the 5 measured legs

                                             datetime fs_time = iTime(_Symbol,PERIOD_CURRENT,0) + (PeriodSeconds(_Period) *5); // projection point 5 bars into the future
                                             DrawTrend(PFX + "FS",time[i],high[i],fs_time, high[i] - avg_price,clrBlue,2,STYLE_DASH); // projected swing line from the average leg size

                                             DrawBox(PFX + "R",time[i],high[i],fs_time,high[i] + atr_buffer[0],clrOrange); // resistance zone sized by ATR
                                             DrawBox(PFX + "S",time[j],low[j],fs_time,low[j] - atr_buffer[0],clrLightSeaGreen); // support zone sized by ATR
                                             CreateTxt(PFX + "TXT",fs_time,high[i] - avg_price,DoubleToString(high[i] - avg_price,_Digits),clrBlue); // label the projected price level

                                             break;
                                            }
                                         }
                                       break;
                                      }
                                   }
                                 break;
                                }
                             }
                           break;
                          }
                       }
                     break;
                    }
                 }
               break;
              }
           }
        }

      //--- Store the current bar time to avoid recalculating on every tick
      last_bar_time = currentBarTime;
     }

   Comment("Swing 1-2: ", NormalizeDouble(s1_s2_interval,_Digits),
           "\nSwing 2-3: ", NormalizeDouble(s2_s3_interval,_Digits),
           "\nSwing 3-4: ", NormalizeDouble(s3_s4_interval,_Digits),
           "\nSwing 4-5: ", NormalizeDouble(s4_s5_interval,_Digits),
           "\nSwing 5-6: ", NormalizeDouble(s5_s6_interval,_Digits),
           "\n\nSwing Average: ", NormalizeDouble(avg_price,_Digits));



//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Check whether a candle is a swing high                           |
//+------------------------------------------------------------------+
bool IsSwingHigh(const double &high[], int index, int lookback)
  {
//--- Compare the current high with the previous highs
   for(int i = 1; i <= lookback; i++)
     {
      if(high[index] < high[index - i])
         return false;
     }

   return true;
  }

//+------------------------------------------------------------------+
//| Check whether a candle is a swing low                            |
//+------------------------------------------------------------------+
bool IsSwingLow(const double &low[], int index, int lookback)
  {
//--- Compare the current low with the previous lows
   for(int i = 1; i <= lookback; i++)
     {
      if(low[index] > low[index - i])
         return false;
     }

   return true;
  }
//+------------------------------------------------------------------+
