Friday 12 May 2017

Addition of Graph to Backtesting in R

After the first post on backtesting a trading strategy in Excel, we followed it up by another post that added a graph in Excel to make it more meaningful. Later, we replicated this trading strategy in R. In this post, we will add a graph showing SMA and LMA versus time in R like we had done in the case of Excel.

We will add a line to the earlier function that we wrote in the last post. This line will return the dataframe that is calculated by the function. We need this data frame as this data frame contains the SMA and LMA values that are needed for plotting the graph in R. The line is 
return(data_frame) added just before the close of the function as shown in bold below:

MovingAverageStrategy <- function(file,SMAStart,LMAStart,TradeStart)
{

# read the downloaded .csv file
data_frame <- read.table(file,sep=",", header=TRUE)
ntotal = nrow(data_frame)

# initialize the new columns
data_frame$TradeSignal = ""
data_frame$TradePrice = 0
data_frame$Return = 0

# for loop to calculate SMA, LMA, TradeSignal, TradePrice, Return 
for (i in 1:ntotal){
  if(i>=SMAStart){
    data_frame$SMA[i] = mean(data_frame$Close[(i-(SMAStart-1)):i])
  }
  if(i>=LMAStart){
    data_frame$LMA[i] = mean(data_frame$Close[(i-(LMAStart-1)):i])
  }
  if(i>=TradeStart){
    data_frame$TradeSignal[i] = ifelse((data_frame$SMA[i-1]<data_frame$LMA[i-1])&&(data_frame$SMA[i]>data_frame$LMA[i]),"BUY",ifelse((data_frame$SMA[i-1]>data_frame$LMA[i-1])&&(data_frame$SMA[i]<data_frame$LMA[i]),"SELL",""))

    data_frame$TradePrice[i] = ifelse(data_frame$TradeSignal[i-1] != "",data_frame$Open[i],data_frame$TradePrice[i-1])

    data_frame$Return[i] = 100*ifelse((data_frame$TradeSignal[i-1] != "")&&(data_frame$TradePrice[i-1] != 0),ifelse( data_frame$TradeSignal[i-1] == "BUY",1-(data_frame$TradePrice[i]/data_frame$TradePrice[i-1]),(data_frame$TradePrice[i]/data_frame$TradePrice[i-1])-1),0)
      }
}

# Trade Calculations
TotalReturns=as.numeric(data_frame$Return)
TotalReturns = sum(TotalReturns)
Positive_trades=length(data_frame$Return[data_frame$Return>0])
Negative_trades=length(data_frame$Return[data_frame$Return<0])
no_of_trades=Positive_trades + Negative_trades

Metrics = c("Positive trades","Negative trades","No.of Trades","Total Return")
Values = c(Positive_trades,Negative_trades,no_of_trades,paste(round(TotalReturns,digits=4),"%",sep=""))
tsummary = data.frame(Metrics,Values)
tsummary
return(data_frame)
}

After the above function has been modified and run, the following command is run to fetch the values of the calculation into a data frame.

data_frame <- MovingAverageStrategy("D:\\blog\\apple_data.csv",5,20,2)

Note that the first column containing dates. But, R may not recognize it as being of type Date. So, let us cast it using below command, else, we might end up an absurd graph otherwise. We use below command:

data_frame[,1] = as.Date(data_frame[,1],format='%d-%b-%y')

Once, this is run, we are ready to run the first draft of the graph using below command:

plot(data_frame[,1],data_frame[,7],col="dodgerblue", type="l",main="SMA & LMA versus Date",xlab="Date",ylab="SMA & LMA",lwd=2)

plot is used draw a graph. The argument, data_frame[,1], is Date column and values lie on the x-axis. data_frame[,7] argument contains the calculated SMA values and are the values on the y-axis. col parameter is set to color "dodgerblue". type is set to "l" so that a line is plotted. main parameter used is set to the title of the graph and is assigned a value, "SMA & LMA versus Date". xlab and ylab parameters are used to the titles for the x-axis and y-axis and are set to "Date" and "SMA & LMA", respectively. Lastly, lwd is line width and default value is 1. To make the line appear bold, the value of lwd is set to 2. Once above command is run, the following plot is seen:














To add LMA, we run below command:

lines(data_frame[,1],data_frame[,8],col="darkorange2",lwd=2)

The arguments are self-explanatory. We get the LMA line on the plot as shown below:














Once LMA is added, we just need to add the legend as shown by the command below:

legend("topleft",legend=c("SMA","LMA"),col=c("dodgerblue","darkorange2"),bg="white",lwd=2,cex=0.60)

The argument "topleft" specifies the position of the legend on the plot. The legend argument specifies the text and col specifies the color corresponding to the text. bg specifies the background color of the legend. lwd, like before specifies the line width and cex specifies the level of magnification of the legend of the text and symbols and is set to 0.60. The final plot is shown below:















The plot is identical to the one shown in the case of excel. With this comparison of graphs, we come to the end of this post.

Monday 1 May 2017

Backtesting Trading Strategy in R

We backtested a simple trading strategy earlier in Excel.

The same strategy has been implemented in R in this post. RStudio (version 0.99.902) was used as IDE. In this case, a function called MovingAverageStrategy has been written that takes four arguments: the file location, start row for SMA calculation, start row for LMA calculation and the row from which trading calculation is to be commenced. The code is fairly simple to understand and uses for loop and ifelse constructs.

The data for this code has been downloaded in the same way as in the post on Excel. Two columns called SMA and LMA have been added in the .csv file with values added for the first 4 and first 19 rows, respectively.

The code is shown below:

MovingAverageStrategy <- function(file,SMAStart,LMAStart,TradeStart)
{

# read the downloaded .csv file
data_frame <- read.table(file,sep=",", header=TRUE)
ntotal = nrow(data_frame)

# initialize the new columns
data_frame$TradeSignal = ""
data_frame$TradePrice = 0
data_frame$Return = 0

# for loop to calculate SMA, LMA, TradeSignal, TradePrice, Return 
for (i in 1:ntotal){
  if(i>=SMAStart){
    data_frame$SMA[i] = mean(data_frame$Close[(i-(SMAStart-1)):i])
  }
  if(i>=LMAStart){
    data_frame$LMA[i] = mean(data_frame$Close[(i-(LMAStart-1)):i])
  }
  if(i>=TradeStart){
    data_frame$TradeSignal[i] = ifelse((data_frame$SMA[i-1]<data_frame$LMA[i-1])&&(data_frame$SMA[i]>data_frame$LMA[i]),"BUY",ifelse((data_frame$SMA[i-1]>data_frame$LMA[i-1])&&(data_frame$SMA[i]<data_frame$LMA[i]),"SELL",""))

    data_frame$TradePrice[i] = ifelse(data_frame$TradeSignal[i-1] != "",data_frame$Open[i],data_frame$TradePrice[i-1])

    data_frame$Return[i] = 100*ifelse((data_frame$TradeSignal[i-1] != "")&&(data_frame$TradePrice[i-1] != 0),ifelse( data_frame$TradeSignal[i-1] == "BUY",1-(data_frame$TradePrice[i]/data_frame$TradePrice[i-1]),(data_frame$TradePrice[i]/data_frame$TradePrice[i-1])-1),0)
      }
}

# Trade Calculations
TotalReturns=as.numeric(data_frame$Return)
TotalReturns = sum(TotalReturns)
Positive_trades=length(data_frame$Return[data_frame$Return>0])
Negative_trades=length(data_frame$Return[data_frame$Return<0])
no_of_trades=Positive_trades + Negative_trades

Metrics = c("Positive trades","Negative trades","No.of Trades","Total Return")
Values = c(Positive_trades,Negative_trades,no_of_trades,paste(round(TotalReturns,digits=4),"%",sep=""))
tsummary = data.frame(Metrics,Values)
tsummary
}

The first 36 lines of the calculation output is shown below and that looks to be in line with the formulas corresponding to the trading strategy.















The result of running the code is shown below and compares well with the results seen on the same strategy in Excel.