Site icon Trading Analytics and Machine Learning

SMA Crossover Strategy (Bitcoin) Gridsearch

What is a moving average crossover?

A moving average crossover is when there are two moving averages with one moving faster than the other. Two well-known crossovers are known as the Death Cross and the Golden Cross, with 50 period SMA crossing below the 200 period SMA for the Death Cross and the 50 period SMA crossing over the 200 period SMA for the Golden Cross.

There are other popular cross overs and analystical methods using multiple moving averages such as the alligator indicator and the Triple Exponential Moving average, both of which will be covered in later posts.

The Meaning of Crossovers

The general hope of Crossover stategies is to objectively, programatically, and quickly identify changes in market direction and behavior and essentially means that for a given period of time the market has changed direction with respect to the longer period moving average.

Simple Moving Average Crossover in Action

## In this section the a function is made to create moving averages
sma <- function(x, length = 20, shift = length){
  ## Initializing the vector
  sma <- c()
  
  ## Taking averages of sections
  for(i in 1:length(x))
    sma[i] <- mean(x[i:(i+length)])
  
  ## Returning the shift values
  return(lag(sma, shift))
}

## Pulling in Bitcoin Data
BTC <- getSymbols("BTC-USD", periodicity = "daily", auto.assign = FALSE)
colnames(BTC) <- c("Open", "High", "Low", "Close", "Volume", "Adjusted")
BTC <- na.omit(BTC)

BTC <- data.frame(date = index(BTC), coredata(BTC))
BTC <- BTC %>% mutate(Ret = c(rep(NA), diff(log(Close)))) %>%
  select(date, Ret, Close) %>% 
  drop_na()

Determining Simple Moving Average Crossover with highest return

# Getting the list of all the SMA's
ma <- 2:250
# Combination of all SMA's with slow having a greater length than fast
grid_search <- expand.grid(slow = ma, fast = ma) %>%
  filter(slow > fast)
# Adding empty column to populate
crossover_df <- data.frame(grid_search, Cummulative_Ret = NA)

Moving Average Crossover by total Return

# For loop to create the returns
for(i in 1:nrow(grid_search)){
  slow_sma <- sma(BTC$Close, length = grid_search[i, "slow"]) 
  fast_sma <- sma(BTC$Close, length = grid_search[i, "fast"])
  signal <- ifelse(fast_sma > slow_sma, 1, -1)
  ret <-  BTC$Ret * lag(signal)
  cum_prod <- cumprod(ret[!is.na(ret)] + 1) - 1
  crossover_df[i, 3] = cum_prod[length(cum_prod)]
}

# Table of the 10 pairs of SMA's with the greatest returns
crossover_df %>% 
  arrange(desc(Cummulative_Ret)) %>%
  top_n(10)
## Selecting by Cummulative_Ret
##    slow fast Cummulative_Ret
## 1    28   26        178.0140
## 2    43    7        151.5061
## 3    42   11        147.9738
## 4    36   13        143.4735
## 5    37   13        140.5507
## 6    41    2        133.2891
## 7    41   11        133.0302
## 8    42    7        123.1486
## 9    37   12        119.0496
## 10   43   12        118.5214

Based on the above list, although, the most popular SMA’s aren’t present, they are stil used to help traders determine market sentiment, trends and other aspects of the market. However, when looking at the strategy there are better options in regards to maximizing the return of the SMA crossover strategy.

BTC <- BTC %>%
  mutate(buy_and_hold = cumprod(Ret + 1) - 1,
         sma_28 = sma(Close, 28),
         sma_26 = sma(Close, 26),
         signal = lag(ifelse(sma_26 > sma_28, 1, -1)),
         cross_ret = signal * Ret,
         cross_cum_ret = c(rep(NA, 29), cumprod(cross_ret[!is.na(cross_ret)] + 1) - 1))

ggplotly(
BTC %>%
ggplot(aes(date, Close)) +
  geom_line(color = "skyblue") +
  geom_line(aes(date, sma_28), color = "red") +
  geom_line(aes(date, sma_26), color = "orange") +
  labs(x = "Date", y = "Price", title = "Bitcoin Price and 26 period and 28 period SMAs") +
  theme_dark())
20162018202020220200004000060000
Bitcoin Price and 26 period and 28 period SMAsDatePrice
ggplotly(
BTC %>%
ggplot(aes(date, buy_and_hold)) +
  geom_line(color = "skyblue") +
  geom_line(aes(date, cross_cum_ret, color = as.factor(signal), group = 1)) +
  labs(x = "Date", y = "Cumulative Returns", title = "Buy and Hold vs SMA Crossover") +
  theme_dark() +
  theme(
    legend.position = "none"
  )
)
2016201820202022050100150200
Buy and Hold vs SMA CrossoverDateCumulative Returns

Conclusion

Although the price crossover had some higher returns than the simple moving average crossover, they still performed better than buying a holding. This may not always be the case though as historical performance does not guarantee future performance. This shows a number of ideas regarding trading:

  1. Complex trading rules do not always lead to better results.
  2. It is beneficial to simplify trading rules and strategies.
  3. A simple trading strategy can increase a person’s investment significantly.

Moreover, even though we are only looking at the cumulative returns of a strategy, there are other aspects to look at such as max drawdown, accuracy, number of days in a trade.

Exit mobile version