Kalman Filter: Modelling Time Series Shocks with KFAS in R

[This article was first published on R Programming – DataScience+, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.


    1. Advanced Modeling


    1. R Programming
    2. Time Series

    When it comes to time series forecasts, conventional models such as ARIMA are often a popular option. While these models can prove to have high degrees of accuracy, they have one major shortcoming – they do not typically 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 currency market as an example. An currency pair 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 sudden 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: USD/CHF

    Firstly, let’s download data for USD/CHF 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
    > currency = Quandl("FRED/DEXSZUS", start_date="2010-01-01",end_date="2018-09-29",type="xts")
    > currency=data.frame(currency)
    > currency=(log(currency$currency))

    We are converting the currency data 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(currency ~ SSMtrend(1, Q = 0.01), H = 0.01)
    > out <- KFS(logmodel)
    > ts.plot(ts(exp(currency[1232:1274]), exp(out$a[1232:1274]), exp(out$att[1232:1274]), exp(out$alpha[1232:1274])
    > title("USD/CHF")

    Let’s go through the above.

    SSModel denotes “state space model”, and observe that we are regressing the USD/CHF 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:

    kalman filter

    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

    For comparison purposes, we will also compute a 10-day moving average to compare smoothing performance with that of the Kalman Filter.


    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(exp(currency[1232:1274]), exp(out$a[1232:1274]), exp(out$att[1232:1274]), exp(out$alpha[1232:1274]),sma10$sma10[1232:1274])
    > View(df)
    > col_headings<-c("currency","a","att","alpha","sma10")
    > names(df)<-col_headings
    > View(df)

    Here is the data frame generated:


    As elaborated in this post on Quora, there are some instances in which high-frequency data – or filtering to extract information from a noisy signal and predict the future state, is the most appropriate use of the Kalman Filter. On the other hand, smoothing relies more on past data, since there are some instances where averaging recent measurements may prove more accurate than using the most recent one.

    In this particular instance, we can see that at time period 31 (which was one day before the sharp fall in the USD/CHF), the value of alpha (the smoothing estimate of state) dropped significantly from 1.002 to 0.97, and then down to 0.915 on the day of the actual shock.

    This makes sense intuitively, as the currency was trading at a level of 0.9658 roughly one month prior. In this regard, the smoothing estimator allowed for a better prediction of the signal than using the filtering estimate a, which did not adjust for the shock until time period 33.

    Additionally, notice that a 10-day SMA is also being used. Even though alpha is being used as a smoother, we see that alpha still adjusted for the shock much quicker than the simple moving average – meaning that the Kalman Filter has given a better prediction than simply using a basic smoothing technique.

    Another Example: GBP/USD

    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:

    gbpusd = Quandl("FRED/DEXUSUK", start_date="2016-01-01",end_date="2016-12-31",type="xts")
    logmodel <- SSModel(gbpusd ~ SSMtrend(1, Q = 0.01), H = 0.01)
    out <- KFS(logmodel)
    df<-data.frame(exp(currency[100:150]), exp(out$a[100:150]), exp(out$att[100:150]), exp(out$alpha[100:150]),sma10$sma10[100:150])
    ts.plot(ts(exp(currency[100:150])), exp(out$a[100:150]), exp(out$att[100:150]), exp(out$alpha[100:150]), col = 1:4)

    Here is a plot of our data. Again, we see that alpha adjusts downwards to a level of 1.438 one day before the shock at t=22:

    gbpusd kalman filter

    Here are the a, att, and alpha statistics:

    a att alpha

    Again, we see that the 10-day SMA takes nearly 10 days to adjust fully for the shock, indicating once again that the smoothing parameter alpha has still proven superior in adjusting for the large change in the currency level.


    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.

    Related Post

    1. Dow Jones Stock Market Index (4/4): Trade Volume GARCH Model
    2. Dow Jones Stock Market Index (3/4): Log Returns GARCH Model
    3. Leaf Plant Classification: Statistical Learning Model – Part 2
    4. NYC buses: C5.0 classification with R; more than 20 minute delay?
    5. NYC buses: Cubist regression with more predictors

    To leave a comment for the author, please follow the link and comment on their blog: R Programming – DataScience+.

    R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
    Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

    Never miss an update!
    Subscribe to R-bloggers to receive
    e-mails with the latest R posts.
    (You will not see this message again.)

    Click here to close (This popup will not appear again)