Search code examples
rquantmod

Backtesting Trading Strategy in R using quantmod: Function and for loop within a Function


I am using R, quantmod and Performanceanalystics packages. As part of a backtesting strategy, I am trying to create a signal/holdings vector that tells me whether I should buy/sell/hold a stock, based on the value of RSI. If RSI<30, buy (so holdings increases by 1), if RSI is between 30 & 50, don't do anything (so holdings remain the same as yesterday). If RSI>=50, sell everything (so holdings become zero). Thereafter, use the dailyReturn() function from Performanceanalytics to calculate and generate a graph of returns.

Note that RSI() is a function that takes "price" and "day", and dailyReturn() function also takes "price"

I have been able to do perfectly fine with the following code below.

library(quantmod)
library(PerformanceAnalytics)
getSymbols("IBM", src= "yahoo", from = "2000-01-01", to ="2015-09-25")

rsi<-RSI(Cl(IBM),14)
rsi<-lag(rsi,1)
rsi[is.na(rsi)] <- 0 

holdings1 <-c() #initialize the vector
holdings1[1:14+1]<-0  #Not holding any shares of IBM from 1st to the 15th day
for (i in 14+2: length(Cl(IBM))){ #assume start buying from the 16th day onwards since we are using RSI where n=14
 if (rsi[i]<30){ #if RSI<30, we buy one share of IBM
  holdings1[i]<-holdings1[i-1]+1  
  } else if (rsi[i]>50){ # if RSI>50, we sell all holdings
  holdings1[i]<-0
 } else 
   holdings1[i]<- holdings1[i-1] # remains the same: if 30<=RSI<=50 we don't do anything, so same holdings as prior
 }
size1<-reclass(holdings1,Cl(IBM))
ret1<-dailyReturn(Cl(IBM))*size1
charts.PerformanceSummary(ret1)

But I am required to create a function called "size1()" that takes in "price" and "day" (Prof says, and I don't do computing). When I try to that, RStudio tells me "Error in lag(rsi, 1) : object 'rsi' not found". Why is that? Is it not legal to create a function or a vector in a function? Or should I structure my code in a different way from the first one above? The code with function(price,day) is below:

library(quantmod)
library(PerformanceAnalytics)
getSymbols("IBM", src= "yahoo", from = "2000-01-01", to ="2015-09-25") #download IBM, from the stipulated range of dates

size1<-function(price,day){
  ris<-RSI(price,day)
  ris<-lag(rsi,1)
  rsi[is.na(rsi)] <- 0
  holdings1<-c()
  holdings1[1:day+1]<-0
 for (i in day+2: length(price)){ #assume start buying from the 15th day onwards since we are using RSI, n=14
  if (rsi[i]<30){ #if RSI<30, we buy one share of IBM
    holdings1[i]<-holdings1[i-1]+1  
  } else if (rsi[i]<50){ # if 30<RSI<50, we don't buy or sell, so that the holdings does not change
    holdings1[i]<-holdings1[i-1]
  } else 
    holdings1[i]<-0 # sell all if RSI>50
  size<-reclass(holdings1,price)
  }
}

ret1<-dailyReturn(Cl(IBM))*size1(Cl(IBM),14)
charts.PerformanceSummary(ret1) 

Solution

  • day+2:length(price) isn't what you expect. : has precedence over + (see ?Syntax). It evaluates to day + (2:length(price)). You want (day+2):length(price). Also note that xts objects are a matrix with an index attribute, and a matrix is just a vector with a dim attribute. Calling length on a matrix returns the total number of observations (the length of the vector). You should use nrow instead.

    It's also good practice to pre-allocate the entire result vector before the for loop. Your current code appends to holdings every time you call holdings[i] <- when i is larger than the current length of holdings.

    Also, your function currently does not return anything. It looks like you intend to return the size object. Note that you do not need to re-create that object at every loop iteration.

    size1 <- function (price, day) {
        rsi <- RSI(price, day)
        rsi <- lag(rsi, 1)
        rsi[is.na(rsi)] <- 0
        holdings1 <- integer(nrow(price))
        # assume start buying from the 15th day onwards,
        # since we are using RSI, n=14
        for (i in (day+1):nrow(price)) {
            if (rsi[i] < 30) {
                # if RSI<30, we buy one share of IBM
                holdings1[i] <- holdings1[i - 1] + 1
            }
            else if (rsi[i] < 50) {
                # if 30<RSI<50, we don't buy or sell,
                # so that the holdings does not change
                holdings1[i] <- holdings1[i - 1]
            } else {
                # sell all if RSI>50
                holdings1[i] <- 0
            }
        }
        reclass(holdings1, price)
    }