Forecasting with HoltWinters Exponential Smoothing

I recently enrolled in the QuantInsti Executive Programme in Algorithmic Trading and one of the areas in quantitative finance that interests me greatly is the analysis of financial time series. During the course we will take on a massive project to build our own trading strategy with the help of a mentor, and in attempt to familiarize myself with the work, I built this simple strategy using the HoltWinters exponential smoothing.

I hope that others may find this useful. I learnt a great deal from writing the following blog post:

1. Build an indicator to forcast a share 1 day into the future using the HoltWinters Exponential Smoothing method.
2. Backtest the strategy and show performance metrics

“The Holt-Winters method was suggested by Holt (1957) and Winters (1960), who were working in the School of Industrial Administration at Carnegie Institute of Technology, and uses exponentially weighted moving averages to update estimates of the seasonally adjusted mean (called the level ), slope, and seasonals. The” (Introductory Time Series with R, By Paul S.P. Cowpertwait, Andrew V. Metcalfe)

Getting Starterd

We will make use of the following packages

require ( quantmod )
require ( fArma )
require ( PerformanceAnalytics )

Get closing price data for the S&P500 from Yahoo Finance

getSymbols( "^GSPC", from="2000-01-01" )

## [1] "GSPC"

ClosingPrices = Cl(GSPC)

##           GSPC.Close
## 2000-01-03   1455.22
## 2000-01-04   1399.42
## 2000-01-05   1402.11
## 2000-01-06   1403.45
## 2000-01-07   1441.47
## 2000-01-10   1457.60

Set up the function to predict the returns one day forward and return 1 for a long and -1 for a short

HWPredictDaily = function(data){

#Check that there are enough data
if(length(data) < 501){
return("Need at least 501 data points, days")
}

# Model the daily returns instead of the prices. There are multiples reasons:
# 1. this way financial series usually becomes stationary, we need some way to "normalize" a series, etc.
# 2. We use the diff and log function to compute the daily returns instead of percentages.
# 3. Not only this is a standard practice in statistics, but it also provides a damn good approximation to the discrete returns.
dailyReturns = diff( log( as.ts(data) ) )

#Format the data coming in as a time series
timeSeries = ts(dailyReturns, frequency = 250)

#Finding the Optimal Values
#Automatically picks the model with the lowest SSE and returns the predicted value
predictedValue <- as.numeric(predict( HoltWinters(timeSeries), n.ahead= 1))

#Set the trading signal to 1 if the predicted value is greater than 0 and -1 if not
if(predictedValue >= 0){
}
else if (predictedValue < 0){
}
}

Backtest

Generate a List of signals for the closing price data

#This is our result set
signalList <- c()

#Generate signals for the entire dataset, Returns a List
for(i in 1:length(ClosingPrices)){

#Check that there are enough data points
if(i > 501){
#counter to make sure we only optimise for 500 datapoints
counter <- (i-500)

#Generate signal
signal <- HWPredictDaily(ClosingPrices[counter:i])

signalList <-c(signalList, signal)
}
else {
#Hold no position (signal = 0)
signal <- 0

signalList <-c(signalList, signal)
}
}

Calculate the Change in Price for the underlying Asset which is the S&P500 in this case

#Let the first element by a 0% price change due to there not being a price change on t = 0
underlyingReturns <- c(0)

#Generate underlying assets daily returns. Returns a List
for(i in 2:length(ClosingPrices)){
pctChange <- (as.numeric(ClosingPrices[i]) / as.numeric(ClosingPrices[i-1])) - 1
underlyingReturns <- c(underlyingReturns, pctChange)
}

Next calculate the daily returns for our portfolio

#Let the first element by a 0% price change in equity because I cant trade on the first day
#Due to lagging the indicator
dailyReturns <- c(0)

#Generate the equity curve, Returns a List
for(i in 2:length(ClosingPrices)){
#Here we lag the signal returns by one day and multiply it by the underlyings daily return
dayReturn <- as.numeric(signalList[i-1]) * as.numeric(underlyingReturns[i])

dailyReturns <- c(dailyReturns, dayReturn)
}

Strategy Reporting

#Get the dates from the index of the S&P500 Closing Prices
dates <- index(ClosingPrices)

#Create an xts object to use in the reporting of the portfolio
performance <- as.xts(dailyReturns, order.by = dates)
performance <- setNames(performance, 'returns')

#Total Returns
Return.cumulative(performance)

##                   returns
## Cumulative Return 5.745615

#Equity curve, Daily returns, and Drawdowns
charts.PerformanceSummary(performance)

#A table of the top 10 drawdowns
table.Drawdowns(performance, top = 10)

##          From     Trough         To   Depth Length To Trough Recovery
## 1 2008-12-08 2009-04-15 2010-05-21 -0.3957   366       88     278
## 2 2004-02-12 2008-02-07 2008-09-29 -0.2928   1166     1004     162
## 3 2012-06-25 2013-09-19 2015-09-08 -0.2599   806       311     495
## 4 2003-03-20 2003-04-23 2003-07-25 -0.1440     89       24       65
## 5 2002-10-09 2003-01-24 2003-03-17 -0.1397   109       74       35
## 6 2008-09-30 2008-10-06 2008-10-09 -0.1195     8         5       3
## 7 2011-09-26 2011-11-03 2011-12-08 -0.1185     53       29       24
## 8 2008-10-13 2008-10-13 2008-10-22 -0.1158     8         1       7
## 9 2008-11-17 2008-11-19 2008-11-21 -0.0944     5         3       2
## 10 2002-02-22 2002-03-07 2002-04-10 -0.0934     33       10       23