https://www.investopedia.com/video/play/ray-dalio-his-portfolio-holy-grail/

From *Principles: Life and Work* by Ray Dalio.

From my earlier failures, I knew that no matter how confident I was in making any one bet I could still be wrong—and that proper diversification was the key to reducing risks without reducing returns. If I could build a portfolio filled with high-quality return streams that were properly diversified (they zigged and zagged in ways that balanced each other out), I could offer clients an overall portfolio return much more consistent and reliable than what they could get elsewhere.”

In this notebook, we'll explore what Ray Dalio referrs to as the *Holy Grail of Investing*, how increasing diversification we are able to reduce overall risk, as measured by the standard deviation of portfolio returns.

The idea is to show that, if we can find a basket of uncorrelated return streams (in practice we allow for low correlation), we can reduce the portfolio risk significantly by increasing the number of streams in our portfolio.

In [1]:

```
%config InlineBackend.figure_format = "retina"
import numpy as np
import pandas as pd
import altair as alt
np.random.seed(42) # Set seed for reproducibility
```

We begin by creating a function that simulates `n`

return streams with a given mean (`mean`

) and standard deviation (`risk`

), and a given average correlation (`corr`

) between them.

In [2]:

```
def correlated_streams(n, mean, risk, corr):
"""Generates `n` return streams with given average `mean` and `risk`,
and with an average correlation level `corr`.
"""
num_samples = 10_000
means = np.full(n, mean)
corr_mat = np.full((n, n), corr, dtype=np.dtype("d"))
np.fill_diagonal(corr_mat, 1,)
cov_mat = corr_mat * risk**2
streams = np.random.multivariate_normal(means, cov_mat, size=num_samples)
return streams.T
```

Just to make sure, let's do a sanity check.

In [3]:

```
n = 5
mean, std, corr = 10, 15, 0.6
streams = correlated_streams(n, mean, std, corr)
```

In [4]:

```
streams.mean(axis=1)
```

Out[4]:

In [5]:

```
streams.std(axis=1)
```

Out[5]:

In [6]:

```
np.corrcoef(streams)
```

Out[6]:

This is the simplest way to construct such a portfolio. We make each pariwise correlation between assets equal to a given level `corr`

. The important point is that the average of all the pairwise correlations should be equal to `corr`

.

We'll create a helper function to calculate the pooled risk of a given number of return streams in the porfolio.

In [7]:

```
def aggregate_risk(return_streams, n):
"""Returns the pooled risk (std) of the `n` first streams
in `return_streams`
"""
assert len(return_streams) >= n
aggregate_returns = np.sum(return_streams[:n], axis=0) / n
return aggregate_returns.std()
```

Next, we'll build our simulated dataset. We'll analyse return streams with risk levels in the range 1% - 14%, for varying number of streams ranging from 1 to 20.

We'll plot the risk levels for different average correlation, ranging from 0 to 0.7.

In [8]:

```
max_assets = 20
assets = range(1, max_assets+1)
mean = 10 # Avg mean return of 10%
risk_levels = range(1, 15)
index = pd.MultiIndex.from_product([risk_levels, assets], names=["risk_level", "num_assets"])
simulated_data = pd.DataFrame(index=index)
for risk in risk_levels:
for corr in np.arange(0.0, .8, 0.1):
return_streams = correlated_streams(max_assets, mean, risk, corr)
risk_level = np.zeros(max_assets)
for num_assets in assets:
risk_level[num_assets-1] = aggregate_risk(return_streams, num_assets)
simulated_data.loc[(risk, ), round(corr, 1)] = risk_level
simulated_data.columns.names = ["correlation"]
```

Let's have a look at our dataset.

In [9]:

```
simulated_data.query("risk_level == 14")
```

Out[9]:

We can already see how portfolio risk *decreases* as we add more assets, with sharper declines when we they have low correlation.

To recreate Dalio's chart (as seen in this video), we create a function that produces a plot given our simulated data and a risk level.

In [10]:

```
def plot_risk_level(data, risk_level):
subset = data.query("risk_level == @risk_level")
stacked = subset.stack().reset_index(name="risk")
stacked.head()
chart = alt.Chart(data=stacked)
highlight = alt.selection(type="single", on="mouseover",
fields=["correlation"], nearest=True)
base = chart.encode(
alt.X("num_assets", axis=alt.Axis(title="Number of Assets")),
alt.Y("risk", axis=alt.Axis(title="Risk %")),
alt.Color("correlation:N", scale=alt.Scale(scheme="set2")))
points = base.mark_circle().encode(
opacity=alt.value(0)
).add_selection(
highlight
).properties(
height=400,
width=600,
title="Risk % by number of assets in portfolio"
)
lines = base.mark_line().encode(
size=alt.condition(~highlight, alt.value(1), alt.value(3)),
tooltip=["correlation"]
)
return points + lines
```

Let's see how diversification benefits a portfolio with assets that have a risk level of 10%.

In [11]:

```
plot_risk_level(simulated_data, 10)
```

Out[11]:

A highly correlated portfolio *does not* benefit much from increased diversification. We get diminishing returns by adding highly correlated assets beyond 3 or 4.

In contrast, we can *halve* the risk by adding just 6 or 7 uncorrelated (or more realistically, weakly correlated) assets to a portfolio.

Let's plot the risk levels for a portfolio with returns streams with 7% risk.

In [12]:

```
plot_risk_level(simulated_data, 7)
```

Out[12]:

The benefits of diversification are generally well known: reduced risk through exposure to different sources of income.

The insight Dalio brings to the forefront, is that the construction of a diversified portfolio through a combination of *uncorrelated* return streams, significantly decreases our overall risk, raising in turn our return / risk ratio. By the careful mixing of uncorrelated assets, we capture true *alpha*, enabling us to use leverage to increase our returns.

In [ ]:

```
```