I am trying to calculate the turnover for a portfolio strategy.

First I generate some random data and assign it dates:

data <- replicate(6,rnorm(1000))
data <- as.data.frame(data)    
dates<-seq(as.Date("1932/09/01"), as.Date("2015/12/01"), by = "1 month",tzone="GMT")-1

I convert it to xts format:


Then I use the Return.portfolio() function to calculate the rebalanced weights assuming an equal weighted strategy:

results <- Return.portfolio(data,rebalance_on="months",geometric=F,verbose=T)

In order to calculate the turnover I'm assuming that I need the beginning of period weights and end of period weight.

I extract these from the results:

bop <- results$BOP.Weight #beginning of period weights

eop <- results$EOP.Weight #end of period weights

Then to calculate the turnover I substract bop from eop and take the absolute value:


Finally, to calculate the turnover I use the following formula:


However, when I test this on real-data (where I know what the turnover should be) I get huge, unrealistic numbers with this method.

What am I doing wrong?

My definition of turnover comes from: Demiguel et al Constraining Portfolio Norms http://faculty.london.edu/avmiguel/DeMiguelGarlappiNogalesUppalMS.pdf page 806

The definition is:

enter image description here

  • $\begingroup$ I do not think your question is clear. Many notations should be defined before they are used; do not assume every one knows them. For the code, you need at least provide some comments. $\endgroup$
    – Gordon
    Commented Aug 11, 2015 at 13:50
  • $\begingroup$ please see my edits. Now the problem is reproducible. $\endgroup$ Commented Aug 11, 2015 at 14:01
  • $\begingroup$ What is your definition of "turnover". Your two line calculation does not resemble any definition I know. $\endgroup$
    – nbbo2
    Commented Aug 11, 2015 at 14:41
  • $\begingroup$ My definition of annual turnover is (dollar value of securities sold + dollar value securities bought) divided by (beginning of year pfolio value + end of year pfolio value). Note that these are dollar values not weights. $\endgroup$
    – nbbo2
    Commented Aug 11, 2015 at 14:53
  • 1
    $\begingroup$ @user1723765, your problem is not reproducible unless you provide which data you used, the exact data sets (you said it works on your random data but not on "real data"), ....I hope you do not expect others to write a complete code solution in exchange for 100....Anyway, from what you describe the problem might be that some weights do not sum up to 1 or the like. But as your question currently stands it makes it very hard for someone to take interest in putting in time for guess work... $\endgroup$
    – Matt Wolf
    Commented Aug 12, 2015 at 3:09

2 Answers 2


First and foremost you are using bad data. min(data) gets me -3.67 (it's random remember) which would be -367% as in the position went bankrupt and took out two other ones (could be possible in a levered porftolio). However for the sake of an reproducible answer lets use the edhec data set, very little changes to your original code need to be done.

results <- Return.portfolio(edhec,rebalance_on="months",verbose=T)

bop <- results$BOP.Weight #beginning of period weights

eop <- results$EOP.Weight #end of period weights

There is a potential for error here in the non equal weight case as you are subtracting the beginning of period weights from the end of period weights when it SHOULD be the following period weights. E.g. the market has shifted your weights and you need to reset.


YourTurnover=sum(f)*(1/(nrow(edhec)-1)) # 0.01242465
SanityCheck=sum(abs(eop-1/ncol(edhec)))/(nrow(edhec)-1) #0.01242465

Please note that the above definition of turnover is the sum of all the % weight changes divided by number of trading periods. Also the authors do NOT count the initial allocation in their turnover (e.g. the 100% change from cash to investments.) The sanity check is ONLY for the EW case.

An average turnover of 1.24% which appears to be in line for an equal weight strategy.

My suspicion if you are not running an equal weight strategy is the bop-eop line. As it should be EoP weights - next BoP weights.

  • $\begingroup$ fyi. the problem was with dividing the dataset by 100. I found this out by comparing my data to the edhec data. But for other portfolios I had to change my formula based on what you said. So thanks for your help! $\endgroup$ Commented Aug 13, 2015 at 18:48

I was trying to compute the turnover when you have the initial monthly weights. Is not so easy to figure out the logic in the package used in the previous answer.

Here I show how to compute the DeMiguel and Nogales (Portfolio Selection with Robust Estimation, Operations Research 57(3), pp. 560–577, 2009) turnover.

# 10 last periods and some assets from edhec
edhecselect = c("Emerging Markets","Long/Short Equity","Short Selling","Funds of Funds")
datar = edhec[(nrow(edhec)-9):nrow(edhec),edhecselect]

set.seed(1234) # weights at beginning of the month
ws = data.frame(replicate(ncol(datar),rnorm(nrow(datar),sd=.1))) #weigths according to a rule, here random
ws = ws/rowSums(ws)  #sum(ws)=1 rowSums(ws) 
rownames(ws) = index(datar) # you know are bop weights for "hand" calculation


Compute by hand

#Create a function
pturnoverDN  = function(weight,rets,port_ret){ 
  weight[is.na(weight)] <- 0  # NAs = 0
  weighteop = weight*(1+rets)/(1+port_ret)
  dweight = abs(weight-lag(weighteop,1))
  out = (rowSums( dweight) )

PortRet =apply(datar*ws,1,sum) # compute returns

mean(turnovDN <- pturnoverDN(coredata(ws),datar,PortRet),na.rm=T)
#[1] 6.952423

Using the Return.portfoliofunction

# for Return.portfolio you need dates of previous month
wsrp= ws


results <- Return.portfolio(datar,weights =wsrp,geometric=F,verbose=T)

results$returns # are the same of hand calculation

bop <- results$BOP.Weight #beginning of period weights

eop <- results$EOP.Weight #end of period weights

(YourTurnover=sum(f)*(1/(nrow(datar)-1))) # 1.140454 Wrong?

toDN<-rowSums(abs(bop-lag(eop,1))) # to get the correct value

mean(toDN[-1]) # 6.952423

#Using the pturnoverDN function

mean(pturnoverDN(bop,datar,as.numeric(results$returns)),na.rm=T) # 6.952423

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.