liveBook · Manning

Portfolio rebalancing

As explained in Build a Robo-Advisor with Python (From Scratch)

A rule or procedure for deciding when and how portfolio holdings should be traded toward target allocations.

Portfolio rebalancing periodically corrects drift from target weights. Different asset returns can change both the portfolio’s risk and its intended exposure, making automated monitoring and trading useful. section 13.0 section 1.2.4

A drifting 60/40 portfolio

Starting with 60% stocks and 40% bonds, assume annual returns of 6% and 2% respectively for 30 years. Without rebalancing, the stock weight rises to about 83% and volatility increases from about 10% to 14%, making the portfolio riskier than intended. section 13.1.1

Common rebalancing policies include:

  • Time-based rebalancing trades on a fixed schedule. section 1.1.1
  • Drift-based rebalancing trades when an allocation deviates from its target by a preset amount. section 1.1.1
  • Tax-aware rebalancing avoids selling appreciated positions when expected taxes are high and can direct deposits or dividends toward underweight assets. section 1.1.1
  • Simple policies can be replaced by optimization that balances target alignment against taxes, trading costs, objectives, and constraints. section 13.3 section 13.1.2

Rebalancing has costs: selling appreciated assets can realize capital gains, while every rebalance requires trading. Liquid ETFs generally reduce trading costs, but taxes often remain the more important consideration. section 13.1.2

Listing 13.1 Growing portfolio weights through time
import numpy as np
n_years = 30
yearly_rets = np.array([.06, .02])
sigma = np.array([[0.0287, 0], [0, 0.0017]])
start_weights = [.6, .4]

portfolios = np.zeros((n_years + 1, 2))
portfolios[0,:] = start_weights
vols = np.zeros(n_years + 1)
vols[0] = np.sqrt(portfolios[0,:] @ sigma @ portfolios[0,:])

for year in range(n_years):
    last_weights = portfolios[year,:]
    grown_weights = last_weights * (1 + yearly_rets)
    grown_weights /= np.sum(grown_weights)
    grown_vol =  np.sqrt(grown_weights @ sigma @ grown_weights)
    portfolios[year + 1, :] = grown_weights
    vols[year + 1] = grown_vol

The Python listing grows asset weights through time, normalizes them, and computes portfolio volatility to expose drift before rebalancing. section 13.1.1

How Build a Robo-Advisor with Python (From Scratch) covers it

How deep it goes
Develops drift examples, schedule and threshold rules, tax-aware practices, optimization, and backtesting with cvxpy.
Examples
Uses a 60/40 stocks-and-bonds portfolio, simulated 30-year weight drift, volatility calculations, and historical backtesting.
What it assumes
Assumes portfolio weights and returns, then develops rebalancing from drift measurement through taxes, trading costs, optimization, and simulation.
What you can do after
Build it from scratch by implementing threshold or schedule rules, tax-aware trades, optimization constraints, and a rebalancing backtester.
How it connects
Connects target allocation, portfolio optimization, transaction costs, taxes, tax-loss harvesting, and behavioral biases.

Also explained in

Compare how 2 books explain portfolio rebalancing →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Build a Robo-Advisor with Python (From Scratch). The section links point to the parts of the book it draws on.