image thumbnail

CVaR Portfolio Optimization

by

 

26 Sep 2012 (Updated )

Conditional Value at Risk (CVaR) portfolio optimization with the PortfolioCVaR object

CVaRPortfolioOptimizationExample.m
%% This workflow is split up into 5 parts.  They are:
% 1) Import data for Small-Cap stocks, Large-Cap stocks, and Bond ETF's
% 2) Find the optimal CVaR portfolios
% 3) Find the optimal Mean-Variance portfolio
% 4) Compare Mean-Variance portfolios with CVaR portfolios
% 5) Look at the distribution of returns data
%
% Copyright (c) 2012, MathWorks, Inc.

clear; close all; clc;

%% 1) Import data
%% 1.1) Open up a connection
C = yahoo;

%% 1.2) Fetch the data
% Data is imported from a Yahoo connection for Small-Cap Stocks, Large-Cap
% Stocks, and Bond ETF's.  The adjusted daily close prices are imported 
% over the range of startDate to endDate.  

startDate = 'Jul 1 07';
endDate = 'Jun 30 12';

% Small-cap data
smallCapTickers = {'ANAT','CPTS','GEF','IFN','MYGN'};
for ii = 1:length(smallCapTickers)
    Price.(smallCapTickers{ii}) = fetch(C,smallCapTickers{ii},...
        'Adj Close',startDate,endDate,'d');
    % Data is ordered newest to oldest, flip it to be oldest to newest
    temp  = Price.(smallCapTickers{ii});
    smallCapClosePrice(:,ii) = flipud(temp(:,2));
end

% Large-cap data
largeCapTickers = {'HPQ','KFT','PG','VZ','XOM'};
for ii = 1:length(largeCapTickers)
    Price.(largeCapTickers{ii}) = fetch(C,largeCapTickers{ii},...
        'Adj Close',startDate,endDate,'d');
    % Data is ordered newest to oldest, flip it to be oldest to newest
    temp  = Price.(largeCapTickers{ii});  
    largeCapClosePrice(:,ii) = flipud(temp(:,2));    
end

% Bond ETF's
bondETFTickers = {'AGG','BLV','BND','CSJ'};
for ii = 1:length(bondETFTickers)
    Price.(bondETFTickers{ii}) = fetch(C,bondETFTickers{ii},...
        'Adj Close',startDate,endDate,'d');
    % Data is ordered newest to oldest, flip it to be oldest to newest
    temp  = Price.(bondETFTickers{ii});
    bondETFClosePrice(:,ii) = flipud(temp(:,2));    
end

% Close data connection
close(C);

%% 1.3) Convert price data to returns
% For portfolio optimization it is recommended to use returns data, so we 
% convert price data into returns.
smallCapReturns = tick2ret(smallCapClosePrice);
largeCapReturns = tick2ret(largeCapClosePrice);
bondETFReturns  = tick2ret(bondETFClosePrice);

%% 1.4) Visual Check on Returns
% Lets just do a quick visual check on our returns data.  The Small-Cap
% returns should have the highest risk, while the Large-Cap and the Bond 
% ETF's should have less variability.
figure;
subplot(3,1,1);  plot(smallCapReturns);  title('Small-Cap Returns');
subplot(3,1,2);  plot(largeCapReturns);  title('Large-Cap Returns');
subplot(3,1,3);  plot(bondETFReturns);   title('Bond ETF Returns');
set(get(gcf,'Children'),'YLim',[-0.5 0.5]); % Set all Y axes to be the same

%% 1.5) Combine assets
% We'll now add all of our data together so we have a single matrix of the
% returns for all of our assets.  We also add the ticker symbols together
% so we have a list of all the assets in our single matrix.
assetTickers = [smallCapTickers largeCapTickers bondETFTickers];
Returns = [smallCapReturns largeCapReturns bondETFReturns];

%% 2) CVaR Portfolio
% Here we create the CVaR object and use the object's methods to pass in
% data and setup the CVaR problem.  By setting the probability level to
% 0.95, we are choosing to minimize the mean loss in the 5% of portfolio 
% returns with the highest losses.
%% 2.1) Setup portfolio
pmc = PortfolioCVaR;
pmc = pmc.setAssetList(assetTickers);
pmc = pmc.setScenarios(Returns);
pmc = pmc.setDefaultConstraints;
pmc = pmc.setProbabilityLevel(0.95);

%% 2.2) Plot the CVaR efficient frontier
% Get the weights of the CVaR efficient portfolios
pmcwgts = pmc.estimateFrontier(10);
% Plot the portfolios, and get the portfolio risks/returns
figure; [pmcRisk, pmcReturns] = pmc.plotFrontier(pmcwgts);

%% 3) Mean-Variance Portfolio
% Its a good idea to look at a portfolio problem according to different 
% risk measures.  So lets create a mean-variance portfolio object, and 
% find the 10 efficient portfolios according to a Mean-Variance risk proxy.  
% The workflow is very similar to the CVaR portfolio workflow, except that 
% we use the estimateAssetMoments method now, to estimate the mean and 
% covariance of our return data.  
%% 3.1) Setup portfolio
pmv = Portfolio;
pmv = pmv.setAssetList(assetTickers);
pmv = pmv.estimateAssetMoments(Returns);
pmv = pmv.setDefaultConstraints;

%% 3.2) Plot the Mean-Variance efficient frontier
% Get the weights of the mean-variance efficient portfolios
pmvwgts = pmv.estimateFrontier(10);
% Plot the portfolios, and get the portfolio risks/returns
figure; [pmvRisk, pmvReturns] = pmv.plotFrontier(pmvwgts);

%% 4) Compare CVaR and Mean-Variance portfolios
%% 4.1) Calculate Mean-Variance for CVaR Portfolios
% One way that we can compare the portfolios is to convert between risk
% proxies.  Here, we'll calculate the Mean-Variance risk of the 10 CVaR
% portfolios.
pmcRiskStd = pmc.estimatePortStd(pmcwgts);

%% 4.2) Add CVaR Portfolios to Mean-Variance Plot
% Now that we have the mean-variance risk of the CVaR portfolios, we can
% add them to the mean-variance frontier plot.  We need to remember to take
% the square root of the variance to get the standard deviation, which is
% used on the x-axis of the plot.
figure;
pmv.plotFrontier(10);
hold on
plot(pmcRiskStd,pmcReturns,'-r','LineWidth',2);
legend('Mean-Variance Efficient Frontier',...
       'CVaR Efficient Frontier',...
       'Location','SouthEast')
   
%% 4.3) Compare Weights
% Another way to compare the efficient portfolios is to look at the 
% weights for the 10 portfolios.  We'll visualize the weights for the CVaR 
% and Mean-Variance side-by-side using area plots. This allows us to
% see the mix of the different assets in each of the 10 efficient 
% portfolios.
figure; 

subplot(1,2,1);
area(pmcwgts');
title('CVaR Portfolio Weights');

subplot(1,2,2);
area(pmvwgts');
title('Mean-Variance Portfolio Weights');

set(get(gcf,'Children'),'YLim',[0 1]);
legend(pmv.AssetList);

%% 5) Analyze normality of data
%% 5.1) Let's analyze  the returns
% Histogram of the returns of the Bond ETF's grouped into 50 bins
figure;
hist(bondETFReturns(:),50); 

%% 5.2) Add a normal fit to the histogram
% If we try to fit a normal distribution to the return data, it becomes 
% evident that a normal distribution may not be a good assumption to make. 
% This helps explain the difference between our Mean-Variance portfolio, 
% which assumes that returns are normally distributed, and our CVaR 
% portfolio, which uses simulations that dont necessarily need to be 
% normal.
histfit(bondETFReturns(:),50); figure(gcf);

Contact us