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)
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
> 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.
No comments:
Post a Comment