Re: Bond duration incorrect ?

Posted by Dirk Eddelbuettel on
URL: http://quantlib.414.s1.nabble.com/Bond-duration-incorrect-tp4434p4435.html

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