Documentation Center

  • Trial Software
  • Product Updates

Portfolio Optimization Examples

The following sequence of examples highlights features of the Portfolio object in the Financial Toolbox™. Specifically, the examples show how to set up mean-variance portfolio optimization problems that focus on the two-fund theorem, the impact of transaction costs and turnover constraints, how to obtain portfolios that maximize the Sharpe ratio, and how to set up two popular hedge-fund strategies - dollar-neutral and 130-30 portfolios.

Set up the Data

Every example works with moments for monthly total returns of a universe of 30 "blue-chip" stocks. Although derived from real data, these data are for illustrative purposes and are not meant to be representative of specific assets or of market performance. The data are contained in the file BlueChipStockMoments.mat with a list of asset identifiers in the variable AssetList, a mean and covariance of asset returns in the variables AssetMean and AssetCovar, and the mean and variance of cash and market returns in the variables CashMean, CashVar, MarketMean, and MarketVar. Since most of the analysis requires the use of the standard deviation of asset returns as the proxy for risk, cash and market variances are converted into standard deviations.

load BlueChipStockMoments

mret = MarketMean;
mrsk = sqrt(MarketVar);
cret = CashMean;
crsk = sqrt(CashVar);

Create a Portfolio Object

The first step is to create a "standard" Portfolio object with the Portfolio constructor and to incorporate the list of assets, the risk-free rate, and the moments of asset returns into the object.

p = Portfolio('AssetList', AssetList, 'RiskFreeRate', CashMean);
p = p.setAssetMoments(AssetMean, AssetCovar);

To provide a basis for comparison, set up an equal-weight portfolio and make it the initial portfolio in the Portfolio object. Keep in mind that the hedged portfolios to be constructed later will require a different initial portfolio. Once the initial portfolio is created, the estimatePortMoments method estimates the mean and standard deviation of equal-weight portfolio returns.

p = p.setInitPort(1/p.NumAssets);
[ersk, eret] = p.estimatePortMoments(p.InitPort);

A specialized "helper" function portfolioexamples_plot makes it possible to plot all results to be developed here. This first plot shows the distribution of individual assets according to their means and standard deviations of returns. In addition, the equal-weight, market, and cash portfolios are plotted on the same plot. Note that the plot function converts monthly total returns into annualized total returns.

clf;
portfolioexamples_plot('Asset Risks and Returns', ...
	{'scatter', mrsk, mret, {'Market'}}, ...
	{'scatter', crsk, cret, {'Cash'}}, ...
	{'scatter', ersk, eret, {'Equal'}}, ...
	{'scatter', sqrt(diag(p.AssetCovar)), p.AssetMean, p.AssetList, '.r'});

Set up a Portfolio Optimization Problem

Set up a "standard" or default mean-variance portfolio optimization problem with the setDefaultConstraints method that requires fully-invested long-only portfolios (non-negative weights that must sum to 1). Given this initial problem, estimate the efficient frontier with the methods estimateFrontier and estimatePortMoments, where estimateFrontier estimates efficient portfolios and estimatePortMoments estimates risks and returns for portfolios. The next figure overlays the efficient frontier on the previous plot.

p = p.setDefaultConstraints;

pwgt = p.estimateFrontier(20);
[prsk, pret] = p.estimatePortMoments(pwgt);

% Plot efficient frontier

clf;
portfolioexamples_plot('Efficient Frontier', ...
	{'line', prsk, pret}, ...
	{'scatter', [mrsk, crsk, ersk], [mret, cret, eret], {'Market', 'Cash', 'Equal'}}, ...
	{'scatter', sqrt(diag(p.AssetCovar)), p.AssetMean, p.AssetList, '.r'});

Illustrate the Tangent Line to the Efficient Frontier

Tobin's mutual fund theorem (Tobin 1958) says that the portfolio allocation problem can be viewed as a decision to allocate between a riskless asset and a risky portfolio. In the mean-variance framework, cash can serve as a proxy for a riskless asset and an efficient portfolio on the efficient frontier serves as the risky portfolio such that any allocation between cash and this portfolio dominates all other portfolios on the efficient frontier. This portfolio is called a tangency portfolio because it is located at the point on the efficient frontier where a tangent line that originates at the riskless asset touches the efficient frontier.

Given that the Portfolio object already has the risk-free rate, obtain the tangent line by creating a copy of the Portfolio object with a budget constraint that permits allocation between 0% and 100% in cash. Since the Portfolio object is a value object, it is easy to create a copy by assigning the output of either the constructor or set methods to a new instance of the object. The plot shows the efficient frontier with Tobin's allocations that form the tangent line to the efficient frontier.

q = p.setBudget(0, 1);

qwgt = q.estimateFrontier(20);
[qrsk, qret] = q.estimatePortMoments(qwgt);

% Plot efficient frontier with tangent line (0 to 1 cash)

clf;
portfolioexamples_plot('Efficient Frontier with Tangent Line', ...
	{'line', prsk, pret}, ...
	{'line', qrsk, qret, [], [], 1}, ...
	{'scatter', [mrsk, crsk, ersk], [mret, cret, eret], {'Market', 'Cash', 'Equal'}}, ...
	{'scatter', sqrt(diag(p.AssetCovar)), p.AssetMean, p.AssetList, '.r'});

Note that cash actually has a small risk so that the tangent line does not pass through the cash asset.

Obtain Range of Risks and Returns

To obtain efficient portfolios with target values of either risk or return, it is necessary to obtain the range of risks and returns among all portfolios on the efficient frontier. This can be accomplished with the estimateFrontierLimits method.

[rsk, ret] = p.estimatePortMoments(p.estimateFrontierLimits);

display(rsk);
display(ret);
rsk =

    0.0348
    0.0903


ret =

    0.0094
    0.0179

The range of monthly portfolio returns is between 0.9% and 1.8% and the range for portfolio risks is between 3.5% and 9.0%. In annualized terms, the range of portfolio returns is 11.2% to 21.5% and the range of portfolio risks is 12.1% to 31.3%.

Find a Portfolio with a Targeted Return and Targeted Risk

Given the range of risks and returns, it is possible to locate specific portfolios on the efficient frontier that have target values for return and risk using the methods estimateFrontierByReturn and estimateFrontierByRisk.

TargetReturn = 0.20;            % input target annualized return and risk here
TargetRisk = 0.15;

% Obtain portfolios with targeted return and risk

awgt = p.estimateFrontierByReturn(TargetReturn/12);
[arsk, aret] = p.estimatePortMoments(awgt);

bwgt = p.estimateFrontierByRisk(TargetRisk/sqrt(12));
[brsk, bret] = p.estimatePortMoments(bwgt);

% Plot efficient frontier with targeted portfolios

clf;
portfolioexamples_plot('Efficient Frontier with Targeted Portfolios', ...
	{'line', prsk, pret}, ...
	{'scatter', [mrsk, crsk, ersk], [mret, cret, eret], {'Market', 'Cash', 'Equal'}}, ...
	{'scatter', arsk, aret, {sprintf('%g%% Return',100*TargetReturn)}}, ...
	{'scatter', brsk, bret, {sprintf('%g%% Risk',100*TargetRisk)}}, ...
	{'scatter', sqrt(diag(p.AssetCovar)), p.AssetMean, p.AssetList, '.r'});

To see what these targeted portfolios look like, use the dataset object to set up "blotters" that contain the portfolio weights and asset names (which are obtained from the Portfolio object).

aBlotter = dataset({100*awgt(awgt > 0),'Weight'}, 'obsnames', p.AssetList(awgt > 0));

fprintf('Portfolio with %g%% Target Return\n', 100*TargetReturn);
disp(aBlotter);

bBlotter = dataset({100*bwgt(bwgt > 0),'Weight'}, 'obsnames', p.AssetList(bwgt > 0));

fprintf('Portfolio with %g%% Target Risk\n', 100*TargetRisk);
disp(bBlotter);
Portfolio with 20% Target Return
            Weight 
    CAT      1.1445
    INTC    0.17452
    MO       9.6521
    MSFT    0.85862
    UTX      56.918
    WMT      31.253

Portfolio with 15% Target Risk
            Weight
    INTC    2.2585
    JNJ     9.2162
    MMM     16.603
    MO      15.388
    MSFT    4.4467
    PG       4.086
    UTX     10.281
    WMT     25.031
    XOM      12.69

Transactions Costs

The Portfolio object makes it possible to account for transaction costs as part of the optimization problem. Although individual costs can be set for each asset, use the scalar expansion features of the Portfolio object's methods to set up uniform transaction costs across all assets and compare efficient frontiers with gross versus net portfolio returns.

BuyCost = 0.0020;
SellCost = 0.0020;

q = p.setCosts(BuyCost, SellCost);

qwgt = q.estimateFrontier(20);
[qrsk, qret] = q.estimatePortMoments(qwgt);

% Plot efficient frontiers with gross and net returns

clf;
portfolioexamples_plot('Efficient Frontier with and without Transaction Costs', ...
	{'line', prsk, pret, {'Gross'}, ':b'}, ...
	{'line', qrsk, qret, {'Net'}}, ...
	{'scatter', [mrsk, crsk, ersk], [mret, cret, eret], {'Market', 'Cash', 'Equal'}}, ...
	{'scatter', sqrt(diag(p.AssetCovar)), p.AssetMean, p.AssetList, '.r'});

Turnover Constraint

In addition to transaction costs, the Portfolio object can handle turnover constraints. The following example demonstrates that a turnover constraint produces an efficient frontier in the neighborhood of an initial portfolio that may restrict trading. Moreover, the introduction of a turnover constraint often implies that multiple trades may be necessary to shift from an initial portfolio to an unconstrained efficient frontier. Consequently, the turnover constraint introduces a form of time diversification that can spread trades out over multiple time periods. In this example, note that the sum of purchases and sales from the estimateFrontier method confirms that the turnover constraint has been satisfied.

BuyCost = 0.0020;
SellCost = 0.0020;
Turnover = 0.2;

q = p.setCosts(BuyCost, SellCost);
q = q.setTurnover(Turnover);

[qwgt, qbuy, qsell] = q.estimateFrontier(20);
[qrsk, qret] = q.estimatePortMoments(qwgt);

% Plot efficient frontier with turnover constraint

clf;
portfolioexamples_plot('Efficient Frontier with Turnover Constraint', ...
	{'line', prsk, pret, {'Unconstrained'}, ':b'}, ...
	{'line', qrsk, qret, {sprintf('%g%% Turnover', 100*Turnover)}}, ...
	{'scatter', [mrsk, crsk, ersk], [mret, cret, eret], {'Market', 'Cash', 'Equal'}}, ...
	{'scatter', sqrt(diag(p.AssetCovar)), p.AssetMean, p.AssetList, '.r'});

fprintf('Sum of Purchases by Portfolio along Efficient Frontier (Max. Turnover %g%%)\n', ...
    100*Turnover);

disp(100*sum(qbuy));

fprintf('Sum of Sales by Portfolio along Efficient Frontier (Max. Turnover %g%%)\n', ...
    100*Turnover);

disp(100*sum(qsell));
Sum of Purchases by Portfolio along Efficient Frontier (Max. Turnover 20%)
  Columns 1 through 7

   20.0000   20.0000   20.0000   20.0000   20.0000   20.0000   20.0000

  Columns 8 through 14

   20.0000   20.0000   20.0000   20.0000   20.0000   20.0000   20.0000

  Columns 15 through 20

   20.0000   20.0000   20.0000   20.0000   20.0000   20.0000

Sum of Sales by Portfolio along Efficient Frontier (Max. Turnover 20%)
  Columns 1 through 7

   20.0000   20.0000   20.0000   20.0000   20.0000   20.0000   20.0000

  Columns 8 through 14

   20.0000   20.0000   20.0000   20.0000   20.0000   20.0000   20.0000

  Columns 15 through 20

   20.0000   20.0000   20.0000   20.0000   20.0000   20.0000

Maximize the Sharpe Ratio

The Sharpe ratio (Sharpe 1966) is a measure of return-to-risk that plays an important role in portfolio analysis. Specifically, a portfolio that maximizes the Sharpe ratio is also the tangency portfolio on the efficient frontier from the mutual fund theorem. The maximum Sharpe ratio portfolio is located on the efficient frontier with the method estimateMaxSharpeRatio and the dataset object is used to list the assets in this portfolio.

p = p.setInitPort(0);

swgt = p.estimateMaxSharpeRatio;
[srsk, sret] = p.estimatePortMoments(swgt);

% Plot efficient frontier with portfolio that attains maximum Sharpe ratio

clf;
portfolioexamples_plot('Efficient Frontier with Maximum Sharpe Ratio Portfolio', ...
	{'line', prsk, pret}, ...
	{'scatter', srsk, sret, {'Sharpe'}}, ...
	{'scatter', [mrsk, crsk, ersk], [mret, cret, eret], {'Market', 'Cash', 'Equal'}}, ...
	{'scatter', sqrt(diag(p.AssetCovar)), p.AssetMean, p.AssetList, '.r'});

% Set up a dataset object that contains the portfolio that maximizes the Sharpe ratio

Blotter = dataset({100*swgt(swgt > 0),'Weight'}, 'obsnames', AssetList(swgt > 0));

fprintf('Portfolio with Maximum Sharpe Ratio\n');
disp(Blotter);
Portfolio with Maximum Sharpe Ratio
            Weight
    INTC    2.6638
    JNJ     9.0044
    MMM     15.502
    MO      13.996
    MSFT    4.4777
    PG      7.4588
    UTX     6.0056
    WMT     22.051
    XOM     18.841

Confirm that Maximum Sharpe Ratio is a Maximum

The following plot demonstrates that this portfolio (which is located at the dot on the plots) indeed maximizes the Sharpe ratio among all portfolios on the efficient frontier.

psratio = (pret - p.RiskFreeRate) ./ prsk;
ssratio = (sret - p.RiskFreeRate) / srsk;

clf;
subplot(2,1,1);
plot(prsk, pret, 'LineWidth', 2);
hold on
scatter(srsk, sret, 'g', 'filled');
title('\bfEfficient Frontier');
xlabel('Portfolio Risk');
ylabel('Portfolio Return');
hold off

subplot(2,1,2);
plot(prsk, psratio, 'LineWidth', 2);
hold on
scatter(srsk, ssratio, 'g', 'filled');
title('\bfSharpe Ratio');
xlabel('Portfolio Risk');
ylabel('Sharpe Ratio');
hold off

Illustrate that Sharpe is the Tangent Portfolio

The next plot demonstrates that the portfolio that maximizes the Sharpe ratio is also a tangency portfolio (in this case, the budget constraint is opened up to permit between 0% and 100% in cash).

q = p.setBudget(0, 1);

qwgt = q.estimateFrontier(20);
[qrsk, qret] = q.estimatePortMoments(qwgt);

% Plot that shows Sharpe ratio portfolio is the tangency portfolio

clf;
portfolioexamples_plot('Efficient Frontier with Maximum Sharpe Ratio Portfolio', ...
	{'line', prsk, pret}, ...
	{'line', qrsk, qret, [], [], 1}, ...
	{'scatter', srsk, sret, {'Sharpe'}}, ...
	{'scatter', [mrsk, crsk, ersk], [mret, cret, eret], {'Market', 'Cash', 'Equal'}}, ...
	{'scatter', sqrt(diag(p.AssetCovar)), p.AssetMean, p.AssetList, '.r'});

Dollar-Neutral Hedge-Fund Structure

To illustrate how to use the portfolio optimization tools in hedge fund management, two popular strategies with dollar-neutral and 130-30 portfolios are examined. The dollar-neutral strategy invests equally in long and short positions such that the net portfolio position is 0. Such a portfolio is said to be dollar-neutral.

To set up a dollar-neutral portfolio, start with the "standard" portfolio problem and set the maximum exposure in long and short positions in the variable Exposure. The bounds for individual asset weights are plus or minus Exposure. Since the net position must be dollar-neutral, the budget constraint is 0 and the initial portfolio must be 0. Finally, the one-way turnover constraints provide the necessary long and short restrictions to prevent "double-counting" of long and short positions. The blotter shows the portfolio weights for the dollar-neutral portfolio that maximizes the Sharpe ratio. The long and short positions are obtained from the buy and sell trades relative to the initial portfolio.

Exposure = 1;

q = p.setBounds(-Exposure, Exposure);
q = q.setBudget(0, 0);
q = q.setOneWayTurnover(Exposure, Exposure, 0);

[qwgt, qlong, qshort] = q.estimateFrontier(20);
[qrsk, qret] = q.estimatePortMoments(qwgt);

[qswgt, qslong, qsshort] = q.estimateMaxSharpeRatio;
[qsrsk, qsret] = q.estimatePortMoments(qswgt);

% Plot efficient frontier for a dollar-neutral fund structure with tangency portfolio

clf;
portfolioexamples_plot('Efficient Frontier with Dollar-Neutral Portfolio', ...
	{'line', prsk, pret, {'Standard'}, 'b:'}, ...
	{'line', qrsk, qret, {'Dollar-Neutral'}, 'b'}, ...
	{'scatter', qsrsk, qsret, {'Sharpe'}}, ...
	{'scatter', [mrsk, crsk, ersk], [mret, cret, eret], {'Market', 'Cash', 'Equal'}}, ...
	{'scatter', sqrt(diag(p.AssetCovar)), p.AssetMean, p.AssetList, '.r'});

% Set up a dataset object that contains the portfolio that maximizes the Sharpe ratio

Blotter = dataset({100*qswgt(abs(qswgt) > 1.0e-4), 'Weight'}, ...
	{100*qslong(abs(qswgt) > 1.0e-4), 'Long'}, ...
	{100*qsshort(abs(qswgt) > 1.0e-4), 'Short'}, ...
	'obsnames', AssetList(abs(qswgt) > 1.0e-4));

fprintf('Dollar-Neutral Portfolio with Maximum Sharpe Ratio\n');
disp(Blotter);

fprintf('Confirm Dollar-Neutral Portfolio\n');
fprintf('  (Net, Long, Short)\n');
disp([ sum(Blotter.Weight), sum(Blotter.Long), sum(Blotter.Short) ]);
Dollar-Neutral Portfolio with Maximum Sharpe Ratio
            Weight     Long       Short 
    AA      0.53992    0.53992         0
    AIG      3.2253     3.2253         0
    AXP     0.98473    0.98473         0
    BA       -3.709          0     3.709
    C        14.859     14.859         0
    CAT       3.954      3.954         0
    DD      -19.168          0    19.168
    DIS     -5.1186          0    5.1186
    GE      -3.8391          0    3.8391
    GM      -3.9487          0    3.9487
    HD       1.1683     1.1683         0
    HON     -1.5227          0    1.5227
    HPQ     0.10515    0.10515         0
    IBM     -8.5514          0    8.5514
    INTC     1.8775     1.8775         0
    JNJ      1.4534     1.4534         0
    JPM     -2.6816          0    2.6816
    KO      -15.074          0    15.074
    MCD      4.1492     4.1492         0
    MMM      8.0644     8.0644         0
    MO       4.3354     4.3354         0
    MRK      3.9762     3.9762         0
    MSFT     4.3262     4.3262         0
    PFE     -9.6523          0    9.6523
    PG       1.7502     1.7502         0
    SBC     -5.5761          0    5.5761
    UTX      6.0968     6.0968         0
    VZ      -2.5871          0    2.5871
    WMT     0.90033    0.90033         0
    XOM      19.662     19.662         0

Confirm Dollar-Neutral Portfolio
  (Net, Long, Short)
    0.0000   81.4284   81.4284

130/30 Fund Structure

Finally, the turnover constraints can be used to set up a 130-30 portfolio structure, which is a structure with a net long position but permits leverage with long and short positions up to a maximum amount of leverage. In the case of a 130-30 portfolio, the leverage is 30%.

To set up a 130-30 portfolio, start with the "standard" portfolio problem and set the maximum value for leverage in the variable Leverage. The bounds for individual asset weights range between -Leverage and 1 + Leverage. Since the net position must be long, the budget constraint is 1 and, once again, the initial portfolio is 0. Finally, the one-way turnover constraints provide the necessary long and short restrictions to prevent "double-counting" of long and short positions. The blotter shows the portfolio weights for the 130-30 portfolio that maximizes the Sharpe ratio. The long and short positions are obtained from the buy and sell trades relative to the initial portfolio.

Leverage = 0.3;

q = p.setBounds(-Leverage, 1 + Leverage);
q = q.setBudget(1, 1);
q = q.setOneWayTurnover(1 + Leverage, Leverage);

[qwgt, qbuy, qsell] = q.estimateFrontier(20);
[qrsk, qret] = q.estimatePortMoments(qwgt);

[qswgt, qslong, qsshort] = q.estimateMaxSharpeRatio;
[qsrsk, qsret] = q.estimatePortMoments(qswgt);

% Plot efficient frontier for a 130-30 fund structure with tangency portfolio

clf;
portfolioexamples_plot(sprintf('Efficient Frontier with %g-%g Portfolio', ...
    100*(1 + Leverage),100*Leverage), ...
	{'line', prsk, pret, {'Standard'}, 'b:'}, ...
	{'line', qrsk, qret, {'130-30'}, 'b'}, ...
	{'scatter', qsrsk, qsret, {'Sharpe'}}, ...
	{'scatter', [mrsk, crsk, ersk], [mret, cret, eret], {'Market', 'Cash', 'Equal'}}, ...
	{'scatter', sqrt(diag(p.AssetCovar)), p.AssetMean, p.AssetList, '.r'});

% Set up a dataset object that contains the portfolio that maximizes the Sharpe ratio

Blotter = dataset({100*qswgt(abs(qswgt) > 1.0e-4), 'Weight'}, ...
	{100*qslong(abs(qswgt) > 1.0e-4), 'Long'}, ...
	{100*qsshort(abs(qswgt) > 1.0e-4), 'Short'}, ...
	'obsnames', AssetList(abs(qswgt) > 1.0e-4));

fprintf('%g-%g Portfolio with Maximum Sharpe Ratio\n',100*(1 + Leverage),100*Leverage);
disp(Blotter);

fprintf('Confirm %g-%g Portfolio\n',100*(1 + Leverage),100*Leverage);
fprintf('  (Net, Long, Short)\n');
disp([ sum(Blotter.Weight), sum(Blotter.Long), sum(Blotter.Short) ]);
130-30 Portfolio with Maximum Sharpe Ratio
            Weight      Long      Short  
    DD       -9.5565         0     9.5565
    HON      -6.0245         0     6.0245
    INTC      4.0335    4.0335          0
    JNJ       7.1234    7.1234          0
    JPM     -0.44583         0    0.44583
    KO       -13.646         0     13.646
    MMM       20.908    20.908          0
    MO        14.433    14.433          0
    MSFT      4.5592    4.5592          0
    PG        17.243    17.243          0
    SBC     -0.32712         0    0.32712
    UTX       5.3584    5.3584          0
    WMT       21.018    21.018          0
    XOM       35.323    35.323          0

Confirm 130-30 Portfolio
  (Net, Long, Short)
  100.0000  130.0000   30.0000

References

  1. R. C. Grinold and R. N. Kahn (2000), Active Portfolio Management, 2nd ed.

  2. H. M. Markowitz (1952), "Portfolio Selection," Journal of Finance, Vol. 1, No. 1, pp. 77-91.

  3. J. Lintner (1965), "The Valuation of Risk Assets and the Selection of Risky Investments in Stock Portfolios and Capital Budgets," Review of Economics and Statistics, Vol. 47, No. 1, pp. 13-37.

  4. H. M. Markowitz (1959), Portfolio Selection: Efficient Diversification of Investments, John Wiley & Sons, Inc.

  5. W. F. Sharpe (1966), "Mutual Fund Performance," Journal of Business, Vol. 39, No. 1, Part 2, pp. 119-138.

  6. J. Tobin (1958), "Liquidity Preference as Behavior Towards Risk," Review of Economic Studies, Vol. 25, No.1, pp. 65-86.

  7. J. L. Treynor and F. Black (1973), "How to Use Security Analysis to Improve Portfolio Selection," Journal of Business, Vol. 46, No. 1, pp. 68-86.

Was this topic helpful?