Contents
Matching a Cash flow with a Bond Portfolio
In this script we will import data on UK Gilts to construct a yield curve for UK interest rates. Using this we will then construct a bond portfolio to match a given cash flow. This will be done by optimisation, constraining the resulting portfolio to have the same duration and convexity as the Cash flow.
close all; clear all; clc
Change figure size
make the default figure position large so that the graphs are more legible.
spos = get(0,'screensize'); fpos0 = get(0,'DefaultFigurePosition'); set(0,'defaultFigurePosition',[spos(3)*.1 spos(4)*.1 spos(3)*.8 spos(4)*.8]);
Fitting the Yield Curve
We do this using the function YieldCurveFit automatically generated by the Curve Fitting toolbox. This function will fit a Svensson curve to the yield and lifetime data for a collection of bonds. Here we provide it with a collection of UK Gilts.
To investigate doing this with the Curve Fitting Toolbox if you have it installed, type:
cftool
and import the data Life and Yield into the tool.
First we load the Gilt data into a dataset array from the Statistics Toolbox.
UKBonds = dataset('xlsfile','UK_Gilts.xlsx'); Life = UKBonds.Life; Yield = UKBonds.Yield; YC = YieldCurveFit(Life,Yield);
Portfolio Construction
We now load up a series of corporate bonds from which we are going to construct the portfolio. Again we use a dataset array to do this. We convert the date format from a string to a numerical representation.
Bonds = dataset('xlsfile','Corporate_Bonds.xlsx'); Bonds.Maturity = datenum(Bonds.Maturity,'dd/mm/yyyy');
view the yields on this bonds along with the UK Yield curve generated. We do this using createfigure2 which was automatically generated by MATLAB from a plot figure.
t = linspace(.1,50); createfigure2(t,YC(t),Bonds.Life,Bonds.Yield)
We are suspicious of anything offering a yield in excess of 5% of the corresponding value from our Yields curve, so remove these bonds.
Idx = find(Bonds.Yield > YC(Bonds.Life)+5); Bonds(Idx,:) = []; createfigure2(t,YC(t),Bonds.Life,Bonds.Yield)
Load up the Cash flow
The cash flow we want to match can be found in the data file CashFlows.xlsx. Load this up - here xlsread is used as an alternative to dataset arrays.
Settle = datenum('12/03/2009','dd/mm/yyyy'); % Date of seminar data = xlsread('CashFlows.xlsx'); T = data(:,1); CF = data(:,2)/1000;
Optimisation Constraints
we want to find the cheapest portfolio which has the same present value, duration and convexity as our cash flow. These values are given by:



Where
the annual yield to time
.
Rates = YC(T); rd = rate2disc(1,Rates/100,T,0); PV = rd'*CF; [D,Cv] = DurationConvexity(PV,CF,T,Rates/100);
We now need the present values, durations and convexities for the bonds we are looking to construct a portfolio from. We do this with the functions prbyzero, bnddurp and bndconvp from the Financial Toolbox.
rates = YC(1:50)/100; dates = datemnth(Settle,12*[1:50]'); Bond_PV = prbyzero([Bonds.Maturity Bonds.Coupon/100],Settle,rates,dates,1); [modDur,Durations] = bnddurp(Bonds.Price,Bonds.Coupon/100,Settle,Bonds.Maturity,2,0); Convexity = bndconvp(Bonds.Price,Bonds.Coupon/100,Settle,Bonds.Maturity,2,0); Price = Bonds.Price;
Now create the constraint matrices
Aeq = [Bond_PV(:)'; Durations(:)'; Convexity(:)']; beq = [PV;D;Cv];
Lower and upper bounds - no shorting of bonds and no bonds to have a value higher than 0.5 (£500).
ub = 0.5*ones(numel(Bonds.Price),1); lb = zeros(numel(Bonds.Price),1);
Perform the optimisation
We invoke the function BondPFConstruction which was automatically generated by optimtool from the Optimisation toolbox.
[wts,fval] = BondPFConstruction(Price,Aeq,beq,lb,ub);
Display the chosen bonds
Idx = find(wts); str = ['Bond',char(9),'Coupon',char(9),'Maturity',char(9),'Number',char(10)]; for ii = 1:numel(Idx) str = [str,char(Bonds.Issuer(Idx(ii))),char(9),num2str(Bonds.Coupon(Idx(ii))),char(9),... datestr(Bonds.Maturity(Idx(ii)),'dd/mm/yyyy'),char(9),num2str(100*wts(Idx(ii))),char(10)]; end disp(str);
Bond Coupon Maturity Number Standard Chartered Bank 6.75 27/04/2009 50 JTI (UK) Finance PLC 6.625 21/05/2009 50 General Elec Cap Corp 6.25 01/09/2009 4.0448 Halifax Plc 11 17/01/2014 50 Hsbc Holdings Plc 9.875 08/04/2018 50 Halifax Plc 9.375 15/05/2021 4.5227 Prudential Finance Bv 6.125 19/12/2031 24.8766
strip out relevant data for next piece of analysis
B = [Bonds.Coupon(Idx)/100 Bonds.Maturity(Idx) Bonds.Price(Idx)]; wts = wts(Idx); Labels = Bonds.Issuer(Idx);
Sensitivity and Default analysis
We can investigate the cash flows from the portfolio and the outgoings to see that they do match each other.
CF_Dates = datemnth(Settle,T*12); figure ModelCashFlow(wts,B,Settle,CF,CF_Dates,YC,Labels);
However this is dependent on the yield curve staying as it is. What happens if it either shifts up or down or flattens or steepens?
figure dy = linspace(-1,1,7); s = linspace(-1,1,7); for ii = 1:numel(dy) for jj = 1:numel(s) f = @(t) YC(t(:))+dy(ii)+(t(:)-5)/5*s(jj); Z(ii,jj) = ModelCashFlow(wts,B,Settle,CF,CF_Dates,f); end end Z(abs(Z) > 100) = NaN; surf(dy*100,100*s,Z') xlabel('Parallel shift in interest rates (bp)'); ylabel('Slope change in interest rates (bp/Yr)');
Defaults
What happens if our bonds default before the cash flow has run it's course? The following graph gives the present value of a default.
ModelBondDefault(wts,B,Settle,CF,CF_Dates,YC,Labels);
Reset figure positions
set(0,'DefaultFigurePosition',fpos0);