Indicators: Timeframe Quality Analyzer

 

Timeframe Quality Analyzer:

Find Perfect Timeframe to trade

Timeframe Quality Analyzer

Author: Rajesh Kumar Nait

 
//+------------------------------------------------------------------+
//|                                   TimeFrame_Quality_Analyzer.mq4 |
//|                                Copyright 2026, Rajesh Kumar Nait |
//|                         https://www.mql5.com/en/users/rajeshnait |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Rajesh Kumar Nait"
#property link      "https://www.mql5.com/en/users/rajeshnait"
#property version   "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots   0

//--- inputs
extern int    InpWindow        = 100;    // Rolling Window Length
extern int    InpEntropyBins   = 20;     // Entropy Bin Count
extern double InpWeightSNR     = 0.25;   // Weight: SNR
extern double InpWeightAC      = 0.15;   // Weight: Autocorrelation
extern double InpWeightHurst   = 0.25;   // Weight: Hurst
extern double InpWeightDER     = 0.20;   // Weight: DER
extern double InpWeightEntropy = 0.15;   // Weight: Inverse Entropy
extern int    InpPanelX        = 10;     // Panel X Position
extern int    InpPanelY        = 30;     // Panel Y Position

string P = "TQA_";

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init() {
   CreatePanel();
   return(0);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit() {
   int total = ObjectsTotal();
   for(int i = total - 1; i >= 0; i--) {
      string name = ObjectName(i);
      if(StringFind(name, P) == 0)
         ObjectDelete(name);
   }
   ChartRedraw();
   return(0);
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start() {
   int rates_total = Bars;

   if(rates_total < InpWindow + 2)
      return(0);

   int last = rates_total - 1;

//--- build closes and returns for latest window only
   double closes[];
   ArrayResize(closes, InpWindow + 1);
   for(int j = 0; j <= InpWindow; j++)
      closes[j] = Close[last - InpWindow + j];

   double returns[];
   ArrayResize(returns, InpWindow);
   for(int j = 0; j < InpWindow; j++)
      returns[j] = closes[j + 1] - closes[j];

   double snr     = CalcSNR(closes, InpWindow + 1);
   double ac      = CalcAutocorrelation(returns, InpWindow);
   double hurst   = CalcHurst(returns, InpWindow);
   double der     = CalcDER(closes, InpWindow + 1);
   double entropy = CalcEntropy(returns, InpWindow, InpEntropyBins);
   double tqs     = CalcTQS(snr, ac, hurst, der, entropy);

   UpdatePanel(snr, ac, hurst, der, entropy, tqs);
   return(0);
}

//+------------------------------------------------------------------+
//| Calculate Signal-to-Noise Ratio                                  |
//+------------------------------------------------------------------+
double CalcSNR(double &data[], int len) {
   double sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
   for(int i = 0; i < len; i++) {
      sumX  += i;
      sumY  += data[i];
      sumXY += i * data[i];
      sumX2 += (double)i * i;
   }
   double n = (double)len;
   double denom = n * sumX2 - sumX * sumX;
   if(MathAbs(denom) < 1e-20) return 0;
   double slope     = (n * sumXY - sumX * sumY) / denom;
   double intercept = (sumY - slope * sumX) / n;
   double meanY = sumY / n;
   double ssExp = 0, ssRes = 0;
   for(int i = 0; i < len; i++) {
      double fitted = intercept + slope * i;
      ssExp += (fitted - meanY) * (fitted - meanY);
      ssRes += (data[i] - fitted) * (data[i] - fitted);
   }
   if(ssRes < 1e-20) return 10.0;
   return MathMin(ssExp / ssRes, 10.0);
}

//+------------------------------------------------------------------+
//| Calculate Autocorrelation (lag-1)                                |
//+------------------------------------------------------------------+
double CalcAutocorrelation(double &ret[], int len) {
   if(len < 3) return 0;
   double mean = 0;
   for(int i = 0; i < len; i++) mean += ret[i];
   mean /= len;
   double num = 0, den = 0;
   for(int i = 0; i < len; i++) {
      double d = ret[i] - mean;
      den += d * d;
      if(i < len - 1)
         num += d * (ret[i + 1] - mean);
   }
   if(MathAbs(den) < 1e-20) return 0;
   return num / den;
}

//+------------------------------------------------------------------+
//| Calculate Hurst Exponent via R/S Analysis                        |
//+------------------------------------------------------------------+
double CalcHurst(double &ret[], int len) {
   if(len < 20) return 0.5;

   int sizes[];
   int cnt = 0;
   for(int s = 8; s <= len / 2; s *= 2) {
      cnt++;
      ArrayResize(sizes, cnt);
      sizes[cnt - 1] = s;
   }
   if(cnt < 2) return 0.5;

   double lnN[];
   double lnRS[];
   ArrayResize(lnN, cnt);
   ArrayResize(lnRS, cnt);

   for(int si = 0; si < cnt; si++) {
      int w = sizes[si];
      int nw = len / w;
      double rsSum = 0;
      int valid = 0;
      for(int b = 0; b < nw; b++) {
         int st = b * w;
         double m = 0;
         for(int k = 0; k < w; k++) m += ret[st + k];
         m /= w;
         double cum = 0, mx = -1e30, mn = 1e30, ss = 0;
         for(int k = 0; k < w; k++) {
            double d = ret[st + k] - m;
            cum += d;
            ss  += d * d;
            if(cum > mx) mx = cum;
            if(cum < mn) mn = cum;
         }
         double S = MathSqrt(ss / w);
         if(S > 1e-20) {
            rsSum += (mx - mn) / S;
            valid++;
         }
      }
      lnN[si]  = MathLog((double)w);
      lnRS[si] = (valid > 0) ? MathLog(rsSum / valid) : 0;
   }

   double sx = 0, sy = 0, sxy = 0, sx2 = 0;
   for(int i = 0; i < cnt; i++) {
      sx  += lnN[i];
      sy  += lnRS[i];
      sxy += lnN[i] * lnRS[i];
      sx2 += lnN[i] * lnN[i];
   }
   double n = (double)cnt;
   double dd = n * sx2 - sx * sx;
   if(MathAbs(dd) < 1e-20) return 0.5;
   return MathMax(0.0, MathMin(1.0, (n * sxy - sx * sy) / dd));
}

//+------------------------------------------------------------------+
//| Calculate Directional Efficiency Ratio                           |
//+------------------------------------------------------------------+
double CalcDER(double &data[], int len) {
   if(len < 2) return 0;
   double net  = MathAbs(data[len - 1] - data[0]);
   double path = 0;
   for(int i = 1; i < len; i++)
      path += MathAbs(data[i] - data[i - 1]);
   if(path < 1e-20) return 0;
   return net / path;
}

//+------------------------------------------------------------------+
//| Calculate Normalised Shannon Entropy                             |
//+------------------------------------------------------------------+
double CalcEntropy(double &ret[], int len, int bins) {
   if(len < 2 || bins < 2) return 1.0;
   double mn = ret[0], mx = ret[0];
   for(int i = 1; i < len; i++) {
      if(ret[i] < mn) mn = ret[i];
      if(ret[i] > mx) mx = ret[i];
   }
   double rng = mx - mn;
   if(rng < 1e-20) return 0;

   int c[];
   ArrayResize(c, bins);
   ArrayInitialize(c, 0);
   for(int i = 0; i < len; i++) {
      int b = (int)((ret[i] - mn) / rng * (bins - 1));
      if(b < 0) b = 0;
      if(b >= bins) b = bins - 1;
      c[b]++;
   }
   double ent = 0, logB = MathLog((double)bins);
   if(logB < 1e-20) return 1.0;
   for(int i = 0; i < bins; i++) {
      if(c[i] > 0) {
         double p = (double)c[i] / (double)len;
         ent -= p * MathLog(p);
      }
   }
   return ent / logB;
}

//+------------------------------------------------------------------+
//| Calculate Timeframe Quality Score                                |
//+------------------------------------------------------------------+
double CalcTQS(double snr, double ac, double hurst, double der, double entropy) {
   double nSNR    = MathMin(snr / 5.0, 1.0);
   double nAC     = MathMin(MathAbs(ac) * 3.0, 1.0);
   double nHurst  = MathMin(MathAbs(hurst - 0.5) * 4.0, 1.0);
   double nDER    = der;
   double nInvEnt = 1.0 - entropy;
   double tw = InpWeightSNR + InpWeightAC + InpWeightHurst +
               InpWeightDER + InpWeightEntropy;
   if(tw < 1e-10) tw = 1.0;
   return (InpWeightSNR * nSNR + InpWeightAC * nAC +
           InpWeightHurst * nHurst + InpWeightDER * nDER +
           InpWeightEntropy * nInvEnt) / tw;
}

//+------------------------------------------------------------------+
//| Create a rectangle label object                                  |
//+------------------------------------------------------------------+
void MakeRect(string name, int x, int y, int w, int h, color bg, color bd) {
   if(ObjectFind(name) >= 0) ObjectDelete(name);
   ObjectCreate(name, OBJ_RECTANGLE_LABEL, 0, 0, 0);
   ObjectSet(name, OBJPROP_CORNER,       CORNER_LEFT_UPPER);
   ObjectSet(name, OBJPROP_XDISTANCE,    x);
   ObjectSet(name, OBJPROP_YDISTANCE,    y);
   ObjectSet(name, OBJPROP_XSIZE,        w);
   ObjectSet(name, OBJPROP_YSIZE,        h);
   ObjectSet(name, OBJPROP_BGCOLOR,      bg);
   ObjectSet(name, OBJPROP_COLOR,        bd);
   ObjectSet(name, OBJPROP_BORDER_TYPE,  BORDER_FLAT);
   ObjectSet(name, OBJPROP_BACK,         false);
   ObjectSet(name, OBJPROP_SELECTABLE,   false);
   ObjectSet(name, OBJPROP_HIDDEN,       true);
}

//+------------------------------------------------------------------+
//| Create a text label object                                       |
//+------------------------------------------------------------------+
void MakeLabel(string name, int x, int y, string text,
               color clr, string font, int sz) {
   if(ObjectFind(name) >= 0) ObjectDelete(name);
   ObjectCreate(name, OBJ_LABEL, 0, 0, 0);
   ObjectSet(name, OBJPROP_CORNER,    CORNER_LEFT_UPPER);
   ObjectSet(name, OBJPROP_XDISTANCE, x);
   ObjectSet(name, OBJPROP_YDISTANCE, y);
   ObjectSetText(name, text, sz, font, clr);
   ObjectSet(name, OBJPROP_BACK,      false);
   ObjectSet(name, OBJPROP_SELECTABLE,false);
   ObjectSet(name, OBJPROP_HIDDEN,    true);
}

//+------------------------------------------------------------------+
//| Build the full panel at init time                                |
//+------------------------------------------------------------------+
void CreatePanel() {
   int x = InpPanelX, y = InpPanelY;

   MakeRect(P+"BG",  x,   y,   340, 360, (color)C'15,15,28',  (color)C'50,50,80');
   MakeRect(P+"BG2", x+2, y+2, 336, 356, (color)C'20,20,35',  (color)C'50,50,80');

   int r = y + 8;
   MakeLabel(P+"T1", x+14, r, "TIMEFRAME QUALITY ANALYZER", clrWhite, "Arial Bold", 12);
   r += 22;
   MakeRect(P+"S1", x+8, r, 324, 1, clrDodgerBlue, clrDodgerBlue);
   r += 8;

   MakeLabel(P+"LW", x+14, r,
             "Window: "+IntegerToString(InpWindow)+"  |  TF: "+PeriodStr(),
             (color)C'120,120,150', "Consolas", 9);
   r += 22;
   MakeRect(P+"S1b", x+8, r, 324, 1, (color)C'40,40,60', (color)C'40,40,60');
   r += 10;

// SNR row
   MakeLabel(P+"LSNR", x+14,  r, "SNR",             (color)C'150,150,180', "Consolas", 10);
   MakeLabel(P+"VSNR", x+160, r, "---",             clrDodgerBlue,         "Consolas", 10);
   MakeLabel(P+"ISNR", x+230, r, "",                (color)C'120,120,150', "Consolas", 9);
   r += 26;

// AC row
   MakeLabel(P+"LAC",  x+14,  r, "Autocorrelation", (color)C'150,150,180', "Consolas", 10);
   MakeLabel(P+"VAC",  x+160, r, "---",             clrLime,               "Consolas", 10);
   MakeLabel(P+"IAC",  x+230, r, "",                (color)C'120,120,150', "Consolas", 9);
   r += 26;

// Hurst row
   MakeLabel(P+"LHU",  x+14,  r, "Hurst Exponent",  (color)C'150,150,180', "Consolas", 10);
   MakeLabel(P+"VHU",  x+160, r, "---",             clrGold,               "Consolas", 10);
   MakeLabel(P+"IHU",  x+230, r, "",                clrGold,               "Consolas", 9);
   r += 26;

// DER row
   MakeLabel(P+"LDER", x+14,  r, "Dir. Efficiency", (color)C'150,150,180', "Consolas", 10);
   MakeLabel(P+"VDER", x+160, r, "---",             clrMagenta,            "Consolas", 10);
   MakeLabel(P+"IDER", x+230, r, "",                (color)C'120,120,150', "Consolas", 9);
   r += 26;

// Entropy row
   MakeLabel(P+"LENT", x+14,  r, "Entropy (norm)",  (color)C'150,150,180', "Consolas", 10);
   MakeLabel(P+"VENT", x+160, r, "---",             clrOrangeRed,          "Consolas", 10);
   MakeLabel(P+"IENT", x+230, r, "",                (color)C'120,120,150', "Consolas", 9);
   r += 30;

// Separator
   MakeRect(P+"S2", x+8, r, 324, 2, clrDodgerBlue, clrDodgerBlue);
   r += 10;

// TQS
   MakeLabel(P+"LTQS", x+14,  r, "QUALITY SCORE", clrWhite, "Arial Bold", 12);
   MakeLabel(P+"VTQS", x+200, r, "---",           clrWhite, "Arial Bold", 16);
   r += 28;

// Progress bar background + fill
   MakeRect(P+"BBG", x+14, r, 312, 18, (color)C'35,35,50', (color)C'55,55,75');
   MakeRect(P+"BFG", x+14, r, 1,   18, clrDodgerBlue, clrDodgerBlue);
   r += 28;

// Verdict
   MakeLabel(P+"VRD", x+14, r, "ANALYZING...", clrYellow, "Arial Bold", 15);
   r += 28;

// Detail lines
   MakeLabel(P+"D1", x+14, r, "", (color)C'130,130,155', "Consolas", 9);
   r += 16;
   MakeLabel(P+"D2", x+14, r, "", (color)C'130,130,155', "Consolas", 9);
   r += 16;
   MakeLabel(P+"D3", x+14, r, "", (color)C'130,130,155', "Consolas", 9);
}

//+------------------------------------------------------------------+
//| Update all panel labels/objects with fresh metric values         |
//+------------------------------------------------------------------+
void UpdatePanel(double snr, double ac, double hurst,
                 double der, double entropy, double tqs) {
// SNR
   string sSNR; color cSNR;
   if(snr > 2.0)      { sSNR = "Strong";   cSNR = clrLime; }
   else if(snr > 0.5) { sSNR = "Moderate"; cSNR = clrGold; }
   else               { sSNR = "Weak";     cSNR = clrRed;  }
   ObjectSetText(P+"VSNR", DoubleToString(snr, 3), 10, "Consolas", cSNR);
   ObjectSetText(P+"ISNR", sSNR, 9, "Consolas", cSNR);

// AC
   bool acSig = MathAbs(ac) > 0.1;
   string sAC  = acSig ? "Persistent" : "Noise";
   color  cAC  = acSig ? clrLime : clrRed;
   ObjectSetText(P+"VAC", DoubleToString(ac, 4), 10, "Consolas", cAC);
   ObjectSetText(P+"IAC", sAC, 9, "Consolas", cAC);

// Hurst
   string sHU; color cHU;
   if(hurst > 0.55)      { sHU = "Trending";    cHU = clrLime;      }
   else if(hurst < 0.45) { sHU = "Mean-Revert"; cHU = clrOrangeRed; }
   else                  { sHU = "Random Walk";  cHU = clrGray;      }
   ObjectSetText(P+"VHU", DoubleToString(hurst, 4), 10, "Consolas", cHU);
   ObjectSetText(P+"IHU", sHU, 9, "Consolas", cHU);

// DER
   string sDER; color cDER;
   if(der > 0.6)      { sDER = "Structured"; cDER = clrLime;    }
   else if(der > 0.3) { sDER = "Mixed";      cDER = clrGold;    }
   else               { sDER = "Noisy";      cDER = clrRed;     }
   ObjectSetText(P+"VDER", DoubleToString(der, 4), 10, "Consolas", cDER);
   ObjectSetText(P+"IDER", sDER, 9, "Consolas", cDER);

// Entropy
   string sENT; color cENT;
   if(entropy < 0.5)       { sENT = "Ordered"; cENT = clrLime;      }
   else if(entropy < 0.75) { sENT = "Mixed";   cENT = clrGold;      }
   else                    { sENT = "Random";  cENT = clrRed;       }
   ObjectSetText(P+"VENT", DoubleToString(entropy, 4), 10, "Consolas", cENT);
   ObjectSetText(P+"IENT", sENT, 9, "Consolas", cENT);

// TQS score + progress bar
   color cTQS = (tqs > 0.65) ? clrLime : (tqs > 0.40) ? clrGold : clrRed;
   ObjectSetText(P+"VTQS", DoubleToString(tqs * 100, 1) + "%", 16, "Arial Bold", cTQS);

   int barW = (int)(tqs * 312);
   if(barW < 1)   barW = 1;
   if(barW > 312) barW = 312;
   ObjectSet(P+"BFG", OBJPROP_XSIZE,  barW);
   ObjectSet(P+"BFG", OBJPROP_BGCOLOR, cTQS);

// Verdict
   string vrd; color cV;
   if(tqs > 0.65)      { vrd = "HIGH STRUCTURE";  cV = clrLime; }
   else if(tqs > 0.40) { vrd = "MODERATE";        cV = clrGold; }
   else                { vrd = "NOISE DOMINATED";  cV = clrRed;  }
   ObjectSetText(P+"VRD", vrd, 15, "Arial Bold", cV);

// Detail text rows
   ObjectSetText(P+"D1",
                 "SNR=" + DoubleToString(snr,2) + " | AC=" + DoubleToString(ac,3),
                 9, "Consolas", (color)C'130,130,155');
   ObjectSetText(P+"D2",
                 "Hurst=" + DoubleToString(hurst,3) + " -> " + sHU,
                 9, "Consolas", (color)C'130,130,155');
   ObjectSetText(P+"D3",
                 "DER=" + DoubleToString(der,3) + " | Ent=" + DoubleToString(entropy,3),
                 9, "Consolas", (color)C'130,130,155');

   ChartRedraw();
}

//+------------------------------------------------------------------+
//| Return a human-readable period string                            |
//+------------------------------------------------------------------+
string PeriodStr() {
   switch(Period()) {
   case PERIOD_M1:  return "M1";
   case PERIOD_M5:  return "M5";
   case PERIOD_M15: return "M15";
   case PERIOD_M30: return "M30";
   case PERIOD_H1:  return "H1";
   case PERIOD_H4:  return "H4";
   case PERIOD_D1:  return "D1";
   case PERIOD_W1:  return "W1";
   case PERIOD_MN1: return "MN1";
   default:         return "??";
   }
}

//+------------------------------------------------------------------+
 
hope it is well coded?