*Intuition:*

Rebalancing a portfolio can be thought of as resetting the weights of individual assets chosen. For example, we could have a portfolio of four different assets with an even 25% allocated in each. These weightings can change dramatically over time as the assets may fluctuate up and down in price. An investor may choose to rebalance the weightings of the assets over time to their original weight settings. This can be done by either buying or selling more shares of the assets. A portfolio can be rebalanced in a multitude of different ways. For example, some investors try to have a set allocation in certain sectors and or industries. An example of this strategy would be setting the combined industry weights of a portfolio similar to the S&P 500 index. The main point to be taken home by rebalancing a portfolio is that an investor is attempting to maintain their original asset allocations.

*When to Rebalance:*

The answer to this question can vary from investor to investor. Some individuals may only want to rebalance after an asset allocation deviates from its original weighting by more than 5%-10%. Some individuals may take a different periodic approach known as calendar rebalancing. With this approach, the investor would examine the portfolio at predetermined intervals in time and rebalance accordingly. For example, some investors rebalance at the end of each year. This question of when to rebalance is extremely subjective and dependent on the investors’ original asset allocations, and the random nature of the stock market over time.

*Creating a Simple Model to Rebalance a Portfolio:*

We can use Python to create a simple model to reset the original weightings of selected assets. We will need a few inputs to make this dynamic model.

- Start date for the allocation period
- End date for recent specified prices
- Series of symbols that constitute the portfolio
- The number of shares purchased for each asset

After the inputs are entered, we can attempt to rebalance the portfolio to its original weightings for each asset. We can write a few loops to solve for a variable which will be the number of shares to obtain the original weighing in the asset. We know the weight of the asset is obtained by dividing the equity position by the total fund value. We also can infer the equity position is derived from the number of shares purchased multiplied by the share price of the asset.

- Equity Position = num shares * share price
- Wi = $invested in Asset i / $Value of Portfolio

We will loop through each of our assets and solve for the correct share amount. Note that many programs and services such as Quantopian offer the option to rebalance your portfolio over a prespecified time frame. Or, you could create your own algorithm to rebalance a portfolio in a multitude of ways!

Step One: Dependencies and Data

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#============================================================================== import numpy as np import pandas as pd import quandl import datetime as dt from matplotlib import style quandl.ApiConfig.api_key = "your key here" #============================================================================== #Get Data for Portfolio and Benchmark #============================================================================== symbols = ['WIKI/ADBE.4', 'WIKI/ALGN.4', 'WIKI/AMGN.4', 'WIKI/AMZN.4', 'WIKI/BK.4', 'WIKI/C.4', 'WIKI/AAPL.4'] #Change Column Names clean_symbols = [] for symbol in symbols: symbol = symbol.split("WIKI/")[-1].split(".4")[0] clean_symbols.append(symbol) allocations = [26,30,15,7,113,100,20] allocation_df = pd.DataFrame({'Symbol': clean_symbols, 'Allocations': allocations}).set_index('Symbol') |

Step Two: Calculating the Weights

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#============================================================================== #Weights Today #============================================================================== today = dt.datetime.today() price_data_today = quandl.get(symbols, start_date="2017-12-08", end_date="2017-12-08", collapse="daily") price_data_today.columns = clean_symbols equity_positions = price_data_today * allocations equity_positions = equity_positions.transpose() port_val_today = equity_positions.sum().sum() weights = equity_positions/port_val_today price_data = price_data_today.transpose() #============================================================================== #Weights Prior #============================================================================== buy_in_date = dt.datetime(2017, 01, 03) price_at_buy_in = quandl.get(symbols, start_date = buy_in_date, end_date= buy_in_date, collapse="daily") price_at_buy_in.columns = clean_symbols equity_positions_prior = price_at_buy_in * allocations equity_positions_prior = equity_positions_prior.transpose() port_val_prior = equity_positions_prior.sum() weights_prior = equity_positions_prior/port_val_prior result = pd.concat([allocation_df, price_data, weights, weights_prior], axis=1) result.columns = ["Original Num Shares", "Current Share Price", "Current Weights", "Weights Prior"] result['Status'] = np.where(result['Current Weights']>result['Weights Prior'], 'Overweight', 'Underweight') |

Step 3: Solve for New Share Amounts

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
#============================================================================== #Solve For Number of Shares #============================================================================== #Solved Weights solve_weights = [] for r in range(0, 7): #share counter x = 0.00 #num shares al = result.iloc[r,0] #stock price sp = result.iloc[r, 1] #equity position eq = al* sp #Current weight cw = eq / port_val_today #Over/Under status = result.iloc[r, 4] #Desired Weight dw = result.iloc[r, 3] while True: # Assumed Formula - this may need to be adjusted to match your criteria e = x * sp w = e/port_val_today x += 0.01 if status == "Overweight": if w >= dw: solve_weights.append(int(x)) break elif status == "Underweight": if w >= dw: solve_weights.append(int(x)) break else: break |

Step 4: Output Results

1 2 3 4 5 6 7 8 9 10 11 12 |
#============================================================================== #Output #============================================================================== solve_weights = pd.DataFrame({'Symbol': clean_symbols, 'New Share Amount': solve_weights}).set_index('Symbol') result = pd.concat([result, solve_weights], axis=1) result['# Buy/Sell'] = np.where(result['Status'] == "Overweight", result['Original Num Shares'] - result['New Share Amount'], result['New Share Amount'] - result['Original Num Shares']) result['Action'] = np.where(result['Status'] == "Overweight","Sell", "Buy") print "---------Number of Shares to Buy/Sell------------" print pd.concat([result['Action'], result['# Buy/Sell']], axis=1) |

Hi, Great post! I have a couple of questions about step 3, (1) could you explain the lines

`e = x * sp`

and`x += 0.01`

?, (2) why does that block need to go inside a`while`

loop?Quick typo note: I noticed a few occurrences of

`>`

, which (if I’m right) should just be`>`

eg.`if w >= dw`

should be`if w >= dw`

. Thanks again.Apologies about the type, I was trying to type “% g t ;” (eg. step 2, line 28) which (in the Python code) should be

`>`

.The > was a result of HTML character conversion that I had to fix. The reason that e = x * sp and x += 0.01 go back into the while loop is that we are trying to solve for a specific amount of shares to match the desired weights (dw) or the original weightings of the portfolio. We do this by creating a while loop and then breaking out when that weight is obtained. In this case we solve incrementally by adding 0.01 (x += .01) to the share amount.

Great. Thanks, that all makes sense! Cheers.