//+------------------------------------------------------------------+ //| mean.mqh | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #include"volatility.mqh" #ifdef __SLSQP__ #include"..\..\slsqp.mqh" #else #include #include #endif //+------------------------------------------------------------------+ //| Abstract base class for mean models | //+------------------------------------------------------------------+ class CArchModel: public CObject { protected: //--- Class Members ArchParameters m_model_spec; // Structure containing execution specifications vector m_params; // Optimized joint parameter values array vector m_fit_indices; // Index flags mapping timestamps during optimization vector m_backcast; // Initial pre-sample variance conditions vector vector m_fit_y; // In-sample target timeline observation variables matrix m_fit_x; // Independent exogenous variables design matrix matrix m_fit_regressors; // Cached design matrix mapping lags/regressors double m_scale; // Internal adjustment scaling multiplier ulong m_nobs; // Number of total parsed dataset elements ulong m_holdback; // Truncated initial sequence element offset ulong m_maxlags; // Maximum depth threshold across mean/volatility processes ulong m_num_params; // Total dedicated mean-model estimation parameter counts bool m_converged; // Boolean indicating successful optimizer exit metrics bool m_rescale; // Operational flag forcing internal auto-scaling routines bool m_constant; // Structural toggle introducing intersect intercepts bool m_rotated; // Indicator mapping variance targeting or HAR adjustments string m_name; // Custom textual identifier for model variants matrix m_regressors; // Dynamic matrix tracking user external series structures matrix m_lags; // Historical lag tracking layout metrics matrix m_var_bounds; // Linear constraint boundaries array for variance limits bool m_delete_vp; // Lifecycle memory management tracking for Volatility object bool m_delete_dist; // Lifecycle memory management tracking for Distribution object CDistribution *m_distribution; // Object pointer managing target error innovations CVolatilityProcess *m_vp; // Object pointer managing conditional variance steps bool m_initialized; // System lock indicating model verification validity //--- Calculate epsilon vector for numerical optimization step intervals vector _get_epsilon(vector &x, double s, ulong n, double epsilon = EMPTY_VALUE) { vector out; if(epsilon == EMPTY_VALUE) // Machine epsilon scaling method based on parameter magnitude bounds out = pow(DBL_EPSILON,1./s)*np::maximum(MathAbs(x),0.1); else { out = vector::Zeros(n); out.Fill(epsilon); } return out; } //--- Approximates the partial derivatives (Jacobian/Gradient vector) of the objective function matrix approx_fprime(vector &x,vector& sigma2, vector& backcast, matrix& varbounds, bool individual=false,bool centered = false, double epsilon = EMPTY_VALUE) { ulong n = x.Size(); vector f0 = _loglikelihood(x,sigma2,backcast,varbounds,individual); matrix grad = matrix::Zeros(n,f0.Size()); vector ei = vector::Zeros(n); vector eps,row; if(!centered) // Standard Forward Finite-Difference estimation { eps = _get_epsilon(x,2.,n,epsilon); for(ulong i = 0; i=1000.|| scale<0.1) && scale>0.0) { if(scale<1.0) rescale*=10.0; else rescale/=10.0; scale = ogscale*pow(rescale,2.0); } if(rescale == 1.0) return; if(!m_rescale) { Print(__FUNCTION__, " y is poorly scaled, which may affect convergence of the optimizer when estimating the model parameters.\n","Calculated scale is ",ogscale," . Parameter estimation works better when this value is between 1 and 1000. The recommended rescaling is ",rescale," * y "); return; } m_scale = rescale; } //--- Virtual hook: triggered natively when localized array tracking scales change virtual void _scalechanged(void) { return; } //--- Native placeholder computing the structural Coefficient of Determination ($R^2$) virtual double _r2(vector& params) { return EMPTY_VALUE; } //--- Virtual hook: calculates structural analytical start guesses bypassing ARCH processes virtual vector _fit_no_arch_normal_errors_params(void) { return vector::Zeros(0); } //--- Evaluates a baseline static Gaussian Log-Likelihood sequence double _static_gaussian_loglikelihood(vector &resids_) { ulong nobs_ = resids_.Size(); double sigma2 = resids_.Dot(resids_)/double(nobs_); double ll = -0.5 * double(nobs_) * log(2.0 * M_PI); ll -= 0.5*double(nobs_)*log(sigma2); ll -= 0.5*double(nobs_); return ll; } //--- Unpacks the unified parameter array into mean, volatility, and error component slices bool _parse_parameters(vector &x, vector& out[]) { ulong km = m_num_params; ulong kv = m_vp.numParams(); ArrayResize(out,3); // Slice array segments explicitly based on model configuration widths out[0] = (km)?np::sliceVector(x,0,long(km)):vector::Zeros(0); out[1] = (kv)?np::sliceVector(x,long(km),long(km+kv)):vector::Zeros(0); out[2] = ((km+kv) K$) if(nobs0); out.param_cov = param_cov; out.r2 = r2; out.resid = resids_; out.conditional_volatility = vol; out.loglikelihood = ll; out.nobs = e.Size(); out.fit_indices[0] = long(m_fit_indices[0]); out.fit_indices[1] = long(m_fit_indices[1]); return out; } //--- Should be called whenever the model is initialized or changed bool _init_model(void) { ResetLastError(); //--- 1. Process and Reformat Model Lag Specifications if(m_model_spec.mean_lags.Size()) { if(!_reformat_lags()) return false; // Determine the largest autoregressive lag index to protect pre-sample sequences m_maxlags = ulong(m_model_spec.mean_lags.Max()); // Enforce a minimum data holdback sample size to prevent index-out-of-bounds errors during lag construction if(!m_holdback) m_holdback = m_maxlags; else { if(m_holdback0)*(ulong(m_constant)+reg_lags.Cols()+m_model_spec.exog_data.Cols())); // Slot in the model intercept constant vector block at column index 0 if(m_constant && nobs_orig) m_regressors.Col(reg_constant,0); // Copy the constructed autoregressive series variables sequentially into adjacent channels if(reg_lags.Cols()) for(ulong i = 0; i(m_model_spec.observations.Size()-start)) { for(uint i = 0; i