2
$\begingroup$

I'm working with 10 bonds with different maturity and want to get the zero curve. I tried the quantlib. However, I cannot understand the parameter in FixedRateBondHelper. Here is my code:

def bootstrap(ytm_data):
calc_date = ql.Date(13, 4, 2020)
ql.Settings.instance().evaluationDate = calc_date

ytm_data['ytm'] = (1+ytm_data['ytm']/2).pow(2)-1 # annulize YTM
calendar = ql.UnitedStates()
bussiness_convention = ql.Unadjusted
#day_count_bill = ql.Actual360()
day_count = ql.ActualActual(ql.ActualActual.Bond)

end_of_month = False
settlement_days = 2
face_amount = 100
coupon_frequency = ql.UnitedStates.GovernmentBond

depo_helper = []
bond_helper = []

for date,rate,quote in ytm_data.values.tolist():
    timedelta = date - calc_date
    period = ql.Period('%dd'%timedelta)
    if timedelta<365:
        depo_helper.append(ql.DepositRateHelper(ql.QuoteHandle(ql.SimpleQuote(rate)), period, settlement_days, calendar, bussiness_convention, end_of_month, day_count))
    if timedelta>365:
        schedule = ql.Schedule(calc_date, date,
           coupon_frequency,
           calendar,
           bussiness_convention,
           bussiness_convention,
           ql.DateGeneration.Backward,
           end_of_month)
        helper = ql.FixedRateBondHelper(ql.QuoteHandle(ql.SimpleQuote(quote)),
                                    settlement_days,
                                    face_amount,
                                    schedule,
                                    [rate],
                                    day_count,
                                    bussiness_convention,
                                    )
        bond_helper.append(helper)

rate_helper = depo_helper + bond_helper
yieldcurve = ql.PiecewiseLogCubicDiscount(calc_date,
                         rate_helper,
                         day_count)

spots = []
tenors = []

for d in yieldcurve.dates():
    yrs = day_count.yearFraction(calc_date, d)
    compounding = ql.Compounded
    freq = ql.Semiannual
    zero_rate = yieldcurve.zeroRate(yrs, compounding, freq)
    tenors.append(yrs)
    eq_rate = zero_rate.equivalentRate(day_count,
                                       compounding,
                                       freq,
                                       calc_date,
                                       d).rate()
    spots.append(eq_rate)

return spots,tenors,yieldcurve

Here is the market data enter image description here Here is ytm_data enter image description here

I have calculate YTM of each bonds. Here is the question:

  1. what's the data should I put into the ql.QuoteHandle(ql.SimpleQuote(rate)). I just use annulized YTM as rate
  2. what's the [rate] in ql.FixedRateBondHelper, the coupon rate our YTM rate.
  3. What's price should I put into ql.QuoteHandle(ql.SimpleQuote(quote)), clean price or full price.
  4. after calculation, the node in yieldcurve has a different datetime with original market data. Why does this happen.

I'm quit new to quantlib. Thanks in advance!

$\endgroup$

2 Answers 2

2
$\begingroup$

The FixedRateBondHelper class in python has the following constructor:

ql.FixedRateBondHelper(
 price, settlementDays, faceAmount, schedule, coupons,
 dayCounter, paymentConv=Following, redemption=100.0,
 issueDate=Date(), paymentCalendar=Calendar(),
 exCouponPeriod=Period(), exCouponCalendar=Calendar(), 
 exCouponConvention=Unadjusted, exCouponEndOfMonth=False,
 useCleanPrice=True
)
  1. The first argumento should be the price.
  2. You should input the coupon.
  3. You should input the coupon. Alternatively, you could use a price of 100 and use the YTM as the coupon, which would be a pretty good approximation.
  4. Unless your calendar and conventions move the dates to business days, the days should match.

Try this simplified example:

import QuantLib as ql

calc_date = ql.Date(13, 4, 2020)
ql.Settings.instance().evaluationDate = calc_date

data = [
    ('17-03-2020', '12-05-2020', 0, 99.99),
    ('09-01-2020', '09-07-2020', 0, 99.97),
    ('10-10-2019', '08-10-2020', 0, 99.94),
    ('26-03-2020', '25-03-2021', 0, 99.88),
    ('31-03-2020', '31-03-2022', 0.375, 100.28),
    ('15-04-2020', '15-04-2023', 0.25, 99.88),
    ('31-03-2020', '31-03-2025', 0.5, 100.4),
    ('18-02-2020', '31-03-2027', 0.625, 100.15),
    ('18-02-2020', '15-02-2030', 1.5, 107.25),
    ('18-02-2020', '15-02-2050', 2.0, 115.95),
]

helpers = []
day_count = ql.ActualActual(ql.ActualActual.Bond)

for issue_date, maturity, coupon, price in data:
    price = ql.QuoteHandle(ql.SimpleQuote(price))
    issue_date = ql.Date(issue_date, '%d-%m-%Y')
    maturity = ql.Date(maturity, '%d-%m-%Y')
    schedule = ql.MakeSchedule(issue_date, maturity, ql.Period(ql.Semiannual))
    helper = ql.FixedRateBondHelper(price, 2, 100, schedule, [coupon / 100], day_count)
    helpers.append(helper)
yieldcurve = ql.PiecewiseLogCubicDiscount(calc_date, helpers, day_count)    

If you check the curve nodes, they should match you instrument maturity dates...

yieldcurve.nodes()

((Date(13,4,2020), 1.0),
(Date(12,5,2020), 0.999892874058905),
(Date(9,7,2020), 0.9996928754839124),
(Date(8,10,2020), 0.9993928776218975),
(Date(25,3,2021), 0.9987928818978993),
(Date(31,3,2022), 0.9954638937930196),
(Date(15,4,2023), 0.9913234618735143),
(Date(31,3,2025), 0.9794138780398628),
(Date(31,3,2027), 0.9586836389927256),
(Date(15,2,2030), 0.9291051282013438),
(Date(15,2,2050), 0.6518640785683848))

$\endgroup$
1
  • $\begingroup$ Oh Thanks! Very helpful $\endgroup$ Jun 7, 2020 at 3:00
0
$\begingroup$

Another example using FixedRateBondHelper on some US treasury data and following the code above:

import QuantLib as ql

calc_date = ql.Date(5, 5, 2023)
ql.Settings.instance().evaluationDate = calc_date
settlement_days = 1
face_amount = 100

data = [
     ('31-08-2021',     '31-08-2023',   0.125,  98.43537),
     ('30-09-2021',     '30-09-2023',   0.25,   98.13213),
     ('01-11-2021',     '31-10-2023',   0.375,  97.82426),
     ('30-11-2021',     '30-11-2023',   0.5,    97.56833),
     ('31-12-2021',     '31-12-2023',   0.75,   97.36831),
     ('31-01-2022',     '31-01-2024',   0.875,  97.16839),
     ('28-02-2022',     '29-02-2024',   1.5,    97.35896),
     ('31-03-2022',     '31-03-2024',   2.25,   97.79973),
     ('02-05-2022',     '30-04-2024',   2.5,    97.88095),
     ('31-05-2022',     '31-05-2024',   2.5,    97.77523),
     ('30-06-2022',     '30-06-2024',   3.0,    98.2304),
     ('01-08-2022',     '31-07-2024',   3.0,    98.21679),
     ('31-08-2022',     '31-08-2024',   3.25,   98.53124),
     ('30-09-2022',     '30-09-2024',   4.25,   99.87726),
     ('31-10-2022',     '31-10-2024',   4.375,  100.14486),
     ('30-11-2022',     '30-11-2024',   4.5,    100.42819),
     ('03-01-2023',     '31-12-2024',   4.25,   100.1332),
     ('31-01-2023',     '31-01-2025',   4.125,  100.0258),
     ('28-02-2023',     '28-02-2025',   4.625,  101.04361),
     ('15-03-2022',     '15-03-2025',   1.75,   95.98058),
     ]
helpers = []

for issue_date, maturity, coupon, price in data:
    price = ql.QuoteHandle(ql.SimpleQuote(price))
    issue_date = ql.Date(issue_date, '%d-%m-%Y')
    maturity = ql.Date(maturity, '%d-%m-%Y')
    schedule = ql.MakeSchedule(issue_date, maturity, ql.Period(ql.Semiannual))
    day_count = ql.ActualActual(ql.ActualActual.Bond, schedule)
    helper = ql.FixedRateBondHelper(price, 1, 100, schedule, [coupon / 100], day_count)
    helpers.append(helper)
yc = ql.PiecewiseLogCubicDiscount(calc_date, helpers, day_count)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.