## Modern Portfolio Theory

### Introduction

In 1952, Harry Markowitz published a seminal paper in the Journal of Finance
titled **Portfolio Selection**, where he first introduced Modern Portfolio Theory
(MPT), which quantifies the **risk** and **return** trade-off when constructing portfolios of risky assets. The theory formalizes
the concept of **diversification** in mathematical terms, and suggests that rational investors seek to make investment decisions
that **maximize portfolio return** for a given level of **risk**. It frames the investment problem not only as an asset selection
challenge, but also of **sizing the positions** of each chosen risky asset in the portfolio. MPT also introduces the concept of
systematic versus non-systematic
risk, the latter of which can be diversified away in a well proportioned portfolio. Systematic risk refers to general market
wide risks such as interest rate risk, business recessions or wars, while non-systematic risk (also known as specific risk)
relates to the **idiosyncratic risks** associated with an individual security.

Modern Portfolio Theory is still in widespread use today in professional investment management circles, and remains one of
the foundational frameworks used to build efficient risk adjusted portfolios. Markowitz was awarded the **Nobel Memorial
Prize in Economic Sciences** in 1990 for his work on MPT.

### Risk & Return

Before we look at an example, we first need to discuss the concepts of **risk** and **return** in the context of a portfolio
of risky assets. It seems eminently reasonable to expect that a rational investor makes decisions so as to **maximize investment
return** while **minimizing the risk** or uncertainty of that return. While return is an unambiguous concept, risk or uncertainty
is not necessarily so easily quantified. In the context of MPT however, risk is defined as the **variance of the portfolio returns**,
which is a function of the **variance and covarinace** of the individual asset returns in the portfolio. More on this later,
but first let us discuss portfolio return.

### Portfolio Return

Consider investment returns generated by a portfolio of risky assets. **Portfolio return** is simply the weighted sum of the
returns on the individual assets in the portfolio, which can be expressed as per the equation below, where \(w_{i}\)
represents the **weight** (percentage of capital) of asset i, \(r_{i}\) the return on asset i, and \(R_{p}\) the overall
portfolio return.

$$ R_{p} = \sum_{i=1}^{n} w_{i} * r_{i} $$

In order to illustrate the validity of this expression, consider a two asset hypothetical portfolio. Let us assume we have $1000 of capital, and we have decided to invest in just two stocks (n=2), namely Amazon and Apple. For lack of a strong opinion, we simply split our investment 50/50 between the two names, and then over a year we see Apple return 15% and Amazon returns 8%. How much does our portfolio return?

Ignoring the above formula for the moment, we can easily calculate the profit from each $500 dollar investment, sum these profits
and then calculate the resulting return on our $1000 initial investment, which comes out to be 11.5%. In general terms, the
**future value** `F`

of some monetary amount can be related to its **present value** `P`

and a **rate of return** `R`

as follows:

$$ F = P (1 + R) $$

Since we are ultimately trying to calculate the portfolio return, we need to write this expression in terms of the **present
value and the return on the individual assets** in the portfolio. The expression below does just this, where \(p_{i}\) and
\(r_{i}\) represent the present value and return of individual assets respectively, and subscript `p`

denotes the portfolio
value:

$$ F_{p} = \sum_{i=1}^{n} p_{i} (1 + r_{i}) \ where \ P_{p} = \sum_{i=1}^{n} p_{i} $$

Considering our **two asset example**, we can expand the equation to yield:

$$ \begin{align} F_{p} &= p_{1} (1 + r_{1}) + p_{2} ( 1 + r_{2}) \\ F_{p} &= p_{1} + p_{2} + p_{1} r_{1} + p_{2} r_{2} \\ F_{p} &= P_{p} + p_{1} r_{1} + p_{2} r_{2} \\ \end{align} $$

We know that from our original future value equation we can express the return of the portfolio as:

$$ R_{p} = \frac{F_{p}}{P_{p}} - 1 $$

Therefore if we divide the prior equation for our two asset scenario by the present value of the portfolio we get an expression for the portfolio return in terms of the individual asset returns, which is essentially the same as the weighted sum of the asset returns as defined earlier.

$$ R_{p} = \frac{F_{p}}{P_{p}} - 1 = \frac{p_{1} r_{1} + p_{2} r_{2}}{P_{p}} = w_{1} r_{1} + w_{2} r_{2} $$

### Portfolio Risk

While **portfolio return** is **conceptually unambiguous**, defining portfolio risk is less clear cut. In the case of Modern
Portfolio Theory, risk is defined as the **variance or volatility of the returns**, which is certainly consistent with intuition,
since high variance implies a high degree of uncertainty. Portfolio returns that are extremely volatile are not only emotionally
hard to stomach, but may force an investor to crystallize losses at a very inopportune time due to a requirement to gain access
to capital. Long term investors often think of risk in different terms, and in fact Warren Buffet,
who is arguably the greatest investor of all time, would probably not consider volatility as an appropriate metric. Instead,
he would be more likely to think in terms of the potential for **permanent loss of capital**.

Few of us have the investment acumen or long horizon of Warren Buffet, so for mere mortals, let us stick with return volatility
as our best measure of portfolio risk. While individual asset return volatility is simple to compute, calculating portfolio
level risk is less trivial as it involves understanding the **interaction of individual asset returns**. That is to say, no
two assets in a portfolio are likely to be 100% correlated, and therefore combining uncorrelated assets is bound to affect
risk in ways that need to be quantified. Let us begin with our definition of portfolio return variance which using the
expectation operator is just the usual expression for variance.

$$ \sigma^2 = E[ ( R_{p} - \bar{R}_{p} )^2 ] $$

In this equation \(r_{p}\) represents the return generated by an instance of the portfolio while \(\bar{r}_{p}\) represents
the **average expected return** from this portfolio. To illustrate, consider a two asset portfolio where we expand the above
expression to be in terms of the individual assets that make up the portfolio.

$$ \begin{align} \sigma^2 &= E[ ( R_{p} - \bar{R_{p}})^2 ] \\ \sigma^2 &= E[ ( w_{1} r_{1} + w_{2} r_{2} - (w_{1} \bar{r_{1}} + w_{2}\bar{r_{2}}) )^2 ] \\ \sigma^2 &= E[ ( w_{1} ( r_{1} - \bar{r_{1}} ) + w_{2} ( r_{2} - \bar{r_{2}}))^2 ] \\ \sigma^2 &= E[ w_{1}^2 ( r_{1} - \bar{r_{1}} )^2 + w_{2}^2 ( r_{2} - \bar{r_{2}})^2 + 2 w_{1} w_{2} E[(r_{1} - \bar{r_{1}})(r_{2} - \bar{r_{2}})]] \\ \end{align} $$

Since the **asset weights are not stochastic in nature**, we can take them outside of the expectation operator to yield the following:

$$ \sigma^2 = w_{1}^2 E[( r_{1} - \bar{r_{1}})^2] + w_{2}^2 E[( r_{2} - \bar{r_{2}})^2] + 2 w_{1} w_{2} E[(r_{1} - \bar{r_{1}})(r_{2} - \bar{r_{2}})]] $$

It now becomes clear that the portfolio variance is a function of the **individual asset variances** as well as their
**covariance**, so we can write the same expression in a more concise manner as shown below. Notice that this expression
suggests that if we combine risky assets in a portfolio that have a **negative covariance**, the overall portfolio **risk will
be reduced**. This is essentially diversification quantified, and it is a central principle of MPT.

$$ \sigma^2 = w_{1}^2 \sigma_{1}^2 + w_{2}^2 \sigma_{2}^2 + 2 w_{1} w_{2} \sigma_{12} $$

While the above derivation is for a 2 asset portfolio, this expression can be generalized to an N asset portfolio and
written in **matrix form** as below. The `w`

term represents an `nx1`

vector of asset weights, and capital sigma represents
the `nxn`

covariance matrix of asset returns, the diagonal elements of which are the individual asset return variances.

$$ \sigma^2 = w^T \Sigma w $$

### Sharpe Ratio

The Sharpe Ratio is a widely used performance metric in finance and is named
after Nobel Laureate William F. Sharpe who first developed it. The ratio
is basically the average return earned in **excess** of the **risk free rate** per unit of volatility, and is therefore a
standardized way of expressing **risk adjusted return**. Mathematically, it is defined as follows (where \(R_{p}\)
represents portfolio return, \(\bar{R_{f}}\) is the risk free return and \(\sigma_{p}\) is portfolio risk):

$$ S_{p} = \frac{E[ R_{p} - \bar{R_{f}}]}{\sigma_{p}} $$

A Sharpe Ratio can be computed *ex-ante* based on **forecasts** of expected risk and return, or otherwise as an *ex-post*
measure based on realized returns. Given that the return measure (the stuff we like) is in the numerator and the risk measure
(the stuff we don't like) is in the denominator, the higher the Sharpe Ratio the better.

While it is very widely used, the Sharpe Ratio is not without its detractors. One of the often quoted grievances with the
measure is that it treats **upside volatility** equally with **downside volatility**. This may be reasonable in a long / short
portfolio, but perhaps less so in a long-only portfolio (i.e. no shorting constraint). In order to address this, a
variation of the Sharpe Ratio exists called the Sortino Ratio which
only includes returns below a certain threshold when computing volatility.

Another potential concern is that the Sharpe Ratio does not distinguish between **systematic** and **non-systematic** risk,
the latter of which can be diversified away in a well balanced portfolio. The Treynor Ratio
attempts to address this by estimating the systematic risk only, and using that as the denominator in the above expression.

Finally, one of the trickiest issues with the Sharpe Ratio is scaling it to different time horizons. For example, how
do you compute an annualized Sharpe from daily returns? Most techniques scale risk and return on the assumption that
returns are **normally distributed**, however it is well known that asset returns are not normal and exhibit excess
**kurtosis** and often **skewness**. For more details on some of the challenges in computing Sharpe Ratios, I high recommend a
paper by Andrew W. Lo titled The Statistics of Sharpe Ratios.

### Examples

Now that we know how to calculate **portfolio return and risk**, let us look at how we can apply this knowledge, and
more importantly, demonstrate how we can use Modern Portfolio Theory to construct an efficient investment portfolio of
risky assets. The examples in the following sections leverage the Morpheus data source adapter for Yahoo Finance,
more details of which can be found here. The library is available on **Maven Central** and can
therefore be added to your build tool of choice:

```
<dependency>
<groupId>com.zavtech</groupId>
<artifactId>morpheus-yahoo</artifactId>
<version>${VERSION}</version>
</dependency>
```

### Two Assets

Consider a **two asset** portfolio where we have already convinced ourselves that we want to buy Apple and Amazon, but we are not sure how much of our capital to invest in each. We obviously do not know what the future may bring,
but let us look back over the past year to see what happened on the assumption that it may help influence our decision.
The plot below shows the cumulative returns of both securities over the past year, which clearly demonstrates what a great
run they have had. With the benefit of hindsight of course, you would have invested all your capital in Apple as it
outperformed Amazon by some margin. Looking forward however, the performance of these stocks could be reversed, so we
should probably spread our bets across the two.

The code to generate this plot is as follows:

```
LocalDate end = LocalDate.now();
LocalDate start = end.minusYears(1);
Array<String> tickers = Array.of("AAPL", "AMZN");
YahooFinance yahoo = new YahooFinance();
DataFrame<LocalDate,String> cumReturns = yahoo.getCumReturns(start, end, tickers);
cumReturns.applyDoubles(v -> v.getDouble() * 100d);
Chart.create().withLinePlot(cumReturns, chart -> {
chart.title().withText("Cumulative Asset Returns");
chart.subtitle().withText("Range: (" + start + " to" + end + ")");
chart.plot().axes().domain().label().withText("Date");
chart.plot().axes().range(0).label().withText("Return");
chart.plot().axes().range(0).format().withPattern("0.00'%';-0.00'%'");
chart.show();
});
```

To get a sense of the risk / return characteristics of our two asset portfolio, we are going to **generate 10,000 random
portfolios** where we invest different amounts in Apple and Amazon ranging from 0% to 100% of our capital in one asset and
the remainder in the other asset. The function below returns a `DataFrame`

of random portfolio weights in N assets that
sum to 1 in all cases, and therefore each portfolio represents a **fully invested** scenario.

```
/**
* A function that generates N long only random portfolios with weights that sum to 1
* @param count the number of portfolios / rows in the DataFrame
* @param tickers the security tickers to include
* @return the frame of N random portfolios, 1 per row
*/
DataFrame<Integer,String> randomPortfolios(int count, Iterable<String> tickers) {
DataFrame<Integer,String> weights = DataFrame.ofDoubles(Range.of(0, count), tickers);
weights.applyDoubles(v -> Math.random());
weights.rows().forEach(row -> {
final double sum = row.stats().sum();
row.applyDoubles(v -> {
double weight = v.getDouble();
return weight / sum;
});
});
return weights;
}
```

In order to calculate the risk and return characteristics of these 10,000 portfolios, we first need to compute the
**covariance matrix of the returns** between Apple and Amazon, and then also compute the **cumulative asset returns**
over the historical horizon in question. With these quantities, it is fairly simple to compute the risk & return of
each portfolio, which can then be plotted on a scatter chart to see how they compare. The resulting plot is shown
below, and is followed by the code to generate it. Note that since we are using daily returns to compute the covariance
matrix, we need to **annualize** it, which we do by multiplying by 252 (on the assumption there are 252 trading days
in the year). Since the cumulative returns are based on a 1-year look back window, there is no need to annualize the
returns.

```
//Define portfolio count, investment horizon and universe
int count = 10000;
LocalDate end = LocalDate.now();
LocalDate start = end.minusYears(1);
Array<String> tickers = Array.of("AAPL", "AMZN");
//Grab daily returns and cumulative returns from Yahoo Finance
YahooFinance yahoo = new YahooFinance();
DataFrame<LocalDate,String> dayReturns = yahoo.getDailyReturns(start, end, tickers);
DataFrame<LocalDate,String> cumReturns = yahoo.getCumReturns(start, end, tickers);
//Compute asset covariance matrix from daily returns and annualize
DataFrame<String,String> sigma = dayReturns.cols().stats().covariance().applyDoubles(v -> v.getDouble() * 252);
DataFrame<LocalDate,String> assetReturns = cumReturns.rows().last().map(DataFrameRow::toDataFrame).get();
//Generate random portfolios and compute risk & return for each
DataFrame<Integer,String> portfolios = randomPortfolios(count, tickers);
DataFrame<Integer,String> results = DataFrame.ofDoubles(Range.of(0, count), Array.of("Risk", "Return"));
portfolios.rows().forEach(row -> {
DataFrame<Integer,String> weights = row.toDataFrame();
double portReturn = weights.dot(assetReturns.transpose()).data().getDouble(0, 0);
double portVariance = weights.dot(sigma).dot(weights.transpose()).data().getDouble(0, 0);
results.data().setDouble(row.key(), "Return", portReturn * 100d);
results.data().setDouble(row.key(), "Risk", Math.sqrt(portVariance) * 100d);
});
//Plot the results using a scatter plot
Chart.create().withScatterPlot(results, false, "Risk", chart -> {
chart.title().withText("Risk / Return Profiles For AAPL+AMZN Portfolios");
chart.subtitle().withText(count + " Portfolio Combinations Simulated");
chart.plot().axes().domain().label().withText("Portfolio Risk");
chart.plot().axes().domain().format().withPattern("0.00'%';-0.00'%'");
chart.plot().axes().range(0).label().withText("Portfolio Return");
chart.plot().axes().range(0).format().withPattern("0.00'%';-0.00'%'");
chart.show();
});
```

There are a few notable observations about the plot as follows:

- It appears to be impossible to create a portfolio with a risk lower than about 15.7% volatility.
- If you were willing to accept 16% risk, there are two portfolios, one of which has a much better return than the other.
- Much beyond 17% risk, it appears returns suffer tremendously due to overweight in one of the assets.
- The best possible return appears to be about 37% at a risk level just north of 17%.

A rational investor clearly wants to be on the upper part of this curve for a **given level of risk**. For example,
at 17% volatility, you could either have achieved 23.5% return with a portfolio on the lower segment of the curve
or close to 37% on the upper part. Which one do you prefer? The upper part of the curve is referred to as the
Efficient Frontier, and moreover, there exists a special case
on this curve often referred to as the **Markowitz Portfolio**, which has the highest risk adjusted return of all
the candidate portfolios. It is essentially the **mean-variance optimal portfolio** and can be calculated by
maximizing the utility defined by the objective function below, subject to whatever constraints you may impose
(in this case we assume **long only** and **fully invested**):

$$ U_{p} = w^T r - w^T \Sigma w $$

The \(w\) and \(r\) terms represent `nx1`

vectors of asset **weights** and **returns** respectively, while capital
**sigma** represents the `nxn`

asset covariance matrix. This objective function is essentially saying we like return
and do not like risk, and our goal is to select an `nx1`

vector of asset weights that maximizes this expression given
asset returns and a covariance matrix. In this example, we are looking back at a historical scenario so we can calculate
returns and the covariance based on realized market prices. In reality, we need to make **judgements about the future**,
and therefore are required to **estimate future returns and covariance**, which is where things can get tricky. Calculating
the **Markowitz Portfolio** given constraints (as in this case where we impose a long only fully invested constraint such
that the elements of W are all positive and sum to 1) is a quadratic optimization
problem that requires appropriate software and is beyond the scope of this article. A good commercial package
to consider is MOSEK, or for an Open Source
solution you may consider OptaPlanner.

### Asset Selection

Modern Portfolio Theory is fundamentally about **sizing positions** of risky assets in a portfolio, it is not
about asset selection. Having said that, it can be useful to compare the risk / return profiles of portfolios
constructed from different risky assets. In the prior example we had already decided that we wanted to invest
in Apple and Amazon, but were there better two asset portfolio combinations we should have considered? The plot
below, followed by the code that generated it, is essentially an extension of the prior example where in this
case we generate multiple 10,000 portfolio combinations with different asset constituents to see how they compare.

It is clear from the chart that the 5 two asset portfolios in this example have very different risk / return
profiles, with the Apple / Amazom combination being the most risky, but certainly having some of the highest
returns. With that being said, if your investment objective was to achieve around a 12.0% return (very
aspirational in today's world of zero interest rates), your best bet would be to choose the VTI / BND combination
as it appears to have the **potentia**l to generate this return for less than 5% risk. Compare this to some of the other
portfolio combinations for which you need to accept a much **higher level of risk** to achieve the **same return**.

```
//Define portfolio count, investment horizon
int count = 10000;
LocalDate end = LocalDate.now();
LocalDate start = end.minusYears(1);
YahooFinance yahoo = new YahooFinance();
Array<DataFrame<Integer,String>> results = Array.of(
Array.of("VTI", "BND"),
Array.of("AAPL", "AMZN"),
Array.of("GOOGL", "BND"),
Array.of("ORCL", "KO"),
Array.of("VWO", "VNQ")
).map(v -> {
//Access tickers
Array<String> tickers = v.getValue();
//Grab daily returns and cumulative returns from Yahoo Finance
DataFrame<LocalDate,String> dayReturns = yahoo.getDailyReturns(start, end, tickers);
DataFrame<LocalDate,String> cumReturns = yahoo.getCumReturns(start, end, tickers);
//Compute asset covariance matrix from daily returns and annualize
DataFrame<String,String> sigma = dayReturns.cols().stats().covariance().applyDoubles(x -> x.getDouble() * 252);
DataFrame<LocalDate,String> assetReturns = cumReturns.rows().last().map(DataFrameRow::toDataFrame).get();
//Generate random portfolios and compute risk & return for each
String label = String.format("%s+%s", tickers.getValue(0), tickers.getValue(1));
DataFrame<Integer,String> portfolios = randomPortfolios(count, tickers);
DataFrame<Integer,String> riskReturn = DataFrame.ofDoubles(Range.of(0, count), Array.of("Risk", label));
portfolios.rows().forEach(row -> {
DataFrame<Integer,String> weights = row.toDataFrame();
double portReturn = weights.dot(assetReturns.transpose()).data().getDouble(0, 0);
double portVariance = weights.dot(sigma).dot(weights.transpose()).data().getDouble(0, 0);
riskReturn.data().setDouble(row.key(), label, portReturn * 100d);
riskReturn.data().setDouble(row.key(), "Risk", Math.sqrt(portVariance) * 100d);
});
return riskReturn;
});
DataFrame<Integer,String> first = results.getValue(0);
Chart.create().<Integer,String>withScatterPlot(first, false, "Risk", chart -> {
for (int i=1; i<results.length(); ++i) {
chart.plot().<String>data().add(results.getValue(i), "Risk");
chart.plot().render(i).withDots();
}
chart.plot().axes().domain().label().withText("Portfolio Risk");
chart.plot().axes().domain().format().withPattern("0.00'%';-0.00'%'");
chart.plot().axes().range(0).label().withText("Portfolio Return");
chart.plot().axes().range(0).format().withPattern("0.00'%';-0.00'%'");
chart.title().withText("Risk / Return Profiles of Various Two Asset Portfolios");
chart.subtitle().withText(count + " Portfolio Combinations Simulated");
chart.legend().on().right();
chart.show();
});
```

### Multiple Assets

So far we have limited our example portfolios to two assets in order to help develop the intuition behind Modern Portfolio Theory. In reality however, real world portfolios are likely to include more assets, although exactly how many are required to achieve a reasonable level of diversification is open to debate. Consider an investable universe of 6 securities represented by broad based low cost ETFs that serve as reasonable proxies for major asset classes. The table below summarizes these candidates.

Ticker | Name | Provider | |
---|---|---|---|

VWO | Vanguard FTSE Emerging Markets ETF | Vanguard | Details |

VNQ | Vanguard REIT ETF | Vanguard | Details |

VEA | Vanguard FTSE Developed Markets ETF | Vanguard | Details |

DBC | PowerShares DB Commodity Tracking ETF | Powershares | Details |

VTI | Vanguard Total Stock Market ETF | Vanguard | Details |

BND | Vanguard Total Bond Market ETF | Vanguard | Details |

To get a sense of how the risk / return profiles of portfolios evolve as we include more assets, we can generate 10,000
random portfolios first with 2 assets, then 3 and all the way to 6. In the case of two assets, we expect to see our
rotated parabola, but what happens when you have more degrees of freedom in the portfolio? The plot below is the answer.
As we go beyond two assets, the scatter becomes more pronounced, and while risk is generally reduced due to the fact that
the assets are not perfectly correlated, return also suffers somewhat. Having said that, the **Efficient Frontiers** of
the more diversified portfolios appear to provide a better risk / return trade off than the two asset portfolio.

The code to generate the above plot of 5 lots of 10,000 portfolios with various assets is as follows:

```
//Define portfolio count, investment horizon
int count = 10000;
LocalDate end = LocalDate.now();
LocalDate start = end.minusYears(1);
YahooFinance yahoo = new YahooFinance();
Array<DataFrame<Integer,String>> results = Array.of(
Array.of("VWO", "VNQ"),
Array.of("VWO", "VNQ", "VEA"),
Array.of("VWO", "VNQ", "VEA", "DBC"),
Array.of("VWO", "VNQ", "VEA", "DBC", "VTI"),
Array.of("VWO", "VNQ", "VEA", "DBC", "VTI", "BND")
).map(v -> {
//Access tickers
Array<String> tickers = v.getValue();
//Grab daily returns and cumulative returns from Yahoo Finance
DataFrame<LocalDate,String> dayReturns = yahoo.getDailyReturns(start, end, tickers);
DataFrame<LocalDate,String> cumReturns = yahoo.getCumReturns(start, end, tickers);
//Compute asset covariance matrix from daily returns and annualize
DataFrame<String,String> sigma = dayReturns.cols().stats().covariance().applyDoubles(x -> x.getDouble() * 252);
DataFrame<LocalDate,String> assetReturns = cumReturns.rows().last().map(DataFrameRow::toDataFrame).get();
//Generate random portfolios and compute risk & return for each
String label = String.format("%s Assets", tickers.length());
DataFrame<Integer,String> portfolios = randomPortfolios(count, tickers);
DataFrame<Integer,String> riskReturn = DataFrame.ofDoubles(Range.of(0, count), Array.of("Risk", label));
portfolios.rows().forEach(row -> {
DataFrame<Integer,String> weights = row.toDataFrame();
double portReturn = weights.dot(assetReturns.transpose()).data().getDouble(0, 0);
double portVariance = weights.dot(sigma).dot(weights.transpose()).data().getDouble(0, 0);
riskReturn.data().setDouble(row.key(), 1, portReturn * 100d);
riskReturn.data().setDouble(row.key(), 0, Math.sqrt(portVariance) * 100d);
});
return riskReturn;
});
DataFrame<Integer,String> first = results.getValue(0);
Chart.create().<Integer,String>withScatterPlot(first, false, "Risk", chart -> {
for (int i=1; i<results.length(); ++i) {
chart.plot().<String>data().add(results.getValue(i), "Risk");
chart.plot().render(i).withDots();
}
chart.plot().axes().domain().label().withText("Portfolio Risk");
chart.plot().axes().domain().format().withPattern("0.00'%';-0.00'%'");
chart.plot().axes().range(0).label().withText("Portfolio Return");
chart.plot().axes().range(0).format().withPattern("0.00'%';-0.00'%'");
chart.title().withText("Risk / Return Profiles of Portfolios With Increasing Assets");
chart.subtitle().withText(count + " Portfolio Combinations Simulated");
chart.legend().on().bottom();
chart.show();
});
```

### Robo-Advisor

A fairly recent innovation in the investment management space relates to what are called Robo-Advisors,
which are essentially online investment solutions aimed mostly at retail investors. They are called Robo-Advisors
because they automate the construction of portfolios using software based on an investor's risk appetite and their
investment objective, which they assess by posing a number of questions via their website. There are already many
players in this space, and most of them construct well-balanced portfolios consisting of 5-7 assets, all of which
are **broad based and low cost** Exchange Traded Funds.

To avoid any suggestion that I am endorsing or recommending these services, I am consciously avoiding naming names, but I visited the website of one of the larger advisors and proceeded to complete the questionnaire after which it proposed the portfolio in the table below. The purpose of this discussion is not to make any judgement on how good this portfolio is versus other potential investments, but really to get a sense of how efficient the proposed portfolio is from a risk / return stand point.

Ticker | Name | Weight | Provider | Details |
---|---|---|---|---|

VTI | Vanguard Total Stock Market ETF | 35% | Vanguard | Details |

VEA | Vanguard FTSE Developed Markets ETF | 21% | Vanguard | Details |

VWO | Vanguard FTSE Emerging Markets ETF | 16% | Vanguard | Details |

VTEB | Vanguard Tax-Exempt Bond ETF | 15% | Vanguard | Details |

VIG | Vanguard Dividend Appreciation ETF | 8% | Vanguard | Details |

XLE | Energy Select Sector SPDR ETF | 5% | State Street | Details |

#### Ramdom Portfolios

Ignoring the proposed weightings for the moment, consider generating 10,000 **long-only fully invested random portfolios**
involving these assets, and then computing the resulting equity curves. This will give us a sense of the various scenarios we can
generate with this asset universe, and in particular allow us to understand the **degree of dispersion** in outcomes. The plot
below illustrates 10,000 equity curves based on the **past 1 year returns**, suggesting a return spread ranging from approximately
2.5% all the way to 18.15%, which is pretty enormous.

The code to generate the above plot is as follows:

```
int portfolioCount = 10000;
Range<LocalDate> range = Range.of(LocalDate.now().minusYears(1), LocalDate.now());
Array<String> tickers = Array.of("VTI", "BND", "VWO", "VTEB", "VIG", "XLE");
DataFrame<Integer,String> portfolios = randomPortfolios(portfolioCount, tickers);
DataFrame<LocalDate,String> performance = getEquityCurves(range, portfolios);
Chart.create().withLinePlot(performance.applyDoubles(v -> v.getDouble() * 100d), chart -> {
chart.title().withText(portfolioCount + " Equity Curves (Past 1 Year Returns)");
chart.subtitle().withText("Robo-Advisor Universe: VTI, BND, VWO, VTEB, VIG, XLE");
chart.plot().axes().domain().label().withText("Date");
chart.plot().axes().range(0).label().withText("Portfolio Return");
chart.plot().axes().range(0).format().withPattern("0.00'%';-0.00'%'");
chart.show();
});
```

The example uses the following function in order to compute the equity curves for the `DataFrame`

of 10,000 random portfolios.
This function expects an `mxn`

frame of portfolio weighting configurations where `m`

is the number of portfolios and `n`

the number
of assets. The resulting `DataFrame`

has `txm`

dimensions where `t`

is the number of dates, and the `m`

columns are labelled `P0`

,
`P1`

through to `P(m)`

.

```
/**
* Calculates equity curves over a date range given a frame on initial portfolio weight configurations
* @param range the date range for historical returns
* @param portfolios MxN DataFrame of portfolio weights, M portfolios, N assets
* @return the cumulative returns for each portfolio, TxM, portfolios labelled P0, P1 etc...
*/
DataFrame<LocalDate,String> getEquityCurves(Range<LocalDate> range, DataFrame<Integer,String> portfolios) {
final YahooFinance yahoo = new YahooFinance();
final Iterable<String> tickers = portfolios.cols().keyArray();
final DataFrame<LocalDate,String> cumReturns = yahoo.getCumReturns(range.start(), range.end(), tickers);
final Range<String> colKeys = Range.of(0, portfolios.rowCount()).map(i -> "P" + i);
return DataFrame.ofDoubles(cumReturns.rows().keyArray(), colKeys, v -> {
double totalReturn = 0d;
for (int i=0; i<portfolios.colCount(); ++i) {
final double weight = portfolios.data().getDouble(v.colOrdinal(), i);
final double assetReturn = cumReturns.data().getDouble(v.rowOrdinal(), i);
totalReturn += (weight * assetReturn);
}
return totalReturn;
});
}
```

#### Proposed Portfolio

Given the **proposed portfolio weights** presented earlier, we can use the past 1 year of returns for the assets in question
to assess what the risk and return of this portfolio would have been had we invested a year ago. The code below performs
this analysis and suggests that the portfolio returned a very respectable `14.9%`

for `7.0%`

risk, which is pretty phenomenal
(a 2 Sharpe portfolio over the past year). The code to generate these results is as follows:

```
//Defines investment horizon & universe
LocalDate end = LocalDate.now();
LocalDate start = end.minusYears(1);
//Define investment universe and weights
Array<String> tickers = Array.of("VTI", "VEA", "VWO", "VTEB", "VIG", "XLE");
//Define DataFrame of position weights suggested by Wealthfront
DataFrame<String,String> portfolio = DataFrame.of(tickers, String.class, columns -> {
columns.add("Weights", Array.of(0.35d, 0.21d, 0.16d, 0.15d, 0.08d, 0.05d));
});
//Grab daily returns and cumulative returns from Yahoo Finance
YahooFinance yahoo = new YahooFinance();
DataFrame<LocalDate,String> dayReturns = yahoo.getDailyReturns(start, end, tickers);
DataFrame<LocalDate,String> cumReturns = yahoo.getCumReturns(start, end, tickers);
//Compute asset covariance matrix from daily returns and annualize
DataFrame<String,String> sigma = dayReturns.cols().stats().covariance().applyDoubles(x -> x.getDouble() * 252);
DataFrame<LocalDate,String> assetReturns = cumReturns.rows().last().map(DataFrameRow::toDataFrame).get();
//Generate DataFrame of portfolio weights
double portReturn = portfolio.transpose().dot(assetReturns.transpose()).data().getDouble(0, 0);
double portVariance = portfolio.transpose().dot(sigma).dot(portfolio).data().getDouble(0, 0);
IO.println(String.format("Portfolio Return: %s", portReturn));
IO.println(String.format("Portfolio Risk: %s", Math.sqrt(portVariance)));
```

Now that we know how the currently proposed portfolio performed over the past year, let us generate 100,000 random portfolios in these 6 assets to get a sense of the overall risk / return profile of this universe, and see how the proposed portfolio compares. The plot below suggests that the proposed portfolio (green dot) is indeed pretty efficient, and may also suggest that this particular Robo-Advisor's future expectations are not that different from the past year.

One may wonder why the **proposed portfolio** is not exactly on the **Efficient Frontier**, and there are several explanations
for this. The first is that the Robo-Advisor proposed weights are for a **forward looking portfolio**, and our example is using
past 1 year returns to test its efficiency. Secondly, there are an infinite number of ways of estimating an **ex-ante** covariance
matrix, which may involve **exponentially smoothing** returns (perhaps using different **half-lives** to estimate diagonal versus
off-diagonal terms), and perhaps applying shrinkage to off-diagonal terms to improve stability. In our example, we have the
**benefit of hindsight** and simply compute an **ex-post** covariance matrix, doing nothing fancy at all. Finally, the Robo-Advisor
may impose risk constraints (such as capping any one position to be no more than 35% of the portfolio perhaps), which may
penalize the strategy versus a less constrained instance, but which may be a very sensible compromise.

The code to generate this plot is as follows:

```
//Define portfolio count, investment horizon and universe
int count = 100000;
LocalDate end = LocalDate.now();
LocalDate start = end.minusYears(1);
Array<String> tickers = Array.of("VTI", "VEA", "VWO", "VTEB", "VIG", "XLE");
//Grab daily returns and cumulative returns from Yahoo Finance
YahooFinance yahoo = new YahooFinance();
DataFrame<LocalDate,String> dayReturns = yahoo.getDailyReturns(start, end, tickers);
DataFrame<LocalDate,String> cumReturns = yahoo.getCumReturns(start, end, tickers);
//Compute asset covariance matrix from daily returns and annualize
DataFrame<String,String> sigma = dayReturns.cols().stats().covariance().applyDoubles(v -> v.getDouble() * 252);
DataFrame<LocalDate,String> assetReturns = cumReturns.rows().last().map(DataFrameRow::toDataFrame).get();
//Generate random portfolios and compute risk & return for each
DataFrame<Integer,String> portfolios = randomPortfolios(count, tickers);
DataFrame<Integer,String> results = DataFrame.ofDoubles(Range.of(0, count), Array.of("Risk", "Random"));
portfolios.rows().forEach(row -> {
DataFrame<Integer,String> weights = row.toDataFrame();
double portReturn = weights.dot(assetReturns.transpose()).data().getDouble(0, 0);
double portVariance = weights.dot(sigma).dot(weights.transpose()).data().getDouble(0, 0);
results.data().setDouble(row.key(), "Random", portReturn * 100d);
results.data().setDouble(row.key(), "Risk", Math.sqrt(portVariance) * 100d);
});
//Create DataFrame with risk / return of proposed Wealthfront portfolio
DataFrame<Integer,String> proposed = DataFrame.of(Range.of(0, 1), String.class, cols -> {
cols.add("Risk", Array.of(0.068950 * 100d));
cols.add("Chosen", Array.of(0.1587613 * 100d));
});
//Plot the results using a scatter plot
Chart.create().withScatterPlot(results, false, "Risk", chart -> {
chart.title().withText("Risk / Return Profile For Wealthfront Portfolio");
chart.subtitle().withText(count + " Portfolio Combinations Simulated");
chart.plot().<String>data().add(proposed, "Risk");
chart.plot().render(1).withDots();
chart.plot().axes().domain().label().withText("Portfolio Risk");
chart.plot().axes().domain().format().withPattern("0.00'%';-0.00'%'");
chart.plot().axes().range(0).label().withText("Portfolio Return");
chart.plot().axes().range(0).format().withPattern("0.00'%';-0.00'%'");
chart.legend().on().right();
chart.show();
});
```

#### Efficiency

The previous section established that the **proposed portfolio** was reasonably efficient in the context of the past 1 year of
returns (how efficient it will be over the next year is of course impossible to know, but all else being equal, it seems a decent
configuration). In this section, we further consider its relative efficiency by looking at the equity curve of the **best** and
**worst** portfolios that we find in our 100,000 random candidates (based on their Sharpe Ratios). In addition,
we overlay the equity curve of the **S&P500** by using the SPY Exchange Traded Fund as a proxy.

The plot below shows that the **proposed portfolio** actually outperformed what is labelled as the **Best** portfolio in pure
**return space**, but not in **risk-adjusted terms**. That is, it yields a lower return per unit of risk than the **best** portfolio,
and the fact that it outperformed in return space is just a fluke. The **worst** portfolio is a genuine shocker, and in fact
spent much of the past year going nowhere before bouncing back starting in August.

The code to generate this plot is as follows, and leverages the `getEquityCurves()`

function discussed earlier.

```
int portfolioCount = 1000;
Range<LocalDate> range = Range.of(LocalDate.now().minusYears(1), LocalDate.now());
Array<String> tickers = Array.of("VTI", "VEA", "VWO", "VTEB", "VIG", "XLE");
Array<Double> proposed = Array.of(0.35d, 0.21d, 0.16d, 0.15d, 0.08d, 0.05d);
DataFrame<Integer,String> portfolios = randomPortfolios(portfolioCount, tickers);
portfolios.rowAt(0).applyDoubles(v -> proposed.getDouble(v.colOrdinal()));
//Compute risk / return / sharpe for random portfolios
DataFrame<Integer,String> riskReturn = calcRiskReturn(range, portfolios).rows().sort(false, "Sharpe");
//Capture portfolio keys for chosen, best and worst portfolio based on sharpe
DataFrame<Integer,String> candidates = portfolios.rows().select(0,
riskReturn.rows().first().get().key(),
riskReturn.rows().last().get().key()
);
//Compute equity curves for chosen, best and worst
DataFrame<LocalDate,String> equityCurves = getEquityCurves(range, candidates).cols().mapKeys(col -> {
switch (col.ordinal()) {
case 0: return "Chosen";
case 1: return "Best";
case 2: return "Worst";
default: return col.key();
}
});
//Capture returns of S&P 500
YahooFinance yahoo = new YahooFinance();
DataFrame<LocalDate,String> spy = yahoo.getCumReturns(range.start(), range.end(), "SPY");
//Plot the equity curves
Chart.create().withLinePlot(equityCurves.applyDoubles(v -> v.getDouble() * 100d), chart -> {
chart.title().withText("Best/Worst/Chosen Equity Curves (Past 1 Year Returns) + SPY");
chart.subtitle().withText("Robo-Advisor Universe: VTI, VEA, VWO, VTEB, VIG, XLE");
chart.plot().<String>data().add(spy.times(100d));
chart.plot().axes().domain().label().withText("Date");
chart.plot().axes().range(0).label().withText("Return");
chart.plot().axes().range(0).format().withPattern("0.00'%';-0.00'%'");
chart.plot().style("Chosen").withColor(Color.BLACK).withLineWidth(1.5f);
chart.plot().style("Best").withColor(Color.GREEN.darker().darker()).withLineWidth(1.5f);
chart.plot().style("Worst").withColor(Color.RED).withLineWidth(1.5f);
chart.plot().style("SPY").withColor(Color.BLUE);
chart.legend().on();
chart.show();
});
```

The implementation of `calcRiskReturn()`

in this example is as follows:

```
/**
* Returns a DataFrame containing risk, return and sharpe ratio for portfolios over the date range
* @param range the date range for historical returns
* @param portfolios the DataFrame of portfolio weights, one row per portfolio configuration
* @return the DataFrame with risk, return and sharpe
*/
DataFrame<Integer,String> calcRiskReturn(Range<LocalDate> range, DataFrame<Integer,String> portfolios) {
YahooFinance yahoo = new YahooFinance();
Array<String> tickers = portfolios.cols().keyArray();
DataFrame<LocalDate,String> dayReturns = yahoo.getDailyReturns(range.start(), range.end(), tickers);
DataFrame<LocalDate,String> cumReturns = yahoo.getCumReturns(range.start(), range.end(), tickers);
//Compute asset covariance matrix from daily returns and annualize
DataFrame<String,String> sigma = dayReturns.cols().stats().covariance().applyDoubles(x -> x.getDouble() * 252);
DataFrame<LocalDate,String> assetReturns = cumReturns.rows().last().map(DataFrameRow::toDataFrame).get();
//Prepare 3 column DataFrame to capture results
DataFrame<Integer,String> riskReturn = DataFrame.ofDoubles(
portfolios.rows().keyArray(),
Array.of("Risk", "Return", "Sharpe")
);
portfolios.rows().forEach(row -> {
DataFrame<Integer,String> weights = row.toDataFrame();
double portReturn = weights.dot(assetReturns.transpose()).data().getDouble(0, 0);
double portVariance = weights.dot(sigma).dot(weights.transpose()).data().getDouble(0, 0);
riskReturn.data().setDouble(row.key(), "Return", portReturn * 100d);
riskReturn.data().setDouble(row.key(), "Risk", Math.sqrt(portVariance) * 100d);
riskReturn.data().setDouble(row.key(), "Sharpe", portReturn / Math.sqrt(portVariance));
});
return riskReturn;
}
```