//+------------------------------------------------------------------+ //| volatility.mqh | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" #include "recursions.mqh" #include "distribution.mqh" //--- //+------------------------------------------------------------------+ //| Container for variance forecasts | //+------------------------------------------------------------------+ struct VarianceForecast { matrix forecasts; matrix forecastpaths[]; matrix shocks[]; VarianceForecast(void) { forecasts = matrix::Zeros(0,0); } VarianceForecast(matrix &_forecasts, matrix &_forecastpaths[],matrix &_shocks[]) { forecasts = _forecasts; ArrayResize(forecastpaths,_forecastpaths.Size()); ArrayResize(shocks,_shocks.Size()); for(uint i = 0 ; i10) m_bootstrap_obs = bts; else return; } string name(void) { return m_name; } bool is_initialized(void) { return m_initialized; } virtual long start(void) { return m_start; } virtual long stop(void) { return m_stop; } virtual void start(long _start) { m_start = _start; } virtual void stop(long _stop) { m_stop = _stop; } virtual ulong numParams(void) { return m_num_params; } virtual bool upDateable(void) { return m_updateable; } virtual bool closeForm(void) { return m_closedform; } virtual VolatilityUpdater* volUpdater(void) { return m_volupdater; } virtual double upDate(ulong _index, vector& parameters, vector& resids, vector& sigma2, vector& backcast, vector& varbounds) { return EMPTY_VALUE; } virtual matrix varianceBounds(vector& resids, double power = 2.) { ulong nobs = resids.Size(); ulong tau = MathMin(75,nobs); vector w = vector::Zeros(tau); for(ulong i = 0; i= 1"); return out; } if(!_check_forecasting_method(ForecastingMethod,horizon)) { Print(__FUNCTION__, " The specified forecasting method is not supported for this specific model "); return out; } if(!_start) _start = resids.Size()-1; switch(ForecastingMethod) { case FORECAST_ANALYTIC: out = _analyticforecast(parameters,resids,_backcast,var_bounds,_start,horizon); break; case FORECAST_SIMULATION: if(rng.is_initialized()) out = _simulationforecast(parameters,resids,_backcast,var_bounds, _start,horizon,simulations,rng); else Print(__FUNCTION__, " ERROR BOOTSTRAP OBJECT IS NULL "); break; case FORECAST_BOOTSTRAP: if(_start<10) Print(__FUNCTION__," Bootstrap forecasting requires at least 10 initial"); else if(double(horizon/_start)>0.2) Print(__FUNCTION__, " observations, and the ratio of horizon-to-start < 20%. "); else out = _bootstrapforecast(parameters,resids,_backcast,var_bounds,_start,horizon,simulations,seed); break; } return out; } virtual matrix simulate(vector& parameters, ulong _nobs, BootstrapRng &rng, ulong burn = 500, double initial_value = NULL) { return matrix::Zeros(0,0); } virtual string parameterNames(void) { return NULL; } }; //+------------------------------------------------------------------+ //| Constant volatility process | //+------------------------------------------------------------------+ class CConstantVariance: public CVolatilityProcess { protected: virtual bool _check_forecasting_method(ENUM_FORECAST_METHOD method, ulong horizon) override { return true; } virtual VarianceForecast _analyticforecast(vector& parameters, vector &resids, vector &backcast, matrix &varbounds, long _start, ulong horizon) override { VarianceForecast out; long t = (long)resids.Size(); matrix forecasts = matrix::Zeros(t-_start,horizon); forecasts.Fill(parameters[0]); out.forecasts = forecasts; return out; } virtual VarianceForecast _simulationforecast(vector& parameters, vector &resids, vector &backcast, matrix &varbounds, long _start, ulong horizon, ulong simulations, BootstrapRng &rng) { VarianceForecast out; long t = (long)resids.Size(); matrix forecasts = matrix::Zeros(t-_start,horizon); ArrayResize(out.forecastpaths,int(t-_start)); ArrayResize(out.shocks,int(t-_start)); forecasts.Fill(parameters[0]); for(long i = 0; i 1 when power != 2"); return false; } return true; } virtual VarianceForecast _analyticforecast(vector& parameters, vector &resids, vector &backcast, matrix &varbounds, long _start, ulong horizon) override { VarianceForecast out; ulong t = resids.Size(); vector sigma2; matrix forecasts; _onestepforecast(parameters,resids,backcast,varbounds,_start,horizon,sigma2,forecasts); if(horizon == 1) { out.forecasts = forecasts; return out; } double omega = parameters[0]; vector alpha = np::sliceVector(parameters,1,long(m_p+1)); vector gamma = np::sliceVector(parameters,long(m_p+1),long(m_p+m_o+1)); vector beta = np::sliceVector(parameters,long(m_p+m_o+1)); ulong m = MathMax(MathMax(m_p,m_o),m_q); vector _resids,_asym_resids,_sigma2,temp; _resids = _asym_resids = _sigma2 = vector::Zeros(m+horizon); for(ulong i = ulong(_start); i=0) { temp = np::sliceVector(resids,long(i-m+1),long(i+1)); np::vectorCopy(_resids,temp,0,long(m)); temp = np::sliceVector(_resids,0,long(m)); temp*=np::whereVectorIsLt(temp,0.0); np::vectorCopy(_asym_resids,temp,0,long(m)); temp = np::sliceVector(sigma2,long(i-m+1),long(i+1)); np::vectorCopy(_sigma2,temp,0,long(m)); } else { np::fillVector(_resids,sqrt(backcast[0]),0,long(m-i-1)); temp = np::sliceVector(resids,0,long(i+1)); np::vectorCopy(_resids,temp,long(m-i-1),long(m)); _asym_resids = _resids*(np::whereVectorIsLt(_resids,0.0)); np::fillVector(_asym_resids,sqrt(0.5*backcast[0]),0,long(m-i-1)); np::fillVector(_sigma2,backcast[0],0,long(m)); temp = np::sliceVector(sigma2,0,long(i+1)); np::vectorCopy(_sigma2,temp,long(m-i-10), long(m)); } for(ulong h = 0; h1)?np::sliceVector(parameters,1,long(m_p+1)):EMPTY_VECTOR; vector gamma = ((m_p+1)<(m_p+m_o+1))?np::sliceVector(parameters,long(m_p+1),long(m_p+m_o+1)):EMPTY_VECTOR; vector beta = (parameters.Size()>(m_p+m_o+1))?np::sliceVector(parameters,long(m_p+m_o+1)):EMPTY_VECTOR; matrix shock = scaled_forecast_paths; vector temp; for(ulong h = 0; h0) { np::fillVector(sv,row[0]/double(m_p),long(1),long(1+m_p)); row[2] -= row[0]; } if(m_o>0) { np::fillVector(sv,row[1]/double(m_o),long(1+m_p),long(1+m_p+m_o)); row[2] -= row[1]/2.; } if(m_q>0) np::fillVector(sv,row[2]/double(m_q),long(1+m_p+m_o),long(1+m_p+m_o+m_q)); svs[i]=sv; llfs[i] = _gaussianloglikelihood(sv,resids,bc,vb); } ulong loc = llfs.ArgMax(); return svs[loc]; } virtual Constraints constraints(void) override { Constraints out; ulong k_arch = m_p+m_o+m_q; out._one = matrix::Zeros(k_arch+2,k_arch+1); for(ulong i = 0; i<(k_arch+1); ++i) out._one[i,i] = 1.; for(ulong i = 0; i<(m_o); ++i) if(i0) initial_value = parameters[0]/(1.-persistence); else { Print(__FUNCTION__, " InitialValueWarning "); initial_value = parameters[0]; } } vector sigma2,data,fsigma,fdata; sigma2 = data = fsigma = fdata = vector::Zeros(_nobs+burn); ulong max_lag = MathMax(MathMax(m_p,m_o),m_q); np::fillVector(fsigma,initial_value,0,long(max_lag)); double dv = pow(initial_value,2./m_power); np::fillVector(sigma2,dv,0,long(max_lag)); for(ulong i = 0; i