118 lines
4 KiB
MQL5
118 lines
4 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| ljung.mqh |
|
|
//| Copyright 2025, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
|
#property link "https://www.mql5.com"
|
|
#include"acf.mqh"
|
|
//+------------------------------------------------------------------+
|
|
//| Ljung-Box test of autocorrelation in residuals.
|
|
//+------------------------------------------------------------------+
|
|
matrix ljungboxtest(vector& x, ulong lags=0, ulong model_df=0, ulong period = 0, bool demean=false,bool boxpierce=false, bool autolag=false)
|
|
{
|
|
if(period==1)
|
|
{
|
|
Print(__FUNCTION__, " period must be >= 2");
|
|
return matrix::Zeros(0,0);
|
|
}
|
|
|
|
ulong nobs = x.Size();
|
|
vector laggs;
|
|
if(autolag)
|
|
{
|
|
ulong maxlag = nobs-1;
|
|
ACFResult sacf = acf(x,maxlag,0.05,false,demean,false);
|
|
if(!sacf.acf.Size())
|
|
return matrix::Zeros(0,0);
|
|
vector ssacf(maxlag);
|
|
vector r(maxlag);
|
|
for(ulong i = 1;i<(maxlag+1);ssacf[i-1]=sacf.acf[i],++i);
|
|
for(ulong i = 0;i<(maxlag);r[i] = double(i+1),++i);
|
|
vector qsacf;
|
|
if(!boxpierce)
|
|
{
|
|
vector n = pow(ssacf,2.)/(double(nobs)-r);
|
|
qsacf = nobs*(nobs+2.)*n.CumSum();
|
|
}
|
|
else
|
|
{
|
|
vector n = pow(ssacf,2.);
|
|
qsacf = nobs*n.CumSum();
|
|
}
|
|
double q = 2.4;
|
|
double threshold = sqrt(q*log(nobs));
|
|
double threshold_metric = MathAbs(sacf.acf).Max()*sqrt(nobs);
|
|
r = vector::Zeros(nobs-1);
|
|
for(ulong i = 0;i<r.Size(); r[i] = double(i+1),++i);
|
|
if(threshold_metric<=threshold)
|
|
qsacf = qsacf - (r*log(nobs));
|
|
else
|
|
qsacf = qsacf - (2.*r);
|
|
ulong m = qsacf.ArgMax();
|
|
laggs=vector::Zeros(m);
|
|
for(ulong i = 0;i<laggs.Size();laggs[i] = double(i+1),++i);
|
|
}
|
|
else
|
|
{
|
|
if(period)
|
|
{
|
|
ulong m = MathMin(nobs/5,2*period);
|
|
laggs = vector::Zeros(m);
|
|
for(ulong i = 0;i<laggs.Size();laggs[i] = double(i+1),++i);
|
|
}
|
|
else
|
|
{
|
|
if(!lags)
|
|
{
|
|
ulong m = MathMin(nobs/5,10);
|
|
laggs = vector::Zeros(m);
|
|
for(ulong i = 0;i<laggs.Size();laggs[i] = double(i+1),++i);
|
|
}
|
|
else
|
|
{
|
|
laggs = vector::Zeros(lags);
|
|
for(ulong i = 0;i<laggs.Size();laggs[i] = double(i+1),++i);
|
|
}
|
|
}
|
|
}
|
|
ulong maxlag = (ulong)laggs.Max();
|
|
ACFResult sacf = acf(x,maxlag,0.05,false,demean,false);
|
|
if(!sacf.acf.Size())
|
|
return matrix::Zeros(0,0);
|
|
vector ssacf(maxlag);
|
|
vector r(maxlag);
|
|
for(ulong i = 1;i<(maxlag+1);ssacf[i-1]=sacf.acf[i],++i);
|
|
for(ulong i = 0;i<(maxlag);r[i] = double(i+1),++i);
|
|
vector ssacf2 = pow(ssacf,2.)/(nobs-r);
|
|
vector temp = ssacf2.CumSum();
|
|
vector qljungbox(temp.Size());
|
|
for(ulong i = 0; i<laggs.Size(); qljungbox[i] = nobs*(nobs+2)*temp[ulong(laggs[i])-1], ++i);
|
|
vector adj_lags = laggs - double(model_df);
|
|
vector pval = qljungbox;
|
|
pval.Fill(double("nan"));
|
|
int e = 0;
|
|
for(ulong i = 0; i<adj_lags.Size(); ++i)
|
|
if(adj_lags[i]>0)
|
|
pval[i] = 1. - MathCumulativeDistributionChiSquare(qljungbox[i],adj_lags[i],e);
|
|
matrix out;
|
|
out = matrix::Zeros(adj_lags.Size(),2);
|
|
out.Col(qljungbox,0);
|
|
out.Col(pval,1);
|
|
if(boxpierce)
|
|
{
|
|
out.Resize(out.Rows(),4);
|
|
temp = pow(ssacf,2.0);
|
|
temp = temp.CumSum();
|
|
vector qboxpierce = temp;
|
|
for(ulong i = 0; i<laggs.Size(); qboxpierce[i] = nobs*temp[ulong(laggs[i])-1], ++i);
|
|
for(ulong i = 0; i<adj_lags.Size(); ++i)
|
|
if(adj_lags[i]>0)
|
|
pval[i] = 1. - MathCumulativeDistributionChiSquare(qboxpierce[i],adj_lags[i],e);
|
|
|
|
out.Col(qboxpierce,2);
|
|
out.Col(pval,3);
|
|
}
|
|
return out;
|
|
}
|
|
//+------------------------------------------------------------------+
|