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_durationDirk
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