//+------------------------------------------------------------------------------------------------------------------+
#property description                                                                    "[!!!-MT5 X-XARDg-STOCH MTF]"
#define   Version                                                                                  "[XARDg-STOCH MTF]"
//+------------------------------------------------------------------------------------------------------------------+
#property link        "https://forex-station.com/viewtopic.php?p=1295409935#p1295409935"
#property description "THIS IS A FREE INDICATOR"
#property description "                                                      "
#property description "Welcome to the World of Forex"
#property description "Let light shine out of darkness and illuminate your world"
#property description "and with this freedom leave behind your cave of denial"
#property indicator_separate_window
#property indicator_buffers  9
#property indicator_plots    9
#property indicator_minimum  0
#property indicator_maximum  100
#property indicator_level1   80
#property indicator_level2   20
#property indicator_level3   50

//+------------------------------------------------------------------------------------------------------------------+
input ENUM_TIMEFRAMES       TimeFrame   = PERIOD_CURRENT;
input int                   StochKPer1  = 30;
input int                   StochDPer1  = 5;
input int                   StochSlow1  = 5;
input int                   SPrice1     = 0;
input ENUM_MA_METHOD        SigMode1    = MODE_EMA;
input string                note2       = "-------------------------";
input int                   StochKPer2  = 32;
input int                   StochDPer2  = 5;
input int                   StochSlow2  = 5;
input int                   SPrice2     = 0;
input ENUM_MA_METHOD        SigMode2    = MODE_EMA;
input string                note3       = "-------------------------";
input int                   StochKPer3  = 34;
input int                   StochDPer3  = 5;
input int                   StochSlow3  = 5;
input int                   SPrice3     = 0;
input ENUM_MA_METHOD        SigMode3    = MODE_EMA;  
input string                note4       = "-------------------------";
input int                   StochKPer4  = 36;
input int                   StochDPer4  = 5;
input int                   StochSlow4  = 5;
input int                   SPrice4     = 0;
input ENUM_MA_METHOD        SigMode4    = MODE_EMA;
input string                note5       = "-------------------------";
input int                   StochKPer5  = 38;
input int                   StochDPer5  = 5;
input int                   StochSlow5  = 5;
input int                   SPrice5     = 0;
input ENUM_MA_METHOD        SigMode5    = MODE_EMA;
input string                note6       = "-------------------------";
input int                   StochKPer6  = 28;
input int                   StochDPer6  = 5;
input int                   StochSlow6  = 5;
input int                   SPrice6     = 0;
input ENUM_MA_METHOD        SigMode6    = MODE_EMA;
input string                note7       = "-------------------------";
input int                   StochKPer7  = 26;
input int                   StochDPer7  = 5;
input int                   StochSlow7  = 5;
input int                   SPrice7     = 0;
input ENUM_MA_METHOD        SigMode7    = MODE_EMA;
input string                note8       = "-------------------------";
input int                   StochKPer8  = 24;
input int                   StochDPer8  = 5;
input int                   StochSlow8  = 5;
input int                   SPrice8     = 0;
input ENUM_MA_METHOD        SigMode8    = MODE_EMA;
input string                note9       = "-------------------------";
input int                   StochKPer9  = 22;
input int                   StochDPer9  = 5;
input int                   StochSlow9  = 5;
input int                   SPrice9     = 0;
input ENUM_MA_METHOD        SigMode9    = MODE_EMA;
input bool                  Interpolate = true;
input string                TimeFrame1  = "input 1 for M1; 5 for M5; 30 for M30";
input string                TimeFrame2  = "60 for H1; 240 for H4; 1440 for D1";
input string                note10      = "-------------------------";
input color                 Color_S1    = clrOrange;
input color                 Color_S2345 = clrRoyalBlue;
input color                 Color_S6789 = clrDeepPink;

//+------------------------------------------------------------------------------------------------------------------+
double S1[], S2[], S3[], S4[], S5[], S6[], S7[], S8[], S9[];
string indicatorFileName, ID = " ";
ENUM_TIMEFRAMES timeFrame;

// Stochastic handles
int hStoch1, hStoch2, hStoch3, hStoch4, hStoch5, hStoch6, hStoch7, hStoch8, hStoch9;

//+------------------------------------------------------------------
int OnInit()
{
   IndicatorSetString(INDICATOR_SHORTNAME, ID);
   
   // Set index buffers
   SetIndexBuffer(0, S1, INDICATOR_DATA);
   SetIndexBuffer(1, S2, INDICATOR_DATA);
   SetIndexBuffer(2, S3, INDICATOR_DATA);
   SetIndexBuffer(3, S4, INDICATOR_DATA);
   SetIndexBuffer(4, S5, INDICATOR_DATA);
   SetIndexBuffer(5, S6, INDICATOR_DATA);
   SetIndexBuffer(6, S7, INDICATOR_DATA);
   SetIndexBuffer(7, S8, INDICATOR_DATA);
   SetIndexBuffer(8, S9, INDICATOR_DATA);
   
   // Set plot properties for S1 (orange, width 2)
   PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_LINE);
   PlotIndexSetInteger(0, PLOT_LINE_STYLE, STYLE_SOLID);
   PlotIndexSetInteger(0, PLOT_LINE_WIDTH, 2);
   PlotIndexSetInteger(0, PLOT_LINE_COLOR, Color_S1);
   
   // Set plot properties for S2-S5 (royal blue, width 1)
   for(int i = 1; i <= 4; i++)
   {
      PlotIndexSetInteger(i, PLOT_DRAW_TYPE, DRAW_LINE);
      PlotIndexSetInteger(i, PLOT_LINE_STYLE, STYLE_SOLID);
      PlotIndexSetInteger(i, PLOT_LINE_WIDTH, 1);
      PlotIndexSetInteger(i, PLOT_LINE_COLOR, Color_S2345);
   }
   
   // Set plot properties for S6-S9 (deep pink, width 1)
   for(int i = 5; i <= 8; i++)
   {
      PlotIndexSetInteger(i, PLOT_DRAW_TYPE, DRAW_LINE);
      PlotIndexSetInteger(i, PLOT_LINE_STYLE, STYLE_SOLID);
      PlotIndexSetInteger(i, PLOT_LINE_WIDTH, 1);
      PlotIndexSetInteger(i, PLOT_LINE_COLOR, Color_S6789);
   }
   
   timeFrame = (TimeFrame == PERIOD_CURRENT) ? Period() : TimeFrame;
   indicatorFileName = MQLInfoString(MQL_PROGRAM_NAME);
   
   // Get timeframe label
   string tfLabel = TimeFrameToString(timeFrame);
   
   // Update plot labels with timeframe and K period
   PlotIndexSetString(0, PLOT_LABEL, "Stoch " + tfLabel + " (" + IntegerToString(StochKPer1) + ")");
   PlotIndexSetString(1, PLOT_LABEL, "Stoch " + tfLabel + " (" + IntegerToString(StochKPer2) + ")");
   PlotIndexSetString(2, PLOT_LABEL, "Stoch " + tfLabel + " (" + IntegerToString(StochKPer3) + ")");
   PlotIndexSetString(3, PLOT_LABEL, "Stoch " + tfLabel + " (" + IntegerToString(StochKPer4) + ")");
   PlotIndexSetString(4, PLOT_LABEL, "Stoch " + tfLabel + " (" + IntegerToString(StochKPer5) + ")");
   PlotIndexSetString(5, PLOT_LABEL, "Stoch " + tfLabel + " (" + IntegerToString(StochKPer6) + ")");
   PlotIndexSetString(6, PLOT_LABEL, "Stoch " + tfLabel + " (" + IntegerToString(StochKPer7) + ")");
   PlotIndexSetString(7, PLOT_LABEL, "Stoch " + tfLabel + " (" + IntegerToString(StochKPer8) + ")");
   PlotIndexSetString(8, PLOT_LABEL, "Stoch " + tfLabel + " (" + IntegerToString(StochKPer9) + ")");
   
   // Get price field enum
   ENUM_STO_PRICE priceField1 = (SPrice1 == 0) ? STO_LOWHIGH : STO_CLOSECLOSE;
   ENUM_STO_PRICE priceField2 = (SPrice2 == 0) ? STO_LOWHIGH : STO_CLOSECLOSE;
   ENUM_STO_PRICE priceField3 = (SPrice3 == 0) ? STO_LOWHIGH : STO_CLOSECLOSE;
   ENUM_STO_PRICE priceField4 = (SPrice4 == 0) ? STO_LOWHIGH : STO_CLOSECLOSE;
   ENUM_STO_PRICE priceField5 = (SPrice5 == 0) ? STO_LOWHIGH : STO_CLOSECLOSE;
   ENUM_STO_PRICE priceField6 = (SPrice6 == 0) ? STO_LOWHIGH : STO_CLOSECLOSE;
   ENUM_STO_PRICE priceField7 = (SPrice7 == 0) ? STO_LOWHIGH : STO_CLOSECLOSE;
   ENUM_STO_PRICE priceField8 = (SPrice8 == 0) ? STO_LOWHIGH : STO_CLOSECLOSE;
   ENUM_STO_PRICE priceField9 = (SPrice9 == 0) ? STO_LOWHIGH : STO_CLOSECLOSE;
   
   // Create stochastic handles
   hStoch1 = iStochastic(_Symbol, timeFrame, StochKPer1, StochDPer1, StochSlow1, SigMode1, priceField1);
   hStoch2 = iStochastic(_Symbol, timeFrame, StochKPer2, StochDPer2, StochSlow2, SigMode2, priceField2);
   hStoch3 = iStochastic(_Symbol, timeFrame, StochKPer3, StochDPer3, StochSlow3, SigMode3, priceField3);
   hStoch4 = iStochastic(_Symbol, timeFrame, StochKPer4, StochDPer4, StochSlow4, SigMode4, priceField4);
   hStoch5 = iStochastic(_Symbol, timeFrame, StochKPer5, StochDPer5, StochSlow5, SigMode5, priceField5);
   hStoch6 = iStochastic(_Symbol, timeFrame, StochKPer6, StochDPer6, StochSlow6, SigMode6, priceField6);
   hStoch7 = iStochastic(_Symbol, timeFrame, StochKPer7, StochDPer7, StochSlow7, SigMode7, priceField7);
   hStoch8 = iStochastic(_Symbol, timeFrame, StochKPer8, StochDPer8, StochSlow8, SigMode8, priceField8);
   hStoch9 = iStochastic(_Symbol, timeFrame, StochKPer9, StochDPer9, StochSlow9, SigMode9, priceField9);
   
   if(hStoch1 == INVALID_HANDLE || hStoch2 == INVALID_HANDLE || hStoch3 == INVALID_HANDLE ||
      hStoch4 == INVALID_HANDLE || hStoch5 == INVALID_HANDLE || hStoch6 == INVALID_HANDLE ||
      hStoch7 == INVALID_HANDLE || hStoch8 == INVALID_HANDLE || hStoch9 == INVALID_HANDLE)
   {
      Print("Error creating stochastic handles");
      return(INIT_FAILED);
   }
   
   Print("AKStoch-Rainbow initialized. TimeFrame input: ", EnumToString(TimeFrame), " -> Parsed: ", EnumToString(timeFrame), " Chart: ", EnumToString(Period()));
   
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   if(hStoch1 != INVALID_HANDLE) IndicatorRelease(hStoch1);
   if(hStoch2 != INVALID_HANDLE) IndicatorRelease(hStoch2);
   if(hStoch3 != INVALID_HANDLE) IndicatorRelease(hStoch3);
   if(hStoch4 != INVALID_HANDLE) IndicatorRelease(hStoch4);
   if(hStoch5 != INVALID_HANDLE) IndicatorRelease(hStoch5);
   if(hStoch6 != INVALID_HANDLE) IndicatorRelease(hStoch6);
   if(hStoch7 != INVALID_HANDLE) IndicatorRelease(hStoch7);
   if(hStoch8 != INVALID_HANDLE) IndicatorRelease(hStoch8);
   if(hStoch9 != INVALID_HANDLE) IndicatorRelease(hStoch9);
}

//+------------------------------------------------------------------
int OnCalculate(const int rates_total,
                const int 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 int &spread[])
{
   if(rates_total < 1) return(0);
   
   // Wait for all indicator handles to be ready
   if(BarsCalculated(hStoch1) <= 0 || BarsCalculated(hStoch2) <= 0 || BarsCalculated(hStoch3) <= 0 ||
      BarsCalculated(hStoch4) <= 0 || BarsCalculated(hStoch5) <= 0 || BarsCalculated(hStoch6) <= 0 ||
      BarsCalculated(hStoch7) <= 0 || BarsCalculated(hStoch8) <= 0 || BarsCalculated(hStoch9) <= 0)
      return(0);

   bool isMTF = (timeFrame != Period());

   // Temporary arrays for stochastic values
   double tempS1[], tempS2[], tempS3[], tempS4[], tempS5[], tempS6[], tempS7[], tempS8[], tempS9[];
   
   int htfBarsNeeded = MathMin(BarsCalculated(hStoch1), rates_total + 100);
   
   int copied1 = CopyBuffer(hStoch1, 0, 0, htfBarsNeeded, tempS1);
   int copied2 = CopyBuffer(hStoch2, 0, 0, htfBarsNeeded, tempS2);
   int copied3 = CopyBuffer(hStoch3, 0, 0, htfBarsNeeded, tempS3);
   int copied4 = CopyBuffer(hStoch4, 0, 0, htfBarsNeeded, tempS4);
   int copied5 = CopyBuffer(hStoch5, 0, 0, htfBarsNeeded, tempS5);
   int copied6 = CopyBuffer(hStoch6, 0, 0, htfBarsNeeded, tempS6);
   int copied7 = CopyBuffer(hStoch7, 0, 0, htfBarsNeeded, tempS7);
   int copied8 = CopyBuffer(hStoch8, 0, 0, htfBarsNeeded, tempS8);
   int copied9 = CopyBuffer(hStoch9, 0, 0, htfBarsNeeded, tempS9);
   
   if(copied1 <= 0 || copied2 <= 0 || copied3 <= 0 || copied4 <= 0 || copied5 <= 0 ||
      copied6 <= 0 || copied7 <= 0 || copied8 <= 0 || copied9 <= 0)
      return(0);

   // Debug output on first calculation
   static bool debugDone = false;
   if(!debugDone && prev_calculated == 0)
   {
      Print("=== AKStoch Rainbow Debug ===");
      Print("TimeFrame: ", EnumToString(timeFrame), " Chart: ", EnumToString(Period()));
      Print("copied1: ", copied1, " rates_total: ", rates_total);
      debugDone = true;
   }

   //--- Copy HTF bar times (needed both for limit calculation and the main loop)
   datetime htfTimes[];
   if(isMTF)
   {
      int htfCopied = CopyTime(_Symbol, timeFrame, 0, copied1, htfTimes);
      if(htfCopied <= 0)
      {
         Print("CopyTime failed for MTF");
         return(0);
      }
   }

   //--- Calculate the recalculation start index (limit)
   //
   //    THE FIX: The previous code used PeriodSeconds() to estimate how many
   //    LTF bars to roll back. That was a step in the right direction but only
   //    covered ONE HTF bar (the current open one). It left two stale groups:
   //
   //    1. Bars inside the CURRENT (still-open) HTF bar: each bar holds the
   //       HTF value from the tick it was first drawn. With the old heuristic,
   //       all bars in the current HTF bar were refreshed — good. But the
   //       PeriodSeconds estimate can be off during session gaps / weekends,
   //       occasionally leaving some bars at the start of the HTF bar stale.
   //
   //    2. Bars inside the PREVIOUS (completed) HTF bar: they interpolate
   //       *toward* the current HTF bar. As the current HTF bar's value
   //       changes each tick, those interpolated values go stale, but the old
   //       heuristic never reached back far enough to refresh them.
   //
   //    Fix: use the actual htfTimes[] array to find the two most recent HTF
   //    bar open times, then recalculate from the start of the previous HTF
   //    bar. This covers both stale groups precisely, with no guesswork.
   //
   int limit;
   if(prev_calculated <= 0)
   {
      limit = 0;
   }
   else if(isMTF && Interpolate && ArraySize(htfTimes) >= 2)
   {
      //--- Find the two most recent HTF bar open times.
      //    We scan the whole array rather than assuming index direction,
      //    keeping the logic robust regardless of AS_SERIES state.
      datetime htfNewest = 0, htfPrevious = 0;
      for(int k = 0; k < ArraySize(htfTimes); k++)
      {
         if(htfTimes[k] > htfNewest)
         {
            htfPrevious = htfNewest;
            htfNewest   = htfTimes[k];
         }
         else if(htfTimes[k] > htfPrevious && htfTimes[k] != htfNewest)
         {
            htfPrevious = htfTimes[k];
         }
      }

      //--- Walk back through LTF bars to find the first one inside the
      //    previous HTF bar (time[i] >= htfPrevious). Recalculate from there.
      limit = prev_calculated - 1; // safe fallback
      for(int i = rates_total - 1; i >= 0; i--)
      {
         if(time[i] < htfPrevious)
         {
            limit = i + 1;
            break;
         }
         if(i == 0) limit = 0;
      }
   }
   else if(isMTF && ArraySize(htfTimes) >= 1)
   {
      //--- Interpolate=false: only need to cover the current HTF bar.
      //    Find the most recent HTF bar open time and recalculate from there.
      datetime htfNewest = 0;
      for(int k = 0; k < ArraySize(htfTimes); k++)
         if(htfTimes[k] > htfNewest) htfNewest = htfTimes[k];

      limit = prev_calculated - 1;
      for(int i = rates_total - 1; i >= 0; i--)
      {
         if(time[i] < htfNewest)
         {
            limit = i + 1;
            break;
         }
         if(i == 0) limit = 0;
      }
   }
   else
   {
      limit = prev_calculated - 1;
   }

   //--- Main calculation loop (time[0]=oldest, time[rates_total-1]=newest)
   for(int i = limit; i < rates_total; i++)
   {
      datetime barTime = time[i];
      int y;
      
      if(!isMTF)
      {
         // Direct index mapping for same timeframe
         y = i;
         if(y >= 0 && y < copied1)
         {
            S1[i] = tempS1[y];
            S2[i] = tempS2[y];
            S3[i] = tempS3[y];
            S4[i] = tempS4[y];
            S5[i] = tempS5[y];
            S6[i] = tempS6[y];
            S7[i] = tempS7[y];
            S8[i] = tempS8[y];
            S9[i] = tempS9[y];
         }
         else
         {
            S1[i] = EMPTY_VALUE; S2[i] = EMPTY_VALUE; S3[i] = EMPTY_VALUE;
            S4[i] = EMPTY_VALUE; S5[i] = EMPTY_VALUE; S6[i] = EMPTY_VALUE;
            S7[i] = EMPTY_VALUE; S8[i] = EMPTY_VALUE; S9[i] = EMPTY_VALUE;
         }
      }
      else
      {
         // MTF mode: find the HTF bar that contains this LTF bar
         y = -1;
         for(int k = ArraySize(htfTimes) - 1; k >= 0; k--)
         {
            if(htfTimes[k] <= barTime)
            {
               y = k;
               break;
            }
         }
         
         if(y >= 0 && y < copied1)
         {
            if(Interpolate && y < copied1 - 1 && y < ArraySize(htfTimes) - 1)
            {
               datetime currentHTFTime = htfTimes[y];
               datetime nextHTFTime    = htfTimes[y + 1];
               
               double timeFraction = 0.0;
               if(nextHTFTime > currentHTFTime)
               {
                  timeFraction = (double)(barTime - currentHTFTime) /
                                 (double)(nextHTFTime - currentHTFTime);
                  timeFraction = MathMin(MathMax(timeFraction, 0.0), 1.0);
               }
               
               S1[i] = tempS1[y] + timeFraction * (tempS1[y + 1] - tempS1[y]);
               S2[i] = tempS2[y] + timeFraction * (tempS2[y + 1] - tempS2[y]);
               S3[i] = tempS3[y] + timeFraction * (tempS3[y + 1] - tempS3[y]);
               S4[i] = tempS4[y] + timeFraction * (tempS4[y + 1] - tempS4[y]);
               S5[i] = tempS5[y] + timeFraction * (tempS5[y + 1] - tempS5[y]);
               S6[i] = tempS6[y] + timeFraction * (tempS6[y + 1] - tempS6[y]);
               S7[i] = tempS7[y] + timeFraction * (tempS7[y + 1] - tempS7[y]);
               S8[i] = tempS8[y] + timeFraction * (tempS8[y + 1] - tempS8[y]);
               S9[i] = tempS9[y] + timeFraction * (tempS9[y + 1] - tempS9[y]);
            }
            else
            {
               S1[i] = tempS1[y]; S2[i] = tempS2[y]; S3[i] = tempS3[y];
               S4[i] = tempS4[y]; S5[i] = tempS5[y]; S6[i] = tempS6[y];
               S7[i] = tempS7[y]; S8[i] = tempS8[y]; S9[i] = tempS9[y];
            }
         }
         else
         {
            S1[i] = EMPTY_VALUE; S2[i] = EMPTY_VALUE; S3[i] = EMPTY_VALUE;
            S4[i] = EMPTY_VALUE; S5[i] = EMPTY_VALUE; S6[i] = EMPTY_VALUE;
            S7[i] = EMPTY_VALUE; S8[i] = EMPTY_VALUE; S9[i] = EMPTY_VALUE;
         }
      }
   }
   
   return(rates_total);
}

//+------------------------------------------------------------------------------------------------------------------+
// Get HTF bar index in non-series array (index 0 = oldest)
int GetHTFBarIndex(datetime barTime, ENUM_TIMEFRAMES tf, int totalBars)
{
   datetime htfTimes[];
   int copied = CopyTime(_Symbol, tf, barTime, 1, htfTimes);
   if(copied <= 0) return(-1);
   
   datetime htfBarTime = htfTimes[0];
   
   datetime allHTFTimes[];
   int allCopied = CopyTime(_Symbol, tf, 0, totalBars, allHTFTimes);
   if(allCopied <= 0) return(-1);
   
   for(int i = allCopied - 1; i >= 0; i--)
   {
      if(allHTFTimes[i] <= htfBarTime)
         return(i);
   }
   
   return(0);
}

//+---Timeframe Function---------------------------------------------------------------------------------------------+
string sTfTable[] = {"M1", "M5", "M15", "M30", "H1", "H4", "D1", "W1", "MN"};
int iTfTable[] = {1, 5, 15, 30, 60, 240, 1440, 10080, 43200};
ENUM_TIMEFRAMES eTfTable[] = {PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M30, PERIOD_H1, PERIOD_H4, PERIOD_D1, PERIOD_W1, PERIOD_MN1};

//+------------------------------------------------------------------------------------------------------------------+
ENUM_TIMEFRAMES StringToTimeFrame(string tfs)
{
   tfs = StringUpperCase(tfs);
   for(int i = ArraySize(iTfTable) - 1; i >= 0; i--)
   {
      if(tfs == sTfTable[i] || tfs == IntegerToString(iTfTable[i]))
      {
         if(eTfTable[i] >= Period())
            return(eTfTable[i]);
         else
            return(Period());
      }
   }
   return(Period());
}

//+------------------------------------------------------------------------------------------------------------------+
string TimeFrameToString(ENUM_TIMEFRAMES tf)
{
   for(int i = ArraySize(eTfTable) - 1; i >= 0; i--)
      if(tf == eTfTable[i])
         return(sTfTable[i]);
   return("");
}

//+------------------------------------------------------------------------------------------------------------------+
string StringUpperCase(string str)
{
   string s = str;
   int length = StringLen(str);
   for(int i = 0; i < length; i++)
   {
      ushort tchar = StringGetCharacter(s, i);
      if((tchar > 96 && tchar < 123) || (tchar > 223 && tchar < 256))
         StringSetCharacter(s, i, (ushort)(tchar - 32));
      else if(tchar > 65503 && tchar < 65536)
         StringSetCharacter(s, i, (ushort)(tchar + 224));
   }
   return(s);
}

//+------------------------------------------------------------------------------------------------------------------+
