Article-23677-EGARCH-MQL5-V.../AsymmetricVolatilityRegimeOscillator.mq5
2026-07-24 23:03:06 +02:00

263 lines
No EOL
11 KiB
MQL5

//+------------------------------------------------------------------+
//| AsymmetricVolatilityRegimeOscillator.mq5 |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
// Preprocessor directives and indicator properties
#define __SLSQP__ // Enable Sequential Least Squares Programming optimization algorithm
#property indicator_separate_window // Render indicator in an independent subwindow
#include "Arch\Univariate\mean.mqh" // Include statistical mean model header dependencies
// Define buffer and plotting counts
#property indicator_buffers 3
#property indicator_plots 3
#property indicator_level1 0.0 // Zero crossover baseline
// --- Plot 1: Fast Mean Residual Window
#property indicator_label1 "Fast"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrRed
#property indicator_style1 STYLE_SOLID
#property indicator_width1 1
// --- Plot 2: Slow Mean Residual Window
#property indicator_label2 "Slow"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrGreen
#property indicator_style2 STYLE_SOLID
#property indicator_width2 1
// --- Plot 3: Asymmetric Volatility Regime Oscillator (AVRO = Fast - Slow)
#property indicator_label3 "Avro"
#property indicator_type3 DRAW_LINE
#property indicator_color3 clrMediumBlue
#property indicator_style3 STYLE_SOLID
#property indicator_width3 1
//+------------------------------------------------------------------+
//| INPUT PARAMETERS |
//+------------------------------------------------------------------+
input int BarsToDraw = 500; // Number of historical bars to calculate and render
input ulong FastWindowLen = 20; // Fast window length for standardized residual mean
input ulong SlowWindowLen = 40; // Slow window length for standardized residual mean
input ulong HistoryLen = 200; // Rolling sample size (lookback period) for EGARCH fitting
input double ScaleFactor = 1000.; // Multiplier applied to log returns (improves numerical optimizer stability)
input ENUM_MEAN_MODEL MeanModel = MEAN_CONSTANT; // Model type for time-series conditional mean
input bool MeanConstant = true; // Include intercept constant in mean specification
input string MeanLags = ""; // Comma-separated list of AR lag terms (e.g. "1,2")
ENUM_VOLATILITY_MODEL VolatilityModel = VOL_EGARCH; // Volatility process specified as Exponential GARCH
input ulong _P_ = 1; // GARCH order (lagged conditional log-variances)
input ulong _O_ = 1; // Asymmetry order (leverage/asymmetric magnitude terms)
input ulong _Q_ = 1; // ARCH order (lagged innovations)
input int Volatility_Seed = 0; // Seed for volatility model random generator initialization
input ENUM_DISTRIBUTION_MODEL ErrorDistribution = DIST_NORMAL; // Innovation error distribution assumption
input int Distribution_Seed = 0; // Seed for distribution model random generator initialization
//+------------------------------------------------------------------+
//| GLOBAL INDICATOR BUFFERS AND STATE VARIABLES |
//+------------------------------------------------------------------+
// Dynamic arrays mapped to indicator plot buffers
double FastBuffer[];
double SlowBuffer[];
double AvroBuffer[];
// Computational vectors and model pointers
vector returns = vector::Zeros(HistoryLen); // Rolling logarithmic returns vector
vector stdresid = returns; // Model-standardized residuals vector
vector fastwindow = vector::Zeros(FastWindowLen); // Sliced fast window vector for local mean calculation
vector slowwindow = vector::Zeros(SlowWindowLen); // Sliced slow window vector for local mean calculation
ArchParameters model_spec; // Struct holding model specifications and hyperparameters
HARX* full_model; // Polymorphic pointer to mean model instance
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Validate lookback window length
if(HistoryLen < 30)
{
Print("Invalid input value for HistoryLen");
return INIT_FAILED;
}
// Validate window hierarchy: Fast window must be strictly smaller than Slow window
if(FastWindowLen >= SlowWindowLen)
{
Print("FastWindowLen should Not be more than or equal to SlowWindowLen");
return INIT_FAILED;
}
// Validate window bounds against total history lookback length
if(MathMax(FastWindowLen, SlowWindowLen) > HistoryLen)
{
Print("Neither FastWindowLen nor SlowWindowLen should be more than HistoryLen");
return INIT_FAILED;
}
// --- Map dynamic dynamic arrays to indicator buffers
SetIndexBuffer(0, FastBuffer, INDICATOR_DATA);
SetIndexBuffer(1, SlowBuffer, INDICATOR_DATA);
SetIndexBuffer(2, AvroBuffer, INDICATOR_DATA);
// --- Set plot drawing offset to suppress initial uncalculated bars
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, BarsToDraw);
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, BarsToDraw);
PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, BarsToDraw);
// --- Initialize buffers with empty markers
ArrayInitialize(FastBuffer, EMPTY_VALUE);
ArrayInitialize(SlowBuffer, EMPTY_VALUE);
// --- Configure model specifications
model_spec.mean_model_type = MeanModel;
// Parse comma-separated mean lag configuration string if provided
if(StringLen(MeanLags))
{
string lag_info[];
int nlags = StringSplit(MeanLags, StringGetCharacter(",", 0), lag_info);
if(nlags > 0)
{
for(uint i = 0; i < uint(nlags); ++i)
{
if(StringLen(lag_info[i]) > 0)
{
if(model_spec.mean_lags.Resize(model_spec.mean_lags.Size() + 1, 3))
model_spec.mean_lags[model_spec.mean_lags.Size() - 1] = StringToDouble(lag_info[i]);
else
{
Print(" error ", GetLastError());
return INIT_FAILED;
}
}
}
}
}
// Set EGARCH structural dynamics and seeds
model_spec.vol_rng_seed = Volatility_Seed;
model_spec.garch_o = _O_;
model_spec.garch_p = _P_;
model_spec.garch_q = _Q_;
model_spec.dist_type = ErrorDistribution;
model_spec.dist_rng_seed = Distribution_Seed;
// Factory instantiation of the requested mean model class
switch(MeanModel)
{
case MEAN_CONSTANT:
full_model = new ConstantMean();
break;
case MEAN_ZERO:
full_model = new ZeroMean();
break;
case MEAN_AR:
full_model = new AR();
break;
default:
full_model = new ConstantMean();
break;
}
// Ensure memory allocation for mean model succeeded
if(CheckPointer(full_model) == POINTER_INVALID)
return INIT_FAILED;
model_spec.vol_model_type = VolatilityModel;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// Free dynamic memory allocated for the mean model instance
if(CheckPointer(full_model) == POINTER_DYNAMIC)
delete full_model;
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int32_t rates_total,
const int32_t 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 int32_t &spread[])
{
// Verify total bars available meet required history length
if(rates_total < int32_t(HistoryLen))
{
Print("Not enough bars for indicator calculation");
return -1;
}
// Determine starting index for incremental bar calculation
int32_t limit = 0;
if(prev_calculated <= 0)
limit = rates_total - int32_t(fabs(BarsToDraw)); // First run: calculate specified historical depth
else
limit = prev_calculated - 1; // Subsequent runs: update only latest bar(s)
// Main calculation loop iterating through historical price bars
for(int32_t shift = limit; shift < rates_total; ++shift)
{
int32_t from = (shift - int32_t(HistoryLen)) + 1;
// Calculate logarithmic returns over the rolling lookback window
for(int32_t i = from, k = 0; k < int32_t(HistoryLen); ++i, ++k)
returns[k] = log(close[i] / close[i - 1]);
// Scale returns to assist optimizer convergence
returns *= fabs(ScaleFactor);
// Load current sample window into specification structure
model_spec.observations = returns;
// Re-initialize model state with new sample window
if(!full_model.initialize(model_spec))
{
Print(" initialization error ");
return 0;
}
// Fit EGARCH model via maximum likelihood optimization
ArchModelResult result = full_model.fit();
if(!result.conditional_volatility.Size())
{
Print(" model fit error ");
return 0;
}
// Extract full vector of standardized residuals
stdresid = result.std_resid();
// Populate fast and slow evaluation windows from the tail of standardized residuals
for(ulong i = 0; i < FastWindowLen; ++i)
fastwindow[i] = stdresid[HistoryLen - FastWindowLen + i];
for(ulong i = 0; i < SlowWindowLen; ++i)
slowwindow[i] = stdresid[HistoryLen - SlowWindowLen + i];
// Compute local mean values and derive the oscillator buffer (AVRO)
FastBuffer[shift] = fastwindow.Mean();
SlowBuffer[shift] = slowwindow.Mean();
AvroBuffer[shift] = FastBuffer[shift] - SlowBuffer[shift];
}
// Return calculated count to optimize subsequent iteration calls
return(rates_total);
}
//+------------------------------------------------------------------+