291 lines
13 KiB
MQL5
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);
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//+------------------------------------------------------------------+
|