Search code examples
rdplyrtechnical-indicator

Generating trade signals and strategies with dplyr


I have recently been playing with technical trading techniques in R.

One of the things that I do find that becomes an issue, especially with large high frequency information, is generating the strategy vector from the signals vector. I was wondering if there isn't a faster way using dplyr?

Lets start by downloading Apple's stock and generating short and long moving averages

library("TTR")
library("quantmod")
library("PerformanceAnalytics")
library("dplyr")

getSymbols("AAPL", src = "google")
stock <- AAPL
stock <- window(stock['2015-10-01::2017-01-01'])

# Plot if you want to see
#lineChart(stock)

Short <- EMA(Cl(stock), n=5)
Long <- EMA(Cl(stock), n=6)

We now have our selected stock, lets generate our signal vector that indicates a buy and sell order when the two moving averages cross

# Signal
Signal <-
  Lag(ifelse(
    Lag(Short) < Lag(Long) & Short > Long, 1,
    ifelse(
      Lag(Short) > Lag(Long) & Short < Long, -1, 0)
  ))
Signal[is.na(Signal)] <- 0

Afterwards we use this signal to construct the strategy - this is the part that takes long in high frequency data - which is obviously due to the for loop

# Strategy
Strategy <- ifelse(Signal > 1, 0, 1)
for (i in 1:length(Cl(stock))) {
  Strategy[i] <-
    ifelse(Signal[i] == 1, 1, ifelse(Signal[i] == -1, 0, Strategy[i - 1]))
}
x <- as.numeric(Strategy$Lag.1)
x[is.na(x)] <- 0

My current dplyr approach is as follows, but it generates the wrong strategy

dplyr_strat <-
Signal %>% tbl_df() %>% 
  mutate(Change = if_else(Lag.1 == -1, "Sell", "Buy", "NoChange") ) %>% 
  mutate(Strategy = ifelse(Change == "Buy", 1, 
                           ifelse( Change == "Sell", 0, 
                                   lag(Strategy)) ) ) %>% select(Strategy)
y <- as.numeric(dplyr_strat$Strategy)

And test

all.equal(x,y)

Solution

  • I agree with epi99's comment about keeping it consistent with your initial for loop. I used data.table and got an exact match, see below:

    ## Using data.table
    dt.Signal <- setDT(as.data.frame(Signal))
    dt.Signal[, Strategy := ifelse(Lag.1 == 1, 1, ifelse(Lag.1 == -1, 0, lag(Strategy)))]
    dt.Signal[is.na(dt.Signal)] <- 0
    z <- as.numeric(dt.Signal[, Strategy])
    
    all.equal(x,z)
    

    The problem you had was likely with the "Buy", "Sell" and "No Change" Logic