Is the Unemployment Rate Simple Moving Average a reliable recession indicator for the stock market?

Famed bond guru Jeff Gundaluch, CEO of Doubleline Capital LP, just saw one of his favorite recession indicators starting to flash red or bearish. I decided to see how reliable the Unemployment Rate indicator is in terms of a trading strategy.

Bloomberg article:
http://www.bloomberg.com/news/articles/2016-10-07/one-of-jeff-gundlach-s-favorite-recession-indicators-just-got-triggered

The recession signal is when the Unemployment Rate (UR) crosses over the 12 month moving average (using Simple Moving Average (SMA) ). Today’s the BLS announced the Unemployment Rate ticked up to 5.0 which is .1 above the 12 month moving average at 4.9.

So looking back let’s take a quick simple look at how “actionable” this indicator is in terms of going long or short the market. In this simple strategy below:

PORTFOLIO PARAMETERS

Capital: $1,0000,000 USD
Period: 1/1/2000 – 10/6/2016
Allocation: All

TRADE RULES

  1. LONG SPY ETF when the current month’s UR < UR 12 SMA, when UR > UR 12 SMA close SPY ETF long and
  2. SHORT SPY ETF when the current month’s UR < UR 12SMA, reverse back long when UR < UR 12 SMA.

ANALYSIS

As you can see below during this time period the “UR 12SMA crossover strategy” outperformed the S&P500 benchmark during the time period. Although the strategy is far from an ideal long/short equity market strategy with a Max Drawdown of around 25%.
If you are a simple investor looking for a simple strategy that may give you a hint as to when it may be a good time to ring the register these kinds of fundamental indicators may be better than watching all the back and forth bull v. bear noise on financial media outlets. Perhaps used in conjunction with Gundaluch’s other recession indicators a more profitable trading approach may be found *On my TDO list now😉
An interesting observation is that the short signal appears to trigger early while the long signal tends to signal late.

TRADES

[1] “2000-08-02 00:00:00 SPY -6950 @ 144.593704”
[1] “2000-09-05 00:00:00 SPY 6950 @ 151.281204”
[1] “2000-09-05 00:00:00 SPY 6557 @ 151.281204”
[1] “2001-01-02 00:00:00 SPY -6557 @ 128.8125”
[1] “2001-01-02 00:00:00 SPY -7623 @ 128.8125”
[1] “2002-10-02 00:00:00 SPY 7623 @ 83.150002”
[1] “2002-10-02 00:00:00 SPY 11666 @ 83.150002”
[1] “2002-11-04 00:00:00 SPY -11666 @ 91.129997”
[1] “2002-11-04 00:00:00 SPY -11078 @ 91.129997”
[1] “2003-10-02 00:00:00 SPY 11078 @ 102.449997”
[1] “2003-10-02 00:00:00 SPY 9796 @ 102.449997”
[1] “2007-06-04 00:00:00 SPY -9796 @ 154.100006”
[1] “2007-06-04 00:00:00 SPY -6490 @ 154.100006”
[1] “2010-05-03 00:00:00 SPY 6490 @ 120.349998”
[1] “2010-05-03 00:00:00 SPY 8417 @ 120.349998”
[1] “2010-11-02 00:00:00 SPY -8417 @ 119.470001”
[1] “2010-11-02 00:00:00 SPY -8437 @ 119.470001”
[1] “2010-12-02 00:00:00 SPY 8437 @ 122.559998”
[1] “2010-12-02 00:00:00 SPY 8264 @ 122.559998”
[1] “2016-09-02 00:00:00 SPY -8264 @ 218.369995”
[1] “2016-09-02 00:00:00 SPY -4600 @ 218.369995”

RETURNS

Annualized Return 0.0816
Annualized Std Dev 0.1816
Annualized Sharpe (Rf=0%) 0.4493
Max Drawdown: .2482572

Here are the results of the backtest:

ur_backtest_image100616

ur_model_perf100616

R Code (Dirty)

# UR 12SMA SPY Trading Strategy

require(quantstrat)
require(FinancialInstrument)
require(blotter)
require(Quandl)
require(quantmod)

# read in model data from csv
# z <- read.zoo("~/Desktop/model.csv", sep=",", header=TRUE,
#   row.names=1, FUN=as.yearmon, format="%b %Y", drop=FALSE)
# x <- as.xts(z)
# colnames(x) <- sub("SPX", "SPY", colnames(x)) # change to SPY, for this example

UNRATE = getSymbols('UNRATE', src='FRED', auto.assign=F)
names(UNRATE) <- "UNRATE"
UNRATE$UNRATE.12SMA <- SMA(UNRATE,12)
# Remove NA rows
UNRATE <- na.omit(UNRATE)
UNRATE$SIGNAL <- ifelse(UNRATE$UNRATE>UNRATE$UNRATE.12SMA,1,0)

# osFixedFollar function
# osFixedDollar <- function(timestamp, orderqty, portfolio, symbol, ruletype, ...)
# {
#     pos <- getPosQty(portfolio, symbol, timestamp)
#     if( isTRUE(all.equal(pos,0)) )
#     {
#         ClosePrice <- as.numeric(Cl(mktdata[timestamp,]))
#         orderqty <- sign(orderqty)*round(tradeSize/ClosePrice,-2)
#     } else {
#         orderqty <- 0
#     }
#     return(orderqty)
# }

osFixedDollar <- function(timestamp,orderqty, portfolio, symbol, ruletype, ...)
{
  #logwarn("started osFixedDollar")
  ClosePrice <- as.numeric(Cl(mktdata[timestamp,]))
  orderqty <- sign(orderqty)*round(tradeSize/ClosePrice)
  return(orderqty)
} 

# Modified maCross demo
ttz <- Sys.getenv('TZ')
Sys.setenv(TZ='UTC')

stock.str <- 'SPY'
currency('USD')
stock(stock.str, currency='USD', multiplier=1)

initDate <- "200-01-01"
endDate <- Sys.Date()

tradeSize <- 1000000
initEq <- tradeSize

portfolio.st <- 'model'
account.st <- 'model'

suppressWarnings({
  rm(list=paste("order_book",portfolio.st,sep="."),pos=.strategy)
  rm(list=c(paste("account",account.st,sep="."),
            paste("portfolio",portfolio.st,sep=".")),pos=.blotter)
  rm("strat")
})
initPortf(portfolio.st,symbols=stock.str, initDate=initDate)
initAcct(account.st,portfolios=portfolio.st, initDate=initDate,initEq=initEq)
initOrders(portfolio=portfolio.st,initDate=initDate)

strat <- strategy(portfolio.st)

# Pull SPY data from Yahoo Finance
getSymbols(stock.str, from=initDate, to=endDate)
# Add model indicator to SPY data
SPY <- na.omit(merge(SPY, UNRATE$SIGNAL, fill=na.locf))

# signals
# not sure what the model values are, so I assume a long signal
# Short
strat <- add.signal(strat, "sigThreshold",
                    arguments = list(column = "SIGNAL",
                                     threshold = 1,
                                     relationship = "eq",
                                     cross = TRUE),
                    label = "pred.gt.03")
strat <- add.signal(strat, "sigThreshold",
                    arguments = list(column = "SIGNAL",
                                     threshold = 0,
                                     relationship = "eq",
                                     cross = TRUE),
                    label = "pred.lt.04")

# Long
strat <- add.signal(strat, "sigThreshold",
                    arguments = list(column = "SIGNAL",
                                     threshold = 1,
                                     relationship = "eq",
                                     cross = TRUE),
                    label = "pred.gt.02")
strat <- add.signal(strat, "sigThreshold",
                    arguments = list(column = "SIGNAL",
                                     threshold = 0,
                                     relationship = "eq",
                                     cross = TRUE),
                    label = "pred.lt.01")

## RULES
# Short
strat <- add.rule(strat, 'ruleSignal',
                  arguments = list(sigcol = "pred.gt.03",
                                   sigval = TRUE,
                                   orderqty = -1,
                                   ordertype = 'market',
                                   orderside = 'short',
                                   osFUN=osFixedDollar,
                                   prefer="Close",
                                   TxnFees=-30,
                                   replace=FALSE),
                  type = 'enter',
                  label = 'enter.shot')

strat <- add.rule(strat, 'ruleSignal',
                  arguments = list(sigcol = "pred.lt.04",
                                   sigval = TRUE,
                                   orderqty = 'all',
                                   ordertype = 'market',
                                   orderside = 'short',
                                   TxnFees=-30,
                                   replace=TRUE),
                  type = 'exit',
                  label = 'exit.short')

# Long
strat <- add.rule(strat, 'ruleSignal',
                  arguments = list(sigcol = "pred.lt.01",
                                   sigval = TRUE,
                                   orderqty = 1,
                                   ordertype = 'market',
                                   orderside = 'long',
                                   osFUN=osFixedDollar,
                                   prefer="Close",
                                   TxnFees=-30,
                                   replace=FALSE),
                  type = 'enter',
                  label = 'enter.long')

strat <- add.rule(strat, 'ruleSignal',
                  arguments = list(sigcol = "pred.gt.02",
                                   sigval = TRUE,
                                   orderqty = 'all',
                                   ordertype = 'market',
                                   orderside = 'long',
                                   TxnFees=-30,
                                   replace=TRUE),
                  type = 'exit',
                  label = 'exit.long')

# run backtest
out <- applyStrategy(strategy=strat, portfolios=portfolio.st)
updatePortf(Portfolio='model', Dates=paste('::',as.Date(Sys.time()),sep=''))

par(lwd=3)
chart.Posn(Portfolio='model',Symbol=stock.str)

book    <- getOrderBook('model')
stats   <- tradeStats('model')
ptstats <- perTradeStats('model')
rets    <- PortfReturns('model')
table.AnnualizedReturns(rets)
maxDrawdown(rets)
txns    <- getTxns('model', stock.str)

# SPY rets v Model rets
SPYrets <- Return.calculate(Ad(SPY))
model.spy.rets <- merge(rets, SPYrets, join='inner')
names(model.spy.rets) <- c("Model","SPY")

charts.PerformanceSummary(model.spy.rets)

#Date workaround, remove later
Sys.setenv(TZ=ttz)

Hello World!

Hello World!

Every programmer’s first line of code and initiation into the wonderful world of computer programming. I can recall pretty clearly typing those 2 words in my first computer science course at the University of California, San Diego.

2016-2017 is going to be THE PIVOTAL year for me as I attempt to take my quantitative research/analytical/developer/trader skills up a few levels in pursuit of mastery status.

My goal for this blog is primarily to perform basic strategy implementation and backtesting. The reason I have decided to start documenting my research is:

a) In general I want to improve my coding skills mainly in Python and C++ as I already proficient in R.
b) But more specifically I want to improve my coding skills as they pertain to quantitative finance.

And the best way to improve quicker and efficiently flatten the learning curve is as the cliche goes “practice, practice, practice.” Of course Allen Iverson may disagree because realistically you need to practice with purpose or deliberately practice.

Please keep in mind that I too am searching for profitable trading strategies so the strategies that I do explore and share will be more of the academic flavor. Also there will be errors, mistakes and lazy coding techniques so please try to understand the basic gist. I have a FULL workload both personally and professionally.

Things will start out very slow and basic with simple strategy testing and hopefully work up to a more intermediate level.

Whenever possible I will provide my code.

If you have any questions, concerns or want to point out how flawed my analysis is and help me improve please feel free to follow and tweet me at @blackmarkt

rubix-cube