Login  Register

FittedBondCurve Example using Real UST OTR Bonds

Posted by gsmith on Jan 06, 2016; 2:13am
URL: http://quantlib.414.s1.nabble.com/FittedBondCurve-Example-using-Real-UST-OTR-Bonds-tp17234.html

Hello all!

Just started with QuantLib recently and am particularly interested in building yield curves and pricing bonds from the created curves.

As a start, I modified the FittedBondCurve example so that the inputs match the 6 on the run US Treasury bonds (2, 3, 5, 7, 10 and 30 year).  This example creates 6 yield curves, 5 of them using the FittedBondDiscountCurve and 1 using PiecewiseYieldCurve.  

After creating the curves, I set the curve as the bondPricingEngine and then priced the input bond from that curve.  These results deviate significantly more from my inputs than I expected, so I suspect I may have done something wrong.  

In yield space, the Nelson-Siegel curve has theoretical yields between 10-40 bp different than my input bonds.  Is this expected?  If not, could someone please take a look at my code and see what mistake I may have made?

Thank you!


int main(int, char* []) {

    try {

        boost::timer timer;

        const Size numberOfBonds = 6;
 
        Real cleanPrice[] = {99.884973, 99.785254, 99.868202, 100.084135, 99.677148, 99.376953};


        std::vector< boost::shared_ptr<SimpleQuote> > quote;
        for (Size i=0; i<numberOfBonds; i++) {
            boost::shared_ptr<SimpleQuote> cp(new SimpleQuote(cleanPrice[i]));
            quote.push_back(cp);
        }

        RelinkableHandle<Quote> quoteHandle[numberOfBonds];
        for (Size i=0; i<numberOfBonds; i++) {
            quoteHandle[i].linkTo(quote[i]);
        }
                             
        Real coupons[] = { 0.01, 0.0125, 0.0175, 0.02125, 0.0225, 0.03};
        Date maturities[] = {Date(31,December,2017), Date(15,December,2018), Date(31,December,2020), Date(31,December,2022), Date(15,November,2025), Date(15,November,2045)};
       
        Frequency frequency = Semiannual;
        DayCounter dc = ActualActual(ActualActual::Bond);
        BusinessDayConvention accrualConvention = Following;
        BusinessDayConvention convention = Following;
        Real redemptionAmount = 100.0;
        Real faceAmount = 100.0;

        Calendar calendar = UnitedStates(UnitedStates::GovernmentBond);
        Date evaluationDay = Date(31,December,2015);        
        Settings::instance().evaluationDate() = evaluationDay;
       
        Natural bondSettlementDays = 1;
        Natural curveSettlementDays = 1;

        Date bondSettlementDate = calendar.advance(evaluationDay, bondSettlementDays*Days);

        cout << endl;
        cout << "EvaluationDay: " << evaluationDay << endl;
        cout << "Bonds' settlement date: " << bondSettlementDate << endl;
        cout << "Calculating fit for 6 bonds....." << endl << endl;

        std::vector<boost::shared_ptr<BondHelper> > instrumentsA;
        std::vector<boost::shared_ptr<RateHelper> > instrumentsB;

        for (Size j=0; j<LENGTH(coupons); j++) {
           
            Schedule schedule(bondSettlementDate, maturities[j], Period(frequency),
                              calendar, accrualConvention, accrualConvention,
                              DateGeneration::Backward, true);
           
            boost::shared_ptr<BondHelper> helperA(
                     new FixedRateBondHelper(quoteHandle[j],
                                             bondSettlementDays,
                                             faceAmount,
                                             schedule,
                                             std::vector<Rate>(1,coupons[j]),
                                             dc,
                                             convention,
                                             redemptionAmount));

            boost::shared_ptr<RateHelper> helperB(
                     new FixedRateBondHelper(quoteHandle[j],
                                             bondSettlementDays,
                                             faceAmount,
                                             schedule,
                                             std::vector<Rate>(1, coupons[j]),
                                             dc,
                                             convention,
                                             redemptionAmount));

            instrumentsA.push_back(helperA);
            instrumentsB.push_back(helperB);
        }
       
        bool constrainAtZero = true;
        Real tolerance = 1.0e-10;
        Size max = 5000;

        boost::shared_ptr<YieldTermStructure> ts0 (
              new PiecewiseYieldCurve<Discount,LogLinear>(curveSettlementDays,
                                                          calendar,
                                                          instrumentsB,
                                                          dc));

        ExponentialSplinesFitting exponentialSplines(constrainAtZero);

        boost::shared_ptr<FittedBondDiscountCurve> ts1 (
                  new FittedBondDiscountCurve(curveSettlementDays,
                                              calendar,
                                              instrumentsA,
                                              dc,
                                              exponentialSplines,
                                              tolerance,
                                              max));

        printOutput("(a) exponential splines", ts1);


        SimplePolynomialFitting simplePolynomial(3, constrainAtZero);

        boost::shared_ptr<FittedBondDiscountCurve> ts2 (
                    new FittedBondDiscountCurve(curveSettlementDays,
                                                calendar,
                                                instrumentsA,
                                                dc,
                                                simplePolynomial,
                                                tolerance,
                                                max));

        printOutput("(b) simple polynomial", ts2);


        NelsonSiegelFitting nelsonSiegel;

        boost::shared_ptr<FittedBondDiscountCurve> ts3 (
                        new FittedBondDiscountCurve(curveSettlementDays,
                                                    calendar,
                                                    instrumentsA,
                                                    dc,
                                                    nelsonSiegel,
                                                    tolerance,
                                                    max));

        printOutput("(c) Nelson-Siegel", ts3);


        // a cubic bspline curve with 11 knot points, implies
        // n=6 (constrained problem) basis functions

        Time knots[] =  { -30.0, -20.0,  0.0,  5.0, 10.0, 15.0,
                           20.0,  25.0, 30.0, 40.0, 50.0 };

        std::vector<Time> knotVector;
        for (Size i=0; i< LENGTH(knots); i++) {
            knotVector.push_back(knots[i]);
        }

        CubicBSplinesFitting cubicBSplines(knotVector, constrainAtZero);

        boost::shared_ptr<FittedBondDiscountCurve> ts4 (
                       new FittedBondDiscountCurve(curveSettlementDays,
                                                   calendar,
                                                   instrumentsA,
                                                   dc,
                                                   cubicBSplines,
                                                   tolerance,
                                                   max));

        printOutput("(d) cubic B-splines", ts4);

       
        SvenssonFitting svensson;

        boost::shared_ptr<FittedBondDiscountCurve> ts5 (
                        new FittedBondDiscountCurve(curveSettlementDays,
                                                    calendar,
                                                    instrumentsA,
                                                    dc,
                                                    svensson,
                                                    tolerance,
                                                    max));
       
        printOutput("(e) Svensson", ts5);

       
        cout << "Output Yield and basis point difference for each curve. In this case, "
             << endl
             //<< "par rates should equal coupons for these par bonds."
             << endl
             << endl;

        cout << setw(6) << "tenor" << " | "
             << setw(6) << "coupon" << " | "
             << setw(6) << "Yield" << " | "
             << setw(6) << "(btstrp)" << "|"
             << setw(6) << "(exp spl)" << "|"
             << setw(6) << "(s poly)" << "|"
             << setw(6) << "(Nel-Sie)" << "|"
             << setw(6) << "(CubBSpl)" << "|"
             << setw(6) << "(Svensson)" << endl;

        for (Size i=0; i<instrumentsA.size(); i++) {
           
            std::vector<boost::shared_ptr<CashFlow> > cfs =
                instrumentsA[i]->bond()->cashflows();

            Size cfSize = instrumentsA[i]->bond()->cashflows().size();
            std::vector<Date> keyDates;
            keyDates.push_back(bondSettlementDate);

            for (Size j=0; j<cfSize-1; j++) {
                if (!cfs[j]->hasOccurred(bondSettlementDate, false)) {
                    Date myDate =  cfs[j]->date();
                    keyDates.push_back(myDate);
                }
            }

            Real tenor = dc.yearFraction(evaluationDay, cfs[cfSize-1]->date());            
         
            boost::shared_ptr< Bond > fixedRateBond = instrumentsA[i]->bond();
           
            boost::shared_ptr<PricingEngine> ts0BondEngine(new DiscountingBondEngine(Handle<YieldTermStructure>(ts0)));
            boost::shared_ptr<PricingEngine> ts1BondEngine(new DiscountingBondEngine(Handle<YieldTermStructure>(ts1)));
            boost::shared_ptr<PricingEngine> ts2BondEngine(new DiscountingBondEngine(Handle<YieldTermStructure>(ts2)));
            boost::shared_ptr<PricingEngine> ts3BondEngine(new DiscountingBondEngine(Handle<YieldTermStructure>(ts3)));
            boost::shared_ptr<PricingEngine> ts4BondEngine(new DiscountingBondEngine(Handle<YieldTermStructure>(ts4)));
            boost::shared_ptr<PricingEngine> ts5BondEngine(new DiscountingBondEngine(Handle<YieldTermStructure>(ts5)));
           
            Real inputYield = fixedRateBond->yield(cleanPrice[i], dc, Simple, frequency, bondSettlementDate);
           
            fixedRateBond->setPricingEngine(ts0BondEngine);
            Real ts0Price = fixedRateBond->cleanPrice();
            Real yieldts0 = fixedRateBond->yield(ts0Price, dc, Simple, frequency, bondSettlementDate);
           
            fixedRateBond->setPricingEngine(ts1BondEngine);
            Real ts1Price = fixedRateBond->cleanPrice();
            Real yieldts1 = fixedRateBond->yield(ts1Price, dc, Simple, frequency, bondSettlementDate);
           
            fixedRateBond->setPricingEngine(ts2BondEngine);
            Real ts2Price = fixedRateBond->cleanPrice();
            Real yieldts2 = fixedRateBond->yield(ts2Price, dc, Simple, frequency, bondSettlementDate);
           
            fixedRateBond->setPricingEngine(ts3BondEngine);
            Real ts3Price = fixedRateBond->cleanPrice();
            Real yieldts3 = fixedRateBond->yield(ts3Price, dc, Simple, frequency, bondSettlementDate);
           
            fixedRateBond->setPricingEngine(ts4BondEngine);            
            Real ts4Price = fixedRateBond->cleanPrice();
            Real yieldts4 = fixedRateBond->yield(ts4Price, dc, Simple, frequency, bondSettlementDate);
           
            fixedRateBond->setPricingEngine(ts5BondEngine);            
            Real ts5Price = fixedRateBond->cleanPrice();
            Real yieldts5 = fixedRateBond->yield(ts5Price, dc, Simple, frequency, bondSettlementDate);

            cout << setw(6) << fixed << setprecision(3) << tenor << " | "
                 << setw(6) << fixed << setprecision(3)
                 << 100.*coupons[i] << " | "                
                 << setw(6) << fixed << setprecision(6)
                 << inputYield << " | "
                 // piecewise bootstrap                
                 << setw(6) << fixed << setprecision(3)
                 << (yieldts0 - inputYield)*10000 << " | "
                 // exponential splines
                 << setw(6) << fixed << setprecision(3)
                 << (yieldts1 - inputYield)*10000 << " | "
                 // simple polynomial
                 << setw(6) << fixed << setprecision(3)
                 << (yieldts2 - inputYield)*10000 << " | "
                 // Nelson-Siegel
                 << setw(6) << fixed << setprecision(3)
                 << (yieldts3 - inputYield)*10000<< " | "
                 // cubic bsplines
                 << setw(6) << fixed << setprecision(3)
                 << (yieldts4 - inputYield)*10000 << " | "
                 // Svensson                
                 << setw(6) << fixed << setprecision(3)
                 << (yieldts5 - inputYield)*10000 << endl;            
        }
                       
        return(0);

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





:::Output:::

EvaluationDay: December 31st, 2015
Bonds' settlement date: January 4th, 2016
Calculating fit for 6 bonds.....

(a) exponential splines
reference date : January 4th, 2016
number of iterations : 7291

(b) simple polynomial
reference date : January 4th, 2016
number of iterations : 268

(c) Nelson-Siegel
reference date : January 4th, 2016
number of iterations : 3013

(d) cubic B-splines
reference date : January 4th, 2016
number of iterations : 590

(e) Svensson
reference date : January 4th, 2016
number of iterations : 3220

Output Yield and basis point difference from Price for each curve. In this case,


 tenor | coupon |  Yield | (btstrp)|(exp spl)|(s poly)|(Nel-Sie)|(CubBSpl)|(Svensson)
 2.000 |  1.000 | 0.010240 |  0.000 | 86.733 | 12.163 | 40.028 | -1.579 |  0.827
 3.000 |  1.250 | 0.013246 |  0.000 | 55.112 | -3.508 | 14.530 | -2.265 | -2.218
 5.000 |  1.750 | 0.017528 |  0.000 | 18.551 | -9.105 | -10.589 |  6.730 |  3.471
 7.000 |  2.125 | 0.020898 |  0.000 | -11.513 | -11.285 | -29.611 | -3.446 | -2.930
 9.917 |  2.250 | 0.022867 | -0.000 | -25.030 | 12.185 | -27.719 |  0.312 |  0.866
29.917 |  3.000 | 0.030319 |  0.000 | -49.796 | -0.337 | 18.809 |  0.000 | -0.015