//+------------------------------------------------------------------+
//|  MicroStructure_Foundation.mqh                                   |
//|  Market Microstructure in MQL5: Robust Foundation — Part 1 of 12 |
//|                                                                  |
//|  PURPOSE                                                         |
//|  Provides the shared numerical foundation for all subsequent     |
//|  articles in the series. Every function here is designed to      |
//|  fail safely rather than silently when given bad data.           |
//|                                                                  |
//|  USAGE                                                           |
//|  Place this file in:                                             |
//|    MQL5\Indicators\MicroStructure\Includes\                      |
//|  Reference from your indicator or EA:                            |
//|    #include "Includes\MicroStructure_Foundation.mqh"             |
//|                                                                  |
//|  DEPENDS ON  : nothing                                           |
//|  REQUIRED BY : Parts 2 through 12                                |
//+------------------------------------------------------------------+
#property copyright "Max Brown"
#property link      ""
#property version   "1.00"

#ifndef MICROSTRUCTURE_FOUNDATION_MQH
#define MICROSTRUCTURE_FOUNDATION_MQH

//+------------------------------------------------------------------+
//| CONSTANTS                                                        |
//+------------------------------------------------------------------+
#define DBL_MIN_POSITIVE          1e-20                   // Smallest value treated as non-zero in division and log guards
#define MS_DBL_EPSILON            1e-10                   // General floating-point comparison tolerance
#define MATH_PI                   3.14159265358979323846  // Pi to full double precision for FFT and trigonometric use
#define MIN_SAMPLE_SIZE           8                       // Minimum bars required before any statistical calculation proceeds
#define MAX_SAMPLE_SIZE           5000                    // Maximum bars fetched in a single CopyClose call
#define MAX_AGGREGATION_LEVEL     10                      // Maximum scale levels used in aggregated variance estimators
#define NUMERICAL_STABILITY_BOUND 1e100                   // Any result exceeding this is treated as a numerical failure
#define GMT_OFFSET_HOURS          2                       // Broker GMT offset; adjust if your broker runs on a different zone

//+------------------------------------------------------------------+
//| STRUCTURES                                                       |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Primary result container for fractal and long-memory analysis.   |
//| Confidence fields support weighted blending of estimators.       |
//| computation_status: 0 = success, non-zero = specific failure.    |
//+------------------------------------------------------------------+
struct RobustFractalAnalysis
  {
   double            hurst_exponent;         // Weighted-average Hurst exponent across estimators
   double            hurst_confidence;       // Confidence weight for the Hurst estimate (0–1)
   double            fractal_dimension;      // Higuchi fractal dimension blended with Hurst fallback
   double            dimension_confidence;   // Confidence weight for the fractal dimension estimate
   double            arfima_d;               // GPH fractional differencing parameter d
   double            multifractal_width;     // Spectrum width from MFDFA-style tau(q) regression
   double            volatility_persistence; // Normalised persistence: 2 * |H - 0.5|
   double            microstructure_noise;   // Enhanced noise estimate from range, body, and close-open
   double            seasonality_strength;   // Intraday seasonality ratio (edge vs midday variance)
   int               computation_status;     // 0 = success; non-zero codes indicate failure mode
   string            validation_message;     // Human-readable description of any failure condition
  };

//+------------------------------------------------------------------+
//| Backward-compatible alias for older call sites.                  |
//| Use RobustFractalAnalysis in all new code.                       |
//+------------------------------------------------------------------+
struct FractalAnalysis
  {
   double            hurst_exponent;
   double            fractal_dimension;
   double            arfima_d;
   double            local_whittle_h;
   double            multifractal_width;
   double            stability_alpha;
   double            volatility_persistence;
   double            microstructure_noise;
   double            seasonality_strength;
  };

//+------------------------------------------------------------------+
//| Container for realized and modelled volatility estimates.        |
//+------------------------------------------------------------------+
struct VolatilityAnalysis
  {
   double            realized_vol;     // Square root of sum of squared log-returns
   double            fractional_vol;   // ARFIMA-weighted realized volatility
   double            figarch_vol;      // Power-law decay weighted conditional volatility
   double            duration_vol;     // Annualized volatility scaled by timeframe seconds
   double            clustering_index; // Lag-1 autocorrelation of squared returns
   double            asymmetry;        // Signed skewness of the return distribution
   double            leverage_effect;  // Negative-vs-positive return volatility asymmetry
   double            jump_intensity;   // Proportion of returns exceeding three standard deviations
  };

//+------------------------------------------------------------------+
//| Composite order flow signal combining directional pressure,      |
//| momentum, exhaustion, and smart-money contribution.              |
//| direction_numeric enables downstream arithmetic without          |
//| string comparisons.                                              |
//+------------------------------------------------------------------+
struct OrderFlowSignal
  {
   double            strength;          // Volume-weighted directional imbalance (-1 to +1)
   double            momentum;          // Short-window minus long-window flow differential
   double            exhaustion;        // Current absolute flow relative to recent peak
   double            smart_money;       // Large-bar directional bias (signed volume ratio)
   double            convergence;       // Mean signal score across sub-indicators
   double            confidence;        // Signal reliability weight (0–1)
   string            direction;         // "BULLISH", "BEARISH", or "NEUTRAL"
   double            direction_numeric; // +1.0, -1.0, or 0.0 for use in calculations
  };

//+------------------------------------------------------------------+
//| Time-aware composite signal layering session context over flow.  |
//| Embeds OrderFlowSignal and adds regime, liquidity, and event     |
//| adjustments for session-adaptive position sizing.                |
//+------------------------------------------------------------------+
struct TimeAwareSignal
  {
   OrderFlowSignal   flow_signal;                // Underlying order flow analysis
   double            fractal_synergy;            // Hurst-regime aligned with flow direction
   double            time_regime_multiplier;     // Session weight: 1.5 at NY open, 0.5 off-peak
   double            session_liquidity_factor;   // Spread-based liquidity scaling (0.3–1.2)
   double            pre_event_boost;            // Additive boost near scheduled events
   double            final_signal;               // Combined directional signal (-2 to +2)
   double            time_adjusted_confidence;   // Confidence scaled by regime and liquidity
   string            time_context;               // Human-readable session label
   datetime          signal_time;                // TimeCurrent() at signal generation
   double            time_until_event;           // Minutes until next scheduled event
   double            max_position_size;          // Suggested size fraction (0–1)
   double            stop_loss_multiplier;       // ATR stop multiplier based on confidence
  };

//+------------------------------------------------------------------+
//| Output of MakeTradingDecision: action, sizing, and levels.       |
//+------------------------------------------------------------------+
struct TradingDecision
  {
   int               action;           // +1 = buy, -1 = sell, 0 = no trade
   double            size;             // Position size fraction (0–1)
   double            stop_loss_pips;   // Stop loss in points
   double            take_profit_pips; // Take profit in points
   double            confidence;       // Decision confidence (0–1)
   string            rationale;        // Textual description of decision basis
  };

//+------------------------------------------------------------------+
//| Pullback quality classification based on Fibonacci retracement   |
//| depth relative to the most recent swing range.                   |
//+------------------------------------------------------------------+
enum PULLBACK_QUALITY
  {
   PBQ_NO_TREND = 0, // No identifiable trend; classification not meaningful
   PBQ_STRONG   = 1, // Retrace below 23.6%: trend intact, shallow pullback
   PBQ_HEALTHY  = 2, // Retrace 23.6%–38.2%: normal corrective structure
   PBQ_WARNING  = 3, // Retrace 38.2%–61.8%: deeper correction, caution advised
   PBQ_DEEP     = 4, // Retrace above 61.8%: trend at risk of reversal
   PBQ_BROKEN   = 5  // Retrace beyond 100%: trend structure broken
  };

//+------------------------------------------------------------------+
//| SAFE MATH FUNCTIONS                                              |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| SafeDivide: two-gate guard.                                      |
//| Gate 1 checks denominator before division.                       |
//| Gate 2 checks result for overflow or NaN after division.         |
//+------------------------------------------------------------------+
double SafeDivide(double numerator, double denominator,
                  double fallback = 0.0)
  {
   if(!MathIsValidNumber(denominator) ||
      MathAbs(denominator) < DBL_MIN_POSITIVE)
      return fallback;

   double result = numerator / denominator;

   if(!MathIsValidNumber(result) ||
      MathAbs(result) > NUMERICAL_STABILITY_BOUND)
      return fallback;

   return result;
  }

//+------------------------------------------------------------------+
//| SafeLog: guards against non-positive input.                      |
//| Fallback of -20.0 signals "invalid point" to downstream          |
//| regression slope calculations without distorting the fit.        |
//+------------------------------------------------------------------+
double SafeLog(double x, double fallback = -20.0)
  {
   if(!MathIsValidNumber(x) || x <= 0)
      return fallback;

   double result = MathLog(x);

   if(!MathIsValidNumber(result))
      return fallback;

   return result;
  }

//+------------------------------------------------------------------+
//| SafeSqrt: guards against negative radicand.                      |
//+------------------------------------------------------------------+
double SafeSqrt(double x, double fallback = 0.0)
  {
   if(!MathIsValidNumber(x) || x < 0)
      return fallback;

   double result = MathSqrt(x);

   if(!MathIsValidNumber(result))
      return fallback;

   return result;
  }

//+------------------------------------------------------------------+
//| SafeExp: soft-clips extreme inputs rather than hard-failing.     |
//| Large inputs are not always bad data; they can be valid          |
//| log-return values. Clamping preserves scale while avoiding NaN.  |
//+------------------------------------------------------------------+
double SafeExp(double x, double fallback = 0.0)
  {
   if(!MathIsValidNumber(x)) return fallback;
   if(x >  100)              return 1e100;
   if(x < -100)              return 1e-100;

   double result = MathExp(x);

   if(!MathIsValidNumber(result))
      return fallback;

   return result;
  }

//+------------------------------------------------------------------+
//| SafeTanh: soft-bounding operator used throughout order flow.     |
//| Compresses signals smoothly to [-1, +1] without hard clipping.   |
//| DBL_MIN_POSITIVE in the denominator prevents degenerate zero.    |
//+------------------------------------------------------------------+
double SafeTanh(double x)
  {
   if(!MathIsValidNumber(x)) return 0.0;
   if(x >  10.0)             return  1.0;
   if(x < -10.0)             return -1.0;

   double ex  = SafeExp(x);
   double emx = SafeExp(-x);

   return (ex - emx) / (ex + emx + DBL_MIN_POSITIVE);
  }

//+------------------------------------------------------------------+
//| MathSign: returns +1.0, -1.0, or 0.0 for the sign of x.          |
//+------------------------------------------------------------------+
double MathSign(double x)
  {
   if(x > 0) return  1.0;
   if(x < 0) return -1.0;
   return 0.0;
  }

//+------------------------------------------------------------------+
//| DATA VALIDATION AND ACCESS                                       |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| ValidateSymbolV2: three-attempt validation with spread check.    |
//| Returns true immediately for the current chart symbol.           |
//| Spread above 10000 points indicates a data feed problem.         |
//+------------------------------------------------------------------+
bool ValidateSymbolV2(const string symbol)
  {
   if(symbol == NULL || symbol == "") return false;
   if(symbol == Symbol())             return true;

   for(int i = 0; i < 3; i++)
     {
      double bid   = SymbolInfoDouble(symbol, SYMBOL_BID);
      double ask   = SymbolInfoDouble(symbol, SYMBOL_ASK);
      double point = SymbolInfoDouble(symbol, SYMBOL_POINT);

      if(bid > 0 && ask > 0 && ask > bid && point > 0)
        {
         double spread = (ask - bid) / point;
         if(spread < 10000) return true;
        }
      Sleep(10);
     }
   return false;
  }

//+------------------------------------------------------------------+
//| Backward-compatible alias for ValidateSymbolV2.                  |
//+------------------------------------------------------------------+
bool ValidateSymbol(const string symbol) { return ValidateSymbolV2(symbol); }

//+------------------------------------------------------------------+
//| SafeCopyClose: validated data fetch with per-value zero check.   |
//| Caps request at MAX_SAMPLE_SIZE to prevent memory overrun.       |
//| Returns 0 if any copied bar has a non-positive close price.      |
//+------------------------------------------------------------------+
int SafeCopyClose(const string symbol, const int tf,
                  const int start, const int count,
                  double &out[])
  {
   if(!ValidateSymbolV2(symbol) || count <= 0) return 0;

   int safe_count = MathMin(count, MAX_SAMPLE_SIZE);
   ArraySetAsSeries(out, true);

   int copied = CopyClose(symbol, (ENUM_TIMEFRAMES)tf, start, safe_count, out);
   if(copied <= 0) return 0;

   for(int i = 0; i < copied; i++)
      if(out[i] <= 0) return 0;

   return copied;
  }

//+------------------------------------------------------------------+
//| Backward-compatible alias for SafeCopyClose.                     |
//+------------------------------------------------------------------+
int SafeCopyClosev2(const string symbol, const int tf,
                    const int start, const int count,
                    double &out[])
  {
   return SafeCopyClose(symbol, tf, start, count, out);
  }

//+------------------------------------------------------------------+
//| STATISTICAL PRIMITIVES                                           |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| mean_var: two-pass variance estimation.                          |
//| Two passes avoid numerical instability when the mean is large    |
//| relative to the variance, as is typical with price data.         |
//+------------------------------------------------------------------+
void mean_var(const double &arr[], const int n,
              double &mean, double &var)
  {
   mean = 0; var = 0;
   if(n <= 0) return;

   for(int i = 0; i < n; i++) mean += arr[i];
   mean /= n;

   for(int i = 0; i < n; i++)
      var += (arr[i] - mean) * (arr[i] - mean);
   var /= n;
  }

//+------------------------------------------------------------------+
//| robust_mean_var: trimmed mean and variance with higher moments.  |
//| Removes the most extreme 10% of values symmetrically before      |
//| computing mean, variance, skewness, and excess kurtosis.         |
//| Falls back to mean_var if the trimmed sample is too small.       |
//+------------------------------------------------------------------+
void robust_mean_var(const double &arr[], const int n,
                     double &mean, double &var,
                     double &skew, double &kurt)
  {
   mean = 0; var = 0; skew = 0; kurt = 0;
   if(n < 4) return;

   double sorted[];
   ArrayResize(sorted, n);
   ArrayCopy(sorted, arr, 0, 0, n);
   ArraySort(sorted);

   int trim = MathMax(1, (int)(n * 0.10));
   int used = n - 2 * trim;
   if(used < 4) { mean_var(arr, n, mean, var); return; }

   for(int i = trim; i < n - trim; i++) mean += sorted[i];
   mean /= used;

   for(int i = trim; i < n - trim; i++)
      var += MathPow(sorted[i] - mean, 2);
   var /= used;

   double sd = SafeSqrt(var);
   if(sd > DBL_MIN_POSITIVE)
     {
      for(int i = trim; i < n - trim; i++)
        {
         double z = (sorted[i] - mean) / sd;
         skew += MathPow(z, 3);
         kurt += MathPow(z, 4);
        }
      skew /= used;
      kurt  = kurt / used - 3.0;
     }
  }

//+------------------------------------------------------------------+
//| LinearRegressionSlope: ordinary least squares slope estimate.    |
//| Used by Hurst estimators (Part 2), fractal dimension (Part 3),   |
//| and multifractal spectrum (Part 3). Centralising here ensures    |
//| identical arithmetic across all three. SafeDivide guards against |
//| the degenerate case where all x values are identical.            |
//+------------------------------------------------------------------+
double LinearRegressionSlope(const double &x[],
                              const double &y[], int n)
  {
   if(n < 2) return 0.0;

   double sx = 0, sy = 0, sxy = 0, sxx = 0;
   for(int i = 0; i < n; i++)
     {
      sx  += x[i];
      sy  += y[i];
      sxy += x[i] * y[i];
      sxx += x[i] * x[i];
     }

   double denom = n * sxx - sx * sx;
   return SafeDivide(n * sxy - sx * sy, denom);
  }

//+------------------------------------------------------------------+
//| FFT IMPLEMENTATION                                               |
//| Cooley-Tukey radix-2 in-place. Input length must be power of 2.  |
//| Reused by: Part 3 (multifractal), Part 10 (Fourier seasonality), |
//|            Part 11 (fractional Gaussian noise generation).       |
//+------------------------------------------------------------------+
void fft(const double &real_in[], const double &imag_in[],
         double &real_out[], double &imag_out[],
         int n, bool inverse)
  {
   ArrayResize(real_out, n);
   ArrayResize(imag_out, n);
   ArrayCopy(real_out, real_in,  0, 0, n);
   ArrayCopy(imag_out, imag_in, 0, 0, n);

   //--- Bit-reversal permutation
   int j = 0;
   for(int i = 1; i < n; i++)
     {
      int bit = n >> 1;
      for(; (j & bit) != 0; bit >>= 1) j ^= bit;
      j ^= bit;
      if(i < j)
        {
         double tr = real_out[i];
         real_out[i] = real_out[j];
         real_out[j] = tr;
         double ti = imag_out[i];
         imag_out[i] = imag_out[j];
         imag_out[j] = ti;
        }
     }

   //--- Cooley-Tukey butterfly
   for(int len = 2; len <= n; len <<= 1)
     {
      double ang = 2.0 * MATH_PI / len * (inverse ? -1 : 1);
      double wr  = MathCos(ang);
      double wi  = MathSin(ang);

      for(int i = 0; i < n; i += len)
        {
         double cr = 1.0, ci = 0.0;
         for(int k = 0; k < len / 2; k++)
           {
            double ur  = real_out[i + k];
            double ui  = imag_out[i + k];
            double vr  = real_out[i + k + len / 2];
            double vi  = imag_out[i + k + len / 2];
            double pvr = vr * cr - vi * ci;
            double pvi = vr * ci + vi * cr;
            real_out[i + k]            = ur + pvr;
            imag_out[i + k]            = ui + pvi;
            real_out[i + k + len / 2]  = ur - pvr;
            imag_out[i + k + len / 2]  = ui - pvi;
            double ncr = cr * wr - ci * wi;
            ci = cr * wi + ci * wr;
            cr = ncr;
           }
        }
     }

   //--- Normalise output for inverse transform
   if(inverse)
      for(int i = 0; i < n; i++)
        {
         real_out[i] /= n;
         imag_out[i] /= n;
        }
  }

//+------------------------------------------------------------------+
//| PeriodSeconds: converts a timeframe constant to seconds.         |
//| Used by volatility scaling functions in Part 4.                  |
//+------------------------------------------------------------------+
int PeriodSeconds(int tf)
  {
   switch(tf)
     {
      case PERIOD_M1:  return 60;
      case PERIOD_M5:  return 300;
      case PERIOD_M15: return 900;
      case PERIOD_M30: return 1800;
      case PERIOD_H1:  return 3600;
      case PERIOD_H4:  return 14400;
      case PERIOD_D1:  return 86400;
      case PERIOD_W1:  return 604800;
      default:         return 86400;
     }
  }

//+---------------------------------------------------------------------+
//  PART 2 ADDITIONS — MicroStructure_Foundation.mqh                    |
//  Market Microstructure in MQL5: Measuring Long Memory (Part 2)       |
//                                                                      |
//                                                                      |
// Functions added in this part:                                        |
//  HurstExponentRS()      — classical rescaled-range estimator         |
//  AdvancedHurstExponent()— aggregated-variance and absolute-          |
//                           moments estimators (method-switched)       |
//  HurstExponentRobust()  — confidence-weighted blend of all three     |
//  PopulateHurstAnalysis()— fills RobustFractalAnalysis from one call  |
//                                                                      |
// Part 2 constants added:                                              |
//  HURST_MIN_BARS         — minimum post-reset bars before H is valid  |
//  HURST_CONF_THRESHOLD   — minimum per-estimator confidence to blend  |
//  HURST_RS_MAX_SCALES    — log-log regression point cap, R/S path     |
//  HURST_AV_MAX_SCALES    — log-log regression point cap, AggVar path  |
//  HURST_AM_MAX_SCALES    — log-log regression point cap, AbsMom path  |
//+---------------------------------------------------------------------+

// ==================================================================
//  Part 2 — Long Memory: Hurst Exponent Estimators
// ==================================================================

//--- Part 2 constants
#define HURST_MIN_BARS        40    // Minimum session bars before any estimator is trusted
#define HURST_CONF_THRESHOLD  0.1   // Estimators below this confidence are excluded from blend
#define HURST_RS_MAX_SCALES   10    // Maximum log-log regression points for R/S estimator
#define HURST_AV_MAX_SCALES   10    // Maximum log-log regression points for AggVar estimator
#define HURST_AM_MAX_SCALES   10    // Maximum log-log regression points for AbsMom estimator

//+------------------------------------------------------------------+
//| HurstExponentRS: classical rescaled-range (R/S) estimator.       |
//| Computes log(E[R/S]) ~ H * log(n) across geometrically spaced    |
//| window sizes. Each scale is averaged over non-overlapping        |
//| segments so that the estimate is less sensitive to a single      |
//| anomalous window.                                                |
//|                                                                  |
//| Parameters                                                       |
//|   returns[]   — log-return array, oldest first                   |
//|   n           — number of valid returns                          |
//|   confidence  — output: k / HURST_RS_MAX_SCALES where k = valid  |
//|                 regression points. Reflects data sufficiency.    |
//|                                                                  |
//| Returns H in [0.01, 0.99]. Returns 0.5 with confidence = 0.0     |
//| when fewer than 3 valid regression points are obtained.          |
//+------------------------------------------------------------------+
double HurstExponentRS(const double &returns[], int n, double &confidence)
  {
   confidence = 0.0;

   //--- Need at least one complete segment at minimum scale 8
   if(n < 30) return 0.5;

   int    m_max = (int)MathMax(10, n / 4);
   double log_n[10], log_rs[10];
   int    k     = 0;
   int    scale = 8;

   while(scale <= m_max && k < HURST_RS_MAX_SCALES)
     {
      int segs = n / scale;
      if(segs < 2) { scale *= 2; continue; }

      double rs_sum = 0.0;
      int    v      = 0;

      for(int seg = 0; seg < segs; seg++)
        {
         int    st  = seg * scale;
         double s1  = 0.0, s2 = 0.0;

         for(int j = 0; j < scale; j++)
           {
            s1 += returns[st + j];
            s2 += returns[st + j] * returns[st + j];
           }

         double mu  = s1 / scale;
         //--- Two-pass variance: stable against catastrophic cancellation
         double var = (s2 - scale * mu * mu) / (scale - 1);
         if(var <= 0) continue;

         double mx  = -1e100, mn = 1e100, cum = 0.0;
         for(int j = 0; j < scale; j++)
           {
            cum += returns[st + j] - mu;
            if(cum > mx) mx = cum;
            if(cum < mn) mn = cum;
           }

         double rng = mx - mn;
         if(rng > 0)
           {
            rs_sum += SafeLog(rng / SafeSqrt(var));
            v++;
           }
        }

      if(v > 0)
        {
         log_n [k] = SafeLog((double)scale);
         log_rs[k] = rs_sum / v;
         k++;
        }

      scale *= 2;
     }

   if(k < 3) return 0.5;

   double h = LinearRegressionSlope(log_n, log_rs, k);
   confidence = (double)k / HURST_RS_MAX_SCALES;
   return MathMax(0.01, MathMin(0.99, h));
  }

//+------------------------------------------------------------------+
//| AdvancedHurstExponent: aggregated variance (method = 0) or       |
//| absolute moments (method = 1) estimator.                         |
//|                                                                  |
//| AggVar:   variance of non-overlapping block averages scales as   |
//|           sigma^2(m) ~ m^(2H-2), so H = 1 + slope/2.             |
//|                                                                  |
//| AbsMom:   mean absolute block average scales as                  |
//|           E[|x_bar(m)|] ~ m^(H-1), so H = 1 + slope.             |
//|                                                                  |
//| Parameters                                                       |
//|   returns[]  — log-return array, oldest first                    |
//|   n          — number of valid returns                           |
//|   method     — 0 = AggVar, 1 = AbsMom                            |
//|   confidence — output: k / max_scales                            |
//|                                                                  |
//| Returns H in [0.01, 0.99]; 0.5 with confidence = 0.0 on failure. |
//+------------------------------------------------------------------+
double AdvancedHurstExponent(const double &returns[], int n,
                              int method, double &confidence)
  {
   confidence = 0.0;

   //--- AggVar needs at least 10 returns per aggregation level
   //--- AbsMom needs at least 15 per level — tighter requirement
   int min_per_seg = (method == 0) ? 10 : 15;
   if(n < min_per_seg * 2) return 0.5;

   int    max_scales = (method == 0) ? HURST_AV_MAX_SCALES
                                     : HURST_AM_MAX_SCALES;
   int    max_agg    = (int)MathMin(max_scales + 1, n / min_per_seg);

   double log_m[10], log_stat[10];
   int    k = 0;

   for(int m = 2; m <= max_agg && k < max_scales; m++)
     {
      int segs = n / m;
      if(segs < 2) continue;

      double stat = 0.0;

      for(int seg = 0; seg < segs; seg++)
        {
         int    st   = seg * m;
         double sum  = 0.0;
         for(int j = 0; j < m; j++) sum += returns[st + j];
         double xbar = sum / m;

         if(method == 0)
            stat += xbar * xbar;      // AggVar: squared block mean
         else
            stat += MathAbs(xbar);    // AbsMom: absolute block mean
        }

      stat /= segs;
      if(stat <= 0) continue;

      log_m   [k] = SafeLog((double)m);
      log_stat[k] = SafeLog(stat);
      k++;
     }

   if(k < 3) return 0.5;

   double beta = LinearRegressionSlope(log_m, log_stat, k);
   double h    = (method == 0) ? 1.0 + beta / 2.0   // AggVar
                               : 1.0 + beta;        // AbsMom
   confidence  = (double)k / max_scales;
   return MathMax(0.01, MathMin(0.99, h));
  }

//+------------------------------------------------------------------+
//| HurstExponentRobust: confidence-weighted blend of three          |
//| independent Hurst estimators.                                    |
//|                                                                  |
//| Three estimators are computed independently:                     |
//|   (1) Classical R/S (Hurst 1951)                                 |
//|   (2) Aggregated Variance                                        |
//|   (3) Absolute Moments                                           |
//|                                                                  |
//| Each estimator earns a confidence proportional to the number of  |
//| valid log-log regression points it produced relative to the      |
//| maximum possible. Estimators below HURST_CONF_THRESHOLD are      |
//| excluded from the blend. The final confidence is the sum of all  |
//| participating weights normalised to [0, 1].                      |
//|                                                                  |
//| Session reset requirement: the log-return array passed here      |
//| must contain only bars from the current trading session. Mixing  |
//| pre-open and post-open bars across the session boundary causes   |
//| a structural break in the return distribution that artificially  |
//| compresses H toward 0.5. Call SafeCopyClose with start = 0 and   |
//| count limited to bars elapsed since session open.                |
//|                                                                  |
//| Parameters                                                       |
//|   symbol     — instrument to analyse                             |
//|   tf         — timeframe                                         |
//|   period     — lookback bars (recommend 90 for M1 intraday)      |
//|   confidence — output: blended confidence weight (0 to 1)        |
//|                                                                  |
//| Returns H in [0.01, 0.99]. Returns 0.5 (random walk boundary)    |
//| with confidence = 0.0 when HURST_MIN_BARS is not satisfied.      |
//+------------------------------------------------------------------+
double HurstExponentRobust(const string symbol, const int tf,
                            const int period, double &confidence)
  {
   confidence = 0.0;

   //--- Validate inputs before touching the price feed
   if(!ValidateSymbolV2(symbol) || period < HURST_MIN_BARS)
      return 0.5;

   //--- Fetch close prices; SafeCopyClose validates each bar
   double close[];
   int    n = SafeCopyClose(symbol, tf, 0, period, close);
   if(n < HURST_MIN_BARS) return 0.5;

   //--- Build log-return array
   //--- Filter returns whose absolute value exceeds 0.1: these are
   //--- almost certainly data artefacts on futures/CFD M1 feeds.
   double returns[];
   ArrayResize(returns, n - 1);
   int valid = 0;

   for(int i = 0; i < n - 1; i++)
     {
      if(close[i] <= 0 || close[i + 1] <= 0) continue;
      double r = SafeLog(close[i]) - SafeLog(close[i + 1]);
      if(MathAbs(r) < 0.1)
         returns[valid++] = r;
     }

   if(valid < HURST_MIN_BARS)
     {
      confidence = 0.1;
      return 0.5;
     }

   ArrayResize(returns, valid);

   //--- Estimator 1: R/S
   double conf_rs, conf_av, conf_am;
   double h_rs = HurstExponentRS(returns, valid, conf_rs);

   //--- Estimator 2: Aggregated Variance (method = 0)
   double h_av = AdvancedHurstExponent(returns, valid, 0, conf_av);

   //--- Estimator 3: Absolute Moments (method = 1)
   double h_am = AdvancedHurstExponent(returns, valid, 1, conf_am);

   //--- Confidence-weighted blend
   //--- Exclude any estimator whose regression was too sparse to trust
   double weighted_sum = 0.0, total_weight = 0.0;

   if(conf_rs > HURST_CONF_THRESHOLD)
     { weighted_sum += h_rs * conf_rs; total_weight += conf_rs; }
   if(conf_av > HURST_CONF_THRESHOLD)
     { weighted_sum += h_av * conf_av; total_weight += conf_av; }
   if(conf_am > HURST_CONF_THRESHOLD)
     { weighted_sum += h_am * conf_am; total_weight += conf_am; }

   if(total_weight <= 0)
     {
      confidence = 0.1;
      return 0.5;
     }

   double h_final = weighted_sum / total_weight;
   confidence = MathMin(1.0, total_weight / 3.0);  // Normalise to [0, 1]

   h_final    = MathMax(0.01, MathMin(0.99, h_final));
   confidence = MathMax(0.0,  MathMin(1.0,  confidence));
   return h_final;
  }

//+------------------------------------------------------------------+
//| PopulateHurstAnalysis: convenience wrapper that calls            |
//| HurstExponentRobust and writes results into the shared           |
//| RobustFractalAnalysis struct.                                    |
//|                                                                  |
//| Fields written:                                                  |
//|   result.hurst_exponent         — blended H                      |
//|   result.hurst_confidence       — blended confidence             |
//|   result.volatility_persistence — 2 * |H - 0.5|, range [0, 1]    |
//|   result.computation_status     — 0 on success, 1 on failure     |
//|   result.validation_message     — diagnostic text on failure     |
//|                                                                  |
//| All other fields in result are left unchanged so that later      |
//| parts of the series can call their own Populate* functions on    |
//| the same struct without overwriting Hurst output.                |
//+------------------------------------------------------------------+
void PopulateHurstAnalysis(const string symbol, const int tf,
                            const int period,
                            RobustFractalAnalysis &result)
  {
   double confidence = 0.0;
   double h          = HurstExponentRobust(symbol, tf, period, confidence);

   //--- Write Hurst fields
   result.hurst_exponent         = h;
   result.hurst_confidence       = confidence;
   result.volatility_persistence = 2.0 * MathAbs(h - 0.5);

   //--- Diagnostics
   if(confidence < HURST_CONF_THRESHOLD)
     {
      result.computation_status  = 1;
      result.validation_message  =
         StringFormat("HurstExponentRobust: confidence %.3f below threshold "
                      "%.3f — insufficient session data for symbol %s tf %d",
                      confidence, HURST_CONF_THRESHOLD, symbol, tf);
     }
   else
     {
      result.computation_status = 0;
      result.validation_message = "OK";
     }
  }

// ==================================================================
//  End of Part 2 additions
// ==================================================================

#endif // MICROSTRUCTURE_FOUNDATION_MQH
//+------------------------------------------------------------------+
