//+------------------------------------------------------------------+ //| 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: ArchParameters m_model_spec; vector m_params,m_fit_indices,m_backcast; vector m_fit_y; matrix m_fit_x,m_fit_regressors; double m_scale; ulong m_nobs; ulong m_holdback; ulong m_maxlags; ulong m_num_params; bool m_converged,m_rescale,m_constant,m_rotated; string m_name; matrix m_regressors,m_lags,m_var_bounds; bool m_delete_vp,m_delete_dist; CDistribution *m_distribution; CVolatilityProcess *m_vp; bool m_initialized; vector _get_epsilon(vector &x, double s, ulong n, double epsilon = EMPTY_VALUE) { vector out; if(epsilon == EMPTY_VALUE) out = pow(DBL_EPSILON,1./s)*np::maximum(MathAbs(x),0.1); else { out = vector::Zeros(n); out.Fill(epsilon); } return out; } 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) { 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 void _scalechanged(void) { return; } virtual double _r2(vector& params) { return EMPTY_VALUE; } virtual vector _fit_no_arch_normal_errors_params(void) { return vector::Zeros(0); } 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; } bool _parse_parameters(vector &x, vector& out[]) { ulong km = m_num_params; ulong kv = m_vp.numParams(); ArrayResize(out,3); 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)0); out.param_cov = param_cov; out.r2 = r2; out.resid = resids_; out.loglikelihood = ll; out.fit_indices[0] = long(m_fit_indices[0]); out.fit_indices[1] = long(m_fit_indices[1]); return out; } bool _init_model(void) { ResetLastError(); if(m_model_spec.mean_lags.Size()) { if(!_reformat_lags()) return false; m_maxlags = ulong(m_model_spec.mean_lags.Max()); if(!m_holdback) m_holdback = m_maxlags; else { if(m_holdback0)*(ulong(m_constant)+reg_lags.Cols()+m_model_spec.exog_data.Cols())); if(m_constant && nobs_orig) m_regressors.Col(reg_constant,0); if(reg_lags.Cols()) for(ulong i = 0; i(m_model_spec.observations.Size()-start)) { for(uint i = 0; i-1 && start<=default_start)?start:default_start; if(start_index<(earliest-1)) { Print(__FUNCTION__," Due to backcasting and/or data availability start cannot be less "\ "than the index of the largest value in the right-hand-side "\ "variables used to fit the first observation."); return out; } vector allparams[]; if(!CArchModel::_parse_parameters(params.Size()?params:m_params,allparams)) { Print(__FUNCTION__, " failed to parse the parameters "); return out; } vector res = resids(allparams[0],vector::Zeros(0),matrix::Zeros(0,0)); vector bc = m_vp.backCast(res); vector ys = np::sliceVector(m_model_spec.observations,long(earliest)); matrix xs = np::sliceMatrixRows(m_regressors,long(earliest)); vector full_res = resids(allparams[0],ys,xs); matrix vb = m_vp.varianceBounds(full_res,2.); BootstrapRng Rng(m_distribution.distribution_type(),allparams[2],(int)m_distribution.randomState()); long vstart = MathMax(0,start_index - earliest); VarianceForecast vfcast = m_vp.forecast(allparams[1],full_res,bc,vb,Rng,seed,vstart,horizon,method,simulations); if(!vfcast.forecasts.Cols()) return out; matrix var_fcasts = vfcast.forecasts; if(start_indexulong(m_constant))?np::sliceVector(arp,long(m_constant)):vector::Zeros(0); matrix expected_x[]; if(!_reformat_forecast_x(ulong(start_index),horizon,x,expected_x)) { Print(__FUNCTION__, " reformating error "); return out; } matrix mean_fcast = _ar_forecast(m_model_spec.observations,horizon,start_index,constant,dynp,expected_x,exog_p); if(!mean_fcast.Cols()) return out; vector impulse = _ar_to_impulse(horizon,dynp); matrix longrun_var_fcasts = var_fcasts; matrix tempm; vector tempv,lrf; for(ulong i = 0; i0) { np::matrixCopy(a,cst[i]._one,rst,ren,STEP,ct,cen); np::vectorCopy(b,cst[i]._two,rst,ren); } } matrix bound[3]; bound[0] = bounds(); bound[1] = m_vp.bounds(res); bound[2] = m_distribution.bounds(res); matrix all_bounds(2,bound[0].Cols()+bound[1].Cols()+bound[2].Cols()); long cols = 0; for(uint i = 0; i0) { np::matrixCopy(a,cst[i]._one,rst,ren,STEP,ct,cen); np::vectorCopy(b,cst[i]._two,rst,ren); } } a.Col(b,ulong(total_params)); b = vector::Ones(a.Rows()); matrix bound[3]; bound[0] = bounds(); bound[1] = m_vp.bounds(res); bound[2] = m_distribution.bounds(res); matrix all_bounds(2,bound[0].Cols()+bound[1].Cols()+bound[2].Cols()); long cols = 0; for(uint i = 0; i0.0); } for(uint i = 0; i0.0) _ones*=scaling; CRowDouble bl = all_bounds.Row(0); CRowDouble bu = all_bounds.Row(1); CMatrixDouble _constraints = a; CMinNLC::MinNLCCreate(_initial_values.Size(),_initial_values,optim_state); CMinNLC::MinNLCSetCond(optim_state,tol,int(maxits)); CMinNLC::MinNLCSetScale(optim_state,_ones); CMinNLC::MinNLCSetSTPMax(optim_state,0.0); CMinNLC::MinNLCSetAlgoAUL(optim_state,10,0); CMinNLC::MinNLCSetBC(optim_state,bl,bu); CRowInt intones; intones.Resize(_constraints.Rows()); intones.Fill(1); CMinNLC::MinNLCSetLC(optim_state,_constraints,intones,intones.Size()); if(guardsmoothness) CMinNLC::MinNLCOptGuardSmoothness(optim_state,guardsmoothness); if(gradient_test_step>0.0) CMinNLC::MinNLCOptGuardGradient(optim_state,gradient_test_step); CMinNLCReport rep; CRowDouble solution; //--- set up other objective function arguments CObjectiveJacFVec fvec; fvec.SetParams(sigma2,bcast,vb,this); //--- CNDimensional_Rep f_rep; CObject object; //---optimize //CMinNLC::MinNLCOP(optim_state,fvec,f_rep,object); //--- check if(!CAp::Assert(GetPointer(fvec)!=NULL,"ALGLIB: error in 'minnlcoptimize()' (fvec is null)")) { m_converged = false; return out; } //--- cycle while(CMinNLC::MinNLCIteration(optim_state)) { if(optim_state.m_needfij) { fvec.Jac(optim_state.m_x,optim_state.m_fi,optim_state.m_j,object); continue; } if(optim_state.m_xupdated) { if(GetPointer(f_rep)!=NULL) f_rep.Rep(optim_state.m_x,optim_state.m_f,object); continue; } CAp::Assert(false,"ALGLIB: error in 'minnlcoptimize' (some derivatives were not provided?)"); break; } //---get results of optimization CMinNLC::MinNLCResults(optim_state,solution,rep); //---check for failed convergence if(rep.m_terminationtype<0) { Print(__FUNCTION__," failed convergence "); m_converged = false; return out; } else m_converged = true; //--- vector allparams[]; //--- m_params = solution.ToVector(); //--- loglikelihood for estimated parameters vector llh = _loglikelihood(m_params,sigma2,bcast,vb); //--- _parse_parameters(m_params,allparams); //--- res = resids(allparams[0],EMPTY_VECTOR,EMPTY_MATRIX); vector vol = vector::Zeros(res.Size()); //--- m_vp.computeVariance(allparams[1],res,vol,bcast,vb); //--- vol = sqrt(vol); //--- double r2 = _r2(allparams[0]); //--- long f_obs,l_obs; f_obs = (long)m_fit_indices[0]; l_obs = (long)m_fit_indices[1]; //--- vector res_final,vol_final; res_final = vol_final = m_model_spec.observations; res_final.Fill(AL_NaN); vol_final = res_final; //--- np::vectorCopy(res_final,res,f_obs,l_obs); np::vectorCopy(vol_final,vol,f_obs,l_obs); matrix pcov = compute_param_cov(m_params,bcast,(cov_type==COVAR_ROBUST)); return ArchModelResult(m_params,pcov,r2,res_final,vol_final,cov_type,-1.0*llh[0],f_obs,l_obs); } #endif bool eval_loglikelihood(vector& params, double &out_loglikelihood) { long first = 0; long last = -1; vector startingvalues = EMPTY_VECTOR; vector backcast = EMPTY_VECTOR; if(!is_initialized()) { Print(__FUNCTION__, " object instance was not successfully initialized "); return false; } if(!m_model_spec.observations.Size()) { Print(__FUNCTION__," Cannot estimate model without data."); return false; } vector offsets(3); offsets[0] = (double)m_num_params; offsets[1] = (double)m_vp.numParams(); offsets[2] = (double)m_distribution.numParams(); double total_params = offsets.Sum(); if(last<0 || last0) { np::matrixCopy(a,cst[i]._one,rst,ren,STEP,ct,cen); np::vectorCopy(b,cst[i]._two,rst,ren); } } a.Col(b,ulong(total_params)); b = vector::Ones(a.Rows()); matrix bound[3]; bound[0] = bounds(); bound[1] = m_vp.bounds(res); bound[2] = m_distribution.bounds(res); matrix all_bounds(2,bound[0].Cols()+bound[1].Cols()+bound[2].Cols()); long cols = 0; for(uint i = 0; i0.0); } for(uint i = 0; i