Imagine this, you have backtested a strategy adhering to all the “general rules”: you did proper in/out of sample testing, you have stable parameters (if the strategy has any), you didn’t overfit, you account for transaction costs and slippage, everything seems good and you are ready to deploy your strategy and earn you some money.

But did you check for capital constraints in your backtest? How do you transform the weights of your individual positions into actual units of the underlying? And how do you calculate the positions in your live trading, as ideally it should be the same?

Well, if you’re trading a massive portfolio and you have never heard of fractional position sizes, then this post (probably also this blog) is not for you. If you’re a small time retail trader just starting out (like me) or run multiple strategies that you allocate capital too, this might be something you should keep an eye on.

Suppose you have a simple momentum rotation strategy and your equity curve looks like this:

Doesn’t look too bad, annualized return of 11.13%, Sharpe Ratio around 1, solid stuff. But this is a hypothetical curve using fractional positions. Would this have really been obtainable for you? Probably not.

Let’s say you’re rebalancing and your systems tells you to have the following positions:

- IEF: 55.43693
- RWX: 26.23092
- VTI: 17.75889
- VNQ: 10.59392
- TLT: 6.765491

You can’t get this exact allocation because you are constrained by your portfolio size. So what do you do? “Just round it down” is an answer I get most of the time. How would our equity curve change if we had done that? Lets take a look:

Uh oh. Our annual returns dropped from 11.13% to 9.96%, a quite significant decrease. And all that because we “just rounded down” and therefore were underinvested at all times. So it seems this is a matter that requires some attention!

One possible solution is rounding all positions and calculating the absolute difference between the position value for fractional units and rounded units. Afterwards, positions are adjusted starting from the smallest absolute difference until there is only one remaining which gets the remaining uninvested capital. What effect does this method have on our performance?

Well this looks promising! Annualized returns drop only 14BP to 10.99% and the Sharpe remains essentially the same. Quite an improvement! But is this an optimal solution? Probably not, as your weights could be changed quite a bit (more if your overall portfolio size is smaller).

Another possible solution is rounding the highest weighted positions first and only employing the minimum absolute difference method from before if weights are equal. This tends to overweight the position with the highest allocation slightly, and underweights the rest.

In this case, our returns even improve slightly to 11.2% p.a. Even though this is entirely random, the suitability of this method is apparent.

So what’s the takeaway from all of this? It is well worth to pay some attention to capital constraints in backtesting (and obviously in live trading!), as everything counts (and compounds).

Is one of the methods presented in this post superior than the other? My experience shows that they both tend to perform similar for a variety of strategies, with no clear winner. If you want to try them out for yourself, you can find the R implementation of the minimum difference method in the appendix and it can easily be adapted for the second one.

Also, if some of you are in a similar position as me (trading a sub 100k portfolio) and have a different (and possibly) superior method, please drop a comment below.

Until next time,

QUANTBEAR

Appendix:

allocateCapital = function(prices,weights,capital,units=NULL) { options(warn=-1) if(length(which(weights > 0)) > 0) { sel = which(weights > 0) full = weights[,sel] * capital / prices[,sel] rounded = round(full,0) sel = sel[which.min(abs((rounded - full) * prices[,sel]))] if(length(which(weights > 0)) == 1) { tmpunit = floor(as.numeric(weights[,sel] * capital / prices[,sel])) }else{ tmpunit = round(as.numeric(weights[,sel] * capital / prices[,sel]),0) } names(tmpunit) = colnames(weights)[sel] units = c(units,tmpunit) allocateCapital(prices[,-sel],weights[,-sel] / sum(weights[,-sel]),as.numeric(capital - tmpunit * prices[,sel]),units) }else{ options(warn=0) return(units) } } #EXAMPLE require(xts) prices = xts(matrix(c(142.27,66.4,858.95),nrow=1),Sys.Date()) colnames(prices) = c("AAPL","MSFT","GOOGL") weights = xts(matrix(c(0.25,0.4,0.35),nrow=1),Sys.Date()) colnames(weights) = c("AAPL","MSFT","GOOGL") capital = 10000 units = allocateCapital(prices,weights,capital) units MSFT AAPL GOOGL 60 18 4

Pingback: Quantocracy's Daily Wrap for 04/23/2017 | Quantocracy

Very nice article. Thanks for sharing. On a similar note, Do you have any thoughts on the following? I am trying to use a few models but finding it a bit difficult to calculate the rebalanced weights for the next week/month before the market closes and place the trades. This problem is pronounced if the tradable universe is large where the end of the day price fluctuations can cause the selection to change. I could go with next day open prices but, I’d like to use end of the day prices since that’s what the models are tested with.

LikeLiked by 1 person

Hello Retail Trader,

thank you for the kind words! As the goal should always be to make your backtests as realistic as possible, I would suggest that you either try to obtain intraday data for your universe and use that in your backtest (good idea in general as it seems your strategy is very sensitive to smaller price movements), or use the open prices as the entry (as you suggested) when you test it. The third option would be to reduce the calculation time of your rebalancing weights/improve the execution of the trades.

Best,

QUANTBEAR

LikeLike

How are about the out-of-sample performances?

LikeLiked by 1 person

Hello Donglei Du,

this post was not meant as backtest per se, it was meant to illustrate one important issue that can arise when you trade a relatively small sized portfolio. If you have specific questions about this sample strategy, please let me know.

Best,

QUANTBEAR

LikeLike

QuantBear,

Another question. In your back tests, How do you handle stocks that seize to trade due to mergers, bankruptcies etc mid-rebalancing period to get the back test results close to real results? Right now, I use a big loop to go through each re-balancing period and calculate weights. This helps in making sure there is no look ahead bias introduced accidentally or otherwise and drop the symbols for the next period, But I am wondering if there is any easy way to accurately calculate the returns. Right now I simply copy the price all the way to the end of the period and calculate. While this works I am sure there is a better way to do this.

Thanks for your thoughts.

LikeLiked by 1 person

Hello Retail Trader,

in my backtests I make sure that at every rebalancing date I have only the subset of assets available that I would have been able to actually trade. I have a check in place that filters out the assets that became unavailable and adds new assets (IPOs, new ticker for two companies that merged etc). If you have further questions about that, feel free to drop me a pm.

Best,

QUANTBEAR

LikeLike

Rather than rebalancing on a fixed period, i.e. every day, every week, 1st of every month etc. Try rebalancing on an individual asset basis when the required allocation (of an asset) has moved a certain percentage away from a target allocation. This reduces transaction costs. Essentially, you’re rebalancing only when you need to rather than forcing a rebalance.

LikeLiked by 1 person

Hello ravic,

yes that is a good method to reduce trading costs in general, however this was not the point of the article. In your example, if two or more assets would require rebalancing at the same time, you would have the same issue as before.

Best,

QUANTBEAR

LikeLike

Thanks so much for sharing this…..I am a beginner /intermediate R learner…Do you mind sharing the rest of the code please?

LikeLiked by 1 person

Hello,

the charts are produced using the PerformanceAnalytics package. The returns are generated using my own backtesting framework and a sample momentum strategy (I might share both in future posts). Feel free to send me a PM if you have further questions.

Best,

QUANTBEAR

LikeLike