Implementing a Trading Algorithm with R | by Adam Gajtkowski | Towards Data Science

This story explains how to implement the moving average trading algorithm with R. If you’re interested in setting up your automated trading pipeline, you should first read this article. This story is a purely technical guide focusing on programming and statistics, not financial advice.
Throughout this story, we will build an R function which takes historical stock data and arbitrary threshold as inputs and based on it decides whether it is a good time to purchase given stock. We will look at Apple stocks. This article may require a certain level of statistical knowledge. University level introduction to statistics modules should be sufficient.
The moving average trading algorithm takes an advantage of fluctuations around the stocks trend. We first identify if the slope of the given time series is positive. For simplicity we designed this algorithm to work only for positively trending stocks. We then detrend the historical time series, check if the most recent fluctuation is above, or below moving average. If the current price is below moving average and we don’t have the stock in our holdings, we buy the stock. If it’s above moving average and the stock is currently in our holdings we sell the stock.
Figure 1 represents the closing price of Apple stock in the past 50 days. We detrend this time-series, so that the red dotted line is aligned with the x-axis.
Figure 2 represents the detrended Apple stock for the past 50 days. Detrended time series looks stationary, that is it has constant mean and variance. If we wanted to be very rigorous, we could test for stationarity to make sure that it has desirable properties. We choose the arbitrary threshold to be +-5%. We purchase the stock if its 5 days average is 5% below the detrended time series, and sell it if it’s 5% above detrended time series. If the trade is successful, we gain 10% + any increase along the trend within this period.
The function takes 2 inputs. Historic data from Yahoo’s API and arbitrary threshold for buying/selling the stock.
moving_average_model <- function(data,trend_deviation_treshold = -5) {...}
We then access our current holdings to check which stocks we have purchased in the past. Below, we check if we currently hold Apple stocks in our portfolio.
holdings <- current_holdings()if ((holdings %>% filter(stock == "APPL"))$stock == "APPL") {stock_in_portfolio = TRUE} else {stock_in_portfolio = FALSE}
We then declare the historic data as a data frame, transform row names including date into a separate column, consider only closing price and dates in the past 70 days (about 50 working days when the stock market is open). Finally, we create an additional column which gives each day a number. Most recent day will have the highest number.
data <- as.data.frame(data) %>%tibble::rownames_to_column("date")data_close <- data %>%select(date, close = AAPL.Close) %>%filter(date > as.Date(Sys.Date() - 70)) %>%arrange(date) %>%mutate(day_number = row_number())
Next step is detrending the time series. We have created a variable with increasing numbers for each date above. If we were to treat this as a separate variable and plot it agains date, this would form a straight line. Hence, we can use this variable for detrending. If we regress the stock price on this variable, this would de trend the time series. We then could work on residuals from the regression to determine if the current price is above or below the moving average.
formula <- close ~ day_numberregression_model <- lm(formula = formula,data = data_close)
You may note that we do not suppress the constant term, nor consider that during further analysis. We simply care about the position of current price relative to the average. Hence, we may only analyse residuals.
recent_5 <- residual_values %>%rename("residual" = "regression_model$residuals") %>%filter(day_number > max(day_number) - 6) %>%summarise(close_mean = mean(residual, na.rm = TRUE))
First of all, we compute the average closing mean for the most recent 5 working days.
recent_5_50 <- residual_values %>%rename("residual" = "regression_model$residuals") %>%filter(day_number <=  max(day_number) - 6) %>%summarise(close_mean = mean(residual, na.rm = TRUE))
We then compute the mean in the past 5 to 50 working days. Next, we compute the deviation of the average of the most recent 5 days to the average of 6 to 50 working days.
trend_deviation <- ((recent_5$close_mean - recent_5_50$close_mean) /recent_5_50$close_mean) * 100
We purchase the stock if the current deviation of the past 5 days is below 5 to 50 days. If we already have APPLE stock in our holdings, we sell the stock if the deviation is above threshold. We also check every time that the time-series of a given stock has a positive slope.
if (trend_deviation < trend_deviation_treshold &regression_model$coefficients[[2]] > 0 &stock_in_portfolio == FALSE) {decision_sell <-  FALSEdecision_buy <- TRUE} else if (trend_deviation > -trend_deviation_treshold &regression_model$coefficients[[2]] > 0 &stock_in_portfolio == TRUE) {decision_sell <-  TRUEdecision_buy <- FALSE}
The last step is creating a data frame, recording details of decisions made by the algorithm. We create a unique id including name of stock and date, record a run_date, the date stock price has been accessed, stock name, closing price of a given stock, computed trend deviation, threshold chosen by us, 5 days and 45 days average, decision to buy, and decision to sell.
output <- data.frame(id = paste0("APPL-", Sys.Date()),run_time = as.character(Sys.time()),stock_date = (data %>% filter(date == max(date)))$date,stock = "APPL",close_price = (data %>% filter(date == max(date)))$AAPL.Close,trend_deviation = trend_deviation,threshold = trend_deviation_treshold,recent_5_avg = recent_5$close_mean,recent_5_50_avg = recent_5_50$close_mean,decision_buy = decision_buy,decision_sell = decision_sell)
We have implemented the moving average trading strategy using R. We simply looked at the historical data of a given stock, checked if it has a positive trend, computed the current average trend deviation and made a decision based on that.
We then call this algorithm in our main trading pipeline, and record details of this decision into google sheets.
In the future, I will extend the above algorithm to multiple stocks, so that we can provide a list of stocks we’re interested in and the algorithm/pipeline automatically trades them. We could also include additional sanity checks of the inputs, and stationarity checks to make sure that the detrended time-series has desirable properties.
Note from Towards Data Science’s editors: While we allow independent authors to publish articles in accordance with our rules and guidelines, we do not endorse each author’s contribution. You should not rely on an author’s works without seeking professional advice. See our Reader Terms for details.
Appendix
Full code for the moving-average trading function
moving_average_model <- function(data,trend_deviation_treshold = 3) {holdings <- current_holdings()if ((holdings %>% filter(stock == "APPL"))$stock == "APPL") {stock_in_portfolio = TRUE} else {stock_in_portfolio = FALSE}data <- as.data.frame(data) %>%tibble::rownames_to_column("date")data_close <- data %>%select(date, close = AAPL.Close) %>%filter(date > as.Date(Sys.Date() - 70)) %>%arrange(date) %>%mutate(day_number = row_number())formula <- close ~ day_numberregression_model <- lm(formula = formula,data = data_close)residual_values <- bind_cols(as.data.frame(regression_model$residuals),data_close)recent_5 <- residual_values %>%rename("residual" = "regression_model$residuals") %>%filter(day_number > max(day_number) - 6) %>%summarise(close_mean = mean(residual, na.rm = TRUE))recent_5_50 <- residual_values %>%rename("residual" = "regression_model$residuals") %>%filter(day_number <= max(day_number) - 6) %>%summarise(close_mean = mean(residual, na.rm = TRUE))trend_deviation <- ((recent_5$close_mean - recent_5_50$close_mean) /recent_5_50$close_mean) * 100if (trend_deviation < trend_deviation_treshold &regression_model$coefficients[[2]] > 0 &stock_in_portfolio == FALSE) {decision_sell <-  FALSEdecision_buy <- TRUE} else if (trend_deviation > -trend_deviation_treshold &regression_model$coefficients[[2]] > 0 &stock_in_portfolio == TRUE) {decision_sell <-  TRUEdecision_buy <- FALSE}output <- data.frame(id = paste0("APPL-", Sys.Date()),run_time = as.character(Sys.time()),stock_date = (data %>% filter(date == max(date)))$date,stock = "APPL",close_price = (data %>% filter(date == max(date)))$AAPL.Close,trend_deviation = trend_deviation,threshold = trend_deviation_treshold,recent_5_avg = recent_5$close_mean,recent_5_50_avg = recent_5_50$close_mean,decision_buy = decision_buy,decision_sell = decision_sell)output}