We have already seen how time series models such as ARIMA can be used to make time series forecasts. While these models can prove to have high degrees of accuracy, they have one major shortcoming – they do not account for “shocks”, or sudden changes in a time series. Let’s see how we can potentially alleviate this problem using a model known as the Kalman Filter.
Time Series Shocks
Let’s take the stock market as an example. An index could have an overall upward trend, and then spike sharply downwards during a sell-off. A conventional time series model wouldn’t necessarily account for this right away, and it would likely take several periods into the future before the suddent change in trend would be taken into account.
Therefore, we wish to use a time series model that is indeed capable of accounting for such shocks. Let’s take a look at a handy model known as the Kalman Filter.
The Kalman Filter is a state-space model that adjusts more quickly for shocks to a time series. Let’s see how this works using an example.
In January 2015, currency markets underwent one of the biggest shocks ever endured, when the Swiss National Bank decided to depeg the Swiss franc from the euro. As a result, the Swiss franc soared in value while other major currencies plummeted:
Let’s see how the Kalman Filter adjusts for such a shock.
Kalman Filter with KFAS library
Firstly, let’s download data for CHF/USD for the month of January 2015:
> require(Quandl) Loading required package: Quandl Loading required package: xts Loading required package: zoo Attaching package: ‘zoo’ The following objects are masked from ‘package:base’: as.Date, as.Date.numeric > usdchf = Quandl("FRED/DEXSZUS", start_date="2015-01-01",end_date="2015-01-30",type="xts") > usdchf=data.frame(usdchf) > usdchf=(log(usdchf$usdchf))
We are converting the usdchf into a data frame, and then converting into log format to structure our time series in terms of returns.
Now, we will attempt to model this time series with the Kalman Filter using the KFAS library.
> #Kalman Filter > library(KFAS) Warning message: package ‘KFAS’ was built under R version 3.5.1 > logmodel <- SSModel(usdchf ~ SSMtrend(1, Q = 0.01), H = 0.01) > out <- KFS(logmodel) > ts.plot(ts(usdchf), out$a, out$att, out$alpha, col = 1:4) > title("CHF/USD")
Let’s go through the above.
SSModel denotes “state space model”, and observe that we are regressing the CHF/USD time series against the SSMtrend, which denotes our smoothed estimates, or state predictions one-step ahead of that of the actual series.
Q and H denote our unconstrained time-invariant covariance estimates. The steps to estimate these can be quite complex, so for our purposes I’m going to set these to a default value of 0.01.
When we plot our time series, here is what we come up with:
We can see that our a, att, and alpha series are adjusting to the shock instantaneously.
- a: One-step-ahead predictions of states
- att: Filtered estimates of states
- alphahat: Smoothed estimates of states
Let’s now combine the above into a data frame along with our original series and see what we come up with:
> df<-data.frame(usdchf,out$a[1:20],out$att[1:20],out$alpha[1:20]) > View(df) > col_headings<-c("usdchf","a","att","alpha") > names(df)<-col_headings > View(df)
Here is the data frame we come up with:
Again, we can see that our estimates are moving largely in line with the CHF/USD.
One commenter below made quite a good point about the Kalman Filter. Its purpose is not to “predict” a shock per se, but the advantage of the model is that it adjusts for a shock much faster than a traditional time series model would. Let’s illustrate this using ARIMA.
How does ARIMA perform?
When we run this model using ARIMA, it is notable that the model fails to adjust significantly for the shock and does not give us a particuarly clear direction regarding trend.
Let’s firstly run and plot our ARIMA model:
> #ARIMA > library(tseries) ‘tseries’ version: 0.10-45 ‘tseries’ is a package for time series analysis and computational finance. See ‘library(help="tseries")’ for details. > library(forecast) > fitusdchf<-auto.arima(usdchf[1:20]) > forecastedvalues_ln=forecast(fitusdchf,h=10) > plot(forecastedvalues_ln)
We yield an ARIMA (0, 1, 0) configuration.
We see that when we run the ARIMA model on 20 days of data (including the shock), the confidence intervals of the ARIMA model still remain quite large, and it is therefore hard to predict future values.
So, we’ve seen how the Kalman Filter adjusted to the sudden movement in the USD/CHF. Let’s take another example of a currency shock. When Britain voted for “Brexit” in June 2016, we saw the GBP/USD subsequently plunge.
How well would the Kalman Filter have modelled this? Let’s find out!
As in the example of USD/CHF, we download our GBP/USD data from Quandl and run the Kalman Filter:
require(Quandl) library(KFAS) gbpusd = Quandl("FRED/DEXUSUK", start_date="2016-01-01",end_date="2016-12-31",type="xts") gbpusd=data.frame(gbpusd) gbpusd=(log(gbpusd$gbpusd)) logmodel <- SSModel(gbpusd ~ SSMtrend(1, Q = 0.01), H = 0.01) out <- KFS(logmodel) df<-data.frame(gbpusd,out$a[1:251],out$att[1:251],out$alpha[1:251]) View(df) col_headings<-c("gbpusd","a","att","alpha") names(df)<-col_headings View(df) ts.plot(ts(gbpusd[100:150]), out$a[100:150], out$att[100:150], out$alpha[100:150], col = 1:4) title("GBP/USD")
Here is a plot of our data. Again, we see that a, att, and alpha are adjusting quickly to the change:
Here are the a, att, and alpha statistics:
Again, if we try plotting using ARIMA, we see that the forecast range is a lot wider, which indicates that our model has not properly accounted for the time series shock:
library(tseries) library(forecast) fitgbpusd<-auto.arima(gbpusd[1:125]) forecastedvalues_ln=forecast(fitgbpusd,h=100) plot(forecastedvalues_ln)
In this tutorial, you have learned:
- Importance of adjusting for time series shocks
- How to implement a Kalman Filter using KFAS in R
- How to interpret output from a Kalman Filter
- Why the Kalman Filter is a suitable model for modelling time-series shocks
Many thanks for reading this tutorial, and please leave any questions you may have in the comments below.