# Tools/R-code to create gain/loss-asymmetry plots

The gain/loss asymmetry is a well known stylized fact: It basically states that real financial time series take longer for going up than going down.

To detect it a heavy statistical machinery is needed: Detrending the time series, calculating the inverse statistics, normalizing the distribution, fitting a Generalized Gamma distribution... to name but a few.

The result are plots like these (from http://papers.ssrn.com/sol3/papers.cfm?abstract_id=844364):

My question
I want to reproduce those plots. Do you know any software, tools and/or preferably R code/packages with which this can be done? Every little hint may be helpful - Thank you.

Here is a quick example, you grab the total returns for each holding period, avg them out and compare the days for each level of return.

You can change tmp1 for whatever is your preferred filtered data set.

 require(PerformanceAnalytics)

require(sqldf)
data(edhec)

tmp1=edhec[,1]

period_seq = 1:nrow(tmp1)
combos=expand.grid(period_seq,period_seq)
###Remove impossible investments
combos=combos[combos[,2]>combos[,1],]
colnames(combos) = c('start','finish')
combos$day_length = combos[,2]-combos[,1] ###Calculater return for each period combos$perreturn=NA

###Calculate return for each combo

for(i in 1:nrow(combos)){
combos[i,]$perreturn = as.numeric(last(cumprod(1+(tmp1[combos[i,1]:combos[i,2]])))-1) } ###Round the total return combos$roundedperreturn = round(combos$perreturn,2) ###Calulate the avg day length per return level ans=sqldf('select avg(day_length) as avg_day_length,roundedperreturn as return_level from combos group by 2') ##Plot it plot(ans$avg_day_length,ans$return_level,main="Holding period per level of return",xlab="periods",ylab='Return level') ##look only at levels that have a + and - up_side=ans[ans$return_level<=abs(min(ans$return_level))&ans$return_level>0,]
down_side = ans[ans$return_level<=abs(min(ans$return_level))&ans$return_level<0,] down_side$return_level = abs(down_side$return_level) plot(up_side,col="blue",type='b',main='Comparison of days required to return a return level') points(x = down_side$avg_day_length,y=down_side$return_level,col="red",type='b') ###Constant level of return plot time_distribution_for_level=combos[combos$roundedperreturn==0.05,]
time_distribution_for_level_down=combos[combos$roundedperreturn==-0.05,] up_five_pct_plot=table(time_distribution_for_level$day_length)/sum(time_distribution_for_level$day_length) up_five_pct_plot = data.frame(density=as.numeric(up_five_pct_plot),periods=as.integer(names(up_five_pct_plot))) down_five_pct_plot=table(time_distribution_for_level_down$day_length)/sum(time_distribution_for_level_down$day_length) down_five_pct_plot = data.frame(density=as.numeric(down_five_pct_plot),periods=as.integer(names(down_five_pct_plot))) plot(x=up_five_pct_plot$periods,y=up_five_pct_plot$density,type='b',col='blue',main='Density plot of time required for a five percent return (loss in red)',xlab='periods',ylab='density') points(x=down_five_pct_plot$periods,y=down_five_pct_plot\$density,type='b',col='red')


It paints a different picture likely due to my use of monthly sample data.

• Could you please elaborate on this because I don't quite see how this could help - Thank you – vonjd Jul 31 '15 at 19:25
• You would use the start of the gain or drawdown till end for days and return at each point in time – Kyle Balkissoon Jul 31 '15 at 20:28
• I think the problem is that you need the lengths of all possible windows until you reach the respective return level for each. I still cannot see how this approach might help you with this. – vonjd Aug 3 '15 at 17:35
• Got it, amended answer. Will add the asymmetry plot later. – Kyle Balkissoon Aug 4 '15 at 2:34
• Thank you. Although this is still different I think it is interesting in its own right, so I upvoted and accepted the answer. When I understand it correctly the original plot sets a return level (e.g. 5%) and counts the number of periods (of all possible periods) that you need to reach it (so how many 1-day periods, 2-day periods, ... 10-day periods and so on). It then calculates a density based on that distribution. The result is the above chart. (more can be found in the papers I linked to in my question). – vonjd Aug 4 '15 at 4:50

I wrote an R function to create those plots:

library(quantmod)

getSymbols("^GSPC", from = "1950-01-01")
## [1] "GSPC"

inv_stat <- function(symbol, name, target = 0.05) {
p <- coredata(Cl(symbol))
end <- length(p)
days_n <- days_p <- integer(end)

# go through all days and look when target is reached the first time from there
for (d in 1:end) {
ret <- cumsum(as.numeric(na.omit(ROC(p[d:end]))))
cond_n <- ret < -target
cond_p <- ret > target
suppressWarnings(days_n[d] <- min(which(cond_n)))
suppressWarnings(days_p[d] <- min(which(cond_p)))
}

days_n_norm <- prop.table(as.integer(table(days_n, exclude = "Inf")))
days_p_norm <- prop.table(as.integer(table(days_p, exclude = "Inf")))

plot(days_n_norm, log = "x", xlim = c(1, 1000), main = paste0(name, " gain-/loss-asymmetry with target ", target), xlab = "days", ylab = "density", col = "red")
points(days_p_norm, col = "blue")

c(which.max(days_n_norm), which.max(days_p_norm))
}

inv_stat(GSPC, name = "S&P 500")
## [1] 10 24


The following plot is being produced (will take some time):

Two things are missing:

• Detrending of time series
• Fitted probability distribution

If you want to add them or if you have ideas how to improve the code, please let me know!