Summary

An algorithmic trading strategy is created in this notebook for Tesla using the simple moving average strategy.

A “buy” or “sell” signal/decision will be generated and executed/traded by the algorithm.

Simple moving averages are calculated by taking the arithmetic mean of a stock price for the desired past n days. Here:

Tesla’s stock price from Yahoo Finance from 2017 Jan to 2022 May are used.

The outcome of this algo strategy yields the following results:


Load Packages & Extract Data from Yahoo Finance

library(quantmod)
library(tidyverse)
library(TTR)
library(kableExtra)
library(PerformanceAnalytics)
library(reshape)

#Obtaining stock price data ONLY column 6. xts = extensible time series
TSLA <- getSymbols("TSLA", source="yahoo", auto.assign=FALSE,
                  return.class="xts")[,6]

#Calculate actual log returns
Actual_Ret <- diff(log(TSLA))
colnames(Actual_Ret)  <- "TSLA"

#Choose start date & view price trend
TSLA <- round(TSLA["2017/"],2)
Actual_Ret <- round(Actual_Ret["2017/"],4)

Visualize Actual Stock Price

plot(TSLA, main="TSLA Actual Stock Price (2017Jan - 2022May)")

Visualize Actual Daily Returns

The actual daily returns (difference in stock price from previous close) plotted below shows an average of 0%. This is a common expectation for the daily returns of a stock which are different from the annualized returns due to the compounding effect of the daily returns.

#Plot the returns
plot(Actual_Ret)

Set up

#Generate Simple Moving Averages (long-term & short-term)
SMA100 <- round(SMA(TSLA, 100),2)   #Long-term moving average
SMA50 <- round(SMA(TSLA, 50),2)    #Short-term moving average

#Put desired columns into a dataframe. "na.omit" to drop rows with n/a. "cbind" to combine desired columns.
Data <- na.omit(as.data.frame(cbind(TSLA, Actual_Ret, SMA50, SMA100)))
colnames(Data) <- c("TSLA_Price","Actual_Ret","SMA50","SMA100" )

Create a Trading Strategy & Backtest

#Condition for trend-following strategy (SMA)."UD" = price moving up or down
Data$UD <- ifelse(Data$SMA50 >= Data$SMA100, 1, 0)

#Generate "buy" (1) signal if short-term moving average > long-term moving average, else signal "Sell" (-1)
Data$Trade <- ifelse(Data$UD == 1, "BUY", "SELL")

#If "Position" is "Buy" show 1, else -1 (Sell)
Data$Position <- ifelse(Data$Trade == "BUY", 1, -1)

#Calculate returns based on this algorithm
Data$AlgoReturns_Daily <- round((Data$Actual_Ret * Data$Position),4)   

#Annualized the Algo Returns
AnnualizedReturn <- ((mean(Data$AlgoReturns_Daily)+1)^252 - 1)

#Calculate the standard deviation for Sharpe ratio. Assume a risk-free rate (rf)
Standev <-StdDev.annualized(Data$AlgoReturns_Daily, scale = 252)
rf <- 0.02
SharpeRatio <- (AnnualizedReturn - rf)/Standev

#Actual Annualized Returns
AnnualizedReturn_Actual <- ((mean(Data$Actual_Ret)+1)^252 - 1)

#For the Actual Annualized Returns, calculate the standard deviation for Sharpe ratio. Assume a risk-free rate (rf)
Standev_Actual <- StdDev.annualized(Data$Actual_Ret, scale = 252)
rf <- 0.02
SharpeRatio_Actual <- (AnnualizedReturn_Actual - rf)/Standev_Actual

Visualize Short- & Long-term Moving Averages against Actual Stock Price

#Visualize Price vs Moving Averages

plot(Data$TSLA_Price, type = "l", col = "black", ylab = "", xlab = "", xaxt='n', cex.axis=1)
title(ylab = "Price", line=2.5, cex.lab=1.2)
title(xlab = 'Time', line=1.0, cex.lab=1.2)
title(xlab = '(2017Jan to 2022May)', line=2.3, cex.lab=0.8)
par(new = TRUE)

plot(Data$SMA50, type = "l" , col = "cyan", ylab = "", xlab = "", yaxt="n",xaxt='n')
par(new = TRUE)
plot(Data$SMA100, type = "l" , col = "blue", ylab = "", xlab = "",yaxt="n",xaxt='n')
legend("topleft",legend=c("Actual Price","SMA 50","SMA100"),col=c("black", "cyan","blue"),lty = 1, cex=0.8)

Obtain Algo Output

The table below displays a subset of the complete table of the algo’s output.

Essentially, based on the conditions set up above, either a “buy” or “sell” signal to trade/execute is generated (under “Trade” column):

  • Buy: If SMA50 > SMA100 when SMA50 crosses SMA100 from below (stock price is anticipated by Algo to go up)
  • Sell: If SMA50 < SMA100 when SMA50 crosses SMA100 from above (stock price is anticipated by Algo to go down)

If the “Actual_Ret” column is negative, and Algo signals a “Sell” (in the “Trade” column), then the “AlgoReturns_Daily” column registers a positive gain since the Algo correctly generated a decision on a falling stock price.

Conversely, If “Actual_Ret” is positive, but a “Sell” signal is generated, that means the Algo incorrectly predicted the direction of stock movement and the corresponding AlgoReturns_Daily for that trading day would then show a negative return.

#Sort results by latest date first
Data_latest_1st <-apply(Data, 2, rev)                    #1st calculate reverse (rows =2)
Data_latest_1st <-as.data.frame(Data_latest_1st)         #Then, convert back into dataframe

# Show results for every 30 trading days
Data_sample = Data_latest_1st %>% slice(which(row_number() %% 30 == 1))

Data_sample %>%
  kbl() %>%
  kable_paper("hover", full_width = F)
TSLA_Price Actual_Ret SMA50 SMA100 UD Trade Position AlgoReturns_Daily
2022-05-06 865.65 -0.0088 943.25 954.88 0 SELL -1 0.0088
2022-03-24 1013.92 0.0147 898.15 985.09 0 SELL -1 -0.0147
2022-02-09 932.00 0.0108 1003.53 978.86 1 BUY 1 0.0108
2021-12-28 1088.47 -0.0050 1048.24 896.85 1 BUY 1 -0.0050
2021-11-12 1033.42 -0.0287 884.19 784.34 1 BUY 1 -0.0287
2021-10-01 775.22 -0.0003 721.62 674.81 1 BUY 1 -0.0003
2021-08-19 673.47 -0.0228 665.10 660.04 1 BUY 1 -0.0228
2021-07-08 652.81 0.0126 630.44 661.26 0 SELL -1 -0.0126
2021-05-25 604.69 -0.0029 663.70 720.12 0 SELL -1 0.0029
2021-04-13 762.32 0.0825 714.76 702.61 1 BUY 1 0.0825
2021-03-01 718.43 0.0616 777.16 630.32 1 BUY 1 0.0616
2021-01-14 845.00 -0.0111 604.85 513.52 1 BUY 1 -0.0111
2020-12-01 584.76 0.0298 443.29 400.86 1 BUY 1 0.0298
2020-10-19 430.83 -0.0203 408.45 327.34 1 BUY 1 -0.0203
2020-09-04 418.32 0.0274 326.87 248.20 1 BUY 1 0.0274
2020-07-24 283.40 -0.0656 219.41 173.25 1 BUY 1 -0.0656
2020-06-11 194.57 -0.0523 152.90 141.86 1 BUY 1 -0.0523
2020-04-29 160.10 0.0400 127.72 117.27 1 BUY 1 0.0400
2020-03-17 86.04 -0.0340 131.67 101.29 1 BUY 1 -0.0340
2020-02-03 156.00 0.1814 88.72 72.16 1 BUY 1 0.1814
2019-12-18 78.63 0.0367 64.18 55.31 1 BUY 1 0.0367
2019-11-05 63.44 -0.0008 50.79 48.66 1 BUY 1 -0.0008
2019-09-24 44.64 -0.0776 46.88 45.41 1 BUY 1 -0.0776
2019-08-12 45.80 -0.0259 45.88 47.61 0 SELL -1 0.0259
2019-06-28 44.69 0.0028 44.52 50.88 0 SELL -1 -0.0028
2019-05-16 45.67 -0.0157 52.72 57.55 0 SELL -1 0.0157
2019-04-03 58.36 0.0205 58.53 63.27 0 SELL -1 -0.0205
2019-02-20 60.51 -0.0101 64.70 63.85 1 BUY 1 -0.0101
2019-01-07 66.99 0.0529 67.89 63.13 1 BUY 1 0.0529
2018-11-20 69.50 -0.0171 60.92 62.16 0 SELL -1 0.0171
2018-10-09 52.56 0.0477 61.57 62.45 0 SELL -1 -0.0477
2018-08-27 63.85 -0.0111 65.71 62.69 1 BUY 1 -0.0111
2018-07-16 62.02 -0.0279 62.83 62.13 1 BUY 1 -0.0279
2018-06-01 58.36 0.0246 57.89 62.55 0 SELL -1 -0.0246
2018-04-19 60.02 0.0227 62.92 64.45 0 SELL -1 -0.0227
2018-03-07 66.46 0.0124 66.92 65.79 1 BUY 1 0.0124
2018-01-23 70.56 0.0035 64.66 66.91 0 SELL -1 -0.0035
2017-12-07 62.25 -0.0065 65.27 67.73 0 SELL -1 0.0065
2017-10-25 65.17 -0.0347 70.56 70.22 1 BUY 1 -0.0347
2017-09-13 73.25 0.0095 68.52 68.10 1 BUY 1 0.0095
2017-08-01 63.91 -0.0121 68.74 63.78 1 BUY 1 -0.0121
2017-06-19 73.96 -0.0043 65.03 58.82 1 BUY 1 -0.0043

Compare Returns & Sharpe Ratio by Algo against Actual

#Print the results

cat(paste("Expected Annualized Return by this Algo:    ", round(AnnualizedReturn*100, digits=2),"%\n",
      "     Standard deviation of Algo Return:    ", round(Standev*100, digits=2),"%\n",
      "             Sharpe ratio of this Algo:     ", round(SharpeRatio, digits=2),"(for a risk-free rate of", round((rf)*100,digits=2),"%)\n\n\n",

"               Actual Annualized Return:   ", round(AnnualizedReturn_Actual*100, digits=2),"%\n","   Standard deviation of Actual Return:    ", round(Standev_Actual*100, digits=2),"%\n","          Sharpe ratio of Actual Return:    ", round(SharpeRatio_Actual, digits=2),"(for a risk-free rate of", round((rf)*100,digits=2),"%)"))
## Expected Annualized Return by this Algo:     82.1 %
##       Standard deviation of Algo Return:     62.38 %
##               Sharpe ratio of this Algo:      1.28 (for a risk-free rate of 2 %)
## 
## 
##                 Actual Annualized Return:    70.26 %
##     Standard deviation of Actual Return:     62.4 %
##            Sharpe ratio of Actual Return:     1.09 (for a risk-free rate of 2 %)