9

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
Hack-R
  • 22,422
  • 14
  • 75
  • 131
SJSU2013
  • 585
  • 3
  • 8
  • 18

2 Answers2

4

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.

Community
  • 1
  • 1
pbible
  • 1,259
  • 1
  • 18
  • 34
  • What I am trying to achieve with MACD, and STOCH is a signal when the lines cross. (1) and all else (0). It essentially acts a confirmation of bollinger bands. The point where the line cross is all I care about. the Bollinger bands are the basis, with MACD and STOCH providing confirmation and affirming original signal. any thoughts? – SJSU2013 Jun 03 '15 at 22:52
  • @SJSU2013 this should solve your problem then. The point where the lines cross will be either 1 or -1 when you apply `diff` to the boolean vector you get from `z$macd > z$signal`. Similarly for the stochastic. – pbible Jun 04 '15 at 17:13
1

How about this

master.signal <- rep(NA, nrow(x))   # init to all NA's or whatever you like
master.signal[x$sig == 1 & y$sig == 1 & z$sig == 1] <- 1
master.signal[x$sig == -1 & y$sig == -1 & z$sig == -1] <- -1
master.signal[x$sig == 0 & y$sig == 0 & z$sig == 0] <- 0
seven7e
  • 798
  • 1
  • 8
  • 19
  • This seems to just return a value, and doesn't add back to the data frame. Also, I am still trying to set up a -1, 0, 1 position for Stochastics and MACD – SJSU2013 May 22 '15 at 07:20
  • @SJSU2013 I am not quite clear what you want. Could you give more detailed description or just make a small example and show me the form of your expected result? – seven7e May 22 '15 at 10:14
  • so the goal is to recrates this output for Bolinger Bands (see snipet below) for Stochastics, and macd – SJSU2013 May 24 '15 at 05:28
  • The goal is to recreate this snipet below for stochastics and MACD. I would like to have the master sig to have positions -1, 0, 1. where -1 is sell, 0 is hold. 1 is buy. the macd and stoch work similarly. when the signal crosses the macd (stoch) that is a signal to either get into trade, or get out of trade. when the lines do not cross that is hold. – SJSU2013 May 24 '15 at 05:34
  • I'm sorry, haven't you already set up -1, 0, 1 for stochastic and MACD? Or do you want one signal to satisfy all Bollinger, stochastic and MACD conditions? – seven7e May 25 '15 at 02:00
  • the Bolinger bands have -1, 0, 1. but I can't seem to find a clean way to create a similar signal for stoch and macd. It was a two part question in essence. fix the stoch and mach to have -1, 0, 1. and create a master signal the will occur when all three signals are satisfied. – SJSU2013 May 25 '15 at 07:00