How does diversification reduce risk?

May 4, 2019

This script will generate a simulation and plot of correlated return streams in a portfolio. The objective is to replicate "The Holy Grail" study of investing from Ray Dalio (see references). This demonstrates the effect of adding uncorrelated assets to a portfolio. Inputs are mean of return stream, standard deviation (risk) of return stream, list of correlation percentages to study, and number of assets in the portfolio.

Assumptions

  • The mean and standard deviation for each asset is the same
  • The standard deviation represents the risk of the portfolio
  • Returns for assets are normally distributed
  • 500 data points for each asset is sufficient
  • Asset allocation across the portfolio is averaged across the number of instruments (1/N)

References:
[0a] "The Holy Grail" from https://www.youtube.com/watch?v=Nu4lHaSh7D4,
[0b] Principles, Ray Dalio, pg 57
[1] Random number generation adapted from: https://realpython.com/python-random/

In [1]:
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
In [2]:
def corr2cov(p: np.ndarray, s: np.ndarray) -> np.ndarray:
    """Covariance matrix from correlation & standard deviations"""
    d = np.diag(s)
    return d @ p @ d
In [3]:
mean_assets = .1
stdev_assets = .1

correlation_test_list = [ .60, .40, .20, .10, 0 ]

df = pd.DataFrame()

for correlation in correlation_test_list:

    # set empty list to gather results
    result_list = []

    # assume we want 1 to 20 assets
    for num_assets in range(1,21):

        # Start with a correlation matrix and standard deviations.
        # Retrieve the correlation between assets for this run, and the correlation
        # of a variable with itself is 1.0.
        # Create dynamically sized correlation matrix based on number of assets.
        corr_matrix = np.zeros((num_assets, num_assets), float)
        corr_matrix.fill(correlation)
        np.fill_diagonal(corr_matrix, 1)

        # Standard deviations/means of assets, respectively
        stdev = np.empty(num_assets, dtype='float')
        stdev.fill(stdev_assets)
        mean = np.empty(num_assets, dtype='float')
        mean.fill(mean_assets)

        cov = corr2cov(corr_matrix, stdev)

        # `size` is the length of time series for 2d data
        # (500 months, days, and so on)
        data = np.random.multivariate_normal(mean=mean, cov=cov, size=500)

        # look at portfolio of average returns
        portfolio_returns = np.mean(data, axis=1)
        portfolio_risk = np.std(portfolio_returns)

        # gather results
        result_list.append(portfolio_risk)

    # collect data in dataframe
    df.insert(0,'Corr '+str(correlation),result_list,True)
In [4]:
plt.figure(figsize=(12,5))
plt.plot(df)
plt.title('The Holy Grail')
plt.legend(df.columns)
plt.xlabel('Number of Assets/Alpha in Portfolio')
plt.ylabel('Portfolio Standard Deviation')
plt.show()

What does it mean?

As you increase the number of returns streams in your portfolio, you don't necessarily obtain healthy diversification, unless they are uncorrelated. Given the assumptions above for this portfolio, this study shows that:

  1. If you have 60% correlation between the instruments, you will receive marginal benefit, it at all, by adding more than 5 or 6 assets. But by having 5 or 6 instruments, you can reduce portfolio risk by about 20%.
  2. If you have 10% correlation between the instruments, you will receive marginal benefit by adding any more than 12 or 13 assets. But by having those 12 or 13 instruments, you can reduce portfolio risk by about 50%.

When you reach this marginal utility threshold, you are able to most effectively generate the same return with decreased risk.

Adding uncorrelated return streams to a portfolio helps you reduce fluctuations while receiving the same expected return.

Future Work

  • make num_assets a parameter
  • include return-to-risk ratio
  • include probability of losing
  • smooth the output over multiple simulation runs
  • generate a tool for analysis of multiple real assets