﻿//+------------------------------------------------------------------+
//|  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;
     }
  }

#endif // MICROSTRUCTURE_FOUNDATION_MQH
//+------------------------------------------------------------------+
