// version 1.1 https://forex-station.com/post1295366893.html#p1295366893
// version 1.2 https://forex-station.com/post1295561850.html#p1295561850
//+------------------------------------------------------------------------------------------------------------------+
#property copyright "c. 2025 mladen"
#property link      "http://www.forex-station.com/"
//+------------------------------------------------------------------------------------------------------------------+
#property indicator_chart_window
#property strict

//extern string Symbols         = "XAUUSDm;EURUSDm;GBPUSDm;AUDUSDm;NZDUSDm;USDJPYm;USDCADm;USDCNHm;US30m;USOILm"; //Exness Symbols to use (separated by ";" in the list)
extern string             Symbols           = "AUDCAD;AUDCHF;AUDJPY;AUDNZD;AUDUSD;CADCHF;CADJPY;CHFJPY;EURAUD;EURCAD;EURCHF;EURGBP;EURJPY;EURNZD;EURUSD;GBPAUD;GBPCAD;GBPCHF;GBPJPY;GBPNZD;GBPUSD;NZDCAD;NZDCHF;NZDJPY;NZDUSD;USDCAD;USDCHF;USDJPY;XAUUSD";               //IC Markets Symbols to use (separated by ";" in the list)
extern string             TimeFrames        = "M15;M30;H1;H4";                       // Time frames to use (separated by ";" in the list)
extern int                StoPeriod         = 14;                                    // Stochastic period
extern int                StoSmoothing      =  3;                                    // Stochastic smoothing
extern ENUM_STO_PRICE     Price             = STO_LOWHIGH;                           // Stochastic price
extern double             lStrongUp         = 80;                                    // Strong up level
extern double             lWeakUp           = 60;                                    // Weak up level
extern double             lWeakDown         = 40;                                    // Weak down level
extern double             lStrongDown       = 20;                                    // Strong down level
extern int                BarToTest         = 0;                                     // Bar to test 
extern color              StrongUp          = clrLimeGreen;                          // Color for strong up
extern color              WeakUp            = clrGreen;                              // Color for weak up
extern color              NoMove            = clrDimGray;                            // Color for no trend
extern color              WeakDown          = clrFireBrick;                          // Color for weak down
extern color              StrongDown        = clrRed;                                // Color for strong down
extern color              TextColor         = clrBlack;                              // Color for butons text
extern color              BorderColor       = clrBlack;                              // Color for butons border
extern color              FillColor         = clrSilver;                             // Color for butons background
extern int                Window            = 0;                                     // Window to use for display
extern ENUM_BASE_CORNER   Corner            = CORNER_LEFT_LOWER;               
extern int                XShift            = 0;                                     // Horizontal shift
extern int                YShift            = 0;                                     // Vertical shift
extern string             UniqueID          = "StochHeatmap102";                     // Indicator unique ID

//
//
//
//
//

int    cpairsLen, ctimesLen, aTimes[];
string cpairs[];
//+------------------------------------------------------------------------------------------------------------------+
void OnChartEvent(const int id, //don't change anything in this function
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{
   // If another indy on the same chart has enabled events for create/delete/mouse-move, just skip this events up front because they aren't
   //    needed, AND in the worst case, this indy might cause MT4 to hang!!  Skipping the events seems to help, along with other (major) changes to the code below.
   if(id==CHARTEVENT_OBJECT_CREATE || id==CHARTEVENT_OBJECT_DELETE) return; // This appears to make this indy compatible with other programs that enabled CHART_EVENT_OBJECT_CREATE and/or CHART_EVENT_OBJECT_DELETE
   if(id==CHARTEVENT_MOUSE_MOVE    || id==CHARTEVENT_MOUSE_WHEEL)   return; // If this, or another program, enabled mouse-events, these are not needed below, so skip it unless actually needed. 

   if (id==CHARTEVENT_OBJECT_CLICK && ObjectGet(sparam,OBJPROP_TYPE)==OBJ_BUTTON)
   {   
      if (StringFind(sparam,UniqueID+"t",0)==0)          
            ChartSetSymbolPeriod(0,ObjectGetString(0,sparam,OBJPROP_TEXT),_Period);
      if (StringFind(sparam,UniqueID+"h",0)==0)   
         {       
          int NewPeriod = 0;
          string Button = ObjectGetString(0,sparam,OBJPROP_TEXT);
          if (Button == "M1")  NewPeriod = PERIOD_M1;
          if (Button == "M5")  NewPeriod = PERIOD_M5;
          if (Button == "M15") NewPeriod = PERIOD_M15;
          if (Button == "M30") NewPeriod = PERIOD_M30;
          if (Button == "H1")  NewPeriod = PERIOD_H1;
          if (Button == "H4")  NewPeriod = PERIOD_H4;
          if (Button == "D1")  NewPeriod = PERIOD_D1;
          if (Button == "W1")  NewPeriod = PERIOD_W1;
          if (Button == "MN1") NewPeriod = PERIOD_MN1;
          ChartSetSymbolPeriod(ChartID(), Symbol(), NewPeriod);
         }
   }
}
//+------------------------------------------------------------------------------------------------------------------+
int init()
{
   Symbols = StringTrimLeft(StringTrimRight(Symbols)); 
   if (StringSubstr(Symbols,StringLen(Symbols)-1,1) != ";")
                    Symbols = StringConcatenate(Symbols,";");

   TimeFrames = StringTrimLeft(StringTrimRight(TimeFrames));
   if (StringSubstr(TimeFrames,StringLen(TimeFrames)-1,1) != ";")
                    TimeFrames = StringConcatenate(TimeFrames,";");

      //
      //
      //
      //
      //
   
      int s = 0, i = StringFind(Symbols,";",s);
         while (i > 0)
         {
            string current = StringSubstr(Symbols,s,i-s);
            ArrayResize(cpairs,ArraySize(cpairs)+1);
                        cpairs[ArraySize(cpairs)-1] = current;
                        s = i + 1;
                        i = StringFind(Symbols,";",s);
         }
      if (Corner==CORNER_LEFT_LOWER  || Corner==CORNER_RIGHT_LOWER) invertArray(cpairs);  cpairsLen = ArraySize(cpairs);
      s = 0; i = StringFind(TimeFrames,";",s);
         while (i > 0)
         {
            string current = StringSubstr(TimeFrames,s,i-s);
            int time = stringToTimeFrame(current);
            if (time > 0) {
                  ArrayResize(aTimes,ArraySize(aTimes)+1);
                              aTimes[ArraySize(aTimes)-1] = time; }
                              s = i + 1;
                                  i = StringFind(TimeFrames,";",s);
         }
      if (Corner==CORNER_RIGHT_UPPER || Corner==CORNER_RIGHT_LOWER) invertiArray(aTimes); ctimesLen = ArraySize(aTimes);

      //
      //
      //
      //
      //
               
   IndicatorShortName(UniqueID);
   return(0);
}
//+------------------------------------------------------------------------------------------------------------------+
int deinit()
{
   int idLength = StringLen(UniqueID);
   for (int i = ObjectsTotal(); i>=0; i--)
         { string name = ObjectName(i); if (StringSubstr(name,0,idLength) == UniqueID) ObjectDelete(name); }         
   return(0);
}
//+------------------------------------------------------------------------------------------------------------------+
int start()
{
   int xshift=0, nxshift=0; if (Corner==CORNER_RIGHT_LOWER || Corner==CORNER_RIGHT_UPPER) { xshift=49; nxshift=66; }
   for (int i = 0; i < cpairsLen; i++ ) ButtonCreate2(0,UniqueID+"t"+(string)i,Window,XShift+nxshift+2,YShift+30+i*15,66,14,Corner,cpairs[i]                   ,"Arial",8,TextColor,BorderColor,FillColor);
   for (int i = 0; i < ctimesLen; i++ ) ButtonCreate2(0,UniqueID+"h"+(string)i,Window,XShift+xshift+i*50+69,YShift+15,49,14,Corner,timeFrameToString(aTimes[i]),"Arial",8,TextColor,BorderColor,FillColor);
   for (int i = 0; i < cpairsLen; i++)
      if (iClose(cpairs[i],0,0)!=0) for (int t = 0; t < ctimesLen; t++)
      {
         double value = iStochastic(cpairs[i],aTimes[t],StoPeriod,1,StoSmoothing,MODE_SMA,Price,MODE_MAIN,BarToTest);
         color  theColor = WeakDown;
         while (true)
         {
            if (value > lStrongUp)                    { theColor = StrongUp;   break; }
            if (value > lWeakUp)                      { theColor = WeakUp;     break; }
            if (value < lWeakUp && value > lWeakDown) { theColor = NoMove;     break; }
            if (value < lStrongDown)                  { theColor = StrongDown; break; }
                                                                               break; 
         }
         ButtonCreate2(0,UniqueID+(string)t+(string)i,Window,XShift+xshift+t*50+69,YShift+30+i*15,49,14,Corner," ","Arial",8,theColor,BorderColor,theColor);
      }
   if (Corner==CORNER_RIGHT_UPPER || Corner==CORNER_RIGHT_LOWER)
         ButtonCreate2(0,UniqueID+"title",Window,XShift+xshift+ctimesLen*50+19,YShift+30+cpairsLen*15,ctimesLen*50-1,14,Corner," Stochastic ("+(string)StoPeriod+","+(string)StoSmoothing+")","Arial",8,TextColor,BorderColor,FillColor);
   else  ButtonCreate2(0,UniqueID+"title",Window,XShift+xshift+69             ,YShift+30+cpairsLen*15,ctimesLen*50-1,14,Corner," Stochastic ("+(string)StoPeriod+","+(string)StoSmoothing+")","Arial",8,TextColor,BorderColor,FillColor);
   return(0);
}
//+------------------------------------------------------------------------------------------------------------------+
bool ButtonCreate2(const long             chart_ID=0,               // chart's ID 
                  const string            name="Button",            // button name 
                  const int               sub_window=0,             // subwindow index 
                  const int               x=0,                      // X coordinate 
                  const int               y=0,                      // Y coordinate 
                  const int               width=50,                 // button width 
                  const int               height=18,                // button height 
                  const ENUM_BASE_CORNER  corner=CORNER_LEFT_UPPER, // chart corner for anchoring 
                  const string            text="Button",            // text 
                  const string            font="Arial",             // font 
                  const int               font_size=8,              // font size 
                  const color             clr=clrBlack,             // text color 
                  const color             clrBorder=clrBlack,       // border color 
                  const color             back_clr=clrGray,         // background color 
                  const bool              state=true,               // pressed/released 
                  const bool              back=false,               // in the background 
                  const bool              selection=false,          // highlight to move 
                  const bool              hidden=true,              // hidden in the object list 
                  const long              z_order=0)                // priority for mouse click 
{ 
      ObjectCreate(chart_ID,name,OBJ_BUTTON,sub_window,0,0);
      ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x); 
      ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y); 
      ObjectSetInteger(chart_ID,name,OBJPROP_XSIZE,width); 
      ObjectSetInteger(chart_ID,name,OBJPROP_YSIZE,height); 
      ObjectSetInteger(chart_ID,name,OBJPROP_CORNER,corner); 
      ObjectSetInteger(chart_ID,name,OBJPROP_FONTSIZE,font_size); 
      ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr); 
      ObjectSetInteger(chart_ID,name,OBJPROP_BGCOLOR,back_clr); 
      ObjectSetInteger(chart_ID,name,OBJPROP_BORDER_COLOR,clrBorder); 
      ObjectSetInteger(chart_ID,name,OBJPROP_BORDER_TYPE,BORDER_FLAT); 
      ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back); 
      ObjectSetInteger(chart_ID,name,OBJPROP_STATE,state); 
      ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection); 
      ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection); 
      ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden); 
      ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order); 
      ObjectSetString(chart_ID,name,OBJPROP_TEXT,text); 
      ObjectSetString(chart_ID,name,OBJPROP_FONT,font); 
      return(true); 
} 
//+------------------------------------------------------------------------------------------------------------------+
string sTfTable[] = {"M1","M5","M15","M30","H1","H4","D1","W1","MN"};
int    iTfTable[] = {1,5,15,30,60,240,1440,10080,43200};

int stringToTimeFrame(string tfs)
{
   StringToUpper(tfs);
   for (int i=ArraySize(iTfTable)-1; i>=0; i--)
         if (tfs==sTfTable[i] || tfs==""+(string)iTfTable[i]) return(iTfTable[i]);
                                                              return(_Period);
}
//+------------------------------------------------------------------------------------------------------------------+
string timeFrameToString(int tf)
{
   for (int i=ArraySize(iTfTable)-1; i>=0; i--) 
         if (tf==iTfTable[i]) return(sTfTable[i]);
                              return("");
}
//+------------------------------------------------------------------------------------------------------------------+
void invertArray(string& array[])
{
   string temp[]; ArrayCopy(temp,array); for (int i=0, s=ArraySize(array); i<s; i++) array[i] = temp[s-i-1];
}
void invertiArray(int& array[])
{
   int temp[]; ArrayCopy(temp,array); for (int i=0, s=ArraySize(array); i<s; i++) array[i] = temp[s-i-1];
}
//+------------------------------------------------------------------------------------------------------------------+
