1
$\begingroup$

In Canada, the CDOR index will be phased out in June 2024, and the replacement is the CORRA (Canadian Overnight Repo Rate Average), which is an OvernightIndex used for evaluating the FRN in Canada. Here is an example bond, the CUSIP is 13607HMS8, maturity date is 2025-03-04, issue date is 2021-02-25, and first settlement date is 2021-03-04, below is my testing code to get the clean price and accrual interest for the evaluation date 2022-04-29, and settlement date is 2022-05-03. I compare the results with the Bloomberg, the clean price is pretty close, but the accrual interest has 2 cents difference, which caught my attention at once, because I feel the accrual interest should be the exactly the same, since the coupon on the last reset date should be the same. Below is my testing code, during debug I see the current payment period is from 2022-03-04 to 2022-06-06, and it used the CORRA rate (0.43%) on 2022-03-04 as the fixing to calculate the rate = gear * fixing + spread, the spread is 0.46%, so the rate = 1 * 0.43% + 0.46% = 0.89%, but some of my friends told me for the overnightIndex should have some kind of compounding, using the single day fixing on 2022-03-04 to calculate the rate is not correct. I thought the OvernightIndex will automatically handle the daily compounding, but it did not, so can someone please help?

#include <ql/qldefines.hpp>
#if !defined(BOOST_ALL_NO_LIB) && defined(BOOST_MSVC)
#  include <ql/auto_link.hpp>
#endif
#include <ql/instruments/bonds/zerocouponbond.hpp>
#include <ql/instruments/bonds/floatingratebond.hpp>
#include <ql/pricingengines/bond/discountingbondengine.hpp>
#include <ql/cashflows/couponpricer.hpp>
#include <ql/termstructures/yield/piecewiseyieldcurve.hpp>
#include <ql/termstructures/yield/bondhelpers.hpp>
#include <ql/termstructures/yield/overnightindexfutureratehelper.hpp>
#include <ql/termstructures/yield/zerocurve.hpp>
#include <ql/termstructures/volatility/optionlet/constantoptionletvol.hpp>
#include <ql/indexes/ibor/euribor.hpp>
#include <ql/indexes/ibor/usdlibor.hpp>
#include <ql/time/calendars/target.hpp>
#include <ql/time/calendars/unitedstates.hpp>
#include <ql/time/daycounters/actualactual.hpp>
#include <ql/time/daycounters/actual360.hpp>
#include <ql/time/daycounters/thirty360.hpp>
#include <ql/time/calendars/canada.hpp>
#include <ql/math/interpolations/linearinterpolation.hpp>

#include <iostream>
#include <iomanip>

using namespace QuantLib;


int main(int, char* []) {

try {

    std::cout << std::endl;

    /*********************
     ***  MARKET DATA  ***
     *********************/

    Calendar calendar = Canada();
    DayCounter dayCounter = Actual365Fixed();
    Currency currency = CADCurrency();

    Date settlementDate(3, May, 2022);
    // must be a business day
    settlementDate = calendar.adjust(settlementDate);

    Integer fixingDays = 2;
    Natural settlementDays = 2;

    Date todaysDate = calendar.advance(settlementDate, -fixingDays, Days);
    // nothing to do with Date::todaysDate
    Settings::instance().evaluationDate() = todaysDate;

    std::cout << "Today: " << todaysDate.weekday()
        << ", " << todaysDate << std::endl;

    std::cout << "Settlement date: " << settlementDate.weekday()
        << ", " << settlementDate << std::endl;

    //
    // Building of the CORRA forecasting curve
    //
    std::vector<ext::shared_ptr<RateHelper>> corraFutureHelpers;
    std::map<Date, Real> futurePrices;
    futurePrices[Date(15, June, 2022)] = 99.12;
    futurePrices[Date(21, September, 2022)] = 98.08;
    futurePrices[Date(21, December, 2022)] = 97.3;
    futurePrices[Date(15, March, 2023)] = 96.99;
    futurePrices[Date(21, June, 2023)] = 96.96;
    futurePrices[Date(20, September, 2023)] = 96.93;
    futurePrices[Date(20, December, 2023)] = 96.90;
    futurePrices[Date(20, March, 2024)] = 96.87;
    futurePrices[Date(19, June, 2024)] = 96.83;
    futurePrices[Date(18, September, 2024)] = 96.80;
    futurePrices[Date(18, December, 2024)] = 96.78;
    futurePrices[Date(19, March, 2025)] = 96.76;

    const ext::shared_ptr<OvernightIndex> corraIndex(new OvernightIndex("CORRA", Natural(fixingDays), currency, calendar, dayCounter));

    std::map<Date, Real>::iterator it = futurePrices.begin();
    // Iterate over the map using Iterator till end.
    while (it != futurePrices.end()) {
        // Accessing KEY from element pointed by it.
        Date date = it->first;
        Real price = it->second;
        ext::shared_ptr<Quote> quoteHandle(new SimpleQuote(price));
        ext::shared_ptr<RateHelper> futureRateHelper(new OvernightIndexFutureRateHelper(
            Handle<Quote>(quoteHandle),
            todaysDate,
            date,
            corraIndex));
        corraFutureHelpers.push_back(futureRateHelper);
        it++;
    }

    ext::shared_ptr<YieldTermStructure> corraForecastTermStructure(new PiecewiseYieldCurve<Discount, LogLinear>(settlementDate, corraFutureHelpers, dayCounter));

    //
    // Loading the CIBC AA discount curve
    //
    std::vector<Rate> rates;
    std::vector<Date> dates;
    std::map<Date, Rate> curvePoints;
    curvePoints[Date(29, April, 2022)] = 0.01175992;
    curvePoints[Date(29, May, 2022)] = 0.01279924;
    curvePoints[Date(29, June, 2022)] = 0.01479078;
    curvePoints[Date(29, July, 2022)] = 0.016785321;
    curvePoints[Date(29, August, 2022)] = 0.018477552;
    curvePoints[Date(29, September, 2022)] = 0.020169783;
    curvePoints[Date(29, October, 2022)] = 0.021807427;
    curvePoints[Date(29, November, 2022)] = 0.023499658;
    curvePoints[Date(29, December, 2022)] = 0.025165168;
    curvePoints[Date(29, January, 2023)] = 0.026893393;
    curvePoints[Date(28, February, 2023)] = 0.028565869;
    curvePoints[Date(29, March, 2023)] = 0.030182596;
    curvePoints[Date(29, April, 2023)] = 0.031910822;
    curvePoints[Date(29, July, 2023)] = 0.032000324;
    curvePoints[Date(29, October, 2023)] = 0.034663883;
    curvePoints[Date(29, January, 2024)] = 0.036994209;
    curvePoints[Date(29, April, 2024)] = 0.037462802;
    curvePoints[Date(29, July, 2024)] = 0.037931396;
    curvePoints[Date(29, October, 2024)] = 0.038628441;
    curvePoints[Date(29, January, 2025)] = 0.039433535;
    curvePoints[Date(29, April, 2025)] = 0.038443031;
    curvePoints[Date(29, July, 2025)] = 0.036722382;
    curvePoints[Date(29, October, 2025)] = 0.038809456;
    curvePoints[Date(29, January, 2026)] = 0.04069903;
    curvePoints[Date(29, April, 2026)] = 0.040963241;
    curvePoints[Date(29, October, 2026)] = 0.041631359;
    curvePoints[Date(29, April, 2027)] = 0.041631359;

    std::map<Date, Rate>::iterator itPoints = curvePoints.begin();
    // Iterate over the map using Iterator till end.
    while (itPoints != curvePoints.end()) {
        // Accessing KEY from element pointed by it.
        Date date = itPoints->first;
        Rate point = itPoints->second;
        dates.push_back(date);
        rates.push_back(point);
        itPoints++;
    }

    ext::shared_ptr<YieldTermStructure> cibcDiscountTermStructure(new InterpolatedZeroCurve<Linear>(dates, rates, dayCounter, calendar, Linear()));

    // Term structures that will be used for pricing:
    // the one used for discounting cash flows
    RelinkableHandle<YieldTermStructure> discountingTermStructure;
    // the one used for forward rate forecasting
    RelinkableHandle<YieldTermStructure> forecastingTermStructure;

    /*********************
    * BONDS TO BE PRICED *
    **********************/

    // Common data
    Real faceAmount = 100;

    // Pricing engine
    ext::shared_ptr<PricingEngine> bondEngine(new DiscountingBondEngine(discountingTermStructure));

    // Floating rate bond (CORRA index)
    // Populate all the past CORRA rate fixing which I downloaded from the Bank Of Canada web site, 
    const ext::shared_ptr<OvernightIndex> corraIndex1(new OvernightIndex("CORRA", Natural(fixingDays), currency, calendar, dayCounter, forecastingTermStructure));
    corraIndex1->addFixing(Date(4, March, 2021), 0.0017);
    corraIndex1->addFixing(Date(4, June, 2021), 0.0018);
    corraIndex1->addFixing(Date(7, September, 2021), 0.0018);
    corraIndex1->addFixing(Date(1, December, 2021), 0.0013);
    corraIndex1->addFixing(Date(2, December, 2021), 0.0015);
    corraIndex1->addFixing(Date(3, December, 2021), 0.0015);
    corraIndex1->addFixing(Date(6, December, 2021), 0.0015);
    corraIndex1->addFixing(Date(7, December, 2021), 0.0012);
    corraIndex1->addFixing(Date(8, December, 2021), 0.0008);
    corraIndex1->addFixing(Date(9, December, 2021), 0.001);
    corraIndex1->addFixing(Date(10, December, 2021), 0.0005);
    corraIndex1->addFixing(Date(13, December, 2021), 0.0005);
    corraIndex1->addFixing(Date(14, December, 2021), 0.0007);
    corraIndex1->addFixing(Date(15, December, 2021), 0.0007);
    corraIndex1->addFixing(Date(16, December, 2021), 0.0009);
    corraIndex1->addFixing(Date(17, December, 2021), 0.0008);
    corraIndex1->addFixing(Date(20, December, 2021), 0.0008);
    corraIndex1->addFixing(Date(21, December, 2021), 0.0008);
    corraIndex1->addFixing(Date(22, December, 2021), 0.0008);
    corraIndex1->addFixing(Date(23, December, 2021), 0.0014);
    corraIndex1->addFixing(Date(24, December, 2021), 0.0013);
    corraIndex1->addFixing(Date(29, December, 2021), 0.0012);
    corraIndex1->addFixing(Date(30, December, 2021), 0.0012);
    corraIndex1->addFixing(Date(31, December, 2021), 0.001);
    corraIndex1->addFixing(Date(4, January, 2022), 0.0012);
    corraIndex1->addFixing(Date(5, January, 2022), 0.0012);
    corraIndex1->addFixing(Date(6, January, 2022), 0.0013);
    corraIndex1->addFixing(Date(7, January, 2022), 0.0015);
    corraIndex1->addFixing(Date(10, January, 2022), 0.0017);
    corraIndex1->addFixing(Date(11, January, 2022), 0.0018);
    corraIndex1->addFixing(Date(12, January, 2022), 0.0018);
    corraIndex1->addFixing(Date(13, January, 2022), 0.0018);
    corraIndex1->addFixing(Date(14, January, 2022), 0.0018);
    corraIndex1->addFixing(Date(17, January, 2022), 0.0018);
    corraIndex1->addFixing(Date(18, January, 2022), 0.0018);
    corraIndex1->addFixing(Date(19, January, 2022), 0.0018);
    corraIndex1->addFixing(Date(20, January, 2022), 0.0018);
    corraIndex1->addFixing(Date(21, January, 2022), 0.0016);
    corraIndex1->addFixing(Date(24, January, 2022), 0.0018);
    corraIndex1->addFixing(Date(25, January, 2022), 0.0017);
    corraIndex1->addFixing(Date(26, January, 2022), 0.0016);
    corraIndex1->addFixing(Date(27, January, 2022), 0.0016);
    corraIndex1->addFixing(Date(28, January, 2022), 0.0015);
    corraIndex1->addFixing(Date(31, January, 2022), 0.0013);

    corraIndex1->addFixing(Date(1, February, 2022), 0.0013);
    corraIndex1->addFixing(Date(2, February, 2022), 0.0013);
    corraIndex1->addFixing(Date(3, February, 2022), 0.0013);
    corraIndex1->addFixing(Date(4, February, 2022), 0.0013);
    corraIndex1->addFixing(Date(7, February, 2022), 0.0018);
    corraIndex1->addFixing(Date(8, February, 2022), 0.0018);
    corraIndex1->addFixing(Date(9, February, 2022), 0.0018);
    corraIndex1->addFixing(Date(10, February, 2022), 0.0018);
    corraIndex1->addFixing(Date(11, February, 2022), 0.0018);
    corraIndex1->addFixing(Date(14, February, 2022), 0.0018);
    corraIndex1->addFixing(Date(15, February, 2022), 0.0018);
    corraIndex1->addFixing(Date(16, February, 2022), 0.0018);
    corraIndex1->addFixing(Date(17, February, 2022), 0.0018);
    corraIndex1->addFixing(Date(18, February, 2022), 0.0019);
    corraIndex1->addFixing(Date(22, February, 2022), 0.0019);
    corraIndex1->addFixing(Date(23, February, 2022), 0.0019);
    corraIndex1->addFixing(Date(24, February, 2022), 0.0019);
    corraIndex1->addFixing(Date(25, February, 2022), 0.0019);
    corraIndex1->addFixing(Date(28, February, 2022), 0.0019);

    corraIndex1->addFixing(Date(1, March, 2022), 0.0018);
    corraIndex1->addFixing(Date(2, March, 2022), 0.0017);
    corraIndex1->addFixing(Date(3, March, 2022), 0.0043);
    corraIndex1->addFixing(Date(4, March, 2022), 0.0043);
    corraIndex1->addFixing(Date(7, March, 2022), 0.0042);
    corraIndex1->addFixing(Date(8, March, 2022), 0.0042);
    corraIndex1->addFixing(Date(9, March, 2022), 0.0042);
    corraIndex1->addFixing(Date(10, March, 2022), 0.0042);
    corraIndex1->addFixing(Date(11, March, 2022), 0.0043);
    corraIndex1->addFixing(Date(14, March, 2022), 0.0043);
    corraIndex1->addFixing(Date(15, March, 2022), 0.0043);
    corraIndex1->addFixing(Date(16, March, 2022), 0.0043);
    corraIndex1->addFixing(Date(17, March, 2022), 0.0043);
    corraIndex1->addFixing(Date(18, March, 2022), 0.0043);
    corraIndex1->addFixing(Date(21, March, 2022), 0.0043);
    corraIndex1->addFixing(Date(22, March, 2022), 0.0043);
    corraIndex1->addFixing(Date(23, March, 2022), 0.0044);
    corraIndex1->addFixing(Date(24, March, 2022), 0.0045);
    corraIndex1->addFixing(Date(25, March, 2022), 0.0045);
    corraIndex1->addFixing(Date(28, March, 2022), 0.0045);
    corraIndex1->addFixing(Date(29, March, 2022), 0.0045);
    corraIndex1->addFixing(Date(30, March, 2022), 0.0045);
    corraIndex1->addFixing(Date(31, March, 2022), 0.0042);
    corraIndex1->addFixing(Date(1, April, 2022), 0.0042);
    corraIndex1->addFixing(Date(4, April, 2022), 0.0044);
    corraIndex1->addFixing(Date(5, April, 2022), 0.0045);
    corraIndex1->addFixing(Date(6, April, 2022), 0.0045);
    corraIndex1->addFixing(Date(7, April, 2022), 0.0045);
    corraIndex1->addFixing(Date(8, April, 2022), 0.0045);
    corraIndex1->addFixing(Date(11, April, 2022), 0.0045);
    corraIndex1->addFixing(Date(12, April, 2022), 0.0045);
    corraIndex1->addFixing(Date(13, April, 2022), 0.0045);
    corraIndex1->addFixing(Date(14, April, 2022), 0.0095);
    corraIndex1->addFixing(Date(18, April, 2022), 0.0093);
    corraIndex1->addFixing(Date(19, April, 2022), 0.0093);
    corraIndex1->addFixing(Date(29, April, 2022), 0.0092);
    corraIndex1->addFixing(Date(2, May, 2022), 0.0093);
    corraIndex1->addFixing(Date(3, May, 2022), 0.0093);
    corraIndex1->addFixing(Date(4, May, 2022), 0.0096);
    corraIndex1->addFixing(Date(5, May, 2022), 0.0095);
    corraIndex1->addFixing(Date(6, May, 2022), 0.0096);
    corraIndex1->addFixing(Date(9, May, 2022), 0.0095);
    corraIndex1->addFixing(Date(10, May, 2022), 0.0096);
    corraIndex1->addFixing(Date(11, May, 2022), 0.0096);
    corraIndex1->addFixing(Date(12, May, 2022), 0.0096);
    corraIndex1->addFixing(Date(13, May, 2022), 0.0096);
    corraIndex1->addFixing(Date(16, May, 2022), 0.0096);
    corraIndex1->addFixing(Date(17, May, 2022), 0.0096);
    corraIndex1->addFixing(Date(18, May, 2022), 0.0096);

    Schedule floatingBondSchedule(
        Date(4, March, 2021),
        Date(4, March, 2025),
        Period(Quarterly),
        calendar,
        ModifiedFollowing, // need to play around this setting
        ModifiedFollowing,
        DateGeneration::Backward,
        false);

    DayCounter floater_day_count = ActualActual(ActualActual::ISMA);
    std::vector<Rate> spreads;
    spreads.push_back(0.0046);
    FloatingRateBond floatingRateBond(
        settlementDays,
        faceAmount,
        floatingBondSchedule,
        corraIndex1,
        floater_day_count,
        ModifiedFollowing,
        Natural(0),
        // Gearings
        std::vector<Rate>(),
        // Spreads
        spreads,
        // Caps
        std::vector<Rate>(),
        // Floors
        std::vector<Rate>(),
        // Fixing in arrears, need to play around this value
        false,
        Real(100.0),
        Date(25, February, 2021));

    floatingRateBond.setPricingEngine(bondEngine);

    // Yield curve bootstrapping
    forecastingTermStructure.linkTo(corraForecastTermStructure);
    discountingTermStructure.linkTo(cibcDiscountTermStructure);

    /***************
     * BOND PRICING *
     ****************/

    std::cout << std::endl;

    Leg leg = floatingRateBond.cashflows();
    Leg::const_iterator i;
    for (i = leg.begin(); i < leg.end(); ++i) {
        std::cout << "cashflow date: "
            << (*i)->date() << " cashflow amount: " << (*i)->amount()
            << std::endl;
    }

    std::cout << "Previous cashflow date: "
        << floatingRateBond.previousCashFlowDate()
        << std::endl;

    std::cout << "Next cashflow date: "
        << floatingRateBond.nextCashFlowDate()
        << std::endl;

    std::cout << "Accrued coupon: "
        << floatingRateBond.accruedAmount()
        << std::endl;

    std::cout << "Previous coupon: "
        << io::rate(floatingRateBond.previousCouponRate())
        << std::endl;

    std::cout << "Next coupon: "
        << io::rate(floatingRateBond.nextCouponRate())
        << std::endl;

    std::cout << "Net present value: "
        << floatingRateBond.NPV()
        << std::endl;

    std::cout << "Clean price: "
        << floatingRateBond.cleanPrice()
        << std::endl;

    std::cout << "Dirty price: "
        << floatingRateBond.dirtyPrice()
        << std::endl;

    std::cout << "Yield: "
        << floatingRateBond.yield(dayCounter, Compounded, Semiannual)
        << std::endl;

    std::cout << std::endl;

    return 0;

}
catch (std::exception& e) {
    std::cerr << e.what() << std::endl;
    return 1;
}
catch (...) {
    std::cerr << "unknown error" << std::endl;
    return 1;
}

}

$\endgroup$

0

Your Answer

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

Browse other questions tagged or ask your own question.