The R code

This files allows to reproduce the Monte Carlo results as in Table 3 of Sbrana and Silvestrini (IJF, 2024).

The code generate two sets of processes (DGP1 and DGP2). You can choose which one to select.

If you have any question please email to :

library("forecast")
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
library("Mcomp")
library("M4comp2018")

THETAstructural<-function(y,steps){
  
  likelihood<-function(parameters){q=abs(parameters[1]);co=abs(parameters[2])
  state<-p<-rep(0,length(y)+1)
  state[1]=y[1]
  p[1]=10000
  k<-inn<-rep(0,length(y))
  k[1]=p[1]/(p[1]+1);
  sigmae=0
  
  p[2]=p[1]-p[1]*k[1]+q;
  state[2]=co+state[1]+k[1]*inn[1]
  
  for(t in (2):length(y)){
    
    k[t]=p[t]/(p[t]+1);
    inn[t]=y[t]-state[t]
    sigmae=sigmae+inn[t]^2/(p[t]+1)
    p[t+1]=p[t]-p[t]*k[t]+q;
    state[t+1]=co+state[t]+k[t]*inn[t]
  }
  
  sum(log(p+1))+length(y)*log(sigmae/length(y))}
  
  results<-optim(c(.01,.001),likelihood)
  
  q=abs(results[[1]][1]);co=abs(results[[1]][2])
  state<-p<-rep(0,length(y)+1)
  state[1]=y[1]
  p[1]=10000
  k<-inn<-rep(0,length(y))
  k[1]=p[1]/(p[1]+1);
  sigmae=0
  
  p[2]=p[1]-p[1]*k[1]+q;
  state[2]=co+state[1]+k[1]*inn[1]
  
  for(t in (2):length(y)){
    
    k[t]=p[t]/(p[t]+1);
    inn[t]=y[t]-state[t]
    sigmae=sigmae+inn[t]^2/(p[t]+1)
    p[t+1]=p[t]-p[t]*k[t]+q;
    state[t+1]=co+state[t]+k[t]*inn[t]
  }
  
  PointF<-rep(0,steps);PointF[1]=state[length(y)+1];
  for( t in 2:steps){PointF[t]=co*(t-1)+state[length(y)+1]}
  
  
  H<-sigmae/(length(y)-1);P<-tail(p,1)*H;Eta<-q*H;
  Inter<-c();Inter[1]=P;
  for(j in 2:steps){Inter[j]=Inter[j-1]+Eta};
  Interv<-c();
  for(j in 1:steps){Interv[j]=Inter[j]+H};
  prob1=.85;prob2=.95; 
  lower1<-PointF-qnorm((1+prob1)/2)*sqrt(Interv);
  #for(d in 1:steps){if(lower1[d]<0){lower1[d]=0}};
  upper1<-PointF+qnorm((1+prob1)/2)*sqrt(Interv);
  lower2<-PointF-qnorm((1+prob2)/2)*sqrt(Interv);
  #for(d in 1:steps){if(lower2[d]<0){lower2[d]=0}};
  upper2<-PointF+qnorm((1+prob2)/2)*sqrt(Interv);
  
  list(mean=PointF,lower=cbind(lower1,lower2),upper=cbind(upper1,upper2))
  
}


AUTO<-function(y,steps){forecast::forecast(auto.arima(y),h=steps,level = c(.85,.95))}
THETA<-function(y,steps){forecast::forecast(thetaf(y,h=steps,level = c(.85,.95)))}
ETS<-function(y,steps){forecast::forecast(ets(y),h=steps)}
competitors<-list(THETAstructural,ETS,AUTO)

####################### data Monte Carlo ##################################################

steps<-6
freq<-frequ<-1
times=10000

set.seed(1)

DGP1=list()
for(u in 1:times){
  n=60+steps
  eta=runif(1,.1,.5)*rnorm(n)
  mu=y=trend=c()
  alpha=runif(1,.1,5);beta=runif(1,.01,.1)
  ar=runif(1,.7,.99)
  delta=runif(1,-.7,-.1)
  trend[1]=alpha+beta
  mu[1]=eta[1];y[1]=trend[1]+mu[1]
  for(t in 2:n){mu[t]=ar*mu[t-1]+eta[t]+delta*eta[t-1]
  trend[t]=alpha+beta*t
  y[t]=trend[t]+mu[t] 
  }
  DGP1[[u]]=y
}


DGP2=list()
for(u in 1:times){
  n=60+steps
  eta=runif(1,.1,.5)*rnorm(n)
  mu=y=c()
  ar=runif(1,.1,.7)
  delta=runif(1,-.7,0)
  co=runif(1,.01,.1);
  trend=somma=c();trend[1]=co
  mu[1]=eta[1];y[1]=co;somma[1]=mu[1]
  for(t in 2:n){mu[t]=ar*mu[t-1]+eta[t]+delta*eta[t-1]
  trend[t]=trend[t-1]+co
  somma[t]=mu[t]+somma[t-1]
  y[t]=somma[t]+trend[t]
  
  }
  DGP2[[u]]=y
}


######################## Select the dataset to use! ##############################
DATA=DGP1
goods=length(DATA)

#############################################################################################

rmsse<-function(y,yf,steps){OutSample=tail(y,length(yf));sqrt(mean((OutSample[1:steps]-yf[1:steps])^2)/mean(diff(head(y,(length(y)-length(yf))),frequ)^2))}
mase<-function(y,yf,steps){OutSample=tail(y,length(yf));(mean(abs(OutSample[1:steps]-yf[1:steps])))/mean(abs(diff(head(y,(length(y)-length(yf))),frequ)))}


mis<-function(yout,lower,upper,prob,yin){
  a<-b<-d<-rep(0,length(yout));
  for(t in 1:length(yout)){
    if(yout[t]<lower[t]){a[t]=(2/(1-prob))*(lower[t]-yout[t])}
    if(yout[t]>upper[t]){b[t]=(2/(1-prob))*(yout[t]-upper[t])}
    d[t]=upper[t]-lower[t]}
  mean(a+b+d)/mean(abs(diff(yin,freq)))
}

par(mfrow=c(1,3))

resMASE<-resRMSSE<-resSMAPE<-matrix(NA,steps,length(competitors))
resCOVER85<-resCOVER95<-resMIS85<-resMIS95<-matrix(NA,1,length(competitors))
results1MASE<-resultsMASE<-results1RMSSE<-resultsRMSSE<-results1SMAPE<-resultsSMAPE<-forec<-list()
resultsCOVER85<-resultsMIS85<-resultsCOVER95<-resultsMIS95<-matrix(NA,goods,length(competitors))
results1COVER85<-results1MIS85<-results1COVER95<-results1MIS95<-list()

for(h in 1:length(DATA)){
  
  y=ts(DATA[[h]],frequency = freq);
  for(j in 1:length(competitors)){
    forec[[j]]<-competitors[[j]](head(y,(length(y)-steps)),steps)
    for(s in 1:steps){
      resRMSSE[s,j]<-rmsse(y,forec[[j]]$mean,s)
      resMASE[s,j]<-mase(y,forec[[j]]$mean,s) 
    }
    
    resMIS85[1,j]<-mis(tail(y,steps),forec[[j]]$lower[,1],forec[[j]]$upper[,1],.85,head(y,length(y)-steps));
    resMIS95[1,j]<-mis(tail(y,steps),forec[[j]]$lower[,2],forec[[j]]$upper[,2],.95,head(y,length(y)-steps));
    
  }
  results1RMSSE[[1]]=resRMSSE
  results1MASE[[1]]=resMASE
  
  resultsRMSSE[[h]]<-Reduce("+", results1RMSSE)
  resultsMASE[[h]]<-Reduce("+", results1MASE)
  resultsMIS85[h,]<-resMIS85
  resultsMIS95[h,]<-resMIS95
  data_frame <- data.frame(col1 = 1:steps)  
  
}
print(round(Reduce("+", resultsRMSSE)/h,3))
##       [,1]  [,2]  [,3]
## [1,] 0.716 0.728 0.729
## [2,] 0.840 0.856 0.857
## [3,] 0.914 0.935 0.933
## [4,] 0.972 0.999 0.994
## [5,] 1.020 1.054 1.046
## [6,] 1.062 1.103 1.090
  print(rbind(round(colMeans(resultsMIS85,na.rm = TRUE),3),round(colMeans(resultsMIS95,na.rm = TRUE),3)))
##       [,1]  [,2]  [,3]
## [1,] 5.554 5.957 5.729
## [2,] 7.115 7.569 7.294