Sunday, April 22, 2018

A Quick Walk Down Wall Street


Early this year equities fell by 10% in a matter of days even while the economy continued to improve. In the previous post I mentioned 8 “signposts” that would help detect an approaching recession but these would have been of little help for predicting this market flash-crash because they are higher level indicators and do not track money flows in real time. Market participants usually have a tendency to view the future as an extrapolation of the current conditions, so if times are good it’s natural to think that the bad times are in the distant the future rather than right around the corner and vice versa. For this reason, many were caught by surprise.

So how can we protect our portfolios from seemingly random sell-offs like this? Let’s begin by identifying some of the major warning signs. For the past 2 years the interest rates had been going up and accelerating during the preceding 6 months. The FED had announced that it was on course to raise interest rates further throughout the year. Market volatility had been at all-time lows and traders had developed trading models that assumed that volatility would remain low and when it spiked they flooded the market with sell orders to protect their capital. Finally, before the market slide started there was unusually low liquidity which amplified the crash.

Let’s assume you had taken note of the warning signs and had seen this crash coming. Naturally the next thing you would have wanted to know is when would the crash unfold. As you contemplated your next move you reasoned that if you closed your position you would miss out on further price gains and if you stayed you took a heightened risk. Unfortunately, it is impossible to predict a market turn with significant confidence. However, an investor could possibly have recognized the parabolic shape of price movement - which is often a sign of irrational behavior - and reduced exposure. However, it often is too late to make adjustments to the portfolio and the staying power will be dictated by the risk profile of the portfolio - a major component of which is the price of entry of the position.

So what’s the takeaway of this article? Markets will always experience flash-crash events and we cannot predict them. But if one understands the current state of the economic business cycle and the long term leverage/deleverage cycle it will be easier to distinguish between a market dip that is temporary and could be used to gain more exposure or a dip that is better left alone to run its course. 

What might cause the next major market correction? The main contributing factor to the previous recession was excessive consumer debt. Since then, consumers have continued to deleverage and the FED has mended the economy through quantitative easing and historically low interest rates. Unfortunately, the side effect of a prolonged low interest rate regime has been that corporations have increased debt to record levels. This is troubling according to research done by Guggenheim Partners which has said that if short term rates cross over 3% we'll be faced with a new wave of corporate bond defaults and a very significant market correction - after all, the ratio of corporate profit to GDP is at levels not seen since the 1950s. If this prediction is true then it would mean that the FED has been creating a new market bubble while fighting another. 

Monday, March 5, 2018

Recession Signposts

Each signpost looks at a particular macroeconomic data series deemed to be of importance in describing the current state of the business cycle. The Federal Reserve uses the FED Funds rate as a lever to stimulate the economy in bad times and slow it when it is overheating. The problem is that the FED is often "behind or ahead of the curve" and adjusts the rates too slowly or too quickly, causing volatility in the markets.

The FED has a mandate to maximize employment, stabilize prices and moderate long-term interest rates. In other words, the FED seeks to keep the unemployment rate close to the natural unemployment rate, seeks to keep inflation near 2% and will increase its balance sheet (print money)   to buy government bonds and keep long term interest rates down when the economy faces bad times. In the process of achieving its objectives the FED causes artificial distortions in the marketplace - triggering or amplifying business cycles.

Now that we gave a brief overview of the business cycle dynamics and its causes, we'll look for signs that are characteristic of the late stages of an expanding economy. The 8 signposts chosen will serve to detect anomalies and will be triggered if the values are deemed to be significantly different from what they should be in "normal" market conditions. The particular parameters that cause the signposts to trigger are chosen based on research conducted by the Federal Reserve, Guggenheim Partners, Bank of America and personal research. A brief explanation is provided for each signpost. Some signposts are single data series and others are the result of multiple data series (see Feature Transformation section).



#Gathering macro economic data from the FED to understand the current state of the economy and the likelihood of encountering a recession within the next 12 months. The more "signposts" that are triggered, the higher the likelihood of a recession.

library(quantmod)
library(PerformanceAnalytics)
library(Hmisc)

start <- as.Date("1950-01-03")
end <- Sys.Date()
#For FRED data, from/to does not work. Only works for Yahoo data.
getSymbols('NFCICREDIT', src = 'FRED')
getSymbols('BAMLC0A4CBBBEY', src = 'FRED')
getSymbols('DGS10', src = 'FRED')
getSymbols('UNRATE', src = 'FRED')
getSymbols('NROU', src = 'FRED')
getSymbols('FEDFUNDS', src = 'FRED')
getSymbols('GDPPOT', src = 'FRED')
getSymbols('T5YIFR', src = 'FRED')
getSymbols('T10Y3M', src = 'FRED')
getSymbols('GDPC1', src = 'FRED')
getSymbols('USSLIND', src = 'FRED')
getSymbols('PRS85006021', src = 'FRED')
getSymbols('TOTBUSSMSA', src = 'FRED')
getSymbols('ACDGNO', src = 'FRED')
getSymbols('HOUST', src = 'FRED')
getSymbols('MSPNHSUS', src = 'FRED')
getSymbols('GPDI', src = 'FRED')
getSymbols('LNU03023706', src = 'FRED')
getSymbols('UMCSENT', src = 'FRED')
getSymbols('DTWEXM', src = 'FRED')
getSymbols('CP', src = 'FRED')
getSymbols('^GSPC', src = 'yahoo', from = start, to = end)

#Create function to merge
multi.xts.merge <- function(listOguys) {
    dat <- Reduce(function(x, y) {merge.xts(x, y, join = 'outer')}, listOguys)
    names(dat) <- as.character(substitute(listOguys))[-1]
    return(dat)
}

#Save merged results
data <- multi.xts.merge(list(NFCICREDIT,BAMLC0A4CBBBEY,DGS10,UNRATE,NROU,FEDFUNDS,GDPPOT,T5YIFR,T10Y3M,GDPC1,USSLIND,PRS85006021,TOTBUSSMSA,ACDGNO,HOUST,MSPNHSUS,GPDI,LNU03023706,UMCSENT,DTWEXM,CP,GSPC$GSPC.Close))
data <- data["19490101/20180303"]
data <- na.locf(data) #Fill NA values with most recent value
na_count <- sapply(data, function(y) sum(length(which(is.na(y))))) #Ensure no NA values are present.

#Feature transformations

#Is the economy operating below or above capacity: Real GDP vs Potential GDP.
data$GDP.Gap <- ((data$GDPC1 - data$GDPPOT)/data$GDPPOT)

#What is the corporate (BBB rated) bond spread from 10yr Treasury Yield.
data$CorpBBB.Spread <- (data$BAMLC0A4CBBBEY - data$DGS10)

#Is the actual unemployment rate above or below the "natural/efficient" rate of unemployment?

data$Unemp.Gap <- (data$UNRATE - data$NROU)

#Are interest rates above or below the equilibrium/natural interest rate? In other words, is the FED Funds rate stimulating or slowing economic activity? To calculate the natural interest rate many assumptions are made and the result is a best guess value.
GDPGAP_Rate <- (data$GDPPOT-lag(data$GDPPOT,k=252))/(lag(data$GDPPOT,k=252)) #GDPGAP_Rate transforms nominal Potential GDP to a growth rate from 1 year ago.
data$Rate.Gap <- (data$FEDFUNDS - data$T5YIFR - GDPGAP_Rate + data$T5YIFR - data$T10Y3M)

#How is the "real" economy performing, what is its proportion to the GDP? Real economy includes durable goods orders, housing expenditures and investment spending.
data$Real.Econ <- (data$ACDGNO*1000000 + data$HOUST*1000*data$MSPNHSUS + data$GPDI*1000000000) / (data$GDPC1*1000000000)


# 1. Check if the rate of unemployment is below the natural level of unemployment. It typically reaches near -1% about 12-16 months ahead of a recession and 6 months before recession it starts to climb back up.
x1 <- ifelse(data$Unemp.Gap < -0.85, 1, 0)

# 2. Check if the [artificial] interest rate set by FED is above the natural interest rate. Approx 12 Mo. before recession it crosses above 0%.
x2 <- ifelse(data$Rate.Gap > 0, 1, 0)
(reference: http://www.frbsf.org/economic-research/economists/LW_replication.zip)

# 3. Check if the GDP is in excess of Potential GDP. Before recession it goes above 0 and then starts falling. https://fred.stlouisfed.org/graph/?g=f1cZ
x3 <- ifelse(data$GDP.Gap > 0, 1, 0)

# 4. Check if yield curve flattens. T-bill yields rise faster than 10-year yields. (10 Yr Treasury - 3 Mo Yield). About 12 months before recession it falls below 1% and is usually within 0.5% of the flat curve.
x4 <- ifelse(data$T10Y3M < 1, 1, 0)

# 5. Check if Leading index falls below 1%
x5 <- ifelse(data$USSLIND < 1, 1, 0)

# 6. Check if hours worked growth (% annual change) falls below 0.5%
x6 <- ifelse(data$PRS85006021 < -0.5, 1, 0)

# 7. Check if the "real economy" increases as a proportion of GDP. The higher the proportion the higher the risk.
quantile(data$Real.Econ, na.rm = TRUE) # Check what the 75% quantile is and use that for trigger
x7 <- ifelse(data$Real.Econ > 0.2, 1, 0)

# 8. Check if consumer sentiment is above 90.
x8 <- ifelse(data$UMCSENT > 90, 1, 0)

signposts <- multi.xts.merge(list(x1,x2,x3,x4,x5,x6,x7,x8))

> tail(signposts)
                    x1 x2 x3 x4 x5 x6 x7 x8
2018-02-23  0  1  1  0  0  0  1  1
2018-02-26  0  1  1  0  0  0  1  1
2018-02-27  0  1  1  0  0  0  1  1
2018-02-28  0  1  1  0  0  0  1  1
2018-03-01  0  1  1  0  0  0  1  1
2018-03-02  0  1  1  0  0  0  1  1

Conclusion: 4 of 8 signposts are currently triggered. Recession is likely at least 1 year away from today.

In addition to the above mentioned signposts the following data sources are also worthy of consideration for predicting recessions:

1. Job Leavers as a Percent of Total Unemployed. (FRED series: LNU03023706).
2. ISM Index of new orders. When Index > 60 level then future expected income is lower.
3. Dollar index. (FRED series: DTWEXM) Since almost half of profits come from abroad, a strong dollar hurts profits.
4. Rule of 20. P/E + Inflation = 20.
5. Volatility Index VIX is low but starts rising in the months before the onset of recession.
6. A 5% market pullback usually precedes recessions.
7. Market gains resemble a parabolic shape.
8. Flow of funds for each category of traders (CFTC Commitment of Traders).
9. Market cap as percentage of GDP. If it rises above 130% then the expected returns over the next 10 years would barely be keeping up with inflation.

source: https://www.guggenheiminvestments.com/perspectives/macroeconomic-research/stocks-for-the-long-run-not-now


Monday, February 19, 2018

S&P 500 Drawdowns (%) and Recovery (Days)

This is the first article to kick start this blog, which is intended to serve as a repository of my market studies and to promote the exchange of opinion from friends with an interest in economics. The studies will seek to model the following: relationship between various asset classes over long time horizons, detecting early warning signs of an impeding recession and position risk management. The data will primarily come from the St. Louis Fed website, Yahoo Finance and measuringworth.com.


The exercise below addresses position risk management by studying and modeling the relationship between Drawdown Depth (%) & Length (days) for the S&P 500 index. The data is not adjusted for dividends or inflation - so the model may underestimate the recovery period for very large drawdowns. For the purpose of this exercise "Close" prices are used because in real trading entering in the middle of a trading session increases the likelihood of overpaying due to intra-day volatility.

It is assumed no leverage is used when trading. For example, at the time of this writing you could buy an E-Mini S&P 500 futures contract worth  $136,611 with only a $5,800 margin. Since each point is equal to $50, theoretically, it will only take a 116-point decline (or a 4.2% drawdown) to completely empty your account (in reality your broker will liquidate your position ahead of time when the margin call isn't met).

The two primary forces driving asset returns are growth & inflation.

# Getting data. Only "Close" prices are used. Quantmod package.
getSymbols('^GSPC', src = 'yahoo', from = "1960-01-01")
sp <- GSPC
sp$Open <- GSPC$GSPC.Open
sp$High <- GSPC$GSPC.High
sp$Low <- GSPC$GSPC.Low
sp$Close <- GSPC$GSPC.Close
sp$GSPC.Adjusted=NULL
sp$GSPC.Volume=NULL
sp$GSPC.Open=NULL
sp$GSPC.High=NULL
sp$GSPC.Low=NULL
sp$GSPC.Close=NULL

#Percent change of returns.
sp$Change_pct <- (sp$Close-lag(sp$Close,k=1))/(lag(sp$Close,k=1))
sp$Change_pct["1960-01-04"]<- 0 #No NAs allowed

#Calculating drawdowns (Performance Analytics package)
Drawdown_Tbl <- table.Drawdowns(sp$Change_pct, top = 9999, digits = 4) #Table of drawdowns
Draw <- Drawdown_Tbl[,c(4,5)] #Depth and Length columns
Draw$Depth <- Draw$Depth*(-1) #Transform negative values to positive in order to do log transformations
stat.desc(Draw)

#Fitting right model to the data.
Days <- Draw$Length
Percent <- Draw$Depth

scatterplot(Days ~ Percent)
plot(Days ~ Percent) #Plot data
lines(lowess(Days ~ Percent)) #Plot lowess function to get an idea on the best fit

mod1 <- lm(Days ~ Percent) #Linear fit model
mod2 <- lm(Days ~ Percent + I(Percent^2)) #Quadratic fit. R^2 is higher than mod1, residuals are not perfect but good enough for this application.

lines(Percent, predict(mod1), col=1) #Plot model on data
lines(Percent, predict(mod2), col=2) #Plot model on data

#Console output

> summary(mod2)

Call:
lm(formula = Days ~ Percent + I(Percent^2))

Residuals:
    Min      1Q  Median      3Q     Max
-504.56   -2.10   -0.20    0.95  508.07

Coefficients:
                     Estimate      Std. Error t value Pr(>|t|) 
(Intercept)     1.431          2.721        0.526    0.599  #Low confidence, exclude from model
Percent          471.977      93.759      5.034    7.09e-07 ***
I(Percent^2)  4997.371    220.148     22.700  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 46.2 on 429 degrees of freedom
Multiple R-squared:  0.9112, Adjusted R-squared:  0.9108
F-statistic:  2202 on 2 and 429 DF,  p-value: < 2.2e-16

-------------------------------------------------------------------------------------

#Model: 
The data displays traits consistent with heteroskedasticity; however, a higher order model function will not be used to avoid the pitfalls of over-fitting. In addition, since this model is non-linear, we cannot rely on p-values without looking at the Std. Error values.

Y = Days until break-even
X = Percent drawdown from previous high

Y = 4997.371(X)^2 + 471.977(X) 









#Distribution of previous drawdowns 
The model predicts how many days must pass until the previous high is reached. However, in day-to-day trading we don't know what the market bottom will be. For example, a 5% fall from the previous high could be where the market turns direction, or it could be a pit-stop where the price pauses for some time and then continues its descent to 10%, 15% and more.

We can't predict the market bottom but we can look at their normal distribution so as to get an idea.

Draw0 <- Draw[which(Draw$Depth >= .05),] #Looking at distribution of drawdowns over 5%
boxplot(Draw0$Depth, main = "Distribution of Drawdowns (over 5%)", ylab = "Percent Fall from High")
points(mean(Draw0$Depth), col = "red", cex = 1.3) #Red dot is the mean

boxplot(Draw$Depth, main = "Distribution of Drawdowns", ylab = "Percent Fall from High")
points(mean(Draw$Depth), col = "red", cex = 1.3) #Red dot is the mean

#Console Output:
> summary(Draw0) #Summary of drawdowns over 5%

     Depth            Length   
 Min.   :0.0500   Min.   :   8.0
 1st Qu.:0.0623   1st Qu.:  40.0
 Median :0.0786   Median :  62.0  
 Mean   :0.1322   Mean   : 225.1  
 3rd Qu.:0.1356   3rd Qu.: 160.0
 Max.   :0.5678   Max.   :1898.0

> summary(Draw) #Summary of all drawdowns

     Depth              Length     
 Min.   :0.000100   Min.   :   2.00
 1st Qu.:0.002475   1st Qu.:   2.00
 Median :0.006700   Median :   4.00  
 Mean   :0.024674   Mean   :  32.63
 3rd Qu.:0.022925   3rd Qu.:  13.00
 Max.   :0.567800   Max.   :1898.00
>


# Interpretation of distributions
A casual interpretation is that if the price falls 5% from the previous high then it is likely to continue falling even further and reach a bottom of 7.86% - which is the median. The mean is 13.22%.

However, looking at all the existing drawdown distributions the median is 0.67% and the mean is 2.5%. This means that the vast majority of drawdowns are short-lived and in a bull market it may be advantageous to enter the market immediately after a correction of 0.67%.



In the section below we look at the drawdown risk if we enter a position at a random point in time with no regards to economic conditions. It is assumed that position is opened at "Close" price and the 3-month window is calculated starting on the following day.

# Probability of encountering a drawdown 3 months into the future.
draw3 <- rollapply(lag(sp$Low,k=-1), 63, min, align = "left") #3-month absolute minimum, starting date is following day
sp$Draw_3mo <- round((draw3-sp$Close)/sp$Close,3)
Days_No_Drawdown <-sp[which(sp$Draw_3mo>0),]
length(Days_No_Drawdown)/length(sp)

Conclusion: Only 1.07% of the days experienced no drawdown so we can be confident in assuming that some drawdown will be encountered into the future after opening a position. Note that the probability of encountering drawdowns goes higher in Bear markets and is lower in Bull markets.

# If/when a drawdown is encountered in the next 3 months, what should we expect it to be.
Days_Yes_Drawdown <-sp[which(sp$Draw_3mo<0),]
summary(Days_Yes_Drawdown$Draw_3mo)
stat.desc(Days_Yes_Drawdown$Draw_3mo)

> stat.desc(Days_Yes_Drawdown$Draw_3mo)
                  Draw_3mo
nbr.val       1.410500e+04
nbr.null      0.000000e+00
nbr.na        0.000000e+00
min          -4.300000e-01
max          -1.000000e-03
range         4.290000e-01
sum          -7.847150e+02
median       -3.900000e-02
mean         -5.563382e-02
SE.mean       4.764645e-04
CI.mean.0.95  9.339335e-04
var           3.202095e-03
std.dev       5.658706e-02
coef.var     -1.017134e+00

Conclusion: Median is 3.9%,  Mean is 5.56%,  Standard Deviation is 5.66%, Range is -0.1% to -43%. In good economic times the drawdowns should be smaller than average and the opposite should be expected in periods of high market volatility; therefore, the data should be interpreted within the context of market conditions.