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 |
Hello, the code doesn't seem to have anything wrong. I don't have experience in fitting bond curves, though, so I can't say if you'd need to tweak the fit or the initial guesses to get better results. The curves that show big differences in yield show it in price, too, as far as I can see... Luigi On Wed, Jan 6, 2016 at 5:24 PM gsmith <[hidden email]> wrote: Hello all! -- <http://leanpub.com/implementingquantlib> ------------------------------------------------------------------------------ Site24x7 APM Insight: Get Deep Visibility into Application Performance APM + Mobile APM + RUM: Monitor 3 App instances at just $35/Month Monitor end-to-end web transactions and take corrective actions now Troubleshoot faster and improve end-user experience. Signup Now! http://pubads.g.doubleclick.net/gampad/clk?id=267308311&iu=/4140 _______________________________________________ QuantLib-users mailing list [hidden email] https://lists.sourceforge.net/lists/listinfo/quantlib-users |
Free forum by Nabble | Edit this page |