//+------------------------------------------------------------------+ //| HurstProfile.mq5 | //| Market Microstructure in MQL5: Measuring Long Memory (Part 2) | //| | //| PURPOSE | //| Plots the confidence-weighted Hurst exponent H*(t) bar by bar. | //| Three horizontal reference lines mark the anti-persistence | //| zone (H < 0.4), random walk boundary (H = 0.5), and | //| persistence zone (H > 0.6). A separate Regime buffer encodes | //| the zone as +1 (persistent), -1 (anti-persistent), or 0. | //| | //| USAGE | //| Place this file in: | //| MQL5\Indicators\HurstProfile\ | //| Place the foundation header in: | //| MQL5\Indicators\HurstProfile\Includes\ | //| Attach to any M1 chart. Recommended period: 90 bars. | //| | //| OUTPUTS | //| Buffer 0 — H_buffer : blended Hurst exponent per bar | //| EMPTY_VALUE for bars 0..InpPeriod-1 | //| Buffer 1 — Conf_buffer : estimator confidence per bar (0 to 1) | //| EMPTY_VALUE for bars 0..InpPeriod-1 | //| Buffer 2 — Regime_buffer: +1 persistent / -1 anti-pers. / 0 | //| 0.0 when confidence < HURST_CONF_THRESHOLD | //| | //| FIX v2.03: Completely rewritten HurstFromClose. Now copies | //| the required close prices into a chronological window first, | //| eliminating all series-indexed out-of-range errors. | //| | //| DEPENDS ON : MicroStructure_Foundation.mqh (Parts 1 and 2) | //+------------------------------------------------------------------+ #property copyright "Max Brown" #property link "" #property version "2.03" #property indicator_separate_window #property indicator_buffers 3 #property indicator_plots 3 //--- Plot 0: Hurst exponent line #property indicator_label1 "H*(t)" #property indicator_type1 DRAW_LINE #property indicator_color1 clrDodgerBlue #property indicator_style1 STYLE_SOLID #property indicator_width1 2 //--- Plot 1: Confidence line (thinner, greyed) #property indicator_label2 "Confidence" #property indicator_type2 DRAW_LINE #property indicator_color2 clrSilver #property indicator_style2 STYLE_DOT #property indicator_width2 1 //--- Plot 2: Regime bar (histogram for quick regime read) #property indicator_label3 "Regime" #property indicator_type3 DRAW_HISTOGRAM #property indicator_color3 clrTomato #property indicator_style3 STYLE_SOLID #property indicator_width3 3 #include "Includes\MicroStructure_Foundation.mqh" //--- Indicator inputs input int InpPeriod = 90; // Lookback period in bars input double InpPersistThresh = 0.60; // H above this → persistent regime input double InpAntiPersThresh = 0.40; // H below this → anti-persistent regime input bool InpShowConfidence = true; // Plot confidence buffer //--- Indicator buffers double H_buffer[]; double Conf_buffer[]; double Regime_buffer[]; //--- Reference line objects (drawn once in OnInit) #define LINE_PERSIST "HP_Persist" // Object name for the persistence threshold line (H = 0.60) #define LINE_RANDOM "HP_Random" // Object name for the random walk boundary line (H = 0.50) #define LINE_ANTIPRS "HP_AntiPers" // Object name for the anti-persistence threshold line (H = 0.40) //+------------------------------------------------------------------+ //| OnInit: register buffers and draw reference lines. | //+------------------------------------------------------------------+ int OnInit() { //--- Register buffers in the order matching #property declarations SetIndexBuffer(0, H_buffer, INDICATOR_DATA); SetIndexBuffer(1, Conf_buffer, INDICATOR_DATA); SetIndexBuffer(2, Regime_buffer, INDICATOR_DATA); //--- Initialise all buffers to empty ArrayInitialize(H_buffer, EMPTY_VALUE); ArrayInitialize(Conf_buffer, EMPTY_VALUE); ArrayInitialize(Regime_buffer, EMPTY_VALUE); //--- Plot labels PlotIndexSetString(0, PLOT_LABEL, "H*(t)"); PlotIndexSetString(1, PLOT_LABEL, "Confidence"); PlotIndexSetString(2, PLOT_LABEL, "Regime"); //--- Minimum bars required before the first valid output PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, InpPeriod); PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, InpPeriod); PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, InpPeriod); //--- Hide confidence plot if user disabled it if(!InpShowConfidence) PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_NONE); //--- Draw horizontal reference lines DrawHLine(LINE_PERSIST, InpPersistThresh, clrForestGreen, STYLE_DASH, 1); DrawHLine(LINE_RANDOM, 0.5, clrDarkGray, STYLE_DOT, 1); DrawHLine(LINE_ANTIPRS, InpAntiPersThresh, clrTomato, STYLE_DASH, 1); //--- Indicator short name shown in the sub-window header IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("HurstProfile(%d)", InpPeriod)); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| OnDeinit: remove reference lines on indicator removal. | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectDelete(0, LINE_PERSIST); ObjectDelete(0, LINE_RANDOM); ObjectDelete(0, LINE_ANTIPRS); } //+------------------------------------------------------------------+ //| HurstFromClose: compute confidence-weighted H from a slice of | //| the close[] array passed into OnCalculate. | //| | //| This avoids CopyClose() entirely and safely handles both | //| series-indexed and chronological close[] arrays. | //| | //| FIX v2.03: Copies the required close prices into a local | //| chronological window first, eliminating all index-out-of-range | //| errors. The return calculation then proceeds sequentially from | //| index 1, so j-1 is always valid. | //| | //| Parameters | //| close[] — full close array from OnCalculate (series-indexed)| //| bar — index of the bar being calculated | //| period — lookback window in bars | //| confidence — output: blended confidence weight | //| | //| Returns H in [0.01, 0.99]; 0.5 with confidence = 0.0 on failure. | //+------------------------------------------------------------------+ double HurstFromClose(const double &close[], int bar, int period, double &confidence) { confidence = 0.0; int total_bars = ArraySize(close); //--- Valid bar index check if(bar < 0 || bar >= total_bars) return 0.5; //--- Calculate start index (series-indexed) int start = bar - period + 1; if(start < 1 || start >= total_bars) return 0.5; //--- Copy the required window into chronological order (oldest first) double close_window[]; ArrayResize(close_window, period); for(int idx = 0; idx < period; idx++) { close_window[idx] = close[start + idx]; } //--- Build log-return array from chronological window double returns[]; ArrayResize(returns, period - 1); int valid = 0; for(int j = 1; j < period; j++) // j starts at 1 → j-1 always safe { if(close_window[j] <= 0 || close_window[j - 1] <= 0) continue; double r = SafeLog(close_window[j]) - SafeLog(close_window[j - 1]); if(MathAbs(r) < 0.1) returns[valid++] = r; } if(valid < HURST_MIN_BARS) { confidence = 0.0; return 0.5; } ArrayResize(returns, valid); //--- Run all three estimators on the windowed returns double conf_rs, conf_av, conf_am; double h_rs = HurstExponentRS(returns, valid, conf_rs); double h_av = AdvancedHurstExponent(returns, valid, 0, conf_av); double h_am = AdvancedHurstExponent(returns, valid, 1, conf_am); //--- Confidence-weighted blend double ws = 0.0, tw = 0.0; if(conf_rs > HURST_CONF_THRESHOLD) { ws += h_rs * conf_rs; tw += conf_rs; } if(conf_av > HURST_CONF_THRESHOLD) { ws += h_av * conf_av; tw += conf_av; } if(conf_am > HURST_CONF_THRESHOLD) { ws += h_am * conf_am; tw += conf_am; } if(tw <= 0) { confidence = 0.0; return 0.5; } confidence = MathMin(1.0, tw / 3.0); return MathMax(0.01, MathMin(0.99, ws / tw)); } //+------------------------------------------------------------------+ //| OnCalculate: compute H*(t) for each bar using the close[] array. | //| | //| Buffer behaviour: | //| Bars 0 .. InpPeriod-1 : EMPTY_VALUE (not enough history) | //| Bar InpPeriod onward : calculated H, confidence, regime | //| | //| On the first call (prev_calculated == 0) all historical bars are | //| processed. On subsequent calls only the new bar is calculated. | //| This ensures each bar receives the data that existed at that bar,| //| not the current most-recent data. | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- Need at least InpPeriod + 1 bars before any output is possible if(rates_total < InpPeriod + 1) return 0; //--- On first call fill pre-period bars with EMPTY_VALUE explicitly if(prev_calculated == 0) { for(int i = 0; i < InpPeriod; i++) { H_buffer [i] = EMPTY_VALUE; Conf_buffer [i] = EMPTY_VALUE; Regime_buffer[i] = EMPTY_VALUE; } } //--- Start bar: on first call process all bars from InpPeriod onward; //--- on subsequent calls recalculate only the last bar int start = (prev_calculated == 0) ? InpPeriod : prev_calculated - 1; for(int i = start; i < rates_total; i++) { double conf = 0.0; double h = HurstFromClose(close, i, InpPeriod, conf); H_buffer [i] = h; Conf_buffer[i] = conf; //--- Regime: 0.0 when confidence is too low to trust (matches article) if(conf < HURST_CONF_THRESHOLD) Regime_buffer[i] = 0.0; else if(h >= InpPersistThresh) Regime_buffer[i] = 1.0; // Persistent — trend-following conditions else if(h <= InpAntiPersThresh) Regime_buffer[i] = -1.0; // Anti-persistent — mean-reversion conditions else Regime_buffer[i] = 0.0; // Transition zone — no directional bias } return rates_total; } //+------------------------------------------------------------------+ //| DrawHLine: creates a named horizontal line in the sub-window. | //| Safe to call repeatedly — deletes any existing object with the | //| same name before recreating it. | //+------------------------------------------------------------------+ void DrawHLine(const string name, const double price, const color clr, const int style, const int width) { ObjectDelete(0, name); ObjectCreate(0, name, OBJ_HLINE, 0, 0, price); ObjectSetInteger(0, name, OBJPROP_COLOR, clr); ObjectSetInteger(0, name, OBJPROP_STYLE, style); ObjectSetInteger(0, name, OBJPROP_WIDTH, width); ObjectSetInteger(0, name, OBJPROP_BACK, true); ObjectSetString(0, name, OBJPROP_TOOLTIP, name); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+