SJSU2013 SJSU2013 - 3 months ago 11
R Question

Creating Trading Signals in R

I'm constructing a trading strategy and am stuck at two key areas. When using Stoch and MACD in

quantmod
, I am trying to create a signal when the slow stochastic crosses over the fast stochastic (1), and visa-versa(-1), and flat when in between (0). MACD the code is identical except with the column names MACD and Signal. Lastly, I am trying to merge the three signals to create a master signal when all three signals equal 1, -1, 0.

library(quantmod)

####################
## BOLINGER BANDS ##
####################

getSymbols("SPY", src="yahoo", from="2013-01-01", to="2015-05-01")
x <- na.omit(merge(SPY, BBands(Cl(SPY))))

x$sig <- NA

# Flat where Close crossed the mavg
x$sig[c(FALSE, diff(sign(Cl(x) - x$mavg), na.pad=FALSE) != 0)] <- 0
x$sig[Cl(x) > x$up] <- -1 # short when Close is above up
x$sig[Cl(x) < x$dn] <- 1 # long when Close is below dn
x$sig[1] <- 0 # flat on the first day
x$sig[nrow(x)] <- 0 # flat on the last day

# Fill in the signal for other times
x$sig <- na.locf(x$sig) # wherever sig is NA, copy previous value to next row

# Now Lag your signal to reflect that you can't trade on the same bar that
# your signal fires
x$sig <- Lag(x$sig)
x$sig[1] <- 0 # replace NA with zero position on first row

####################
### STOCHASTICS ####
####################

y <- na.omit(merge(SPY, stoch(Cl(SPY))))

y$sig <- NA

# Flat where between crosses. Not sure how to write
#y$sig[c(FALSE, diff(sign(y$slowD == y$fastD), na.pad=FALSE !=0)] <- 0
y$sig[y$fastD > y$slowD] <- -1 # short when Close is above up
y$sig[y$fastD < y$slowD] <- 1 # long when Close is below dn
y$sig[1] <- 0 # flat on the first day
y$sig[nrow(x)] <- 0 # flat on the last day

# Fill in the signal for other times
y$sig <- na.locf(y$sig) # wherever sig is NA, copy previous value to next row

# Now Lag your signal to reflect that you can't trade on the same bar that
# your signal fires
y$sig <- Lag(y$sig)
y$sig[1] <- 0

####################
###### MACD ########
####################

z <- na.omit(merge(SPY, MACD(Cl(SPY))))

z$sig <- NA

# Flat where between crosses. Not sure how to write
z$sig[c(FALSE, diff(sign(z$signal == z$macd), na.pad=FALSE) != 1)] <- 1
z$sig[z$signal > z$macd] <- -1 # short when Close is above up
z$sig[z$signal < z$macd] <- 1 # long when Close is below dn
z$sig[1] <- 0 # flat on the first day
z$sig[nrow(z)] <- 0 # flat on the last day

# Fill in the signal for other times
z$sig <- na.locf(z$sig) # wherever sig is NA, copy previous value to next row

# Now Lag your signal to reflect that you can't trade on the same bar that
# your signal fires
z$sig <- Lag(z$sig)
z$sig[1] <- 0

# Merge xyz by date and create new signal when all three conditions are met

Answer

Update: I fixed all the nasty loops using a diff instead after this answer.

This is how I would approach this problem. You are calculating all position that have the desired relationships. You only want the first position that satisfies the trading signal to act on it as soon as possible.

I would set up the Bollinger band signal like this:

price.over.up <- Cl(x) > x$up
price.under.dn <- Cl(x) < x$dn

x$sig <- rep(0,nrow(x))
#sell which price breaks top band
x$sig[which(diff(price.over.up)==1] <- -1
#buy when price breaks bottom band
x$sig[which(diff(price.under.dn)==1)] <- 1

x$sig <- Lag(x$sig)
x$sig[1] <- 0

I would create the stochastic signal like this:

fast.over.slow <- y$fastD > y$slowD
y$sig <- rep(0,nrow(y))
y$sig[which(diff(fast.over.slow) == 1 & y$slowD < 0.2)] <- 1
y$sig[which(diff(fast.over.slow) == -1 & y$slowD > 0.8)] <- -1
y$sig <- Lag(y$sig)
y$sig[1] <- 0

Once you calculate the difference, you want to find the first crossover where one is higher than the other so you need to consider the ith and i-1th positions. Also the signal will be stronger if you are in overbought or oversold territory (0.8 or 0.2).

Similarly for MACD:

mac.over.signal <- z$macd > z$signal
z$sig <- rep(0,nrow(z))
z$sig[diff(mac.over.signal) == 1] <- 1
z$sig[diff(mac.over.signal) == -1] <- -1
z$sig <- Lag(z$sig)
z$sig[1] <- 0 

Now we merge them and calculate the combine signal:

all <- merge(x$sig,y$sig,z$sig)
all[is.na(all)] <- 0

If it were me, I would rather have a sum of the signals because it will tell you how trust worthy each signal is. If you have a 3, that is stong but a 1 or 2 not as strong. So I would go with the sum as the combined signal.

all <- cbind(all,rowSums(all))

Now all is a matrix with all the signals and the last column is the combined signal strength.

Also think about how this may not give you a good signal. Using the approach for this chart, The strongest signals I get are -2, and I only get 5 occasions. Kind of odd since the chart goes straight up but there are no strong buys.

> all[which(all[,4] == -2),]
           sig sig.1 sig.2 ..2
2013-04-16   0    -1    -1  -2
2013-08-07   0    -1    -1  -2
2013-11-08   0    -1    -1  -2
2014-04-07   0    -1    -1  -2
2014-06-24   0    -1    -1  -2

These sell signals only give a short downside and then the chart rockets higher. Of course it all depends on the stock etc.

You also get situations like this:

2014-07-07  -1     0     1   0
2014-07-08   0    -1     0  -1
2014-07-09   0     0    -1  -1

Some indicators are faster or slower than others. This would be my approach, but you should do broad based tests and determine if you think these will be actionable trades and if you would make any money acting on them minus commission and hold duration.