0
$\begingroup$

I am using python quantlib, according to one of the Quantlib sample code to evaluate the CHT (Canada Housing Trust) Floaters, and the index I am using is CDOR, and somehow the price I got is always higher than the market, so I post my code below, please help me whether I made something wrong.

  1. First, the CDOR forward rates I am using in the code was downloaded from this site: https://www.chathamfinancial.com/technology/canadian-forward-curves#contact;
  2. And then I build the CDOR forward curve using ql.ForwardCurve
  3. Build the 3 month CDOR index using the CDOR forward curve
  4. Find out the index spread from this web site for each CHT floater: https://www.cmhc-schl.gc.ca/en/professionals/project-funding-and-mortgage-financing/securitization/canada-mortgage-bonds/list-of-outstanding-cht-debt-issues
  5. Construct each FRN schedule and bond
  6. Price the FRN using the Bootstrap CHT curve I built
    # 1. build CDOR forward curve term structure
    rates = [0.0043574, 0.0044015, 0.0044471, 0.0044961, 0.0045767, 0.0046476, 0.0047762, 0.0049175, 0.0050456,
             0.0051272, 0.005215, 0.0052943, 0.0054934, 0.0058323, 0.0061394, 0.0065789, 0.007122, 0.0076312,
             0.0079265, 0.0085458, 0.009145, 0.0098384, 0.0105592, 0.0112552, 0.0120167, 0.0127172, 0.0133951,
             0.014063, 0.0146859, 0.0153088, 0.0159396, 0.0164637, 0.0170059, 0.0175708, 0.0180457, 0.0185048,
             0.0189963, 0.0193824, 0.0197952, 0.020192, 0.0205116, 0.0208632, 0.021149, 0.0214018, 0.0216628,
             0.0218877, 0.0220687, 0.0222321, 0.0224091, 0.0225604, 0.0227268, 0.0228641, 0.0229961, 0.0231323,
             0.0232522, 0.0233617, 0.0234712, 0.0235739, 0.0236691, 0.0237499, 0.0238338, 0.0239213, 0.0240147,
             0.0240994, 0.0241908, 0.0242822, 0.0243707, 0.024468, 0.0245506, 0.0246429, 0.0247457, 0.0248357,
             0.0249312, 0.0250218, 0.0251154, 0.025205, 0.0252976, 0.0253789, 0.0254632, 0.0255553, 0.0256387,
             0.0257273, 0.0258128, 0.0258928, 0.0259805, 0.0260832, 0.0261826, 0.026284, 0.0264028, 0.0265216,
             0.0266478, 0.0267761, 0.0269088, 0.027054, 0.0272047, 0.0273454, 0.0275008, 0.0276, 0.0277098,
             0.0278129, 0.0278731, 0.0279375, 0.0279934, 0.0280133, 0.0280332, 0.0280468, 0.0280241, 0.0280035,
             0.0279915, 0.0280254, 0.0280617, 0.0281093, 0.0282009, 0.0283017, 0.0283966, 0.0285495, 0.0287074,
             0.0288604, 0.0290749, 0.0292687, 0.0294786]
    dates = ['2021-04-14', '2021-05-14', '2021-06-14', '2021-07-14', '2021-08-16', '2021-09-14', '2021-10-14',
             '2021-11-15', '2021-12-14', '2022-01-14', '2022-02-14', '2022-03-14', '2022-04-14', '2022-05-16',
             '2022-06-14', '2022-07-14', '2022-08-15', '2022-09-14', '2022-10-14', '2022-11-14', '2022-12-14',
             '2023-01-16', '2023-02-14', '2023-03-14', '2023-04-14', '2023-05-15', '2023-06-14', '2023-07-14',
             '2023-08-14', '2023-09-14', '2023-10-16', '2023-11-14', '2023-12-14', '2024-01-15', '2024-02-14',
             '2024-03-14', '2024-04-15', '2024-05-14', '2024-06-14', '2024-07-15', '2024-08-14', '2024-09-16',
             '2024-10-14', '2024-11-14', '2024-12-16', '2025-01-14', '2025-02-14', '2025-03-14', '2025-04-14',
             '2025-05-14', '2025-06-16', '2025-07-14', '2025-08-14', '2025-09-15', '2025-10-14', '2025-11-14',
             '2025-12-15', '2026-01-14', '2026-02-16', '2026-03-16', '2026-04-14', '2026-05-14', '2026-06-15',
             '2026-07-14', '2026-08-14', '2026-09-14', '2026-10-14', '2026-11-16', '2026-12-14', '2027-01-14',
             '2027-02-15', '2027-03-15', '2027-04-14', '2027-05-14', '2027-06-14', '2027-07-14', '2027-08-16',
             '2027-09-14', '2027-10-14', '2027-11-15', '2027-12-14', '2028-01-14', '2028-02-14', '2028-03-14',
             '2028-04-14', '2028-05-15', '2028-06-14', '2028-07-14', '2028-08-14', '2028-09-14', '2028-10-16',
             '2028-11-14', '2028-12-14', '2029-01-15', '2029-02-14', '2029-03-14', '2029-04-16', '2029-05-14',
             '2029-06-14', '2029-07-16', '2029-08-14', '2029-09-14', '2029-10-15', '2029-11-14', '2029-12-14',
             '2030-01-14', '2030-02-14', '2030-03-14', '2030-04-15', '2030-05-14', '2030-06-14', '2030-07-15',
             '2030-08-14', '2030-09-16', '2030-10-14', '2030-11-14', '2030-12-16', '2031-01-14', '2031-02-14',
             '2031-03-14', '2031-04-14']
    ql_dates = [utils.to_quantlib_date(utils.string_to_date(d)) for d in dates]
    cdor_forward_curve = ql.ForwardCurve(ql_dates, rates, ql.Actual365Fixed(), ql.Canada(), ql.BackwardFlat())
    ql_forecast_curve = ql.RelinkableYieldTermStructureHandle()
    ql_forecast_curve.linkTo(cdor_forward_curve)

    # 2. create Cdor index using the CDOR forward curve
    ql_index = ql.Cdor(ql.Period(3, ql.Months), ql_forecast_curve)
    ql_index.addFixing(ql.Date(15, ql.March, 2021), 0.004375)

    # 3. instantiate FRN schedule and bond
    interest_accrual_date = calendar.advance(quantlib_business_date, ql.Period(-1, ql.Years))
    schedule = ql.Schedule(interest_accrual_date,
                           quantlib_maturity_date,
                           tenor,
                           calendar,
                           business_day_convention,
                           business_day_convention,  # termination convention
                           date_generation,
                           end_of_month)

    floating_bond = ql.FloatingRateBond(settlement_days,
                                        face_value,
                                        schedule,
                                        ql_index,
                                        ql.Actual365Fixed(),
                                        business_day_convention,
                                        ql_index.fixingDays(),
                                        [],  # Gearings
                                        [initial_margin],  # Spreads
                                        [],  # Caps
                                        [],  # Floors
                                        False,  # Fixing in arrears
                                        face_value,
                                        interest_accrual_date)

    # 4. Using the CHT bootstrap curve I have built (detail code is skipped), I evaluate the CHT floaters
floating_bond.setPricingEngine(ql.DiscountingBondEngine(ql.YieldTermStructureHandle(cht_curve)))
    clean_price = floating_bond.cleanPrice()
$\endgroup$
4
  • $\begingroup$ You project some cash flows. You discount using a credit-risk-free curve. You get price that's higher than the price observed for a credi-risky instrument with these cash flows? $\endgroup$ Apr 14, 2021 at 22:05
  • $\begingroup$ The discount curve I am using is the CHT curve built with all the normal CHT securities excluding the CHT floaters; the market price I compared to is from the bank's daily quote. $\endgroup$
    – Bill Qiu
    Apr 15, 2021 at 15:01
  • $\begingroup$ very well then, are you able to reproduce the price of CHT non-floaters when you discount their cash flows with yur CHT curve? Also, sorry, did you remember to subtract the accrued to go from dirty to clean price? $\endgroup$ Apr 15, 2021 at 15:08
  • $\begingroup$ normal CHT securities are all priced correctly. Because you see I used quantlib FloatingRateBond's cleanPrice() method, so I thought it should be taken care by the quantlib. $\endgroup$
    – Bill Qiu
    Apr 15, 2021 at 15:12

1 Answer 1

0
$\begingroup$

Bill, you are?

  1. Building cht discount curve from the fixed rate cht bonds,
  2. Creating a cht floating rate bond using the contractual spread over cdor
  3. Pricing the bonds? This will only approximately price them, but shouldn't be totally awful.

If you also add that index spread to cdor I'm not sure what you are doing... Mike. Yes, datavault mike.

$\endgroup$
8
  • $\begingroup$ add the spread to cdor, just like we add a initial margin on top of the CDOR. For instance, for the security: 13509PFK4, May 26, 2016 the coupon is: 3m CDOR +0.175%, that 175 bps is the spread I refer to $\endgroup$
    – Bill Qiu
    Apr 15, 2021 at 3:42
  • $\begingroup$ And if you see the constructor of the FloatingRateBond, there is one parameter in the middle called spread: [initial_margin], the initial margin for the case above is 175bps $\endgroup$
    – Bill Qiu
    Apr 15, 2021 at 3:46
  • $\begingroup$ Hi Mike Clayton, is that you? Wow, thanks so much for your answer. I know you are coming back soon, right? Man, we probably need some of your help, I know you are just doing some analytic work for us (I know you get bored on other simple stuff easily), but we kind of having some trouble to get FRN pricing work correctly. $\endgroup$
    – Bill Qiu
    Apr 15, 2021 at 3:50
  • $\begingroup$ Ah, OK, I think I misinterpreted one of the links you had. How different are your prices? I believe the floaters are less liquid anyway, so I would expect this to result in a somewhat lower price... $\endgroup$
    – Mike
    Apr 15, 2021 at 12:25
  • $\begingroup$ Oops... "higher" $\endgroup$
    – Mike
    Apr 15, 2021 at 12:34

Your Answer

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

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