4
$\begingroup$

I'm currently working on SVR to predict stock market returns and I would like to know if anyone of you could give me some advise regarding interesting papers on it.

These are the articles I'm working with:

  1. Cao, L., Tay, F.E.H. (2001). Financial forecasting using support vector machines. Neural Computing and Applications, 10:184-192.
  2. Wang,L., Zhu, J. (2010). Financial market forecasting using a two-step kernel learning method for the support vector regression. Annals of Operations Research, 174:103-120. [DOI]

The second paper is really interesting but quite hard to put into practise as the author is using a two-step kernel learning method to find the optimal kernel function for the SVR. All the papers I found does not provide many details about tuning parameters (C and sigma for the Gaussian kernel).

I'm using R for my computations (Kernlab and e 1071 packages).

$\endgroup$
1
$\begingroup$

Correctly tuning C and sigma is of course essential to getting good forecasts. It looks like what the authors of the second paper are doing is a time-series cross-validation, where they are selecting C and sigma using a training set at each cross-validation step. I've written some R code for cross-validating generic time-series forecasting functions, which might help solve this problem. Lets try to replicate the plain SVR method of Wang,L., Zhu, J. (2010). First we load the data and build the moving averages referenced in the paper:

set.seed(60444)
library(quantmod)
library(caret)
library(forecast)
library(foreach)
source('https://raw.github.com/zachmayer/cv.ts/master/R/cv.ts.R')

#Load data
getSymbols('SPY', from='1900-01-01')
SPY <- adjustOHLC(SPY)

#Define target
y <- na.omit(ClCl(log(SPY)))

#Define predictors
periods <- c(1, 5, 10, 20, 50)
X <- do.call(cbind, lapply(periods, function(x) SMA(y, n=x)))
colnames(X) <- paste('R', periods, sep='')

#Omit NAs and subset to our timeperiod
#(1997-11-03 is the first Monday of November)
dat <- na.omit(xts(cbind(Next(y), X), order.by=index(X)))
dat <- dat['1997-11-03::']
y <- ts(dat[,1])
X <- dat[,-1]

Note that we've loaded the cv.ts function from my github repository. Next we define the SVR forecast function, following the method described in the paper of fitting the SVM to 150 observations, tuning the kernel parameters on the next 10 observations, and then re-fitting the SVM to observations 11:160:

#Define forecasting function
#Modeled after https://raw.github.com/zachmayer/cv.ts/master/R/forecast%20functions.R
forecastSVM <- function(x, h, xreg, newxreg=NULL, ...) {
  require(caret)
  stopifnot(length(x)==160)

  #Train SVM on first 150 points, and tune the kernel on the next 10 points
  myData <- data.frame(target=as.numeric(x), xreg)
  fit <- train(target~., data=myData,
                trControl=trainControl(index=list(Resample1=1:150)), 
               ...)

  #Retrain the SVM on exactly 150 points
  fit2 <- train(target~., data=myData[11:160,], tuneGrid=fit$bestTune,
                trControl=trainControl(number=1), 
                ...)


  predict(fit2, newdata=newxreg)
}

Next we define the cross-validation parametersm using the tseriesControl function from the github repo:

cvParameters <- tseriesControl(stepSize=10, maxHorizon=10, minObs=160, fixedWindow=TRUE)

We require 160 observations to fit the model at each step (150 for training, 10 for tuning the kernel), and after forecasting at each point in time we forecast out 10 periods (maxHorizon) and then step forward 10 periods to the next forecasting point.

After defining the prediction function and the cross validation method we wish to use, it's easy to run the cross validation:

model <- cv.ts(y, forecastSVM, xreg=X, method='svmRadial', tuneLength=20,
               tsControl=cvParameters)

The cv.ts function makes use of the foreach package, so you can register a parallel backend if you wish, to speed up the cross-validation. The cross-validation function outputs a matrix where the rows are the points in time at which the model was fit, and the columns are the 1-10 day forecasts from that point in time:

#Construct equity curves
A <- na.omit(as.numeric(t(model$actuals)))
P <- as.numeric(t(model$forecasts))[1:length(A)]
index <- seq.Date(from=as.Date('1997-11-01'), by=1, length.out=length(A))
returns <- xts(sign(P)*A, order.by=index)
BnH <- xts(A, order.by=index)

#Plot equity curves
library(PerformanceAnalytics)
chartData <- data.frame(Buy.and.Hold=BnH, SVR=returns)
charts.PerformanceSummary(chartData, geometric=TRUE)
table.Arbitrary(chartData, 
                metrics=c('Return.annualized', 'SharpeRatio.annualized', 
                'DownsideDeviation', 'maxDrawdown'), 
                metricsNames=c('annual return', 'annual sharpe', 
                 'downside deviation', 'max drawdown'))

As you can see from the table and the equity curve, the SVR strategy I've defined is terrible.

                   Buy.and.Hold          SVR
annual return       0.006768856 -0.005348179
annual sharpe       0.141642865 -0.111909928
downside deviation  0.002143119  0.002193550
max drawdown        0.161992521  0.196183743

RPlot

I can see 2 possible reasons why this may be:

  1. I haven't defined the input features correctly. I had a bit of trouble understanding this part of the paper (bottom of page 109, top of page 110), so I probably did it wrong.
  2. I'm tuning the SVM parameters using a heuristic for choosing gamma and a grid search for choosing cost. This could probably be improved to more closely match the results in the paper. At the least, I could change this to use a grid search for both parameters.
$\endgroup$
  • $\begingroup$ Tanks a lot Zach for your very interesting and helpful answer! I'm actually using a C-classification SVM ("C-svc" in the Kernlab package) making a one-step ahead forecast of the return's sign. Each time I'm fitting a model, I compute the 10-fold CV and for a given C and a moving sigma. I then average all the CV errors in a matrix a make a levelplot (latticepackage). I repeat the same work but with a varying sigma parameter thsi time. It finally gives me the optimal tuning parameters for a whole strategy. $\endgroup$ – marino89 Sep 6 '12 at 10:28
  • $\begingroup$ @marino89: 10-fold cv might not give you the best values for the parameters. Maybe you could "roll-through" each training period 10 observations at a time, always testing on future data? $\endgroup$ – Zach Sep 6 '12 at 13:17
  • $\begingroup$ Yes I could do what you suggest. The problem is that I re-fitting my model at each close so that to forecast the next day return's sign (and so adding a new value to my "rolling" training set while removing the oldest one). So, theoretically, I sould find out the optimal tuning parameters for each model. I don't know what is the most relevant to cope with "rolling" training set. $\endgroup$ – marino89 Sep 6 '12 at 13:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.