Main Content

bondDefaultBootstrap

Bootstrap default probability curve from bond prices

Description

example

[ProbabilityData,HazardData] = bondDefaultBootstrap(ZeroData,MarketData,Settle) bootstraps the default probability curve from bond prices.

Using bondDefaultBootstrap, you can:

  • Extract discrete default probabilities for a certain period from market bond data.

  • Interpolate these default probabilities to get the default probability curve for pricing and risk management purposes.

example

[ProbabilityData,HazardData] = bondDefaultBootstrap(___,Name,Value) adds optional name-value pair arguments.

Examples

collapse all

Use the following bond data.

 Settle = datenum('08-Jul-2016');
 MarketDate = datenum({'06/15/2018', '01/08/2019', '02/01/2021', '03/18/2021', '08/04/2025'}','mm/dd/yyyy');
 CouponRate = [2.240 2.943 5.750 3.336 4.134]'/100;
 MarketPrice = [101.300 103.020 115.423 104.683 108.642]';
 MarketData = [MarketDate,MarketPrice,CouponRate];

Calculate the ProbabilityData and HazardData.

TreasuryParYield = [0.26 0.28 0.36 0.48 0.61 0.71 0.95 1.19 1.37 1.69 2.11]'/100;
TreasuryDates = datemnth(Settle, [[1 3 6], 12 * [1 2 3 5 7 10 20 30]]');
[ZeroRates, CurveDates] = pyld2zero(TreasuryParYield, TreasuryDates, Settle);
ZeroData = [CurveDates, ZeroRates];
format longg
[ProbabilityData,HazardData]=bondDefaultBootstrap(ZeroData,MarketData,Settle)
ProbabilityData = 5×2

                    737226        0.0299675399937611
                    737433        0.0418832295824674
                    738188         0.090518332884262
                    738233         0.101248065083713
                    739833         0.233002708031915

HazardData = 5×2

                    737226        0.0157077745460244
                    737433        0.0217939816590403
                    738188         0.025184912824721
                    738233        0.0962608718640789
                    739833        0.0361632398787917

In bondDefaultBootstrap, the first column of the ProbabilityData output and the first column of the HazardData output contain the respective ending dates for the corresponding default probabilities and hazard rates. However, the starting dates used for the computation of the time ranges for default probabilities can be different from those of hazard rates. For default probabilities, the time ranges are all computed from the Settle date to the respective end dates shown in the first column of ProbabilityData. In contrast, the time ranges for the hazard rates are computed using the Settle date and the first column of HazardData, so that the first hazard rate applies from the Settle date to the first market date, the second hazard rate from the first to the second market date, and so on, and the last hazard rate applies from the second-to-last market date onwards.

datestr(Settle)
ans = 
'08-Jul-2016'
datestr(ProbabilityData(:,1))
ans = 5x11 char array
    '15-Jun-2018'
    '08-Jan-2019'
    '01-Feb-2021'
    '18-Mar-2021'
    '04-Aug-2025'

datestr(HazardData(:,1))
ans = 5x11 char array
    '15-Jun-2018'
    '08-Jan-2019'
    '01-Feb-2021'
    '18-Mar-2021'
    '04-Aug-2025'

The time ranges for the default probabilities all start on '08-Jul-2016' and they end on '15-Jun-2018', '08-Jan-2019', '01-Feb-2021', '18-Mar-2021', and '04-Aug-2025', respectively. As for the hazard rates, the first hazard rate starts on '08-Jul-2016' and ends on '15-Jun-2018', the second hazard rate starts on '15-Jun-2018' and ends on '08-Jan-2019', the third hazard rate starts on '08-Jan-2019' and ends on '01-Feb-2021', and so forth.

Reprice one of the bonds from bonds list based on the default probability curve. The expected result of this repricing is a perfect match with the market quote.

Use the following Treasury data from US Department of the Treasury.

Settle = datetime('08-Jul-2016','Locale','en_US');
TreasuryParYield = [0.26 0.28 0.36 0.48 0.61 0.71 0.95 1.19 1.37 1.69 2.11]'/100;
TreasuryDates = datemnth(Settle, [[1 3 6], 12 * [1 2 3 5 7 10 20 30]]');

Preview the bond date using semiannual coupon bonds with market quotes, coupon rates, and a settle date of July-08-2016.

MarketDate = datenum({'06/01/2017','06/01/2019','06/01/2020','06/01/2022'}','mm/dd/yyyy');
CouponRate = [7 8 9 10]'/100;
MarketPrice = [101.300 109.020 114.42 118.62]';
MarketData = [MarketDate, MarketPrice, CouponRate];

BondList = array2table(MarketData, 'VariableNames', {'Maturity', 'Price','Coupon'});
BondList.Maturity = datetime(BondList.Maturity,'Locale','en_US','ConvertFrom','datenum');
BondList.Maturity.Format = 'MMM-dd-yyyy'
BondList=4×3 table
     Maturity      Price     Coupon
    ___________    ______    ______

    Jun-01-2017     101.3     0.07 
    Jun-01-2019    109.02     0.08 
    Jun-01-2020    114.42     0.09 
    Jun-01-2022    118.62      0.1 

Choose the second coupon bond as the one to be priced.

number = 2;
TestCase = BondList(number, :);

Preview the risk-free rate data provided here that is based on a continuous compound rate.

[ZeroRates, CurveDates] = pyld2zero(TreasuryParYield, TreasuryDates, Settle);
ZeroData = [datenum(CurveDates), ZeroRates];
RiskFreeRate = array2table(ZeroData, 'VariableNames', {'Date', 'Rate'});
RiskFreeRate.Date = datetime(RiskFreeRate.Date,'Locale','en_US','ConvertFrom','datenum');
RiskFreeRate.Date.Format = 'MMM-dd-yyyy'
RiskFreeRate=11×2 table
       Date          Rate   
    ___________    _________

    Aug-08-2016    0.0026057
    Oct-08-2016    0.0027914
    Jan-08-2017    0.0035706
    Jul-08-2017    0.0048014
    Jul-08-2018    0.0061053
    Jul-08-2019    0.0071115
    Jul-08-2021    0.0095416
    Jul-08-2023     0.012014
    Jul-08-2026     0.013883
    Jul-08-2036     0.017359
    Jul-08-2046     0.022704

Bootstrap the probability of default (PD) curve from the bonds.

format longg
[defaultProb1, hazard1] = bondDefaultBootstrap(ZeroData, MarketData, Settle)
defaultProb1 = 4×2

                    736847        0.0704863142317494
                    737577         0.162569420050034
                    737943         0.217308133826188
                    738673          0.38956773145021

hazard1 = 4×2

                    736847        0.0813390794774647
                    737577        0.0521615800986281
                    737943        0.0674145844133183
                    738673          0.12428587278862

format

Reformat the default probability and hazard rate for a better representation.

DefProbHazard = [defaultProb1, hazard1(:,2)];
DefProbHazardTable = array2table(DefProbHazard, 'VariableNames', {'Date', 'DefaultProbability', 'HazardRate'});
DefProbHazardTable.Date = datetime(DefProbHazardTable.Date,'Locale','en_US','ConvertFrom','datenum');
DefProbHazardTable.Date.Format = 'MMM-dd-yyyy'
DefProbHazardTable=4×3 table
       Date        DefaultProbability    HazardRate
    ___________    __________________    __________

    Jun-01-2017         0.070486          0.081339 
    Jun-01-2019          0.16257          0.052162 
    Jun-01-2020          0.21731          0.067415 
    Jun-01-2022          0.38957           0.12429 

Preview the selected bond to reprice based on the PD curve.

TestCase
TestCase=1×3 table
     Maturity      Price     Coupon
    ___________    ______    ______

    Jun-01-2019    109.02     0.08 

To reprice the bond, first generate cash flows and payment dates.

[Payments, PaymentDates] = cfamounts(TestCase.Coupon, Settle, TestCase.Maturity);
AccInt=-Payments(1);
    % Truncate the payments as well as payment dates for calculation
    % PaymentDates(1) is the settle date, no need for following calculations
PaymentDates = PaymentDates(2:end)
PaymentDates = 1x6 datetime
   01-Dec-2016   01-Jun-2017   01-Dec-2017   01-Jun-2018   01-Dec-2018   01-Jun-2019

Payments = Payments(2:end)
Payments = 1×6

     4     4     4     4     4   104

Calculate the discount factors on the payment dates.

DF = zero2disc(interp1(RiskFreeRate.Date, RiskFreeRate.Rate, PaymentDates, 'linear', 'extrap'), PaymentDates, Settle, -1)
DF = 1×6

    0.9987    0.9959    0.9926    0.9887    0.9845    0.9799

Assume that the recovery amount is a fixed proportion of bond's face value. The bond’s face value is 100, and the recovery ratio is set to 40% as assumed in bondDefaultBootstrap.

Num = length(Payments);
RecoveryAmount = repmat(100*0.4, 1, Num)
RecoveryAmount = 1×6

    40    40    40    40    40    40

Calculate the probability of default based on the default curve.

DefaultProb1 = bondDefaultBootstrap(ZeroData, MarketData, Settle, 'ZeroCompounding', -1, 'ProbabilityDates', PaymentDates');
SurvivalProb = 1 - DefaultProb1(:,2)
SurvivalProb = 6×1

    0.9680
    0.9295
    0.9055
    0.8823
    0.8595
    0.8375

Calculate the model-based clean bond price.

DirtyPrice = DF * (SurvivalProb.*Payments') + (RecoveryAmount.*DF) * (-diff([1;SurvivalProb]));
ModelPrice = DirtyPrice - AccInt
ModelPrice = 109.0200

Compare the repriced bond to the market quote.

ResultTable = TestCase;
ResultTable.ModelPrice = ModelPrice;
ResultTable.Difference = ModelPrice - TestCase.Price
ResultTable=1×5 table
     Maturity      Price     Coupon    ModelPrice    Difference
    ___________    ______    ______    __________    __________

    Jun-01-2019    109.02     0.08       109.02      1.4211e-14

Input Arguments

collapse all

Zero rate data, specified as an M-by-2 matrix of dates and zero rates or an IRDataCurve object of zero rates. For array input, the dates must be entered as serial date numbers, and discount rate must be in decimal form.

When ZeroData is an IRDataCurve object, ZeroCompounding and ZeroBasis are implicit in ZeroData and are redundant inside this function. In this case, specify these optional parameters when constructing the IRDataCurve object before using this bondDefaultBootstrap function.

For more information on an IRDataCurve (Financial Instruments Toolbox) object, see Creating an IRDataCurve Object (Financial Instruments Toolbox).

Data Types: double

Bond market data, specified as an N-by-3 matrix of maturity dates, market prices, and coupon rates for bonds. The dates must be entered as serial date numbers, market prices must be numeric values, and coupon rate must be in decimal form.

Note

A warning is displayed when MarketData is not sorted in ascending order by time.

Data Types: double

Settlement date, specified as a scalar datetime, string, or date character vector. Settle must be earlier than or equal to the maturity dates in MarketData.

To support existing code, bondDefaultBootstrap also accepts serial date numbers as inputs, but they are not recommended.

Data Types: char | datetime | string

Name-Value Arguments

Specify optional pairs of arguments as Name1=Value1,...,NameN=ValueN, where Name is the argument name and Value is the corresponding value. Name-value arguments must appear after other arguments, but the order of the pairs does not matter.

Before R2021a, use commas to separate each name and value, and enclose Name in quotes.

Example: [ProbabilityData,HazardData] = bondDefaultBootstrap(ZeroData,MarketData,Settle,'RecoveryRate',Recovery,'ZeroCompounding',-1)

Note

Any optional input of size N-by-1 is also acceptable as an array of size 1-by-N, or as a single value applicable to all contracts.

Recovery rate, specified as the comma-separated pair consisting of 'RecoveryRate' and a N-by-1 vector of recovery rates, expressed as a decimal from 0 through 1.

Data Types: double

Dates for the output of default probability data, specified as the comma-separated pair consisting of 'ProbabilityDates' and a P-by-1 vector using a datetime array, string array, or date character vectors.

To support existing code, bondDefaultBootstrap also accepts serial date numbers as inputs, but they are not recommended.

Data Types: char | datetime | string

Compounding frequency of the zero curve, specified as the comma-separated pair consisting of 'ZeroCompounding' and a N-by-1 vector. Values are:

  • 1 — Annual compounding

  • 2 — Semiannual compounding

  • 3 — Compounding three times per year

  • 4 — Quarterly compounding

  • 6 — Bimonthly compounding

  • 12 — Monthly compounding

  • −1 — Continuous compounding

Data Types: double

Basis of the zero curve, specified as the comma-separated pair consisting of 'ZeroBasis' and the same values listed for Basis.

Data Types: double

Recovery method, specified as the comma-separated pair consisting of 'RecoveryMethod' and a character vector or a string with a value of 'presentvalue' or 'facevalue'.

  • 'presentvalue' assumes that upon default, a bond is valued at a given fraction to the hypothetical present value of its remaining cash flows, discounted at risk-free rate.

  • 'facevalue' assumes that a bond recovers a given fraction of its face value upon recovery.

Data Types: char | string

Face or par value, specified as the comma-separated pair consisting of 'Face' and a NINST-by-1 vector of bonds.

Data Types: double

Payment frequency, specified as the comma-separated pair consisting of 'Period' and a N-by-1 vector with values of 0, 1, 2, 3, 4, 6, or 12.

Data Types: double

Day-count basis of the instrument, specified as the comma-separated pair consisting of 'Basis' and a positive integer using a NINST-by-1 vector. Values are:

  • 0 = actual/actual

  • 1 = 30/360 (SIA)

  • 2 = actual/360

  • 3 = actual/365

  • 4 = 30/360 (PSA)

  • 5 = 30/360 (ISDA)

  • 6 = 30/360 (European)

  • 7 = actual/365 (Japanese)

  • 8 = actual/actual (ICMA)

  • 9 = actual/360 (ICMA)

  • 10 = actual/365 (ICMA)

  • 11 = 30/360E (ICMA)

  • 12 = actual/365 (ISDA)

  • 13 = BUS/252

For more information, see Basis.

Data Types: double

End-of-month rule flag, specified as the comma-separated pair consisting of 'EndMonthRule' and a nonnegative integer, 0 or 1, using a NINST-by-1 vector. This rule applies only when Maturity is an end-of-month date for a month having 30 or fewer days.

  • 0 = Ignore rule, meaning that a bond coupon payment date is always the same numerical day of the month.

  • 1 = Set rule on, meaning that a bond coupon payment date is always the last actual day of the month.

Data Types: double

Bond issue date, specified as the comma-separated pair consisting of 'IssueDate' and a N-by-1 vector using a datetime array, string array, or date character vectors.

To support existing code, bondDefaultBootstrap also accepts serial date numbers as inputs, but they are not recommended.

Data Types: char | datetime | string

First actual coupon date, specified as the comma-separated pair consisting of 'FirstCouponDate' and a scalar datetime, string, or date character vector. FirstCouponDate is used when a bond has an irregular first coupon period. When FirstCouponDate and LastCouponDate are both specified, FirstCouponDate takes precedence in determining the coupon payment structure.

To support existing code, bondDefaultBootstrap also accepts serial date numbers as inputs, but they are not recommended.

Data Types: char | datetime | string

Last actual coupon date, specified as the comma-separated pair consisting of 'LastCouponDate' and a scalar datetime, string, or date character vector. LastCouponDate is used when a bond has an irregular last coupon period. In the absence of a specified FirstCouponDate, a specified LastCouponDate determines the coupon structure of the bond. The coupon structure of a bond is truncated at the LastCouponDate, regardless of where it falls, and is followed only by the bond's maturity cash flow date.

To support existing code, bondDefaultBootstrap also accepts serial date numbers as inputs, but they are not recommended.

Data Types: char | datetime | string

Forward starting date of payments, specified as the comma-separated pair consisting of 'StartDate' and a scalar datetime, string, or date character vector. StartDate is when a bond actually starts (the date from which a bond cash flow is considered). To make an instrument forward-starting, specify this date as a future date.

To support existing code, bondDefaultBootstrap also accepts serial date numbers as inputs, but they are not recommended.

Data Types: char | datetime | string

Business day conventions, specified as the comma-separated pair consisting of 'BusinessDayConvention' and a character vector or a string object. The selection for business day convention determines how nonbusiness days are treated. Nonbusiness days are defined as weekends plus any other date that businesses are not open (for example, statutory holidays). Values are:

  • 'actual' — Nonbusiness days are effectively ignored. Cash flows that fall on non-business days are assumed to be distributed on the actual date.

  • 'follow' — Cash flows that fall on a nonbusiness day are assumed to be distributed on the following business day.

  • 'modifiedfollow' — Cash flows that fall on a non-business day are assumed to be distributed on the following business day. However if the following business day is in a different month, the previous business day is adopted instead.

  • 'previous' — Cash flows that fall on a nonbusiness day are assumed to be distributed on the previous business day.

  • 'modifiedprevious' — Cash flows that fall on a nonbusiness day are assumed to be distributed on the previous business day. However if the previous business day is in a different month, the following business day is adopted instead.

Data Types: char | cell | string

Output Arguments

collapse all

Default probability values, returned as a P-by-2 matrix with dates and corresponding cumulative default probability values. The dates match those in MarketData, unless the optional input parameter ProbabilityDates is provided.

Hazard rate values, returned as an N-by-2 matrix with dates and corresponding hazard rate values for the survival probability model. The dates match those in MarketData.

Note

A warning is displayed when nonmonotone default probabilities (that is, negative hazard rates) are found.

More About

collapse all

Bootstrap Default Probability

A default probability curve can be bootstrapped from a collection of bond market quotes.

Extracting discrete default probabilities for a certain period from market bond data is represented by the formula

Price=Disc(tN)×FV×Q(tN)+Cf×i=1NDisc(ti)×Q(ti)+(i=1)NDisc(ti)×R(ti)×(Q(ti1)Q(ti))

where:

FV — Face value

Q — Survival probability

C — Coupon

R — Recovery amount

f — Payment frequency (for example, 2 for semiannual coupon bonds)

The default probability is:

DefaultProbability = 1SurvivalProbability

References

[1] Jarrow, Robert A., and Stuart Turnbull. "Pricing Derivatives on Financial Securities Subject to Credit Risk." Journal of Finance. 50.1, 1995, pp. 53–85.

[2] Berd, A., Mashal, R. and Peili Wang. “Defining, Estimating and Using Credit Term Structures.” Research report, Lehman Brothers, 2004.

Version History

Introduced in R2017a

expand all

See Also

(Financial Instruments Toolbox) |

Topics