//+-----------------------------------------------------------------+
//|                        Color Stochastic Multitime Frame V2.mq5 	|
//|                          Converted to MQL5                      |
//+-----------------------------------------------------------------+
#property indicator_separate_window
#property indicator_buffers   7
#property indicator_plots     6
#property indicator_color1    Yellow
#property indicator_color2    DimGray
#property indicator_color3    Green
#property indicator_color4    DeepSkyBlue
#property indicator_color5    Red
#property indicator_color6    Red
#property indicator_style1    STYLE_DOT
#property indicator_width3    2
#property indicator_width4    2
#property indicator_width5    2
#property indicator_width6    2
#property indicator_minimum   0
#property indicator_maximum   100
#property indicator_level1    80
#property indicator_level2    20
#property indicator_level3    50

//--- Input parameters
input ENUM_TIMEFRAMES TimeFrame    = PERIOD_CURRENT;
input int    KPeriod               = 30;
input int    Slowing               = 5;
input int    DPeriod               = 5;
input ENUM_MA_METHOD MAMethod      = MODE_EMA;
input ENUM_STO_PRICE PriceField    = STO_LOWHIGH;
input int    overBought            = 80;
input int    overSold              = 20;
input bool   Interpolate           = true;
input bool   showArrows            = false;
input bool   showArrowsOnZoneEnter = false;
input bool   showArrowsOnZoneExit  = false;
input string arrowsIdentifier      = "Color stochastic";
input color  arrowsOBColor         = clrWhite;
input color  arrowsOSColor         = clrRed;

//--- Alert inputs
input bool   alertsOn              = false;
input bool   alertsOnZoneEnter     = false;
input bool   alertsOnZoneExit      = false;
input bool   alertsOnCurrent       = false;
input bool   alertsMessage         = false;
input bool   alertsSound           = false;
input bool   alertsEmail           = false;

//--- Indicator buffers
double KFull[];
double DFull[];
double Uppera[];
double Upperb[];
double Lowera[];
double Lowerb[];
double trend[];

//--- Global variables
ENUM_TIMEFRAMES timeFrame;
string IndicatorFileName;
int hStoch;
int actualDPeriod;
int actualOverBought;
int actualOverSold;

//+-------------------------------------------------------------------
//|                                                                  
//+-------------------------------------------------------------------
int OnInit()
{
   // Set buffers
   SetIndexBuffer(0, DFull, INDICATOR_DATA);
   SetIndexBuffer(1, KFull, INDICATOR_DATA);
   SetIndexBuffer(2, Uppera, INDICATOR_DATA);
   SetIndexBuffer(3, Upperb, INDICATOR_DATA);
   SetIndexBuffer(4, Lowera, INDICATOR_DATA);
   SetIndexBuffer(5, Lowerb, INDICATOR_DATA);
   SetIndexBuffer(6, trend, INDICATOR_CALCULATIONS);
   
   // Set plot styles
   PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_LINE);
   PlotIndexSetInteger(0, PLOT_LINE_STYLE, STYLE_DOT);
   PlotIndexSetInteger(0, PLOT_LINE_COLOR, clrYellow);
   
   PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_LINE);
   PlotIndexSetInteger(1, PLOT_LINE_COLOR, clrDimGray);
   
   PlotIndexSetInteger(2, PLOT_DRAW_TYPE, DRAW_LINE);
   PlotIndexSetInteger(2, PLOT_LINE_WIDTH, 2);
   PlotIndexSetInteger(2, PLOT_LINE_COLOR, clrGreen);
   PlotIndexSetString(2, PLOT_LABEL, NULL);
   
   PlotIndexSetInteger(3, PLOT_DRAW_TYPE, DRAW_LINE);
   PlotIndexSetInteger(3, PLOT_LINE_WIDTH, 2);
   PlotIndexSetInteger(3, PLOT_LINE_COLOR, clrDeepSkyBlue);
   PlotIndexSetString(3, PLOT_LABEL, NULL);
   
   PlotIndexSetInteger(4, PLOT_DRAW_TYPE, DRAW_LINE);
   PlotIndexSetInteger(4, PLOT_LINE_WIDTH, 2);
   PlotIndexSetInteger(4, PLOT_LINE_COLOR, clrRed);
   PlotIndexSetString(4, PLOT_LABEL, NULL);
   
   PlotIndexSetInteger(5, PLOT_DRAW_TYPE, DRAW_LINE);
   PlotIndexSetInteger(5, PLOT_LINE_WIDTH, 2);
   PlotIndexSetInteger(5, PLOT_LINE_COLOR, clrRed);
   PlotIndexSetString(5, PLOT_LABEL, NULL);
   
   // Set empty value for plots
   PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetDouble(4, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetDouble(5, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   
   IndicatorFileName = MQLInfoString(MQL_PROGRAM_NAME);
   timeFrame = (TimeFrame == PERIOD_CURRENT) ? Period() : TimeFrame;
   
   actualDPeriod = MathMax(DPeriod, 1);
   actualOverBought = overBought;
   actualOverSold = overSold;
   if(actualOverBought < actualOverSold) actualOverBought = actualOverSold;
   
   if(actualDPeriod == 1)
   {
      PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_NONE);
      PlotIndexSetString(0, PLOT_LABEL, NULL);
   }
   else
   {
      PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_LINE);
      PlotIndexSetString(0, PLOT_LABEL, "Signal");
   }
   PlotIndexSetString(1, PLOT_LABEL, "Stochastic");
   
   // Create stochastic handle for the target timeframe
   hStoch = iStochastic(_Symbol, timeFrame, KPeriod, actualDPeriod, Slowing, MAMethod, PriceField);
   if(hStoch == INVALID_HANDLE)
   {
      Print("Error creating stochastic handle");
      return(INIT_FAILED);
   }
   
   // Set short name
   string shortName = "Stochastic " + TimeFrameToString(timeFrame) + " (" + 
                      IntegerToString(KPeriod) + "," + IntegerToString(actualDPeriod) + "," + 
                      IntegerToString(Slowing) + "," + MADescription(MAMethod) + "," + PriceDescription(PriceField);
   if(actualOverBought < 100) shortName = shortName + "," + IntegerToString(actualOverBought);
   if(actualOverSold > 0) shortName = shortName + "," + IntegerToString(actualOverSold);
   IndicatorSetString(INDICATOR_SHORTNAME, shortName + ")");
   
   Print("Color Stochastic initialized. TimeFrame: ", EnumToString(TimeFrame), " -> ", EnumToString(timeFrame), " Chart: ", EnumToString(Period()));
   
   return(INIT_SUCCEEDED);
}

//+-------------------------------------------------------------------
//|                                                                  
//+-------------------------------------------------------------------
void OnDeinit(const int reason)
{
   if(showArrows) DeleteArrows();
   if(hStoch != INVALID_HANDLE) IndicatorRelease(hStoch);
}

//+-------------------------------------------------------------------
//|                                                                  
//+-------------------------------------------------------------------
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 indicator to be ready
   int barsCalc = BarsCalculated(hStoch);
   if(barsCalc <= 0)
   {
      Print("Waiting for stochastic... BarsCalculated=", barsCalc);
      return(0);
   }
   
   int limit;
   if(prev_calculated > 0)
      limit = prev_calculated - 1;
   else
      limit = 0;
   
   // Temporary arrays for stochastic values
   double tempK[], tempD[];
   
   // For MTF: we need to get data using series indexing (0=newest)
   // Copy from bar 0 (newest) going back
   int htfBarsNeeded = MathMin(barsCalc, rates_total + 100);
   int copied = CopyBuffer(hStoch, 0, 0, htfBarsNeeded, tempK);   // MAIN_LINE
   int copiedD = CopyBuffer(hStoch, 1, 0, htfBarsNeeded, tempD);  // SIGNAL_LINE
   
   if(copied <= 0 || copiedD <= 0)
   {
      Print("CopyBuffer failed: K=", copied, " D=", copiedD, " needed=", htfBarsNeeded);
      return(0);
   }
   
   // Debug first run
   static bool debugDone = false;
   if(!debugDone && prev_calculated == 0)
   {
      Print("=== Color Stoch Debug ===");
      Print("TimeFrame input: ", EnumToString(TimeFrame), " resolved to: ", EnumToString(timeFrame));
      Print("Chart TF: ", EnumToString(Period()));
      Print("Is MTF mode: ", (timeFrame != PERIOD_CURRENT && timeFrame != Period()) ? "YES" : "NO");
      Print("BarsCalculated: ", barsCalc, " copied K: ", copied, " copied D: ", copiedD);
      Print("rates_total: ", rates_total);
      if(copied > 0)
         Print("tempK[0]=", tempK[0], " tempK[last]=", tempK[copied-1]);
      debugDone = true;
   }
   
   bool isMTF = (timeFrame != PERIOD_CURRENT && timeFrame != Period());
   
   // Get HTF bar times if MTF mode
   datetime htfTimes[];
   if(isMTF)
   {
      int htfCopied = CopyTime(_Symbol, timeFrame, 0, copied, htfTimes);
      if(htfCopied <= 0)
      {
         Print("CopyTime for HTF failed");
         return(0);
      }
   }
   
   // Calculate values (standard MQL5 indexing: 0=oldest, rates_total-1=newest)
   for(int i = limit; i < rates_total; i++)
   {
      datetime barTime = time[i];
      int y;
      
      if(!isMTF)
      {
         // Same timeframe: direct index mapping
         y = i;
         if(y >= 0 && y < copied)
         {
            KFull[i] = tempK[y];
            DFull[i] = tempD[y];
         }
         else
         {
            KFull[i] = EMPTY_VALUE;
            DFull[i] = EMPTY_VALUE;
         }
      }
      else
      {
         // MTF mode with interpolation
         // Find which HTF bar contains barTime
         y = -1;
         for(int k = ArraySize(htfTimes) - 1; k >= 0; k--)
         {
            if(htfTimes[k] <= barTime)
            {
               y = k;
               break;
            }
         }
         
         if(y >= 0 && y < copied)
         {
            if(Interpolate && y < copied - 1 && y < ArraySize(htfTimes) - 1)
            {
               // Interpolate between current and next HTF bar
               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); // Clamp to [0,1]
               }
               
               // Interpolate K and D values
               KFull[i] = tempK[y] + timeFraction * (tempK[y + 1] - tempK[y]);
               DFull[i] = tempD[y] + timeFraction * (tempD[y + 1] - tempD[y]);
            }
            else
            {
               // No interpolation or at last bar
               KFull[i] = tempK[y];
               DFull[i] = tempD[y];
            }
         }
         else
         {
            KFull[i] = EMPTY_VALUE;
            DFull[i] = EMPTY_VALUE;
         }
      }
      
      // Trend logic
      if(i > 0)
         trend[i] = trend[i - 1];
      else
         trend[i] = 0;
      
      if(KFull[i] != EMPTY_VALUE && DFull[i] != EMPTY_VALUE)
      {
         if(KFull[i] > actualOverSold && KFull[i] > DFull[i]) trend[i] = 1;
         if(KFull[i] < actualOverBought && KFull[i] < DFull[i]) trend[i] = -1;
         if(KFull[i] < actualOverSold && KFull[i] > DFull[i]) trend[i] = 0;
         if(KFull[i] > actualOverBought && KFull[i] < DFull[i]) trend[i] = 0;
      }
   }
   
   // Plot colored lines (standard indexing)
   for(int i = limit; i < rates_total; i++)
   {
      Uppera[i] = EMPTY_VALUE;
      Upperb[i] = EMPTY_VALUE;
      Lowera[i] = EMPTY_VALUE;
      Lowerb[i] = EMPTY_VALUE;
      
      if(trend[i] == 1) PlotPointStd(i, Uppera, Upperb, KFull);
      if(trend[i] == -1) PlotPointStd(i, Lowera, Lowerb, KFull);
   }
   
   return(rates_total);
}

//+------------------------------------------------------------------------------------------------------------------+
// Get HTF bar index in non-series array (index 0 = oldest)
int GetHTFBarIndex(datetime barTime, ENUM_TIMEFRAMES tf, int totalBars)
{
   // Get the open time of the HTF bar that contains barTime
   datetime htfTimes[];
   int htfCopied = CopyTime(_Symbol, tf, barTime, 1, htfTimes);
   if(htfCopied <= 0) return(-1);
   
   datetime htfBarTime = htfTimes[0];
   
   // Get all HTF bar times to find the matching index
   datetime allHTFTimes[];
   int allCopied = CopyTime(_Symbol, tf, 0, totalBars, allHTFTimes);
   if(allCopied <= 0) return(-1);
   
   // Search for matching time (allHTFTimes[0] = oldest)
   for(int i = allCopied - 1; i >= 0; i--)
   {
      if(allHTFTimes[i] <= htfBarTime)
         return(i);
   }
   
   return(0);
}

//+-------------------------------------------------------------------
//| Plot Point for standard indexing (0=oldest)
//+-------------------------------------------------------------------
void PlotPointStd(int i, double &first[], double &second[], double &from[])
{
   if(i < 1) return;
   
   if(first[i - 1] == EMPTY_VALUE)
   {
      if(i < 2 || first[i - 2] == EMPTY_VALUE)
      {
         first[i] = from[i];
         first[i - 1] = from[i - 1];
         second[i] = EMPTY_VALUE;
      }
      else
      {
         second[i] = from[i];
         second[i - 1] = from[i - 1];
         first[i] = EMPTY_VALUE;
      }
   }
   else
   {
      first[i] = from[i];
      second[i] = EMPTY_VALUE;
   }
}

//+-------------------------------------------------------------------
//|  Arrow Functions                                                 
//+-------------------------------------------------------------------
void DrawArrow(int i, datetime barTime, double highPrice, double lowPrice, color theColor, int theCode, bool up)
{
   string name = arrowsIdentifier + ":" + IntegerToString((long)barTime);
   
   // Get ATR for gap calculation
   int hATR = iATR(_Symbol, PERIOD_CURRENT, 20);
   double atr[];
   CopyBuffer(hATR, 0, i, 1, atr);
   IndicatorRelease(hATR);
   
   double gap = (ArraySize(atr) > 0) ? 6.0 * atr[0] / 4.0 : 0.001;
   
   ObjectCreate(0, name, OBJ_ARROW, 0, barTime, 0);
   ObjectSetInteger(0, name, OBJPROP_ARROWCODE, theCode);
   ObjectSetInteger(0, name, OBJPROP_COLOR, theColor);
   
   if(up)
      ObjectSetDouble(0, name, OBJPROP_PRICE, highPrice + gap);
   else
      ObjectSetDouble(0, name, OBJPROP_PRICE, lowPrice - gap);
}

void DeleteArrows()
{
   string lookFor = arrowsIdentifier + ":";
   int lookForLength = StringLen(lookFor);
   
   for(int i = ObjectsTotal(0) - 1; i >= 0; i--)
   {
      string objectName = ObjectName(0, i);
      if(StringSubstr(objectName, 0, lookForLength) == lookFor)
         ObjectDelete(0, objectName);
   }
}

void DeleteArrow(datetime barTime)
{
   string lookFor = arrowsIdentifier + ":" + IntegerToString((long)barTime);
   ObjectDelete(0, lookFor);
}

//+-------------------------------------------------------------------
//|  Alert Function                                                  
//+-------------------------------------------------------------------
void DoAlert(int forBar, string doWhat)
{
   static string previousAlert = "nothing";
   static datetime previousTime = 0;
   
   datetime barTime = iTime(_Symbol, PERIOD_CURRENT, forBar);
   
   if(previousAlert != doWhat || previousTime != barTime)
   {
      previousAlert = doWhat;
      previousTime = barTime;
      
      string message = _Symbol + " at " + TimeToString(TimeLocal(), TIME_SECONDS) + " stochastic level " + doWhat;
      
      if(alertsMessage) Alert(message);
      if(alertsEmail) SendMail(_Symbol + " Color stochastic", message);
      if(alertsSound) PlaySound("alert2.wav");
   }
}

//+-------------------------------------------------------------------
//|  Description Functions                                           
//+-------------------------------------------------------------------
string PriceDescription(ENUM_STO_PRICE mode)
{
   switch(mode)
   {
      case STO_LOWHIGH:    return("Low/High");
      case STO_CLOSECLOSE: return("Close/Close");
      default:             return("Invalid");
   }
}

string MADescription(ENUM_MA_METHOD mode)
{
   switch(mode)
   {
      case MODE_SMA:  return("SMA");
      case MODE_EMA:  return("EMA");
      case MODE_SMMA: return("SMMA");
      case MODE_LWMA: return("LWMA");
      default:        return("Invalid");
   }
}

//+-------------------------------------------------------------------
//|  Timeframe Functions                                             
//+-------------------------------------------------------------------
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()); // Can't show lower TF on higher TF chart
      }
   }
   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));
   }
   return(s);
}
//+-------------------------------------------------------------------
