Bond duration incorrect ?

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

Bond duration incorrect ?

Dirk Eddelbuettel
Rainy day here so I got to play a little, at long last, with the fixed income
functions. I can't make sense of duration() in CashFlows/analysis.{cpp,hpp}
In particular this line seems wrong:

        totalNPV -= marketPrice;

before the sum of present value times time (here variable totalDuration) gets
divided by totalNPV as in

        return totalDuration/totalNPV;

which is supported via quick googling [ http://www.finpipe.com/duration.htm ]
and one of the investments texts here.

Likewise, modified duration is normaly given as Macaulay Duration divided by
(1 + y / nbcoupons) -- so that a second fix would be

  case Duration::Modified:
    //return totalDuration / totalNPV / y;
    return totalDuration / totalNPV / (1 + y/rate.frequency());

A simplistic program, taken from one of the regression test cases, is below
and I added a local copy of the duration function from analysis.cpp.

I may have overlooked something, but I guess we should at least add some
validated regression tests for some duration, convexity, ... cases.
Any volunteers?

Oh, and one last question: why isn't the redemption at par payment already
included when I call cashflow() on a bond?

Dirk


#include <iostream>
#include <ql/quantlib.hpp>

using namespace std;
using namespace QuantLib;

Time myDuration(const std::vector<boost::shared_ptr<CashFlow> >& cashflows,
                Real marketPrice,
                const InterestRate& rate,
                Duration::Type type,
                Date settlementDate) {

  if (settlementDate == Date())
    settlementDate = Settings::instance().evaluationDate();

  Real totalDuration = 0.0;
  Real totalNPV = 0.0;

  Rate y = 0.0;
  if (type == Duration::Macaulay || type == Duration::Modified) {
    y = Cashflows::irr(cashflows, marketPrice, rate.dayCounter(),
                       rate.compounding(), rate.frequency(), settlementDate);
    cout << "Y is " << y << " " << rate.frequency() << endl;
  }

  for (Size i = 0; i < cashflows.size(); i++) {
    Time t = rate.dayCounter().yearFraction(settlementDate,
                                            cashflows[i]->date());
    Real c = cashflows[i]->amount();
    DiscountFactor discount;
    if (type == Duration::Macaulay)
      discount = std::exp(-y*t);
    else
      discount = rate.discountFactor(t);

    totalNPV += c * discount;
    totalDuration += t * c * discount;
  }
  //totalNPV -= marketPrice;      // edd 28-Jan-2006: this is probably in error

  if (totalNPV == 0.0)
    // what to do?
    return 0.0;

  switch (type){
  case Duration::Modified:
    //return totalDuration / totalNPV / y;  // edd 28-Jan-2006: error?
    return totalDuration / totalNPV / (1 + y/rate.frequency());
  case Duration::Simple:
  case Duration::Macaulay:
    return totalDuration/totalNPV;
  default:
    QL_FAIL("unknown duration type");
  }
}


int main(void) {

  Calendar calendar = TARGET();
  Date today = calendar.adjust(Date::todaysDate());
  Settings::instance().evaluationDate() = today;

  Real redemption = 100.0; // bond pay $100 face at term
  Integer issueMonth = -12; // bond issued 12 months ago
  Integer bondTerm =  5; // bond has a 5yr term-to-maturity
  Real bondCoupon = 0.05;        // bond has 5% coupon
  Frequency bondCoupFreq = Semiannual; // bond pays coupon annually
  DayCounter bondDayCount = Thirty360();
  Compounding compounding = Compounded;
  BusinessDayConvention convention = ModifiedFollowing;
  Integer settlementDays = 3;

  Rate currentYield = 0.0475;

  Date dated = calendar.advance(today, issueMonth, Months);
  Date issue = dated;
  Date maturity = calendar.advance(issue, bondTerm, Years);

  boost::shared_ptr<YieldTermStructure>
    flatRate(new FlatForward(dated, currentYield, bondDayCount,
                             compounding, bondCoupFreq));

  FixedCouponBond bond(issue, dated, maturity, settlementDays,
                       std::vector<Rate>(1, bondCoupon),
                       bondCoupFreq, bondDayCount,
                       calendar, convention, redemption,
                       Handle<YieldTermStructure>(flatRate));

  Real cleanPrice = bond.cleanPrice(currentYield, compounding);
  Real dirtyPrice = bond.dirtyPrice(currentYield, compounding);
  Rate calculated = bond.yield(cleanPrice, compounding);
  Real accrued = bond.accruedAmount();

  cout << "Dirty Price: " << dirtyPrice << endl;
  cout << "Clean Price: " << cleanPrice << endl;
  cout << "Yield      : " << calculated << endl;
  cout << "Accrued    : " << accrued << endl;

  // need to explicitly push redemption cash flow into cf vector -- why ?
  std::vector<boost::shared_ptr<CashFlow> > cfVec(bond.cashflows());
  cfVec.push_back(bond.redemption());

  Real irr = Cashflows::irr(cfVec, cleanPrice,
                            bondDayCount, compounding, bondCoupFreq, issue);
  cout << "Irr        : " << irr << endl;

  InterestRate rate(calculated, bondDayCount, compounding, bondCoupFreq);
  cout << "Rate       : " << rate << endl;
  //  Time duration = Cashflows::duration(cfVec, cleanPrice, rate,
  // Duration::Modified, dated);
  Time macDuration = myDuration(cfVec, cleanPrice, rate,
                                Duration::Macaulay, dated);
  Time modDuration = myDuration(cfVec, cleanPrice, rate,
                                Duration::Modified, dated);
  Time simDuration = myDuration(cfVec, cleanPrice, rate,
                                Duration::Simple, dated);
  cout << "MacDuration: " << macDuration << endl;
  cout << "ModDuration: " << modDuration << endl;
  cout << "SimDuration: " << simDuration << endl;

  for (unsigned int i=0; i<cfVec.size(); i++) {
    cout << "Date " << i << " :\t" << cfVec[i]->date() << "\t"
         << "Amount: " << cfVec[i]->amount() << endl;
  }

}



--
Hell, there are no rules here - we're trying to accomplish something.
                                                  -- Thomas A. Edison


Reply | Threaded
Open this post in threaded view
|

Re: Bond duration incorrect ?

Dirk Eddelbuettel
Dudes,

Nobody followed on the most-likely-a-bug report I made -- does anybody have
access to an alternate calculator to check simple, modified and macaulay
duration for a few securities?  As indicated, I think what we have in
analysis.{cpp,hpp} is wrong.  Yet another source of documentation is at
Wikipedia at http://en.wikipedia.org/wiki/Bond_duration

Dirk

On 28 January 2006 at 18:46, Dirk Eddelbuettel wrote:
|
| Rainy day here so I got to play a little, at long last, with the fixed income
| functions. I can't make sense of duration() in CashFlows/analysis.{cpp,hpp}
| In particular this line seems wrong:
|
|         totalNPV -= marketPrice;
|
| before the sum of present value times time (here variable totalDuration) gets
| divided by totalNPV as in
|
| return totalDuration/totalNPV;
|
| which is supported via quick googling [ http://www.finpipe.com/duration.htm ]
| and one of the investments texts here.
|
| Likewise, modified duration is normaly given as Macaulay Duration divided by
| (1 + y / nbcoupons) -- so that a second fix would be
|
|   case Duration::Modified:
|     //return totalDuration / totalNPV / y;
|     return totalDuration / totalNPV / (1 + y/rate.frequency());
|
| A simplistic program, taken from one of the regression test cases, is below
| and I added a local copy of the duration function from analysis.cpp.
|
| I may have overlooked something, but I guess we should at least add some
| validated regression tests for some duration, convexity, ... cases.
| Any volunteers?
|
| Oh, and one last question: why isn't the redemption at par payment already
| included when I call cashflow() on a bond?
|
| Dirk
|
|
| #include <iostream>
| #include <ql/quantlib.hpp>
|
| using namespace std;
| using namespace QuantLib;
|
| Time myDuration(const std::vector<boost::shared_ptr<CashFlow> >& cashflows,
| Real marketPrice,
| const InterestRate& rate,
| Duration::Type type,
| Date settlementDate) {
|
|   if (settlementDate == Date())
|     settlementDate = Settings::instance().evaluationDate();
|
|   Real totalDuration = 0.0;
|   Real totalNPV = 0.0;
|
|   Rate y = 0.0;
|   if (type == Duration::Macaulay || type == Duration::Modified) {
|     y = Cashflows::irr(cashflows, marketPrice, rate.dayCounter(),
|       rate.compounding(), rate.frequency(), settlementDate);
|     cout << "Y is " << y << " " << rate.frequency() << endl;
|   }
|
|   for (Size i = 0; i < cashflows.size(); i++) {
|     Time t = rate.dayCounter().yearFraction(settlementDate,
|    cashflows[i]->date());
|     Real c = cashflows[i]->amount();
|     DiscountFactor discount;
|     if (type == Duration::Macaulay)
|       discount = std::exp(-y*t);
|     else
|       discount = rate.discountFactor(t);
|
|     totalNPV += c * discount;
|     totalDuration += t * c * discount;
|   }
|   //totalNPV -= marketPrice;      // edd 28-Jan-2006: this is probably in error
|
|   if (totalNPV == 0.0)
|     // what to do?
|     return 0.0;
|
|   switch (type){
|   case Duration::Modified:
|     //return totalDuration / totalNPV / y;  // edd 28-Jan-2006: error?
|     return totalDuration / totalNPV / (1 + y/rate.frequency());
|   case Duration::Simple:
|   case Duration::Macaulay:
|     return totalDuration/totalNPV;
|   default:
|     QL_FAIL("unknown duration type");
|   }
| }
|
|
| int main(void) {
|
|   Calendar calendar = TARGET();
|   Date today = calendar.adjust(Date::todaysDate());
|   Settings::instance().evaluationDate() = today;
|
|   Real redemption = 100.0; // bond pay $100 face at term
|   Integer issueMonth = -12; // bond issued 12 months ago
|   Integer bondTerm =  5; // bond has a 5yr term-to-maturity
|   Real bondCoupon = 0.05;        // bond has 5% coupon
|   Frequency bondCoupFreq = Semiannual; // bond pays coupon annually
|   DayCounter bondDayCount = Thirty360();
|   Compounding compounding = Compounded;
|   BusinessDayConvention convention = ModifiedFollowing;
|   Integer settlementDays = 3;
|
|   Rate currentYield = 0.0475;
|
|   Date dated = calendar.advance(today, issueMonth, Months);
|   Date issue = dated;
|   Date maturity = calendar.advance(issue, bondTerm, Years);
|
|   boost::shared_ptr<YieldTermStructure>
|     flatRate(new FlatForward(dated, currentYield, bondDayCount,
|     compounding, bondCoupFreq));
|
|   FixedCouponBond bond(issue, dated, maturity, settlementDays,
|       std::vector<Rate>(1, bondCoupon),
|       bondCoupFreq, bondDayCount,
|       calendar, convention, redemption,
|       Handle<YieldTermStructure>(flatRate));
|
|   Real cleanPrice = bond.cleanPrice(currentYield, compounding);
|   Real dirtyPrice = bond.dirtyPrice(currentYield, compounding);
|   Rate calculated = bond.yield(cleanPrice, compounding);
|   Real accrued = bond.accruedAmount();
|
|   cout << "Dirty Price: " << dirtyPrice << endl;
|   cout << "Clean Price: " << cleanPrice << endl;
|   cout << "Yield      : " << calculated << endl;
|   cout << "Accrued    : " << accrued << endl;
|
|   // need to explicitly push redemption cash flow into cf vector -- why ?
|   std::vector<boost::shared_ptr<CashFlow> > cfVec(bond.cashflows());
|   cfVec.push_back(bond.redemption());
|
|   Real irr = Cashflows::irr(cfVec, cleanPrice,
|    bondDayCount, compounding, bondCoupFreq, issue);
|   cout << "Irr        : " << irr << endl;
|
|   InterestRate rate(calculated, bondDayCount, compounding, bondCoupFreq);
|   cout << "Rate       : " << rate << endl;
|   //  Time duration = Cashflows::duration(cfVec, cleanPrice, rate,
|   // Duration::Modified, dated);
|   Time macDuration = myDuration(cfVec, cleanPrice, rate,
| Duration::Macaulay, dated);
|   Time modDuration = myDuration(cfVec, cleanPrice, rate,
| Duration::Modified, dated);
|   Time simDuration = myDuration(cfVec, cleanPrice, rate,
| Duration::Simple, dated);
|   cout << "MacDuration: " << macDuration << endl;
|   cout << "ModDuration: " << modDuration << endl;
|   cout << "SimDuration: " << simDuration << endl;
|
|   for (unsigned int i=0; i<cfVec.size(); i++) {
|     cout << "Date " << i << " :\t" << cfVec[i]->date() << "\t"
| << "Amount: " << cfVec[i]->amount() << endl;
|   }
|
| }
|
|
|
| --
| Hell, there are no rules here - we're trying to accomplish something.
|                                                   -- Thomas A. Edison
|
|
| -------------------------------------------------------
| This SF.net email is sponsored by: Splunk Inc. Do you grep through log files
| for problems?  Stop!  Download the new AJAX search engine that makes
| searching your log files as easy as surfing the  web.  DOWNLOAD SPLUNK!
| http://sel.as-us.falkag.net/sel?cmd=lnk&kid=103432&bid=230486&dat=121642
| _______________________________________________
| Quantlib-users mailing list
| [hidden email]
| https://lists.sourceforge.net/lists/listinfo/quantlib-users

--
Hell, there are no rules here - we're trying to accomplish something.
                                                  -- Thomas A. Edison


Reply | Threaded
Open this post in threaded view
|

Re: Bond duration incorrect ?

Luigi Ballabio
On 2/2/06, Dirk Eddelbuettel <[hidden email]> wrote:
> Nobody followed on the most-likely-a-bug report I made -- does anybody have
> access to an alternate calculator to check simple, modified and macaulay
> duration for a few securities?  As indicated, I think what we have in
> analysis.{cpp,hpp} is wrong.  Yet another source of documentation is at
> Wikipedia at http://en.wikipedia.org/wiki/Bond_duration

I second the request. Anybody?

Luigi


Reply | Threaded
Open this post in threaded view
|

Re: Bond duration incorrect ?

Toyin Akin
Hi,

Check out the following towards the bottom of the page...

http://www.fincad.com/support/developerfunc/mathref/lcb.htm

You could also play with the 7-day demo as well...

Toy out.

>From: Luigi Ballabio <[hidden email]>
>To: Dirk Eddelbuettel <[hidden email]>
>CC: quantlib-users <[hidden email]>
>Subject: Re: [Quantlib-users] Bond duration incorrect ?
>Date: Thu, 2 Feb 2006 16:53:41 +0100
>
>On 2/2/06, Dirk Eddelbuettel <[hidden email]> wrote:
> > Nobody followed on the most-likely-a-bug report I made -- does anybody
>have
> > access to an alternate calculator to check simple, modified and macaulay
> > duration for a few securities?  As indicated, I think what we have in
> > analysis.{cpp,hpp} is wrong.  Yet another source of documentation is at
> > Wikipedia at http://en.wikipedia.org/wiki/Bond_duration
>
>I second the request. Anybody?
>
>Luigi
>
>
>-------------------------------------------------------
>This SF.net email is sponsored by: Splunk Inc. Do you grep through log
>files
>for problems?  Stop!  Download the new AJAX search engine that makes
>searching your log files as easy as surfing the  web.  DOWNLOAD SPLUNK!
><a href="http://sel.as-us.falkag.net/sel?cmd=lnk&kid3432&bid#0486&dat1642">http://sel.as-us.falkag.net/sel?cmd=lnk&kid3432&bid#0486&dat1642
>_______________________________________________
>Quantlib-users mailing list
>[hidden email]
>https://lists.sourceforge.net/lists/listinfo/quantlib-users




Reply | Threaded
Open this post in threaded view
|

Credit derivatives implementation...

Toyin Akin
In reply to this post by Luigi Ballabio
Hi,

There actually is an implementation of building credit default curves and
the pricing of credit default swaps.

The library actually uses a cut down version of quantlib for most of the
building blocks.

However the default probability curves built does have the annoying
assumption that defaults can only happen once a year and does not take into
consideration accural payments.

The math is sound and the coding pretty clean, however I don't like the
recursion approach for the solving of probabilities (I prefer Quantlib's
rateHelpers approach) coupled with a maximum length that a credit curve can
occupy.

What I do like is that they have a spreadsheet (creditCurveYann.xls)
implementation of a credit default curve stripper using Excel alone (coupled
with Excel's solver). Now this is a pretty good basis for a rateHelper
approach

The library happens to contain other financial building blocks currently not
present within quantlib (ie volatility swaps...).

The library, I believe, is LGPL.

check it out at : http://terreneuve.sourceforge.net/

You may want to read the pdf first to get an idea of what the library is
capable of :

http://terreneuve.sourceforge.net/1.0/Report.pdf

Very nice library.

Toy out.




Reply | Threaded
Open this post in threaded view
|

RE: Credit derivatives implementation...

Toyin Akin
Hi,

Oh... when I talk about rateHelpers for credit derivatives, I meant a new
category of rateHelpers that can cater for deriving default probabilities
instead of discout factors (ie - creditHelpers). These classes can probabliy
be used within a new PiecewiseCreditCurve (similar to the current
PiecewiseYieldCurve)

As you may or may not know, you can derive default probabilities from credit
default swaps, Bonds, Transition matrices etc...

What do you think...

Toy out.

>From: "Toyin Akin" <[hidden email]>
>To: [hidden email]
>CC: [hidden email]
>Subject: [Quantlib-users] Credit derivatives implementation...
>Date: Thu, 02 Feb 2006 17:38:43 +0000
>
>
>Hi,
>
>There actually is an implementation of building credit default curves and
>the pricing of credit default swaps.
>
>The library actually uses a cut down version of quantlib for most of the
>building blocks.
>
>However the default probability curves built does have the annoying
>assumption that defaults can only happen once a year and does not take into
>consideration accural payments.
>
>The math is sound and the coding pretty clean, however I don't like the
>recursion approach for the solving of probabilities (I prefer Quantlib's
>rateHelpers approach) coupled with a maximum length that a credit curve can
>occupy.
>
>What I do like is that they have a spreadsheet (creditCurveYann.xls)
>implementation of a credit default curve stripper using Excel alone
>(coupled with Excel's solver). Now this is a pretty good basis for a
>rateHelper approach
>
>The library happens to contain other financial building blocks currently
>not present within quantlib (ie volatility swaps...).
>
>The library, I believe, is LGPL.
>
>check it out at : http://terreneuve.sourceforge.net/
>
>You may want to read the pdf first to get an idea of what the library is
>capable of :
>
>http://terreneuve.sourceforge.net/1.0/Report.pdf
>
>Very nice library.
>
>Toy out.
>
>
>
>
>-------------------------------------------------------
>This SF.net email is sponsored by: Splunk Inc. Do you grep through log
>files
>for problems?  Stop!  Download the new AJAX search engine that makes
>searching your log files as easy as surfing the  web.  DOWNLOAD SPLUNK!
>http://sel.as-us.falkag.net/sel?cmd=lnk&kid=103432&bid=230486&dat=121642
>_______________________________________________
>Quantlib-users mailing list
>[hidden email]
>https://lists.sourceforge.net/lists/listinfo/quantlib-users