Article-22598-Market-Micros.../HurstProfile.mq5
2026-06-05 15:00:50 +00:00

291 lines
13 KiB
MQL5

//+------------------------------------------------------------------+
//| 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);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+