Login  Register

Re: Duration and Convexity of a Floating Rate Bond

Posted by Luigi Ballabio on Dec 10, 2013; 11:47am
URL: http://quantlib.414.s1.nabble.com/Duration-and-Convexity-of-a-Floating-Rate-Bond-tp14689p14712.html

Hello,
    [note: your mail client seems to have stripped some template
arguments from the pasted code, so I haven't been able to try and run
it. Next time, it would help if you might attach the file instead of
pasting the code.]

However: the problem is that, in the duration() method, the yield that
is passed is only used for discounting and not for forecasting the
Libor coupons. This way, the bond is not really used as a
floating-rate bond, but rather as a fixed-rate bond which happens to
pay the same rates as the floating-rate bond (hence the duration close
to the time to maturity).

Unfortunately, there's no simple way to change the method so that it
can detect the kind of coupons contained in the bond (especially since
you could define your own coupon classes).

Instead of using the duration method, I suggest you calculate the
duration manually. You'll have to link both the forwarding term
structure and the discounting term structure to a flat curve with rate
equal to the yield, which gives you the price P as function of the
yield y; once you have that, you can calculate the modified duration
as 1/P dP/dy.

Luigi



On Thu, Dec 5, 2013 at 4:32 PM, asavoldi <[hidden email]> wrote:

> So far, I've been able to simulate a floating rate bond with the following
> code. Surprisingly, the duration, which is expected to be the residual time
> until the following coupon (should be less than 0.25 being the period
> quarterly), is evaluated as the residual time to expiration (in this case
> the calculated duration is about 2 that is the time to expiration)
>
> Is anyone able to provide an explanation about this result?
>
>
> ---------------------------------
> Today: Monday, September 15th, 2008
> Settlement date: Thursday, September 18th, 2008
> Floating Rate Bond
> Net present value = 102.359
> Clean price = 101.797
> Dirty price = 102.359
> Accrued coupon = 0.562113
> Previous coupon = 2.886250 %
> Next coupon = 3.429841 %
> Yield = 2.200956 %
>
> Sample indirect computations (for the floating rate bond):
> Yield to Clean Price = 101.797
> Clean Price to Yield = 2.200956 %
> Macaulay Duration = 2.01408
> Modified Duration = 2.00306
> Convexity = 4.60674
> Price Duration = 103.836
> Price Convexity = 103.86
>
>
>
>
>
> #include <ql/quantlib.hpp>
> #include <iostream>
> #include <iomanip>
> using namespace QuantLib;
> #if defined(QL_ENABLE_SESSIONS)
> namespace QuantLib {
> Integer sessionId() { return 0; }
> }
> #endif
>
>
> /*
> Definitions:
>
> gearings: optional multipliers of the LIBOR fixing (some bonds might pay,
> for instance, 0.8 times the LIBOR)
>
> spreads:  are the added spreads. In your case, the gearing is 1 and the
> spread is 0.0140 (that is, 140 bps; rates
>                   and spread must be expressed in decimal form).
>
> curve: the curve you want to use for discounting. Using the swap curve plus
> a spread is a possibility. However, note
>            that the CDS spread might not be an exact proxy for the spread to be
> applied. At the very least, the CDS spread
>            is a quarterly rate simply compounded, whereas (depending on what
> classes are exported) you might need a continuously
>            compounded rate in order to spread the swap curve. You'll have to
> perform the conversion first.
>
> index: an instance of an index class such as Euribor3M, whose constructor in
> turn would take the swap curve.
> */
>
> void floatingRateBondIndicators() {
>
>         //*********************
>         //***  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 << "---------------------------------" << std::endl;
>         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 zc3mRate(new SimpleQuote(zc3mQuote));
>         boost::shared_ptr zc6mRate(new SimpleQuote(zc6mQuote));
>         boost::shared_ptr zc1yRate(new SimpleQuote(zc1yQuote));
>         DayCounter zcBondsDayCounter = Actual365Fixed();
>         boost::shared_ptr<RateHelper> zc3m(new DepositRateHelper(Handle(zc3mRate),
> 3*Months, fixingDays, calendar, ModifiedFollowing, true,
> zcBondsDayCounter));
>         boost::shared_ptr<RateHelper> zc6m(new DepositRateHelper(Handle(zc6mRate),
> 6*Months, fixingDays, calendar, ModifiedFollowing, true,
> zcBondsDayCounter));
>         boost::shared_ptr<RateHelper> zc1y(new DepositRateHelper(Handle(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&lt;SimpleQuote> cp(new SimpleQuote(marketQuotes[i]));
>                 quote.push_back(cp);
>         }
>         RelinkableHandle quoteHandle[numberOfBonds];
>         for (Size i=0; i<numberOfBonds; i++) {
>                 quoteHandle[i].linkTo(quote[i]);
>         }
>         // Definition of the rate helpers
>         std::vector&lt;boost::shared_ptr&lt;FixedRateBondHelper> > 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&lt;FixedRateBondHelper> bondHelper(new
> FixedRateBondHelper(
>                                 quoteHandle[i],
>                                 settlementDays,
>                                 100.0,
>                                 schedule,
>                                 std::vector<Rate>(1,couponRates[i]),
>                                 ActualActual(ActualActual::Bond),
>                                 Unadjusted,
>                                 redemption,
>                                 issueDates[i]));
>                 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&lt;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]);
>         }
>
>
> //--------------------------------------------------------------------------------------------------------------------------------------------------------
>         //bondDiscountingTermStructure => Discounting Curve (this is one of the
> curves that must be present in order to evaluate properly a floating Rate
> Bond)
>
> //--------------------------------------------------------------------------------------------------------------------------------------------------------
>         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 d1wRate(new SimpleQuote(d1wQuote));
>         boost::shared_ptr d1mRate(new SimpleQuote(d1mQuote));
>         boost::shared_ptr d3mRate(new SimpleQuote(d3mQuote));
>         boost::shared_ptr d6mRate(new SimpleQuote(d6mQuote));
>         boost::shared_ptr d9mRate(new SimpleQuote(d9mQuote));
>         boost::shared_ptr d1yRate(new SimpleQuote(d1yQuote));
>         // swaps
>         boost::shared_ptr s2yRate(new SimpleQuote(s2yQuote));
>         boost::shared_ptr s3yRate(new SimpleQuote(s3yQuote));
>         boost::shared_ptr s5yRate(new SimpleQuote(s5yQuote));
>         boost::shared_ptr s10yRate(new SimpleQuote(s10yQuote));
>         boost::shared_ptr 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(d1wRate),
> 1*Weeks, fixingDays, calendar, ModifiedFollowing, true, depositDayCounter));
>         boost::shared_ptr<RateHelper> d1m(new DepositRateHelper(Handle(d1mRate),
> 1*Months, fixingDays, calendar, ModifiedFollowing, true,
> depositDayCounter));
>         boost::shared_ptr<RateHelper> d3m(new DepositRateHelper(Handle(d3mRate),
> 3*Months, fixingDays, calendar, ModifiedFollowing, true,
> depositDayCounter));
>         boost::shared_ptr<RateHelper> d6m(new DepositRateHelper(Handle(d6mRate),
> 6*Months, fixingDays, calendar, ModifiedFollowing, true,
> depositDayCounter));
>         boost::shared_ptr<RateHelper> d9m(new DepositRateHelper(Handle(d9mRate),
> 9*Months, fixingDays, calendar, ModifiedFollowing, true,
> depositDayCounter));
>         boost::shared_ptr<RateHelper> d1y(new DepositRateHelper(Handle(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(s2yRate),
> 2*Years, calendar, swFixedLegFrequency, swFixedLegConvention,
> swFixedLegDayCounter, swFloatingLegIndex, Handle(),forwardStart));
>         boost::shared_ptr<RateHelper> s3y(new SwapRateHelper(Handle(s3yRate),
> 3*Years, calendar, swFixedLegFrequency, swFixedLegConvention,
> swFixedLegDayCounter, swFloatingLegIndex, Handle(),forwardStart));
>         boost::shared_ptr<RateHelper> s5y(new SwapRateHelper(Handle(s5yRate),
> 5*Years, calendar, swFixedLegFrequency, swFixedLegConvention,
> swFixedLegDayCounter, swFloatingLegIndex, Handle(),forwardStart));
>         boost::shared_ptr<RateHelper> s10y(new SwapRateHelper(Handle(s10yRate),
> 10*Years, calendar, swFixedLegFrequency, swFixedLegConvention,
> swFixedLegDayCounter, swFloatingLegIndex, Handle(),forwardStart));
>         boost::shared_ptr<RateHelper> s15y(new SwapRateHelper(Handle(s15yRate),
> 15*Years, calendar, swFixedLegFrequency, swFixedLegConvention,
> swFixedLegDayCounter, swFloatingLegIndex, Handle(),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&lt;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));
>
>         // 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); //278.625 bps; this is
> spot value of LIBOR rate
>
>
>         Schedule floatingBondSchedule(
>                         Date(21, October, 2005),                //issue day
>                         Date(21, October, 2010),                //maturity day
>                         Period(Quarterly),                              //frequency
>                         UnitedStates(UnitedStates::NYSE), //calendar
>                         Unadjusted,                                             //day convention
>                         Unadjusted,                                             //day termination
>                         DateGeneration::Backward,               //date generation
>                         true                                                    //end of the month
>                         );
>
>         FloatingRateBond floatingRateBond(
>                         settlementDays,                                 //days for bond settlement [Natural settlementDays]
>                         faceAmount,                                             //face amount (e.g. 100) [Real faceAmount]
>                         floatingBondSchedule,                   //schedule object [const Schedule& schedule]
>                         libor3m,                                                //IborIndex -> linked index used for coupon discounting
> [const boost::shared_ptr<IborIndex>& index]
>                         Actual360(),                                    //paymentDayCounter [const DayCounter&
> paymentDayCounter]
>                         ModifiedFollowing,                              //paymentConvention [BusinessDayConvention
> paymentConvention]
>                         Natural(2),                                             //[Natural fixingDays]
>                         // Gearings
>                         std::vector<Real>(1, 1.0),              //[const std::vector<Real>& gearings] ->
> multiplier
>                         // Spreads
>                         std::vector<Rate>(1, 0.001),    //[const std::vector<Spread>& spreads] ->
> rate added to index curve
>                         // Caps
>                         std::vector<Rate>(),                    //[const std::vector<Rate>& caps] -> upper limit
> which limit the coupon rate
>                         // Floors
>                         std::vector<Rate>(),                    //[const std::vector<Rate>& floors] -> minimum
> rate guaranteed as coupon rate
>                         // Fixing in arrears
>                         true,                                                   //[bool inArrears]
>                         Real(100.0),                                    //[Real redemption]
>                         Date(21, October, 2005));               //[const Date& issueDate]
>
>
>         //-----------------------
>         //Setting price enginge
>         //-----------------------
>         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 << "Floating Rate Bond" << std::endl;
>
>         //Net present value
>         Real npv = floatingRateBond.NPV();
>         std::cout << "Net present value = " << npv << std::endl;
>
>         //Clean price
>         Real cleanPrice = floatingRateBond.cleanPrice();
>         std::cout << "Clean price = " << cleanPrice << std::endl;
>
>         //Dirty price
>         Real dirtyPrice = floatingRateBond.dirtyPrice();
>         std::cout << "Dirty price = " << dirtyPrice << std::endl;
>
>         //Accrued amount
>         Real accruedAmount = floatingRateBond.accruedAmount();
>         std::cout << "Accrued coupon = " << accruedAmount << std::endl;
>
>         //Previous coupon
>         Real previousCoupon = floatingRateBond.previousCouponRate();
>         std::cout << "Previous coupon = " << io::rate(previousCoupon) << std::endl;
>
>         //Next coupon
>         Real nextCoupon = floatingRateBond.nextCouponRate();
>         std::cout << "Next coupon = " << io::rate(nextCoupon) << std::endl;
>
>         //yield to maturity
>         Real yield = floatingRateBond.yield(Actual360(),Compounded,Annual);
>         std::cout << "Yield = " << io::rate(yield) << std::endl;
>         std::cout << std::endl;
>
>         // Other computations
>         std::cout << "Sample indirect computations (for the floating rate bond): "
> << std::endl;
>
>         //clean price from yield to maturity
>         Real cleanPriceFromYield =
> floatingRateBond.cleanPrice(floatingRateBond.yield(Actual360(),Compounded,Annual),Actual360(),Compounded,Annual,settlementDate);
>         std::cout << "Yield to Clean Price = " << cleanPriceFromYield << std::endl;
>
>         //yield to maturity from clean price
>         Real yieldFromCleanPrice =
> floatingRateBond.yield(floatingRateBond.cleanPrice(),Actual360(),Compounded,Annual,settlementDate);
>         std::cout << "Clean Price to Yield = " << io::rate(yieldFromCleanPrice) <<
> std::endl;
>
>         DayCounter dayCounter = ActualActual(ActualActual::Bond);
>         Compounding interestCompounding = Compounding::Compounded;
>         Frequency frequency = Frequency::Quarterly;
>
>         //Macauly Duration
>         Time macDuration =
> BondFunctions::duration(floatingRateBond,yield,dayCounter,interestCompounding,frequency,Duration::Macaulay,todaysDate);
>         std::cout << "Macaulay Duration = " << macDuration << std::endl;
>
>         //Modified duration
>         Time modDuration =
> BondFunctions::duration(floatingRateBond,yield,dayCounter,interestCompounding,frequency,Duration::Modified,todaysDate);
>         std::cout << "Modified Duration = " << modDuration << std::endl;
>
>         //Convexity
>         Real convexity =
> BondFunctions::convexity(floatingRateBond,yield,dayCounter,interestCompounding,frequency,todaysDate);
>         std::cout << "Convexity = " << convexity << std::endl;
>
>         //Estimate new bond price for an increase in interest rate of 1% using
> modified duration
>         Real priceDuration = cleanPrice + cleanPrice * (modDuration * .01);
>         std::cout << "Price Duration = " << priceDuration << std::endl;
>
>         //Estimate new bond price for an increase in interest rate of 1% using
> duration and convexity
>         Real priceConvexity = cleanPrice + cleanPrice * (modDuration * .01 + (.5 *
> convexity * std::pow(.01, 2)));
>         std::cout << "Price Convexity = " << priceConvexity << std::endl;
>
>
> }
>
>
>
> --
> View this message in context: http://quantlib.10058.n7.nabble.com/Duration-and-Convexity-of-a-Floating-Rate-Bond-tp14689p14691.html
> Sent from the quantlib-users mailing list archive at Nabble.com.
>
> ------------------------------------------------------------------------------
> Sponsored by Intel(R) XDK
> Develop, test and display web and hybrid apps with a single code base.
> Download it for free now!
> http://pubads.g.doubleclick.net/gampad/clk?id=111408631&iu=/4140/ostg.clktrk
> _______________________________________________
> QuantLib-users mailing list
> [hidden email]
> https://lists.sourceforge.net/lists/listinfo/quantlib-users



--
<https://implementingquantlib.blogspot.com>
<https://twitter.com/lballabio>

------------------------------------------------------------------------------
Sponsored by Intel(R) XDK
Develop, test and display web and hybrid apps with a single code base.
Download it for free now!
http://pubads.g.doubleclick.net/gampad/clk?id=111408631&iu=/4140/ostg.clktrk
_______________________________________________
QuantLib-users mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/quantlib-users