/*! Bonds */ /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* This example shows how to set up a term structure and then price some simple bonds. The last part is dedicated to peripherical computations such as "Yield to Price" or "Price to Yield" */ // the only header you need to use QuantLib #include <ql/quantlib.hpp> #include <boost/timer.hpp> #include <iostream> #include <iomanip> using namespace QuantLib; #ifdef BOOST_MSVC # ifdef QL_ENABLE_THREAD_SAFE_OBSERVER_PATTERN # include <ql/auto_link.hpp> # define BOOST_LIB_NAME boost_system # include <boost/config/auto_link.hpp> # undef BOOST_LIB_NAME # define BOOST_LIB_NAME boost_thread # include <boost/config/auto_link.hpp> # undef BOOST_LIB_NAME # endif #endif #if defined(QL_ENABLE_SESSIONS) namespace QuantLib { Integer sessionId() { return 0; } } #endif int main(int, char* []) { try { boost::timer timer; std::cout << std::endl; /********************* *** MARKET DATA *** *********************/ Calendar calendar = TARGET(); Date settlementDate(18, September, 2008); // must be a business day settlementDate = calendar.adjust(settlementDate); Integer fixingDays = 3; Natural settlementDays = 3; 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 bonds discounting yield curve /********************* *** RATE HELPERS *** *********************/ // RateHelpers are built from the above quotes together with // other instrument dependant infos. Quotes are passed in // relinkable handles which could be relinked to some other // data source later. // Common data // ZC rates for the short end Rate zc3mQuote=0.0096; Rate zc6mQuote=0.0145; Rate zc1yQuote=0.0194; boost::shared_ptr<Quote> zc3mRate(new SimpleQuote(zc3mQuote)); boost::shared_ptr<Quote> zc6mRate(new SimpleQuote(zc6mQuote)); boost::shared_ptr<Quote> zc1yRate(new SimpleQuote(zc1yQuote)); DayCounter zcBondsDayCounter = Actual365Fixed(); boost::shared_ptr<RateHelper> zc3m(new DepositRateHelper( Handle<Quote>(zc3mRate), 3*Months, fixingDays, calendar, ModifiedFollowing, true, zcBondsDayCounter)); boost::shared_ptr<RateHelper> zc6m(new DepositRateHelper( Handle<Quote>(zc6mRate), 6*Months, fixingDays, calendar, ModifiedFollowing, true, zcBondsDayCounter)); boost::shared_ptr<RateHelper> zc1y(new DepositRateHelper( Handle<Quote>(zc1yRate), 1*Years, fixingDays, calendar, ModifiedFollowing, true, zcBondsDayCounter)); // setup bonds Real redemption = 100.0; const Size numberOfBonds = 5; Date issueDates[] = { Date (15, March, 2005), Date (15, June, 2005), Date (30, June, 2006), Date (15, November, 2002), Date (15, May, 1987) }; Date maturities[] = { Date (31, August, 2010), Date (31, August, 2011), Date (31, August, 2013), Date (15, August, 2018), Date (15, May, 2038) }; Real couponRates[] = { 0.02375, 0.04625, 0.03125, 0.04000, 0.04500 }; Real marketQuotes[] = { 100.390625, 106.21875, 100.59375, 101.6875, 102.140625 }; std::vector< boost::shared_ptr<SimpleQuote> > quote; for (Size i=0; i<numberOfBonds; i++) { boost::shared_ptr<SimpleQuote> cp(new SimpleQuote(marketQuotes[i])); quote.push_back(cp); } RelinkableHandle<Quote> quoteHandle[numberOfBonds]; for (Size i=0; i<numberOfBonds; i++) { quoteHandle[i].linkTo(quote[i]); } // Definition of the rate helpers std::vector<boost::shared_ptr<BondHelper> > bondsHelpers; for (Size i=0; i<numberOfBonds; i++) { Schedule schedule(issueDates[i], maturities[i], Period(Semiannual), UnitedStates(UnitedStates::GovernmentBond), Unadjusted, Unadjusted, DateGeneration::Backward, false); boost::shared_ptr<FixedRateBondHelper> bondHelper(new FixedRateBondHelper( quoteHandle[i], settlementDays, 100.0, schedule, std::vector<Rate>(1,couponRates[i]), ActualActual(ActualActual::Bond), Unadjusted, redemption, issueDates[i])); // the above could also be done by creating a // FixedRateBond instance and writing: // // boost::shared_ptr<BondHelper> bondHelper( // new BondHelper(quoteHandle[i], bond)); // // This would also work for bonds that still don't have a // specialized helper, such as floating-rate bonds. bondsHelpers.push_back(bondHelper); } /********************* ** CURVE BUILDING ** *********************/ // Any DayCounter would be fine. // ActualActual::ISDA ensures that 30 years is 30.0 DayCounter termStructureDayCounter = ActualActual(ActualActual::ISDA); double tolerance = 1.0e-15; // A depo-bond curve std::vector<boost::shared_ptr<RateHelper> > bondInstruments; // Adding the ZC bonds to the curve for the short end bondInstruments.push_back(zc3m); bondInstruments.push_back(zc6m); bondInstruments.push_back(zc1y); // Adding the Fixed rate bonds to the curve for the long end for (Size i=0; i<numberOfBonds; i++) { bondInstruments.push_back(bondsHelpers[i]); } boost::shared_ptr<YieldTermStructure> bondDiscountingTermStructure( new PiecewiseYieldCurve<Discount,LogLinear>( settlementDate, bondInstruments, termStructureDayCounter, tolerance)); // Building of the Libor forecasting curve // deposits Rate d1wQuote=0.043375; Rate d1mQuote=0.031875; Rate d3mQuote=0.0320375; Rate d6mQuote=0.03385; Rate d9mQuote=0.0338125; Rate d1yQuote=0.0335125; // swaps Rate s2yQuote=0.0295; Rate s3yQuote=0.0323; Rate s5yQuote=0.0359; Rate s10yQuote=0.0412; Rate s15yQuote=0.0433; /******************** *** QUOTES *** ********************/ // SimpleQuote stores a value which can be manually changed; // other Quote subclasses could read the value from a database // or some kind of data feed. // deposits boost::shared_ptr<Quote> d1wRate(new SimpleQuote(d1wQuote)); boost::shared_ptr<Quote> d1mRate(new SimpleQuote(d1mQuote)); boost::shared_ptr<Quote> d3mRate(new SimpleQuote(d3mQuote)); boost::shared_ptr<Quote> d6mRate(new SimpleQuote(d6mQuote)); boost::shared_ptr<Quote> d9mRate(new SimpleQuote(d9mQuote)); boost::shared_ptr<Quote> d1yRate(new SimpleQuote(d1yQuote)); // swaps boost::shared_ptr<Quote> s2yRate(new SimpleQuote(s2yQuote)); boost::shared_ptr<Quote> s3yRate(new SimpleQuote(s3yQuote)); boost::shared_ptr<Quote> s5yRate(new SimpleQuote(s5yQuote)); boost::shared_ptr<Quote> s10yRate(new SimpleQuote(s10yQuote)); boost::shared_ptr<Quote> s15yRate(new SimpleQuote(s15yQuote)); /********************* *** RATE HELPERS *** *********************/ // RateHelpers are built from the above quotes together with // other instrument dependant infos. Quotes are passed in // relinkable handles which could be relinked to some other // data source later. // deposits DayCounter depositDayCounter = Actual360(); boost::shared_ptr<RateHelper> d1w(new DepositRateHelper( Handle<Quote>(d1wRate), 1*Weeks, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d1m(new DepositRateHelper( Handle<Quote>(d1mRate), 1*Months, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d3m(new DepositRateHelper( Handle<Quote>(d3mRate), 3*Months, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d6m(new DepositRateHelper( Handle<Quote>(d6mRate), 6*Months, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d9m(new DepositRateHelper( Handle<Quote>(d9mRate), 9*Months, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); boost::shared_ptr<RateHelper> d1y(new DepositRateHelper( Handle<Quote>(d1yRate), 1*Years, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter)); // setup swaps Frequency swFixedLegFrequency = Annual; BusinessDayConvention swFixedLegConvention = Unadjusted; DayCounter swFixedLegDayCounter = Thirty360(Thirty360::European); boost::shared_ptr<IborIndex> swFloatingLegIndex(new Euribor6M); const Period forwardStart(1*Days); boost::shared_ptr<RateHelper> s2y(new SwapRateHelper( Handle<Quote>(s2yRate), 2*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex, Handle<Quote>(),forwardStart)); boost::shared_ptr<RateHelper> s3y(new SwapRateHelper( Handle<Quote>(s3yRate), 3*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex, Handle<Quote>(),forwardStart)); boost::shared_ptr<RateHelper> s5y(new SwapRateHelper( Handle<Quote>(s5yRate), 5*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex, Handle<Quote>(),forwardStart)); boost::shared_ptr<RateHelper> s10y(new SwapRateHelper( Handle<Quote>(s10yRate), 10*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex, Handle<Quote>(),forwardStart)); boost::shared_ptr<RateHelper> s15y(new SwapRateHelper( Handle<Quote>(s15yRate), 15*Years, calendar, swFixedLegFrequency, swFixedLegConvention, swFixedLegDayCounter, swFloatingLegIndex, Handle<Quote>(),forwardStart)); /********************* ** CURVE BUILDING ** *********************/ // Any DayCounter would be fine. // ActualActual::ISDA ensures that 30 years is 30.0 // A depo-swap curve std::vector<boost::shared_ptr<RateHelper> > depoSwapInstruments; depoSwapInstruments.push_back(d1w); depoSwapInstruments.push_back(d1m); depoSwapInstruments.push_back(d3m); depoSwapInstruments.push_back(d6m); depoSwapInstruments.push_back(d9m); depoSwapInstruments.push_back(d1y); depoSwapInstruments.push_back(s2y); depoSwapInstruments.push_back(s3y); depoSwapInstruments.push_back(s5y); depoSwapInstruments.push_back(s10y); depoSwapInstruments.push_back(s15y); boost::shared_ptr<YieldTermStructure> depoSwapTermStructure( new PiecewiseYieldCurve<Discount,LogLinear>( settlementDate, depoSwapInstruments, termStructureDayCounter, tolerance)); // 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 boost::shared_ptr<PricingEngine> bondEngine( new DiscountingBondEngine(discountingTermStructure)); // Zero coupon bond ZeroCouponBond zeroCouponBond( settlementDays, UnitedStates(UnitedStates::GovernmentBond), faceAmount, Date(15,August,2013), Following, Real(116.92), Date(15,August,2003)); zeroCouponBond.setPricingEngine(bondEngine); // Fixed 4.5% US Treasury Note Schedule fixedBondSchedule(Date(15, May, 2007), Date(15,May,2017), Period(Semiannual), UnitedStates(UnitedStates::GovernmentBond), Unadjusted, Unadjusted, DateGeneration::Backward, false); FixedRateBond fixedRateBond( settlementDays, faceAmount, fixedBondSchedule, std::vector<Rate>(1, 0.045), ActualActual(ActualActual::Bond), ModifiedFollowing, 100.0, Date(15, May, 2007)); fixedRateBond.setPricingEngine(bondEngine); // Floating rate bond (3M USD Libor + 0.1%) // Should and will be priced on another curve later... RelinkableHandle<YieldTermStructure> liborTermStructure; const boost::shared_ptr<IborIndex> libor3m( new USDLibor(Period(3,Months),liborTermStructure)); libor3m->addFixing(Date(17, July, 2008),0.0278625); Schedule floatingBondSchedule(Date(21, October, 2005), Date(21, October, 2010), Period(Quarterly), UnitedStates(UnitedStates::NYSE), Unadjusted, Unadjusted, DateGeneration::Backward, true); FloatingRateBond floatingRateBond( settlementDays, faceAmount, floatingBondSchedule, libor3m, Actual360(), ModifiedFollowing, Natural(2), // Gearings std::vector<Real>(1, 1.0), // Spreads std::vector<Rate>(1, 0.001), // Caps std::vector<Rate>(), // Floors std::vector<Rate>(), // Fixing in arrears true, Real(100.0), Date(21, October, 2005)); floatingRateBond.setPricingEngine(bondEngine); // Coupon pricers boost::shared_ptr<IborCouponPricer> pricer(new BlackIborCouponPricer); // optionLet volatilities Volatility volatility = 0.0; Handle<OptionletVolatilityStructure> vol; vol = Handle<OptionletVolatilityStructure>( boost::shared_ptr<OptionletVolatilityStructure>(new ConstantOptionletVolatility( settlementDays, calendar, ModifiedFollowing, volatility, Actual365Fixed()))); pricer->setCapletVolatility(vol); setCouponPricer(floatingRateBond.cashflows(),pricer); // Yield curve bootstrapping forecastingTermStructure.linkTo(depoSwapTermStructure); discountingTermStructure.linkTo(bondDiscountingTermStructure); // We are using the depo & swap curve to estimate the future Libor rates liborTermStructure.linkTo(depoSwapTermStructure); /*************** * BOND PRICING * ****************/ std::cout << std::endl; // write column headings Size widths[] = { 18, 10, 10, 10 }; std::cout << std::setw(widths[0]) << " " << std::setw(widths[1]) << "ZC" << std::setw(widths[2]) << "Fixed" << std::setw(widths[3]) << "Floating" << std::endl; std::string separator = " | "; Size width = widths[0] + widths[1] + widths[2] + widths[3]; std::string rule(width, '-'), dblrule(width, '='); std::string tab(8, ' '); std::cout << rule << std::endl; std::cout << std::fixed; std::cout << std::setprecision(2); std::cout << std::setw(widths[0]) << "Net present value" << std::setw(widths[1]) << zeroCouponBond.NPV() << std::setw(widths[2]) << fixedRateBond.NPV() << std::setw(widths[3]) << floatingRateBond.NPV() << std::endl; std::cout << std::setw(widths[0]) << "Clean price" << std::setw(widths[1]) << zeroCouponBond.cleanPrice() << std::setw(widths[2]) << fixedRateBond.cleanPrice() << std::setw(widths[3]) << floatingRateBond.cleanPrice() << std::endl; std::cout << std::setw(widths[0]) << "Dirty price" << std::setw(widths[1]) << zeroCouponBond.dirtyPrice() << std::setw(widths[2]) << fixedRateBond.dirtyPrice() << std::setw(widths[3]) << floatingRateBond.dirtyPrice() << std::endl; std::cout << std::setw(widths[0]) << "Accrued coupon" << std::setw(widths[1]) << zeroCouponBond.accruedAmount() << std::setw(widths[2]) << fixedRateBond.accruedAmount() << std::setw(widths[3]) << floatingRateBond.accruedAmount() << std::endl; std::cout << std::setw(widths[0]) << "Previous coupon" << std::setw(widths[1]) << "N/A" // zeroCouponBond << std::setw(widths[2]) << io::rate(fixedRateBond.previousCouponRate()) << std::setw(widths[3]) << io::rate(floatingRateBond.previousCouponRate()) << std::endl; std::cout << std::setw(widths[0]) << "Next coupon" << std::setw(widths[1]) << "N/A" // zeroCouponBond << std::setw(widths[2]) << io::rate(fixedRateBond.nextCouponRate()) << std::setw(widths[3]) << io::rate(floatingRateBond.nextCouponRate()) << std::endl; std::cout << std::setw(widths[0]) << "Yield" << std::setw(widths[1]) << io::rate(zeroCouponBond.yield(Actual360(),Compounded,Annual)) << std::setw(widths[2]) << io::rate(fixedRateBond.yield(Actual360(),Compounded,Annual)) << std::setw(widths[3]) << io::rate(floatingRateBond.yield(Actual360(),Compounded,Annual)) << std::endl; std::cout << std::endl; // Other computations std::cout << "Sample indirect computations (for the floating rate bond): " << std::endl; std::cout << rule << std::endl; std::cout << "Yield to Clean Price: " << floatingRateBond.cleanPrice(floatingRateBond.yield(Actual360(),Compounded,Annual),Actual360(),Compounded,Annual,settlementDate) << std::endl; std::cout << "Clean Price to Yield: " << io::rate(floatingRateBond.yield(floatingRateBond.cleanPrice(),Actual360(),Compounded,Annual,settlementDate)) << std::endl; /* "Yield to Price" "Price to Yield" */ double seconds = timer.elapsed(); Integer hours = int(seconds/3600); seconds -= hours * 3600; Integer minutes = int(seconds/60); seconds -= minutes * 60; std::cout << " \nRun completed in "; if (hours > 0) std::cout << hours << " h "; if (hours > 0 || minutes > 0) std::cout << minutes << " m "; std::cout << std::fixed << std::setprecision(0) << seconds << " s\n" << 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; } }