For our investments class, we had to conceive and test a trading strategy using technical analysis. As a lover of R, I decided to reference some code I had seen earlier on Modern Toolmaking via R-Bloggers:Backtesting a Simple Stock Trading Strategy. The example provided R code that was very helpful in getting me to understand the math behind the testing part (as opposed to the conceptually easy trading rules).

Only one problem stood out: our professor wanted us to use SAS!

SAS is still an industry leader and will be surely be seen in our future jobs, so I took the opportunity to try to use the concept behind the R code and translate it to SAS' language. Though I didn't have time to implement the extra features found in the excellent PerformanceAnalytics package for R, I did manage to replicate the basic rule selection and calculation of cumulative return and win/loss. So with that said, I will start with the R script I started from:

^{?}View Code RSPLUS

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | #inspired by code here: http://moderntoolmaking.blogspot.com/2011/09/backtesting-simple-stock-trading.html #Set important variables ticker <- '^GSPC' start <- as.Date('2005-01-01') end <- as.Date('2012-04-01') period <- 10 tradingCost <- .001 riskFree <- .035 #End set important variables library(quantmod) library(PerformanceAnalytics) bias <- function(x,period){ biasResult <- ((x-SMA(x,n=period))/SMA(x,n=period))*100 } tradingRule <- function(stock,numDays) { close <- Cl(stock) volume <- Vo(stock) position <- ifelse(((bias(close,numDays) + bias(volume,numDays)) > 0),1,-1) } stock <- getSymbols(ticker, from=start, to=end, auto.assign=FALSE) position <- tradingRule(stock,period) underlyingReturn <- dailyReturn(Cl(stock),type='arithmetic') ruleReturn <- ifelse(Lag(position,k=1)==Lag(position,k=2),underlyingReturn * Lag(position,1),underlyingReturn * Lag(position,1) - tradingCost) ruleReturn[1:(period+1)] <- 0; names(underlyingReturn) <- 'SP500' names(ruleReturn) <- 'Trading' charts.PerformanceSummary(cbind(ruleReturn,underlyingReturn), colorset=redfocus) |

The code is minimized since it is not too important. Keep in mind that R allows us to download data from Yahoo, so I use quantmod to do that, rather than including it in a data step as I did in SAS. Here is the code minus the data step containing OHLC data to conserve screen space:

^{?}View Code SAS

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | /*Set length of moving average and costs of trading*/ %let n=10; %let cost=.001; /*Find closing moving average*/ data work.GSPC (drop = s); retain s; set work.GSPC; s = sum(s,Close,-lag&n(Close)); closeMA = s / &n; run; /*find volume moving average*/ data work.GSPC (drop=s); retain s; set work.GSPC; s = sum(s,Volume,-lag&n(Volume)); volumeMA = s / &n; run; /*find cumulative returns*/ data work.GSPC; set work.GSPC; dailyReturn = (close - lag(close))/lag(close); retain cumulativeReturn 1; if _N_ >1 then cumulativeReturn = cumulativeReturn*(1+dailyReturn); if dailyReturn > 0 then assetWinLoss = 1; else assetWinLoss = 0; run; /*run trading strategy*/ data work.GSPC (drop=temp); set work.GSPC; retain flag 0; retain tradingReturn 1; retain tradingwinloss 0; temp = (lag(close)-lag(closeMA))*(lag(volume)-lag(volumeMA)); /*Find Portfolio Return*/ if _N_ > &n then if temp > 0 then flag = 1; else flag = -1; if _N_ >1 then tradingReturn = tradingReturn * (1 + (dailyReturn * flag)); if lag(flag) ~= flag and _N_ >1 then tradingReturn = tradingReturn * (1-&cost); /*Make a portfolio to test alpha/beta*/ portfolioValue = 1000 * tradingReturn; /*Find win/loss of our strategy*/ if (dailyReturn* flag) < 0 then tradingWinLoss = 0; else tradingWinLoss = 1; run; proc means; var tradingWinLoss assetWinLoss; run; |

First of all, our data is entirely self-contained within the SAS Code. We use a data step and input cards directly, rather than using code. In the R version, quotes are downloaded directly from Yahoo Finance using an addon called quantmod. Example:

^{?}View Code SAS

1 2 3 4 5 6 | data GSPC; input Date Open High Low Close Volume; datalines; 1/3/05 1211.92 1217.8 1200.32 1202.08 1510800000 1/4/05 1202.08 1205.84 1185.39 1188.05 1721000000 1/5/05 1188.05 1192.73 1183.72 1183.74 1738900000 |

To get a moving average, we use an algorithm with a macro variable and retain a rolling selection of length &n observations. We then divide this total by &n to get the simple moving average and drop the temporary variable used to hold the rolling total:

^{?}View Code SAS

1 2 3 4 5 6 7 8 9 10 11 | /*Set length of moving average and costs of trading*/ %let n=10; %let cost=.001; /*Find closing moving average*/ data work.GSPC (drop = s); retain s; set work.GSPC; s = sum(s,Close,-lag&n(Close)); closeMA = s / &n; run; |

After that, we also use the same technique to find the simple moving average of volume. After those data steps, we find the daily+cumulative returns of the underlying assets and its win/loss percentage. We do these steps all in separate data steps for ease of debugging.

^{?}View Code SAS

1 2 3 4 5 6 7 8 9 | /*find cumulative returns*/ data work.GSPC; set work.GSPC; dailyReturn = (close - lag(close))/lag(close); retain cumulativeReturn 1; if _N_ >1 then cumulativeReturn = cumulativeReturn*(1+dailyReturn); if dailyReturn > 0 then assetWinLoss = 1; else assetWinLoss = 0; run; |

Our next data step runs the trading strategy. We make the assumption that we will buy at close instantaneously if our rule is triggered at the end of the trading day. We then set a flag that equals our position for that next day. 1 for long, -1 for short, and 0 for no position. We multiply the underlying asset return times the “flag” variable to get our return, then do the same calculation of cumulative return and win/loss percentage:

^{?}View Code SAS

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | /*run trading strategy*/ data work.GSPC (drop=temp); set work.GSPC; retain flag 0; retain tradingReturn 1; retain tradingwinloss 0; temp = (lag(close)-lag(closeMA))*(lag(volume)-lag(volumeMA)); /*Find Portfolio Return*/ if _N_ > &n then if temp > 0 then flag = 1; else flag = -1; if _N_ >1 then tradingReturn = tradingReturn * (1 + (dailyReturn * flag)); if lag(flag) ~= flag and _N_ >1 then tradingReturn = tradingReturn * (1-&cost); /*Make a portfolio to test alpha/beta*/ portfolioValue = 1000 * tradingReturn; /*Find win/loss of our strategy*/ if (dailyReturn* flag) < 0 then tradingWinLoss = 0; else tradingWinLoss = 1; run; |

Finally, we run a proc means to give our win/loss percentage for our trading rules and underlying asset

^{?}View Code SAS

1 2 3 | proc means; var tradingWinLoss assetWinLoss; run; |