Forecasting Inflation: Romanian Case Study Using SARIMA Models

    Jonathan Legrand
    Developer Advocate

In this article, we look at how one may choose an optimal SARIMA model by selecting the one with lowest in-sample errors. We also look into how one may use both Eikon and Datastream data together, as well as statistical concepts of stationarity and differencing among others. We also investigate and compare models only using comparative months (e.g.: Jan. with Jan., Feb. with Feb., etc.). You may see the Webinar recording at the end.

We find that the optimal model is a SARIMA(5,0,4)(4,0,0)$_{12}$.

 

To do so we look at (i) how one may check for stationarity graphically and via (ADF, KPSS & PP) test statistics, (ii) differencing, (iii) ex-ante (before modelling) parameter identification via autocorrelation & partial autocorrelation functions, (iv) the difference between Autoregressive Moving Average (ARMA) & Seasonal Integrated ARMA (SARIMA) models, (v) ex-post (after modelling) parameter identification via information criteria and mean of absolute & squared errors (as well as the use of Python's 'pickle' library), (vi) how one may choose an optimal model specification - reducing errors, (vii) recursive one-step-ahead out-of-sample forecasts, and finally (iix) corresponding month models before concluding.

As a precursor to ARMA modelling, the reader is advised to read 'Information Demand and Stock Return Predictability (Coded in R): Part 2: Econometric Modelling of Non-Linear Variances' - an article without coding that looks into our methods of modelling.

 

Webinar

SARIMA(5,0,4)(4,0,0)$_{12}$:

$$\begin{align} y_t &= c + \sum_{j=1}^{5} \phi_j y_{t-j} + \sum_{i=1}^{4} \psi_i \epsilon_{t-i} + \sum_{J=1}^{4} \Phi_i y_{t-(J*12)} \\ &= c + \phi_1 y_{t-1} + \phi_2 y_{t-2} + \phi_3 y_{t-3} + \phi_4 y_{t-4} + \phi_5 y_{t-5} + \psi_1 \epsilon_{t-1} + \psi_2 \epsilon_{t-2} + \psi_3 \epsilon_{t-3} + \psi_4 \epsilon_{t-4} + \Phi_1 y_{t-(1*12)}  + \Phi_2 y_{t-(2*12)} + \Phi_3 y_{t-(3*12)} + \Phi_4 y_{t-(4*12)} \\ &= c + \phi_1 y_{t-1} + \phi_2 y_{t-2} + \phi_3 y_{t-3} + \phi_4 y_{t-4} + \phi_5 y_{t-5} + \psi_1 \epsilon_{t-1} + \psi_2 \epsilon_{t-2} + \psi_3 \epsilon_{t-3} + \psi_4 \epsilon_{t-4} + \Phi_1 y_{t-12}  + \Phi_2 y_{t-24} + \Phi_3 y_{t-36} + \Phi_4 y_{t-48} \end{align}$$

In our study, we will also use exogenous variables ($x_t$'s) too such that - for a model with $m$ exogenous variables with potentially different $r_k$ lags each:

$ y_t = c + \sum_{j=1}^{5} \phi_j y_{t-j} + \sum_{i=1}^{4} \psi_i \epsilon_{t-i} + \sum_{J=1}^{4} \Phi_i y_{t-(J*12)} + \sum_{k=1}^{m} \left( \sum_{n= 1}^{r_k} \beta_{k,n} x_{k , t-{r_k}} \right)$

Get to Coding

Development Tools & Resources

The example code demonstrating the use case is based on the following development tools and resources:

  • Refinitiv's DataStream Web Services (DSWS): Access to DataStream data. A DataStream or Refinitiv Workspace IDentification (ID) will be needed to run the code below.
  • Refinitiv's Eikon API: To access Eikon data. An Eikon API key will be needed to use this API.

Import Libraries

We need to gather our data. Since Refinitiv's DataStream Web Services (DSWS) allows for access to ESG data covering nearly 70% of global market cap and over 400 metrics, naturally it is more than appropriate. We can access DSWS via the Python library "DatastreamDSWS" that can be installed simply by using pip install.

We use Refinitiv's Eikon Python Application Programming Interface (API) to access financial data such as Clc1. We can access it via the Python library "eikon" that can be installed simply by using pip install.

The library 'eikon' in this notebook is version 1.1.8
The library 'numpy' in this notebook is version 1.18.2
The library 'pandas' in this notebook is version 1.2.4
The library 'plotly' in this notebook is version 4.14.3
The library 'cufflinks' in this notebook is version 0.17.3
The library 'seaborn' in this notebook is version 0.11.1

Data-sets 

  • Dependent variables (Y):

    • RMCPANNL: Romania, Consumer Prices, by Commodity, All Items, Total, Change y/y

      Annual inflation rate – the increase (in percentage term) in consumer prices in one month of the current year compared to the same month of the previous year. This rate is calculated as a ratio, expressed as a percentage, between the price index of one month of the current year and that of the same month of the previous year, calculated against the same base, from which 100 is subtracted.

      This data-set will be differenced once to be stationary.

    • RMCONPRCF

Due to popular demand, we decided not to use Consumer Prices (Romania, Consumer Prices, by Commodity, All Items, Total, Index, Corresponding Period of the Previous Year = 100) as a dependent variable, but RMCPANNL instead.

Collecting Datastream Data 

We can go ahead and collect our data from Datastream:

Note that RMCPANNL's values are percentages, we thus ought to divide it by 100 to get proper fractions:

Let's have a look at our Pandas data-frame ' df '

Instrument RMCPANNL RMUNPTOTP RMXRUSD. RMXREUR. RMGOVBALA
Field X X X X X
Dates          
2000-01-15 0.5680 1175.0 1.84 1.8636 -162.8
2000-02-15 0.5570 1196.6 1.87 1.8421 -461.9
2000-03-15 0.4900 1166.7 1.92 1.8538 -826.0
2000-04-15 0.4890 1139.2 1.98 1.8713 -1210.9
2000-05-15 0.4400 1097.4 2.04 1.8507 -1367.8
... ... ... ... ... ...
2020-12-15 0.0206 296.1 4.00 4.8707 -105906.6
2021-01-15 0.0299 292.2 4.00 4.8728 -112.3
2021-02-15 0.0316 293.5 4.03 4.8741 -8430.8
2021-03-15 0.0305 NaN 4.11 4.8878 NaN
2021-04-15 NaN NaN NaN NaN NaN

256 rows × 5 columns

Note on timing: I aim to make this article as realistic as possible, putting myself in the shoes of a professional using this workflow to predict inflation. I do not expect our SARIMA models to perform particularly well, mainly because we did not do extensive research on the best exogenous variables to use - I leave that to you to decide. But while I do not expect this model to perform well, the workflow ought to be at its best. To use it optimally, please try to use exogenous variables (such as RMXRUSD.) as soon as their monthly values are released. It is possible for the values as of the - 15th of the month - of one variable to be published at another time to another (exempli gratia (e.g.): values for RMXRUSD. and RMGOVBALA could be published on the 20th and 25th of the month respectively, even though they are both representing values for the 15th of the month).

The 'X' is the default value for each of the items we requested. Let's change those with relevant value names:

Collecting Eikon data 

Now let's add our CLc1 data from Eikon. Note that we want monthly data, so let's collect monthly (30 day) moving average data (MAVG(TR.CLOSEPRICE,-29)). 1st let's collect that data:

  Instrument Timestamp Close Price MAVG(TR.CLOSEPRICE,-29)
0 CLc1 1999-01-01T00:00:00Z <NA> 11.5477
1 CLc1 1999-01-04T00:00:00Z 12.36 11.5667
2 CLc1 1999-01-05T00:00:00Z 12 11.611
3 CLc1 1999-01-06T00:00:00Z 12.85 11.646
4 CLc1 1999-01-07T00:00:00Z 13.05 11.6833
... ... ... ... ...
5592 CLc1 2021-04-13T00:00:00Z 60.45 61.6431
5593 CLc1 2021-04-14T00:00:00Z 62.75 <NA>
5594 CLc1 2021-04-15T00:00:00Z 63.32 <NA>
5595 CLc1 2021-04-16T00:00:00Z 63.07 <NA>
5596 CLc1 2021-04-19T00:00:00Z <NA> <NA>

5597 rows × 4 columns

But note that the last few moving average data points are NaN. Just in case these last few days are useful, let's make our own moving average:

  Instrument Timestamp Close Price CLc1 30D Moving Average
0 CLc1 1999-01-01T00:00:00Z <NA> NaN
1 CLc1 1999-01-04T00:00:00Z 12.36 NaN
2 CLc1 1999-01-05T00:00:00Z 12 NaN
3 CLc1 1999-01-06T00:00:00Z 12.85 NaN
4 CLc1 1999-01-07T00:00:00Z 13.05 NaN
... ... ... ... ...
5592 CLc1 2021-04-13T00:00:00Z 60.45 61.645
5593 CLc1 2021-04-14T00:00:00Z 62.75 61.7543
5594 CLc1 2021-04-15T00:00:00Z 63.32 61.8297
5595 CLc1 2021-04-16T00:00:00Z 63.07 61.7977
5596 CLc1 2021-04-19T00:00:00Z <NA> NaN

5597 rows × 4 columns

Joining the Datastream and Eikon data-frames together 

Now, if we want to combine the data from df and CLc1, we may want to do so joining the two data-frames on their dates; but one may notice that CLc1's Timestamp and df's index (Dates) portray time in different ways:

'1999-01-01T00:00:00Z'

'2000-01-15'

To harmonize the two, I will use df's way:

'2000-01-31'

  Instrument Timestamp Close Price CLc1 30D Moving Average
01/01/1999 CLc1 1999-01-01T00:00:00Z <NA> NaN
04/01/1999 CLc1 1999-01-04T00:00:00Z 12.36 NaN
05/01/1999 CLc1 1999-01-05T00:00:00Z 12 NaN
06/01/1999 CLc1 1999-01-06T00:00:00Z 12.85 NaN
07/01/1999 CLc1 1999-01-07T00:00:00Z 13.05 NaN
... ... ... ... ...
13/04/2021 CLc1 2021-04-13T00:00:00Z 60.45 61.645
14/04/2021 CLc1 2021-04-14T00:00:00Z 62.75 61.7543
15/04/2021 CLc1 2021-04-15T00:00:00Z 63.32 61.8297
16/04/2021 CLc1 2021-04-16T00:00:00Z 63.07 61.7977
19/04/2021 CLc1 2021-04-19T00:00:00Z <NA> NaN

5597 rows × 4 columns

Now we are encountering the issue whereby the 15th of the month every month is not necessarily a trading day. But we want to normalise the two data-frames to have monthly data on the 15th of every month (since that's how Datastream returns data). What we can do is use CLc1 30D Moving Average on the 15th of every month and (if it's not available) we'll use the 14th's; if that's unavailable we'll use the 13th; and so on until a set limit - say the 12th of every month (repeating this process 3 times). This is what the function below does:

  Instrument Timestamp Close Price CLc1 30D Moving Average
13/01/2000 CLc1 2000-01-13T00:00:00Z 26.69 25.8317
15/01/2000 CLc1 2000-01-14T00:00:00Z 28 25.9453
18/01/2000 CLc1 2000-01-18T00:00:00Z 28.85 26.0753

As per the above, we can see the difference between the index and Timestamp in row 259, indicating that our function above worked.

CLc1
  Close Price Close Price 30D Moving Average
15/01/1999 12.18 NaN
15/03/1999 14.46 12.6067
15/04/1999 16.88 15.5177
15/06/1999 18.54 17.667
15/07/1999 20.2 18.655
... ... ...
15/12/2020 47.59 43.203
15/01/2021 52.04 48.7013
15/02/2021 59.73 53.606
15/03/2021 65.28 60.8053
15/04/2021 63.32 61.8297

265 rows × 2 columns

Now we can join our two data-frames:

Instrument RMCPANNL RMUNPTOTP RMXRUSD. RMXREUR. RMGOVBALA CLc1
Field yoy raw raw raw raw Close Price
2000-01-15 0.5680 1175.0 1.84 1.8636 -162.8 28.0
2000-02-15 0.5570 1196.6 1.87 1.8421 -461.9 30.03
2000-03-15 0.4900 1166.7 1.92 1.8538 -826.0 30.65
2000-04-15 0.4890 1139.2 1.98 1.8713 -1210.9 25.4
2000-05-15 0.4400 1097.4 2.04 1.8507 -1367.8 29.97
... ... ... ... ... ... ...
2020-12-15 0.0206 296.1 4.00 4.8707 -105906.6 47.59
2021-01-15 0.0299 292.2 4.00 4.8728 -112.3 52.04
2021-02-15 0.0316 293.5 4.03 4.8741 -8430.8 59.73
2021-03-15 0.0305 NaN 4.11 4.8878 NaN 65.28
2021-04-15 NaN NaN NaN NaN NaN 63.32

256 rows × 7 columns

Now, we want comparable data, and RMCPANNL is the only series depicting the increase in one month compared to the same month of the previous year. So let's transform our other series to depict the data similarly:

Instrument CLc1 RMCPANNL RMGOVBALA RMUNPTOTP RMXREUR. RMXRUSD.
Field Close Price Close Price 30D Moving Average yoy yoy raw yoy raw yoy raw yoy raw yoy
15/01/2001 30.05 28.535 0.09981 0.399 -306.1 0.88022 1032.9 -0.1209 2.4646 0.32249 2.62 0.42391
15/02/2001 28.8 29.8113 0.07816 0.4 -601.2 0.30158 1032.3 -0.1373 2.4729 0.34244 2.68 0.43316
15/03/2001 26.58 28.976 -0.0423 0.403 -865.2 0.04746 992.8 -0.1491 2.4849 0.34044 2.73 0.42188
15/04/2001 28.2 27.25 -0.0318 0.375 -1087.5 -0.1019 948.4 -0.1675 2.488 0.32956 2.79 0.40909
15/05/2001 28.98 27.9327 0.0575 0.374 -1404.5 0.02683 890.8 -0.1883 2.491 0.34598 2.85 0.39706
... ... ... ... ... ... ... ... ... ... ... ... ...
15/10/2020 40.92 39.4793 -0.2909 0.0224 -76512 1.81579 285.7 0.10437 4.8733 0.02514 4.14 -0.0372
15/11/2020 40.12 39.5773 -0.2836 0.0214 -85982 1.47431 290.7 0.12066 4.8699 0.02131 4.12 -0.0441
15/12/2020 47.59 43.203 -0.2489 0.0206 -105907 1.20275 296.1 0.14812 4.8707 0.01955 4 -0.0698
15/01/2021 52.04 48.7013 -0.1892 0.0299 -112.3 -0.8426 292.2 0.12862 4.8728 0.01973 4 -0.0719
15/02/2021 59.73 53.606 -0.0281 0.0316 -8430.8 -0.199 293.5 0.14336 4.8741 0.01909 4.03 -0.0799

242 rows × 12 columns

Stationarity 

You may wonder why we spoke of differencing our data above. This is because our data may not be stationary. We need to verify that our data is stationary (for more information as to why, please see here - I suggest reading this article part (2) from the start - it is very much relevant to our current investigation). differencing it once may resolve that problem. In our case, for a time series $\{z\}_1^T$ (i.e.: going from $z_1, z_2, ...$ till $z_T$), differencing it once would render $\{z_2 - z_1, z_3 - z_2, ..., z_T - z_{T-1}\}$. Let's check if our independent variable - RMCPANNL's y/y - is stationary. We can check for stationarity (i) via graphical analysis, via the (ii) Augmented Dickey-Fuller (ADF), (iii) Phillips-Perron (PP) or the (iv) Kwiatkowski–Phillips–Schmidt–Shin (KPSS) test statistics.

Checking For Stationarity Via Graphical Analysis 

We must admit that our RMCPANNL dataset does not look stationary

RMCPANNL's 1st difference (1d) does look rather stationary:

Upon quick graphical visualisation, it seems as though we ought to choose RMCPANNL's 1st difference

Checking For Stationarity Via Test Statistics 

We can create a Python function for the three aforementioned test-statistics for stationarity: Augmented Dickey-Fuller (ADF), Phillips-Perron (PP) and the Kwiatkowski–Phillips–Schmidt–Shin (KPSS) ones.

For the ADF test, one may use the adfuller function from the statsmodels library (imported via the line from statsmodels.tsa.stattools import adfuller), but that library doesn't have functions for all the tests in question. the arch library does - however. So I will define a Python function Stationarity_Table returning all relevant arch stationarity results of interest:

4.19

      RMCPANNL yoy RMUNPTOTP raw RMUNPTOTP yoy RMXRUSD. raw RMXRUSD. yoy RMXREUR. raw RMXREUR. yoy RMGOVBALA raw RMGOVBALA yoy CLc1 Close Price 30D Moving Average CLc1 Close Price yoy
ADF Test-statistic   -4.0516 -2.2359 -3.6895 -1.5542 -3.439 -2.4889 -2.642 -0.0676 -15.433 -2.5897 -2.9546
p-value   0.00116 0.19348 0.00426 0.50657 0.0097 0.1182 0.08463 0.95257 2.93E-28 0.09515 0.03935
Number of lags used   15 15 15 1 13 1 14 15 0 2 13
Number of observations used for the regression and critical values'calculation   226 226 226 240 228 240 227 226 241 239 228
Critical values 1% -3.4596 -3.4596 -3.4596 -3.4579 -3.4594 -3.4579 -3.4595 -3.4596 -3.4578 -3.458 -3.4594
5% -2.8744 -2.8744 -2.8744 -2.8737 -2.8743 -2.8737 -2.8744 -2.8744 -2.8736 -2.8737 -2.8743
10% -2.5736 -2.5736 -2.5736 -2.5732 -2.5736 -2.5732 -2.5736 -2.5736 -2.5732 -2.5733 -2.5736
Null hypothesis   The process contains a unit root. The process contains a unit root. The process contains a unit root. The process contains a unit root. The process contains a unit root. The process contains a unit root. The process contains a unit root. The process contains a unit root. The process contains a unit root. The process contains a unit root. The process contains a unit root.
PP Test-statistic   -6.2226 -2.6368 -2.9359 -1.4358 -3.3385 -2.4428 -2.8822 -6.6524 -15.445 -2.0948 -3.1094
p-value   5.18E-08 0.08563 0.04133 0.56501 0.01324 0.13002 0.04746 5.08E-09 2.84E-28 0.2466 0.02587
Number of lags used   15 15 15 15 15 15 15 15 15 15 15
Number of observations used for the regression and critical values'calculation   241 241 241 241 241 241 241 241 241 241 241
Critical values 1% -3.4578 -3.4578 -3.4578 -3.4578 -3.4578 -3.4578 -3.4578 -3.4578 -3.4578 -3.4578 -3.4578
5% -2.8736 -2.8736 -2.8736 -2.8736 -2.8736 -2.8736 -2.8736 -2.8736 -2.8736 -2.8736 -2.8736
10% -2.5732 -2.5732 -2.5732 -2.5732 -2.5732 -2.5732 -2.5732 -2.5732 -2.5732 -2.5732 -2.5732
Null hypothesis   The process contains a unit root. The process contains a unit root. The process contains a unit root. The process contains a unit root. The process contains a unit root. The process contains a unit root. The process contains a unit root. The process contains a unit root. The process contains a unit root. The process contains a unit root. The process contains a unit root.

 

Interpreting the stationarity table: As per the above, the null hypothesis of the ADF is that there is a unit root, with the alternative that there is no unit root. If the P-value is above a critical size, then we cannot reject that there is a unit root. If the process has a unit root, then it is a non-stationary time series. If the p-value is close to significant, then the critical values should be used to judge whether to reject the null.

 

Since ${ADF}_{{Test-statistic}_{RMCPANNL yoy}} \approx -6.025 < {ADF}_{{1\% Critical value}_{RMCPANNL yoy}} \approx -3.471$, we can reject the hypothesis that there is a unit root and presume our series to be stationary. Same conclusions can be taken from the PP and KPSS tests of our RMCPANNL data. This is an interesting finding since our graphical analysis suggested otherwise.

Nonetheless, analysing the table's figures for RMUNPTOTP, RMXRUSD, RMXREUR, and RMGOVBALA shows us that they're non-stationary (for the most part - especially with the ADF test). We ought to difference them once and see if they are stationary then.

Differencing 

As aforementioned, differencing a time series $\{z\}_1^T$ once would render $\{z_2 - z_1, z_3 - z_2, ..., z_T - z_{T-1}\}$. Let's add the 1st difference of our data to our Pandas data-frame ```df```:

Instrument CLc1 RMCPANNL RMGOVBALA RMUNPTOTP RMXREUR. RMXRUSD.
Field Close Price Close Price 30D Moving Average yoy yoy1d yoy yoy1d raw yoy yoy1d raw yoy yoy1d raw yoy yoy1d raw yoy yoy1d
15/02/2001 28.8 29.8113 0.07816 -0.0217 0.4 0.001 -601.2 0.30158 -0.5786 1032.3 -0.1373 -0.0164 2.4729 0.34244 0.01994 2.68 0.43316 0.00924
15/03/2001 26.58 28.976 -0.0423 -0.1205 0.403 0.003 -865.2 0.04746 -0.2541 992.8 -0.1491 -0.0117 2.4849 0.34044 -0.002 2.73 0.42188 -0.0113
15/04/2001 28.2 27.25 -0.0318 0.01048 0.375 -0.028 -1087.5 -0.1019 -0.1494 948.4 -0.1675 -0.0184 2.488 0.32956 -0.0109 2.79 0.40909 -0.0128
15/05/2001 28.98 27.9327 0.0575 0.08932 0.374 -0.001 -1404.5 0.02683 0.12874 890.8 -0.1883 -0.0208 2.491 0.34598 0.01642 2.85 0.39706 -0.012
15/06/2001 28.53 28.6177 -0.0403 -0.0978 0.357 -0.017 -2268.9 0.25973 0.2329 840.3 -0.2125 -0.0242 2.4732 0.23846 -0.1075 2.9 0.38095 -0.0161
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
15/10/2020 40.92 39.4793 -0.2909 -0.0379 0.0224 -0.0021 -76512 1.81579 -0.0034 285.7 0.10437 0.00959 4.8733 0.02514 -0.0004 4.14 -0.0372 0.00465
15/11/2020 40.12 39.5773 -0.2836 0.00733 0.0214 -0.001 -85982 1.47431 -0.3415 290.7 0.12066 0.0163 4.8699 0.02131 -0.0038 4.12 -0.0441 -0.0069
15/12/2020 47.59 43.203 -0.2489 0.03465 0.0206 -0.0008 -105907 1.20275 -0.2716 296.1 0.14812 0.02746 4.8707 0.01955 -0.0018 4 -0.0698 -0.0257
15/01/2021 52.04 48.7013 -0.1892 0.05969 0.0299 0.0093 -112.3 -0.8426 -2.0454 292.2 0.12862 -0.0195 4.8728 0.01973 0.00018 4 -0.0719 -0.0022
15/02/2021 59.73 53.606 -0.0281 0.16113 0.0316 0.0017 -8430.8 -0.199 0.64362 293.5 0.14336 0.01474 4.8741 0.01909 -0.0006 4.03 -0.0799 -0.008

241 rows × 18 columns

      RMCPANNL yoy1d RMUNPTOTP yoy1d RMXRUSD. yoy1d RMXREUR. yoy1d RMGOVBALA yoy1d CLc1 Close Price yoy1d
ADF Test-statistic   -3.9779 -5.1251 -5.8272 -3.691 -8.2178 -8.5853
p-value   0.00153 1.25E-05 4.05E-07 0.00424 6.55E-13 7.53E-14
Number of lags used   14 12 12 15 9 11
Number of observations used for the regression and critical values'calculation   226 228 228 225 231 229
Critical values 1% -3.4596 -3.4594 -3.4594 -3.4598 -3.459 -3.4592
5% -2.8744 -2.8743 -2.8743 -2.8745 -2.8741 -2.8742
10% -2.5736 -2.5736 -2.5736 -2.5737 -2.5735 -2.5735
Null hypothesis   The process contains a unit root. The process contains a unit root. The process contains a unit root. The process contains a unit root. The process contains a unit root. The process contains a unit root.
PP Test-statistic   -13.791 -9.5569 -9.553 -10.36 -57.826 -7.7634
p-value   8.94E-26 2.49E-16 2.54E-16 2.41E-18 0 9.33E-12
Number of lags used   15 15 15 15 15 15
Number of observations used for the regression and critical values'calculation   240 240 240 240 240 240
Critical values 1% -3.4579 -3.4579 -3.4579 -3.4579 -3.4579 -3.4579
5% -2.8737 -2.8737 -2.8737 -2.8737 -2.8737 -2.8737
10% -2.5732 -2.5732 -2.5732 -2.5732 -2.5732 -2.5732
Null hypothesis   The process contains a unit root. The process contains a unit root. The process contains a unit root. The process contains a unit root. The process contains a unit root. The process contains a unit root.

 

Now they're all stationary. We ought to use datasets treated similarly for consistency (e.g.: compare 'RMCPANNL yoy1d' with 'RMUNPTOTP yoy1d' rather than with 'RMUNPTOTP yoy'), so we will use the ones shown in the last table (i.e.: 'yoy1d' datasets).

N.B.: The RMGOVBALA yoy1d dataset is causing issues with the KPSS test, but that one is only for weak stationarity, and is not of as much interest to us in our study - I just wanted to include it to portray the extent of the Stationarity_Table function. The RMGOVBALA yoy1d dataset is causing issues with the KPSS test because of its large variance, as can be seen in this graph:

Modelling 

Now we will look at which ARMA-family model to implement. We'll look at the best model orders to choose before (ex-ante) and after (ex-post) going through modelling procedures.

Ex-Ante Parameter Identification: Autocorrelation and Partial Autocorrelation Functions 

To figure out our parameter identification (i.e.: what lags to use in our model), we may want to plot ACF and PACF diagrams:

N.B.: the semicolon at the end of the code for our plots below is there to avoid duplicates.

From these graphs (also known as (a.k.a.): correlograms), we could identify an AR(1,4,5) (non-consecutive) model; but the MA part is harder to decipher. Let's have a look at how those models would fair:

  • 1st: a simple ARMA(5,2) model (i.e.: a SARIMA(5,0,2) model), then
  • 2nd: a SARIMA(5,0,2)(1, 0, 0)12 model:

ARMA(5,2)

SARIMAX Results      
Dep. Variable: y No. Observations: 241      
Model: ARIMA(5, 0, 2) Log Likelihood 847.684      
Date: Mon, 19 Apr 2021 AIC -1677.4      
Time: 14:33:02 BIC -1646      
Sample: 0 HQIC -1664.7      
  -241          
Covariance Type: opg          
             
  coef std err z P>|z| [0.025 0.975]
const -0.0015 0.001 -1.913 0.056 -0.003 3.66E-05
ar.L1 0.0858 17.28 0.005 0.996 -33.783 33.955
ar.L2 0.0603 8.153 0.007 0.994 -15.919 16.039
ar.L3 0.1161 1.11 0.105 0.917 -2.059 2.292
ar.L4 -0.0028 1.219 -0.002 0.998 -2.393 2.387
ar.L5 -0.0027 0.782 -0.003 0.997 -1.535 1.53
ma.L1 0.0995 17.281 0.006 0.995 -33.77 33.969
ma.L2 0.0615 5.295 0.012 0.991 -10.317 10.44
sigma2 5.15E-05 3.30E-06 15.595 0 4.51E-05 5.80E-05
             
Ljung-Box (L1) (Q): 0 Jarque-Bera (JB): 274.28      
Prob(Q): 0.96 Prob(JB): 0      
Heteroskedasticity (H): 0.53 Skew: -0.6      
Prob(H) (two-sided): 0.01 Kurtosis: 8.09      

Warnings:

[1] Covariance matrix calculated using the outer product of gradients (complex-step).

SARIMA(5,0,2)(1, 0, 0)12 

SARIMAX Results      
Dep. Variable: y No. Observations: 241      
Model: ARIMA(5, 0, 2)x(1, 0, [], 12) Log Likelihood 868.387      
Date: Mon, 19 Apr 2021 AIC -1716.8      
Time: 14:33:04 BIC -1681.9      
Sample: 0 HQIC -1702.7      
  -241          
Covariance Type: opg          
             
  coef std err z P>|z| [0.025 0.975]
const -0.0015 0.001 -1.856 0.063 -0.003 8.27E-05
ar.L1 0.0761 2.209 0.034 0.973 -4.253 4.405
ar.L2 0.0641 1.92 0.033 0.973 -3.698 3.827
ar.L3 0.2522 0.31 0.815 0.415 -0.355 0.859
ar.L4 0.052 0.474 0.11 0.913 -0.877 0.981
ar.L5 0.0496 0.366 0.136 0.892 -0.667 0.766
ma.L1 0.1203 2.203 0.055 0.956 -4.198 4.439
ma.L2 0.0607 1.679 0.036 0.971 -3.231 3.352
ar.S.L12 -0.4396 0.042 -10.401 0 -0.522 -0.357
sigma2 4.29E-05 2.85E-06 15.025 0 3.73E-05 4.85E-05
             
Ljung-Box (L1) (Q): 0.01 Jarque-Bera (JB): 142.78      
Prob(Q): 0.92 Prob(JB): 0      
Heteroskedasticity (H): 0.53 Skew: -0.56      
Prob(H) (two-sided): 0 Kurtosis: 6.6      

Warnings:

[1] Covariance matrix calculated using the outer product of gradients (complex-step).

Ex-Post Parameter Identification: Information Criteria and Mean of Absolute & squared Errors 

Graphical Analysis 

After having fit a model, one may try to look at the residuals to compare models:

Looking at such graphical information is not simple. (i) Not only is it arbitrary to chose a model over another from graphical information (ii) it quickly becomes difficult when comparing many models together. That's why we may compare them instead with performance measurements such as Information Criteria:

Information Criteria 

  AIC AICC BIC HQIC
arima502c -1677.4 -1676.6 -1646 -1664.7
s1arima502c -1716.8 -1715.8 -1681.9 -1702.7

We can similarly go through each permutation within limits of our SARIMA model's parameters, and go beyond just recording IC information - including mean squared error (MSE) and sum of squared error (SSE, a.k.a.: RSS) information too.

Mean Squared Errors and Sum of squared Errors 

We can define the below function to return a table of

  • the Akaike information criterion (AIC),
  • the AICc (AIC with a correction for small sample sizes),
  • the Bayesian information criterion (BIC) (aka Schwarz criterion (also SBC, SBIC)) and
  • the Hannan-Quinn information criterion (HQC) as well as
  • the Mean Squared Errors (MSE) and
  • the Sum of Squared Errors (SSE) (aka: residual sum of squares (RSS))

of any specified ARIMAX model, and then choose the one providing the lowest (of any one chosen error measure above) as the optimal model of choice:

N.B.: Note that we need to lag our exogenous variables since we are putting ourselves in the shoes of a programmer who is using past values to predict tomorrow's.

Now let's put this Python function into practice:

Let's have a look at that table returned:

  AIC AICC BIC HQIC MSE SSE
sarima1,0,0,0,0,0,12,c,exo=('CLc1',) -1067.6 -1067.4 -1055.4 -1062.7 6.8E-05 0.01073
sarima1,0,0,0,0,0,12,c,exo=('RMGOVBALA',) -1067 -1066.7 -1054.7 -1062 6.8E-05 0.01078
sarima1,0,0,0,0,0,12,c,exo=('RMUNPTOTP',) -1067.7 -1067.4 -1055.4 -1062.7 6.7E-05 0.01073
sarima1,0,0,0,0,0,12,c,exo=('RMXREUR.',) -1067 -1066.8 -1054.8 -1062 6.8E-05 0.01078
sarima1,0,0,0,0,0,12,c,exo=('RMXRUSD.',) -1069.2 -1068.9 -1056.9 -1064.2 6.7E-05 0.01063
... ... ... ... ... ... ...
sarima1,0,4,4,0,4,12,c,exo=('CLc1', 'RMGOVBALA', 'RMUNPTOTP', 'RMXRUSD.') -1047.2 -1041.8 -988.92 -1023.6 6.3E-05 0.01005
sarima1,0,4,4,0,4,12,c,exo=('CLc1', 'RMGOVBALA', 'RMXREUR.', 'RMXRUSD.') -1047.9 -1042.4 -989.59 -1024.2 6.3E-05 0.01002
sarima1,0,4,4,0,4,12,c,exo=('CLc1', 'RMUNPTOTP', 'RMXREUR.', 'RMXRUSD.') -1057.4 -1051.9 -999.09 -1033.7 0.00006 0.00951
sarima1,0,4,4,0,4,12,c,exo=('RMGOVBALA', 'RMUNPTOTP', 'RMXREUR.', 'RMXRUSD.') -1043.1 -1037.6 -984.75 -1019.4 6.5E-05 0.01029
sarima1,0,4,4,0,4,12,c,exo=('CLc1', 'RMGOVBALA', 'RMUNPTOTP', 'RMXREUR.', 'RMXRUSD.') -1048.2 -1042.1 -986.82 -1023.3 6.2E-05 0.00994

3875 rows × 6 columns

Let's have a look at that table lowest returned:

  AIC AICC BIC HQIC MSE SSE
1st lowest sarima1,0,1,0,0,0,12,c,exo=('RMUNPTOTP', 'RMXR... sarima1,0,1,0,0,0,12,c,exo=('RMUNPTOTP', 'RMXR... sarima1,0,0,0,0,0,12,c,exo=('RMXRUSD.',) sarima1,0,1,0,0,0,12,c,exo=('RMUNPTOTP',) sarima1,0,4,4,0,0,12,c,exo=('RMUNPTOTP', 'RMXR... sarima1,0,4,4,0,0,12,c,exo=('RMUNPTOTP', 'RMXR...
2nd lowest sarima1,0,4,0,0,2,12,c,exo=('RMUNPTOTP', 'RMXR... sarima1,0,1,0,0,0,12,c,exo=('RMUNPTOTP',) sarima1,0,1,0,0,0,12,c,exo=('RMUNPTOTP',) sarima1,0,1,0,0,0,12,c,exo=('RMXRUSD.',) sarima1,0,4,4,0,0,12,c,exo=('CLc1', 'RMUNPTOTP... sarima1,0,4,4,0,0,12,c,exo=('CLc1', 'RMUNPTOTP...
3rd lowest sarima1,0,4,0,0,0,12,c,exo=('RMUNPTOTP', 'RMXR... sarima1,0,4,0,0,0,12,c,exo=('RMUNPTOTP',) sarima1,0,1,0,0,0,12,c,exo=('RMXRUSD.',) sarima1,0,1,0,0,0,12,c,exo=('CLc1',) sarima1,0,4,4,0,3,12,c,exo=('CLc1', 'RMUNPTOTP... sarima1,0,4,4,0,3,12,c,exo=('CLc1', 'RMUNPTOTP...
Pickle 

It is quite time consuming to request DSWS for all these codes. Furthermore, the table test1_df is rather large, and we only accounted for p, q, P, Q values up to (but not including) 3. What we could do is save our table in a file outside our code, then delete our table in our code, continue our analysis with test2_df and compare the two afterwards. Let's save our progress this far using Pickle. Since this is a step that can overwrite existing files, I commented them out. Feel free to uncomment them back in if you would like to save your data:

As aforementioned, let's delete our tables in our code for now - after having saved them outside our code:

Now let's continue with our examination:

pmdarima 

If we knew which exogenous variables to use, we could have used pmdarima to find the best model specification too:

ARIMA(maxiter=40, order=(0, 1, 1), scoring_args={},
seasonal_order=(0, 0, 1, 12), suppress_warnings=True)

Unfortunately, we don't know what permutation of exogenous variables is best, and Information-Criteria-optimisation methods are thus not sufficient.

Optimal model 

Finally, let's compare our two tables to check the lowest MSE and SSE:

  AIC AICC BIC HQIC MSE SSE
sarima1,0,4,4,0,0,12,c,exo=('RMUNPTOTP', 'RMXRUSD.') -1070.6 -1068.1 -1030.7 -1054.4 5.9E-05 0.00946
             
  AIC AICC BIC HQIC MSE SSE
sarima2,0,4,1,0,4,12,c,exo=('CLc1', 'RMUNPTOTP', 'RMXRUSD.') -1064.8 -1060.9 -1015.7 -1044.8 5.9E-05 0.00944
             
  AIC AICC BIC HQIC MSE SSE
sarima3,0,4,4,0,0,12,c,exo=('CLc1', 'RMUNPTOTP', 'RMXRUSD.') -1073.9 -1070.1 -1024.8 -1054 5.6E-05 0.00896
             
  AIC AICC BIC HQIC MSE SSE
sarima4,0,4,4,0,0,12,c,exo=('CLc1', 'RMUNPTOTP', 'RMXRUSD.') -1070 -1065.7 -1017.8 -1048.8 5.7E-05 0.00904
             
  AIC AICC BIC HQIC MSE SSE
sarima5,0,4,4,0,0,12,c,exo=('RMUNPTOTP', 'RMXRUSD.') -1072.2 -1067.9 -1020.1 -1051.1 5.6E-05 0.00892

 

Thus, the best model out of our choices is the 'sarima5,0,4,4,0,0,12,c,exo=('RMUNPTOTP', 'RMXRUSD.')' model (best as in SSE and MSE reducing):

SARIMAX Results      
Dep. Variable: y No. Observations: 240      
Model: ARIMA(5, 0, 4)x(4, 0, [], 12) Log Likelihood 892.165      
Date: Mon, 19 Apr 2021 AIC -1750.3      
Time: 14:33:44 BIC -1691.2      
Sample: 0 HQIC -1726.5      
  -240          
Covariance Type: opg          
             
  coef std err z P>|z| [0.025 0.975]
const -0.0013 0.001 -1.947 0.052 -0.003 8.87E-06
x1 -0.0066 0.014 -0.484 0.629 -0.033 0.02
x2 0.0167 0.014 1.188 0.235 -0.011 0.044
ar.L1 0.4023 0.445 0.905 0.366 -0.469 1.274
ar.L2 0.264 0.596 0.443 0.658 -0.904 1.432
ar.L3 0.1468 0.31 0.473 0.636 -0.461 0.755
ar.L4 -0.1802 0.435 -0.415 0.678 -1.032 0.672
ar.L5 0.2342 0.103 2.266 0.023 0.032 0.437
ma.L1 -0.2364 0.446 -0.53 0.596 -1.11 0.637
ma.L2 -0.2758 0.555 -0.497 0.619 -1.363 0.812
ma.L3 -0.0235 0.327 -0.072 0.943 -0.664 0.617
ma.L4 0.1263 0.429 0.294 0.768 -0.714 0.967
ar.S.L12 -0.8138 0.08 -10.118 0 -0.971 -0.656
ar.S.L24 -0.6277 0.101 -6.204 0 -0.826 -0.429
ar.S.L36 -0.3844 0.103 -3.746 0 -0.586 -0.183
ar.S.L48 -0.1285 0.085 -1.517 0.129 -0.295 0.038
sigma2 3.25E-05 2.55E-06 12.775 0 2.75E-05 3.75E-05
             
Ljung-Box (L1) (Q): 0.07 Jarque-Bera (JB): 166.83      
Prob(Q): 0.8 Prob(JB): 0      
Heteroskedasticity (H): 0.54 Skew: -0.63      
Prob(H) (two-sided): 0.01 Kurtosis: 6.88      

Warnings:
[1] Covariance matrix calculated using the outer product of gradients (complex-step).

Looking at P-Values, we can see that the only coefficients with statistical significance are ar.L5 and ar.S.L12 to ar.S.L36.

Fitted values of our 'Optimal Model' 

Recursive One-Step-Ahead Out-of-Sample Forecasts 

Now we can simply apply our model but train it on a smaller dataset (say 80% of our data, i.e.: approx. 200 first observations) to simulate an out-of-sample test:

200
210
220
230

 

Let's save our data in s4arima504c_osaoosf:

  Recursive One-Step-Ahead Out-of-Sample Forecasts endogenous variable (RMCPANNL) RMUNPTOTP RMXRUSD.
15/11/2020 0.00258 -0.001 0.01044 -0.0042
15/12/2020 0.00089 -0.0008 0.00959 0.00465
15/01/2021 -0.0003 0.0093 0.0163 -0.0069
15/02/2021 -0.0039 0.0017 0.02746 -0.0257
15/03/2021 -0.0025 NaN -0.0195 -0.0022
Instrument CLc1 RMCPANNL RMGOVBALA RMUNPTOTP RMXREUR. RMXRUSD.
Field Close Price Close Price 30D Moving Average yoy yoy1d s4arima504c Recursive OSAOOSF yoy yoy1d raw yoy yoy1d raw yoy yoy1d raw yoy yoy1d raw yoy yoy1d
15/01/2001 30.05 28.535 0.09981 NaN NaN 0.399 NaN -306.1 0.88022 NaN 1032.9 -0.1209 NaN 2.4646 0.32249 NaN 2.62 0.42391 NaN
15/02/2001 28.8 29.8113 0.07816 -0.0217 NaN 0.4 0.001 -601.2 0.30158 -0.5786 1032.3 -0.1373 -0.0164 2.4729 0.34244 0.01994 2.68 0.43316 0.00924
15/03/2001 26.58 28.976 -0.0423 -0.1205 NaN 0.403 0.003 -865.2 0.04746 -0.2541 992.8 -0.1491 -0.0117 2.4849 0.34044 -0.002 2.73 0.42188 -0.0113
15/04/2001 28.2 27.25 -0.0318 0.01048 NaN 0.375 -0.028 -1087.5 -0.1019 -0.1494 948.4 -0.1675 -0.0184 2.488 0.32956 -0.0109 2.79 0.40909 -0.0128
15/05/2001 28.98 27.9327 0.0575 0.08932 NaN 0.374 -0.001 -1404.5 0.02683 0.12874 890.8 -0.1883 -0.0208 2.491 0.34598 0.01642 2.85 0.39706 -0.012
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
15/10/2020 40.92 39.4793 -0.2909 -0.0379 -0.0019 0.0224 -0.0021 -76512 1.81579 -0.0034 285.7 0.10437 0.00959 4.8733 0.02514 -0.0004 4.14 -0.0372 0.00465
15/11/2020 40.12 39.5773 -0.2836 0.00733 0.00258 0.0214 -0.001 -85982 1.47431 -0.3415 290.7 0.12066 0.0163 4.8699 0.02131 -0.0038 4.12 -0.0441 -0.0069
15/12/2020 47.59 43.203 -0.2489 0.03465 0.00089 0.0206 -0.0008 -105907 1.20275 -0.2716 296.1 0.14812 0.02746 4.8707 0.01955 -0.0018 4 -0.0698 -0.0257
15/01/2021 52.04 48.7013 -0.1892 0.05969 -0.0003 0.0299 0.0093 -112.3 -0.8426 -2.0454 292.2 0.12862 -0.0195 4.8728 0.01973 0.00018 4 -0.0719 -0.0022
15/02/2021 59.73 53.606 -0.0281 0.16113 -0.0039 0.0316 0.0017 -8430.8 -0.199 0.64362 293.5 0.14336 0.01474 4.8741 0.01909 -0.0006 4.03 -0.0799 -0.008

242 rows × 19 columns

As you can see, we are missing the last row of data in df2. We can remedy this by appending it with the last row of s4arima504c_osaoosf_plot_df:

Instrument CLc1 RMCPANNL RMGOVBALA RMUNPTOTP RMXREUR. RMXRUSD.
Field Close Price Close Price 30D Moving Average yoy yoy1d s4arima504c Recursive OSAOOSF yoy yoy1d raw yoy yoy1d raw yoy yoy1d raw yoy yoy1d raw yoy yoy1d
15/01/2001 30.05 28.535 0.09981 NaN NaN 0.399 NaN -306.1 0.88022 NaN 1032.9 -0.1209 NaN 2.4646 0.32249 NaN 2.62 0.42391 NaN
15/02/2001 28.8 29.8113 0.07816 -0.0217 NaN 0.4 0.001 -601.2 0.30158 -0.5786 1032.3 -0.1373 -0.0164 2.4729 0.34244 0.01994 2.68 0.43316 0.00924
15/03/2001 26.58 28.976 -0.0423 -0.1205 NaN 0.403 0.003 -865.2 0.04746 -0.2541 992.8 -0.1491 -0.0117 2.4849 0.34044 -0.002 2.73 0.42188 -0.0113
15/04/2001 28.2 27.25 -0.0318 0.01048 NaN 0.375 -0.028 -1087.5 -0.1019 -0.1494 948.4 -0.1675 -0.0184 2.488 0.32956 -0.0109 2.79 0.40909 -0.0128
15/05/2001 28.98 27.9327 0.0575 0.08932 NaN 0.374 -0.001 -1404.5 0.02683 0.12874 890.8 -0.1883 -0.0208 2.491 0.34598 0.01642 2.85 0.39706 -0.012
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
15/11/2020 40.12 39.5773 -0.2836 0.00733 0.00258 0.0214 -0.001 -85982 1.47431 -0.3415 290.7 0.12066 0.0163 4.8699 0.02131 -0.0038 4.12 -0.0441 -0.0069
15/12/2020 47.59 43.203 -0.2489 0.03465 0.00089 0.0206 -0.0008 -105907 1.20275 -0.2716 296.1 0.14812 0.02746 4.8707 0.01955 -0.0018 4 -0.0698 -0.0257
15/01/2021 52.04 48.7013 -0.1892 0.05969 -0.0003 0.0299 0.0093 -112.3 -0.8426 -2.0454 292.2 0.12862 -0.0195 4.8728 0.01973 0.00018 4 -0.0719 -0.0022
15/02/2021 59.73 53.606 -0.0281 0.16113 -0.0039 0.0316 0.0017 -8430.8 -0.199 0.64362 293.5 0.14336 0.01474 4.8741 0.01909 -0.0006 4.03 -0.0799 -0.008
15/03/2021 NaN NaN NaN NaN -0.0025 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

243 rows × 19 columns

Corresponding Month Model 

Many in the industry only focus on the same month of previous years in their predicting models (e.g.: only predict this year's March inflation value with previous March values, similarly so with each month of the year). There is some merit to the thought, as other datapoints may sway our data with irrelevant variables. The reason is that demand/supply profiles might be different each month of the day and it would be incorrect to compare months in a continuous series (i.e.: January to February et cetera (etc.)). To do so, we can exploit the SARIMA model and only use Seasonal AutoRegressive variables (in addition to our exogenous variables):

1st: Let's check the best SAMIMA model with ARMA(0,0) imposed:

  AIC AICC BIC HQIC MSE SSE
sarima0,0,0,0,0,0,12,c,exo=('CLc1',) -1656.6 -1656.5 -1646.2 -1652.4 5.6E-05 0.01333
sarima0,0,0,0,0,0,12,c,exo=('RMGOVBALA',) -1656.3 -1656.2 -1645.8 -1652.1 5.6E-05 0.01335
sarima0,0,0,0,0,0,12,c,exo=('RMUNPTOTP',) -1657.4 -1657.2 -1646.9 -1653.1 5.6E-05 0.01329
sarima0,0,0,0,0,0,12,c,exo=('RMXREUR.',) -1665 -1664.9 -1654.6 -1660.8 5.4E-05 0.01287
sarima0,0,0,0,0,0,12,c,exo=('RMXRUSD.',) -1660.1 -1660 -1649.7 -1655.9 5.5E-05 0.01313
... ... ... ... ... ... ...
sarima0,0,0,4,0,4,12,c,exo=('CLc1', 'RMGOVBALA', 'RMUNPTOTP', 'RMXRUSD.') -1625.7 -1623.8 -1577 -1606.1 5.4E-05 0.01283
sarima0,0,0,4,0,4,12,c,exo=('CLc1', 'RMGOVBALA', 'RMXREUR.', 'RMXRUSD.') -1632.4 -1630.5 -1583.7 -1612.7 5.2E-05 0.01249
sarima0,0,0,4,0,4,12,c,exo=('CLc1', 'RMUNPTOTP', 'RMXREUR.', 'RMXRUSD.') -1669.6 -1667.7 -1620.9 -1650 0.00005 0.01196
sarima0,0,0,4,0,4,12,c,exo=('RMGOVBALA', 'RMUNPTOTP', 'RMXREUR.', 'RMXRUSD.') -1636.1 -1634.2 -1587.4 -1616.5 5.2E-05 0.0124
sarima0,0,0,4,0,4,12,c,exo=('CLc1', 'RMGOVBALA', 'RMUNPTOTP', 'RMXREUR.', 'RMXRUSD.') -1633.8 -1631.6 -1581.6 -1612.8 5.2E-05 0.01241

775 rows × 6 columns

Index(['sarima0,0,0,3,0,0,12,c,exo=('RMUNPTOTP', 'RMXREUR.', 'RMXRUSD.')'], dtype='object')

 

Note that our Reg_Perm_Table function's default Seasonal parameters (P and Q) range from 0 to 5 and that the optimum (i.e.: lowest SSE producing one) is lower than 5; this would indicate that we may very well have our optimal SAMIMA model with ARMA(0,0) imposed

2nd: Fit the sarima0,0,0,3,0,0,12 model:

3rd: Recursive One-Step-Ahead Out-of-Sample Forecasts

200
210
220
230

 

Let's save our data in s3arima000c_osaoosf:

Comparing Models 

0.0013175028279920704 0.0012139805753142835

 

From the two SSE values above, we can determine that our s4arima504c model is the better of the two.

Conclusion 

The fact that a model with seasonal components (SARIMA(5,0,4)(4,0,0)1212) was the optimal one (even before we investigate Corresponding Month Models) suggests that there was a lot of information in corresponding months in previous years. As a matter of fact, even the directly previous months' coefficients seemed of little relevance. Indeed, s4arima504c_fit P-values of these cofactors from lag 1 to 4 (ar.L1 to ar.L4) never went below 0.1; i.e.: they were never statistically significant. The 5th lag however (ar.L5, with value 0.2342) had a P-value lower than 0.05, suggesting it to be statistically significant at the 5% Confidence Level (CL). Moving Average elements (ma.L1 to ma.L4) never showed statistical significance (as per their P-values). (Statistical significance is only really considered at the 10% CL, when the P-value is lower than 0.1.)

Seasonal coefficients 1 to 3 (ar.S.L12 to ar.S.L36) proved significant at the 5% CL. The 4th (ar.S.L48) did not.

It would be interesting to look at a non-consecutive lags model with only ar.L5 & ar.S.L12 to ar.S.L36 and investigate it's out-of-sample one-step-ahead forecasts as a back-test.

Additionally, what make this model interesting is that one can use it for more than one-step-ahead forecasts depending on the frequency of exogenous data provision. Say that the exogenous RMXRUSD variable is only published on the 30th of the month (showing data for the 15th of that month); than one can use data published a month prior to forecast two-steps-ahead, alleviating this issue - although not solving it per se (by itself).

Romanian inflation is a particular case study. High inflation levels a the start of our data-set might have skewed results significantly. A look at models such as the ones above in predicting other countries' inflation levels could be more fruitful. Alternatively, looking into more differenced data could be equally interesting. (Indeed, using Stationarity_Table with more differenced data seem to yield more stationary data.)