This example shows how to set up a basic asset allocation problem that uses mean-variance portfolio optimization to estimate efficient portfolios. Suppose you want to manage an asset allocation fund with four asset classes: bonds, large-cap equities, small-cap equities, and emerging equities. The fund is long-only with no borrowing or leverage, should have no more than 85% of the portfolio in equities, and no more than 35% of the portfolio in emerging equities.

The cost to trade the first three assets is 10 basis points annualized and the cost to trade emerging equities is four times higher. Finally, you want to ensure that average turnover is no more than 15%. To solve this problem, you will set up a basic mean-variance portfolio optimization problem and then slowly introduce the various constraints on the problem to get to a solution.

For information on the workflow when using Portfolio objects, see Portfolio Object Workflow.

To set up the portfolio optimization problem, start with basic
definitions of known quantities associated with the structure of this
problem. Each asset class is assumed to have a tradeable asset with
a real-time price. Such assets can be, for example, exchange-traded
funds (ETFs). The initial portfolio with holdings in each asset that
has a total of $7.5 million along with an additional cash position
of $60,000. These basic quantities and the costs to trade are set
up in the following variables with asset names in the cell array `Asset`

,
current prices in the vector `Price`

, current portfolio
holdings in the vector `Holding`

, and transaction
costs in the vector `UnitCost`

.

Asset = { 'Bonds', 'Large-Cap Equities', 'Small-Cap Equities', 'Emerging Equities' }; Price = [ 52.4; 122.7; 35.2; 46.9 ]; Holding = [ 42938; 24449; 42612; 15991 ]; UnitCost = [ 0.001; 0.001; 0.001; 0.004 ];

To analyze this portfolio, you can set up a blotter in a `dataset`

object
to help track prices, holdings, weights, and so forth. In particular,
you can compute the initial portfolio weights and maintain them in
a new blotter field called `InitPort`

.

Blotter = dataset({Price, 'Price'}, {Holding, 'InitHolding'},'obsnames',Asset); Wealth = sum(Blotter.Price .* Blotter.InitHolding); Blotter.InitPort = (1/Wealth)*(Blotter.Price .* Blotter.InitHolding); Blotter.UnitCost = UnitCost; disp(Blotter);

Price InitHolding InitPort UnitCost Bonds 52.4 42938 0.3 0.001 Large-Cap Equities 122.7 24449 0.4 0.001 Small-Cap Equities 35.2 42612 0.2 0.001 Emerging Equities 46.9 15991 0.1 0.004

Since this is a hypothetical example, to simulate asset prices
from a given mean and covariance of annual asset total returns for
the asset classes, `portsim`

is
used to create asset returns with the desired mean and covariance.
Specifically, `portsim`

is used
to simulate five years of monthly total returns. The mean and covariance
of annual asset total returns are maintained in the variables `AssetMean`

and `AssetCovar`

.
The simulated asset total return prices (which are compounded total
returns) are maintained in the variable `Y`

. All
initial asset total return prices are normalized to `1`

in
this example.

AssetMean = [ 0.05; 0.1; 0.12; 0.18 ]; AssetCovar = [ 0.0064 0.00408 0.00192 0; 0.00408 0.0289 0.0204 0.0119; 0.00192 0.0204 0.0576 0.0336; 0 0.0119 0.0336 0.1225 ]; X = portsim(AssetMean'/12, AssetCovar/12, 60); % monthly total returns for 5 years (60 months) [Y, T] = ret2tick(X, [], 1/12); % form total return prices

This plot shows the log of the simulated total return prices:

plot(T, log(Y)); title('\bfSimulated Asset Class Total Return Prices'); xlabel('Year'); ylabel('Log Total Return Price'); legend(Asset,'Location','best');

If working with actual historical asset prices, income, and corporate actions data, you would compute total returns for your assets by other means.

To explore portfolios on the efficient frontier, set up a Portfolio object using these specifications:

Portfolio weights are nonnegative and sum to

`1`

.Equity allocation is no more than 85% of the portfolio.

Emerging equity is no more than 35% of the portfolio.

These specifications are incorporated into the Portfolio
object `p`

in the following sequence of functions
that starts with using the `Portfolio`

function:

p = Portfolio('Name', 'Asset Allocation Portfolio', ... 'AssetList', Asset, 'InitPort', Blotter.InitPort);

`Blotter`

gives
the number of assets in your universe so you do not need to specify
the `NumAssets`

property directly. Next, set up default
constraints (long-only with a budget constraint). In addition, set
up the group constraint that imposes an upper bound on equities in
the portfolio (equities are identified in the group matrix with `1`

s)
and the upper bound constraint on emerging equities.p = setDefaultConstraints(p); p = setGroups(p, [ 0, 1, 1, 1 ], [], 0.85); p = addGroups(p, [ 0, 0, 0, 1 ], [], 0.35);

Although you could have set the upper bound on emerging equities
using the `setBounds`

function,
notice how you used the `addGroups`

function
to set up this constraint.

Finally, to have a fully specified mean-variance portfolio optimization
problem, you must specify the mean and covariance of asset returns.
Since starting with these moments in the variables `AssetMean`

and `AssetCovar`

,
you could use the `setAssetMoments`

function
to enter these variables into your Portfolio object in the following
way (remember that you are assuming that your raw data are monthly
returns which is why you divide your annual input moments by 12 to
get monthly returns).

p = setAssetMoments(p, AssetMean/12, AssetCovar/12);

To make things more interesting, however, you can use the total
return prices and use the `estimateAssetMoments`

function
with a specification that your data in `Y`

are prices,
and not returns, to estimate asset return moments for your Portfolio
object.

p = estimateAssetMoments(p, Y, 'DataFormat', 'Prices');

Although the returns in your Portfolio object are in units of
monthly returns, and since subsequent costs are annualized, it is
convenient to specify them as annualized total returns with this direct
transformation of the `AssetMean`

and `AssetCovar`

properties
of your object:

p.AssetMean = 12*p.AssetMean; p.AssetCovar = 12*p.AssetCovar;

Now, the Portfolio object is ready:

display(p);

p = Portfolio Properties: BuyCost: [] SellCost: [] RiskFreeRate: [] AssetMean: [4x1 double] AssetCovar: [4x4 double] Turnover: [] Name: 'Asset Allocation Portfolio' NumAssets: 4 AssetList: {'Bonds' 'Large-Cap Equities' 'Small-Cap Equities' 'Emerging Equities'} InitPort: [4x1 double] AInequality: [] bInequality: [] AEquality: [] bEquality: [] LowerBound: [4x1 double] UpperBound: [] LowerBudget: 1 UpperBudget: 1 GroupMatrix: [2x4 double] LowerGroup: [] UpperGroup: [2x1 double] GroupA: [] GroupB: [] LowerRatio: [] UpperRatio: []

An important step in portfolio optimization is to validate that
the portfolio problem is feasible and the main test is to ensure that
the set of portfolios is nonempty and bounded. Use the `estimateBounds`

function to determine
the bounds for the portfolio set:

[lb, ub] = estimateBounds(p); display([lb, ub]);

ans = 0.1500 1.0000 0.0000 0.8500 0.0000 0.8500 0.0000 0.3500

`lb`

and `ub`

are
finite, the set is bounded.Given the constructed Portfolio object, use the `plotFrontier`

function to view the efficient
frontier. Instead of using the default of 10 portfolios along the
frontier, you can display the frontier with 40 portfolios:

plotFrontier(p, 40);

Notice gross efficient portfolio returns fall between approximately 6% and 16% per years.

The Portfolio object `p`

does not include transaction
costs so that the portfolio optimization problem specified in `p`

uses
gross portfolio return as the return proxy. To handle net returns,
create a second Portfolio object `q`

that includes
transaction costs:

q = setCosts(p, UnitCost, UnitCost); display(q);

q = Portfolio Properties: BuyCost: [4x1 double] SellCost: [4x1 double] RiskFreeRate: [] AssetMean: [4x1 double] AssetCovar: [4x4 double] Turnover: [] Name: 'Asset Allocation Portfolio' NumAssets: 4 AssetList: {'Bonds' 'Large-Cap Equities' 'Small-Cap Equities' 'Emerging Equities'} InitPort: [4x1 double] AInequality: [] bInequality: [] AEquality: [] bEquality: [] LowerBound: [4x1 double] UpperBound: [] LowerBudget: 1 UpperBudget: 1 GroupMatrix: [2x4 double] LowerGroup: [] UpperGroup: [2x1 double] GroupA: [] GroupB: [] LowerRatio: [] UpperRatio: []

To be more concrete about the ranges of efficient portfolio
returns and risks, use the `estimateFrontierLimits`

function
to obtain portfolios at the endpoints of the efficient frontier. Given
these portfolios, compute their moments using `estimatePortMoments`

.
The following code generates a table that lists the risk and return
of the initial portfolio as well as the gross and net moments of portfolio
returns for the portfolios at the endpoints of the efficient frontier:

[prsk0, pret0] = estimatePortMoments(p, p.InitPort); pret = estimatePortReturn(p, p.estimateFrontierLimits); qret = estimatePortReturn(q, q.estimateFrontierLimits); fprintf('Annualized Portfolio Returns ...\n'); fprintf(' %6s %6s\n','Gross','Net'); fprintf('Initial Portfolio Return %6.2f %% %6.2f %%\n',100*pret0,100*pret0); fprintf('Minimum Efficient Portfolio Return %6.2f %% %6.2f %%\n',100*pret(1),100*qret(1)); fprintf('Maximum Efficient Portfolio Return %6.2f %% %6.2f %%\n',100*pret(2),100*qret(2));

Annualized Portfolio Returns ... Gross Net Initial Portfolio Return 9.70 % 9.70 % Minimum Efficient Portfolio Return 5.90 % 5.77 % Maximum Efficient Portfolio Return 13.05 % 12.86 %

This result shows that the cost to trade ranges from 14 to 19 basis points to get from the current portfolio to the efficient portfolios at the endpoints of the efficient frontier (these costs are the difference between gross and net portfolio returns.) In addition, notice that the maximum efficient portfolio return (13%) is less than the maximum asset return (18%) due to the constraints on equity allocations.

A common approach to select efficient portfolios is to pick
a portfolio that has a desired fraction of the range of expected portfolio
returns. To obtain the portfolio that is 30% of the range from the
minimum to maximum return on the efficient frontier, obtain the range
of net returns in `qret`

using the Portfolio object `q`

and
interpolate to obtain a 30% level with `interp1`

to
obtain a portfolio `qwgt`

:

Level = 0.3; qret = estimatePortReturn(q, q.estimateFrontierLimits); qwgt = estimateFrontierByReturn(q, interp1([0, 1], qret, Level)); [qrsk, qret] = estimatePortMoments(q, qwgt); fprintf('Portfolio at %g%% return level on efficient frontier ...\n',100*Level); fprintf('%10s %10s\n','Return','Risk'); fprintf('%10.2f %10.2f\n',100*qret,100*qrsk); display(qwgt);

Portfolio at 30% return level on efficient frontier ... Return Risk 7.90 9.09 qwgt = 0.6252 0.1856 0.0695 0.1198

Although you could accept this result, suppose you want to target
values for portfolio risk. Specifically, suppose that you have a conservative
target risk of 10%, a moderate target risk of 15%, and an aggressive
target risk of 20% and you want to obtain portfolios that satisfy
each risk target. Use the `estimateFrontierByRisk`

function
to obtain targeted risks specified in the variable `TargetRisk`

.
The resultant three efficient portfolios are obtained in `qwgt`

:

TargetRisk = [ 0.10; 0.15; 0.20 ]; qwgt = estimateFrontierByRisk(q, TargetRisk); display(qwgt);

qwgt = 0.5407 0.2020 0.1500 0.2332 0.4000 0.0318 0.0788 0.1280 0.4682 0.1474 0.2700 0.3500

`estimatePortRisk`

to compute the portfolio
risks for the three portfolios to confirm that the target risks have
been attained:display(estimatePortRisk(q, qwgt));

ans = 0.1000 0.1500 0.2000

[qwgt, qbuy, qsell] = estimateFrontierByRisk(q, 0.15);

If you average the purchases and sales for this portfolio, you can see that the average turnover is 17%, which is greater than the target of 15%:

disp(sum(qbuy + qsell)/2)

0.1700

Since you also want to ensure that average turnover is no more than 15%, you can add the average turnover constraint to the Portfolio object:

q = setTurnover(q, 0.15); [qwgt, qbuy, qsell] = estimateFrontierByRisk(q, 0.15);

You can enter the estimated efficient portfolio with purchases
and sales into the `Blotter`

:

```
qbuy(abs(qbuy) < 1.0e-5) = 0;
qsell(abs(qsell) < 1.0e-5) = 0; % zero out near 0 trade weights
Blotter.Port = qwgt;
Blotter.Buy = qbuy;
Blotter.Sell = qsell;
display(Blotter);
```

Blotter = Price InitHolding InitPort UnitCost Port Buy Sell Bonds 52.4 42938 0.3 0.001 0.18787 0 0.11213 Large-Cap Equities 122.7 24449 0.4 0.001 0.4 0 0 Small-Cap Equities 35.2 42612 0.2 0.001 0.16213 0 0.037871 Emerging Equities 46.9 15991 0.1 0.004 0.25 0.15 0

The `Buy`

and `Sell`

elements
of the `Blotter`

are changes in portfolio weights
that must be converted into changes in portfolio holdings to determine
the trades. Since you are working with net portfolio returns, you
must first compute the cost to trade from your initial portfolio to
the new portfolio. This can be accomplished as follows:

TotalCost = Wealth * sum(Blotter.UnitCost .* (Blotter.Buy + Blotter.Sell))

TotalCost = 5.6248e+003

The cost to trade is $5,625, so that, in general, you would have to adjust your initial wealth accordingly before setting up your new portfolio weights. However, to keep the analysis simple, note that you have sufficient cash ($60,0000) set aside to pay the trading costs and that you will not touch the cash position to build up any positions in your portfolio. Thus, you can populate your blotter with the new portfolio holdings and the trades to get to the new portfolio without making any changes in your total invested wealth.

First, compute portfolio holding:

Blotter.Holding = Wealth * (Blotter.Port ./ Blotter.Price);

Next, compute number of shares to `Buy`

and `Sell`

in
your `Blotter`

:

Blotter.BuyShare = Wealth * (Blotter.Buy ./ Blotter.Price); Blotter.SellShare = Wealth * (Blotter.Sell ./ Blotter.Price);

Finally, clean up the blotter by removing the unit costs and the buy and sell portfolio weights:

Blotter.Buy = []; Blotter.Sell = []; Blotter.UnitCost = [];

The final result is a blotter that contains proposed trades to get from your current portfolio to a moderate-risk portfolio. To make the trade, you would need to sell 16,049 shares of your bond asset and 8,069 shares of your small-cap equity asset and would need to purchase 23,986 shares of your emerging equities asset.

display(Blotter);

Blotter = Price InitHolding InitPort Port Holding BuyShare SellShare Bonds 52.4 42938 0.3 0.18787 26889 0 16049 Large-Cap Equities 122.7 24449 0.4 0.4 24449 0 0 Small-Cap Equities 35.2 42612 0.2 0.16213 34543 0 8068.8 Emerging Equities 46.9 15991 0.1 0.25 39977 23986 0

The final plot uses `plotFrontier`

to
display the efficient frontier and the initial portfolio for the fully
specified portfolio optimization problem. It also adds the location
of the moderate-risk or final portfolio on the efficient frontier.

plotFrontier(q, 40); hold on scatter(estimatePortRisk(q, qwgt), estimatePortReturn(q, qwgt), 'filled', 'r'); h = legend('Initial Portfolio', 'Efficient Frontier', 'Final Portfolio', 'location', 'best'); set(h, 'Fontsize', 8); hold off

`addGroups`

| `estimateAssetMoments`

| `estimateBounds`

| `estimateFrontierByRisk`

| `estimateFrontierLimits`

| `estimatePortRisk`

| `plotFrontier`

| `Portfolio`

| `setAssetMoments`

| `setBounds`

- Creating the Portfolio Object
- Working with Portfolio Constraints
- Validate the Portfolio Problem for Portfolio Object
- Estimate Efficient Portfolios for Portfolio Object
- Estimate Efficient Frontiers for Portfolio Object
- Postprocessing Results
- Portfolio Optimization Examples
- Portfolio Optimization Examples

Was this topic helpful?