Trading with Support Vector Machines (SVM)

[This article was first published on Quintuitive » R, 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.

Finally all the stars have aligned and I can confidently devote some time for back-testing of new trading systems, and Support Vector Machines (SVM) are the new “toy” which is going to keep me busy for a while.

SVMs are a well-known tool from the area of supervised Machine Learning, and they are used both for classification and regression. For more details refer to the literature.

It seems to me that the most intuitive application for trading is regression, so let’s start by building an SVM regression model.

Following our experience with ARMA+GARCH models, we will start by trying to forecast returns, instead of prices. Likewise, in our first tests, we will use only the returns of the previous 5 days as the features determining the return of a particular day. We will start with history of 500 days as the training set.

In more mathematical terms, for the training set we have N features, for each of them we have M samples. We also have M responses.

\begin{pmatrix} F^{1}_{1} & F^{2}_{1} & F^{3}_{1} & ... & F^{N}_{1} \\ F^{1}_{2} & F^{2}_{2} & F^{3}_{2} & ... & F^{N}_{2} \\ F^{1}_{3} & F^{2}_{3} & F^{3}_{3} & ... & F^{N}_{3} \\ \cdots \\ F^{1}_{M} & F^{2}_{M} & F^{3}_{M} & ... & F^{N}_{M} \end{pmatrix} \Rightarrow \begin{pmatrix} R_{1} \\ R_{2} \\ R_{3} \\ \cdots \\ R_{M} \end{pmatrix}

Given a row of feature values, the left matrix, the SVM is trained to produce the response value. In our specific example, we have five columns (features), each column corresponding to the returns with a different lag (from 1 to 5). We have 500 samples and the corresponding responses.

\begin{pmatrix} F^{1}_{1} & F^{2}_{1} & F^{3}_{1} & F^{4}_{1} & F^{5}_{1} \\ F^{1}_{2} & F^{2}_{2} & F^{3}_{2} & F^{4}_{2} & F^{5}_{2} \\ F^{1}_{3} & F^{2}_{3} & F^{3}_{3} & F^{4}_{3} & F^{5}_{3} \\ \cdots \\ F^{1}_{500} & F^{2}_{500} & F^{3}_{500} & F^{4}_{500} & F^{5}_{500} \end{pmatrix} \Rightarrow \begin{pmatrix} R_{1} \\ R_{2} \\ R_{3} \\ \cdots \\ R_{500} \end{pmatrix}

Once the SVM is trained on this set, we can start feeding it with sets of five features, corresponding to the returns for the five previous days, and the SVM will provide us with the response, which is the forecasted return. For example, after training the SVM on the previous 500 days, we will use the returns for days 500, 499, 498, 497 and 496 (these are ours (F^{1}_{501}, F^{2}_{501}, F^{3}_{501}, F^{4}_{501}, F^{5}_{501}) as the input to obtain the forecasted return for day 501.

From all the packages available in R, I decided to choose the e1071 package. A close second choice was the kernlab package, which I am still planning to try in the future.

Then I tried a few strategies. First I tried something very similar to the ARMA+GARCH approach – the lagged returns from the five previous days. I was quite surprised to see this strategy performing better than the ARMA+GARCH (this is the home land of the ARMA+GARCH and I would have been quite happy just with comparable performance)!

Next, I tried to the same five features, but trying to select the best subset. The selection was done using a greedy approach, starting with 0 features, and interactively adding the feature which minimizes the error best. This approach improved things further.

Finally, I tried a different approach with about a dozen features. The features included returns over different period of time (1-day, 2-day, 5-day, etc), some statistics (mean, median, sd, etc) and volume. I used the same greedy approach to select features. This final system showed a very good performance as well, but it took a hell of a time to run.

Time to end this post, the back-testing results have to wait. Until then you can play with the full source code yourself. Here is an example of using it:



tt = get( getSymbols( "^GSPC", from="1900-01-01" ) )

rets = na.trim( ROC( Cl( tt ), type="discrete" ) )

# only the first two features so that we may see some results in reasonable time
data = svmFeatures( tt )[,c(1,2)]

rets = rets[index(data)]
data = data[index(rets)]

stopifnot( NROW( rets ) == NROW( data ) )

fore = svmComputeForecasts(
               featureSelection="all" )

To leave a comment for the author, please follow the link and comment on their blog: Quintuitive » R. 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)