Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update yearFraction calculation to next coupon date for #282 #292

Merged
merged 3 commits into from
Sep 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
132 changes: 45 additions & 87 deletions ql/cashflows/cashflows.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -579,6 +579,42 @@ namespace QuantLib {
return -1; return -1;
} }


// helper fucntion used to calculate Time-To-Discount for each stage when calculating discount factor stepwisely
Time getStepwiseDiscountTime(const boost::shared_ptr<QuantLib::CashFlow> cashFlow,
const DayCounter& dc,
Date npvDate,
Date lastDate) {
Date cashFlowDate = cashFlow->date();
Date refStartDate, refEndDate;
shared_ptr<Coupon> coupon =
boost::dynamic_pointer_cast<Coupon>(cashFlow);
if (coupon) {
refStartDate = coupon->referencePeriodStart();
refEndDate = coupon->referencePeriodEnd();
} else {
if (lastDate == npvDate) {
// we don't have a previous coupon date,
// so we fake it
refStartDate = cashFlowDate - 1*Years;
} else {
refStartDate = lastDate;
}
refEndDate = cashFlowDate;
}

if (coupon && lastDate!=coupon->accrualStartDate()) {
Time couponPeriod = dc.yearFraction(coupon->accrualStartDate(),
cashFlowDate, refStartDate, refEndDate);
Time accruedPeriod = dc.yearFraction(coupon->accrualStartDate(),
lastDate, refStartDate, refEndDate);
return couponPeriod - accruedPeriod;
}
else {
return dc.yearFraction(lastDate, cashFlowDate,
refStartDate, refEndDate);
}
}

Real simpleDuration(const Leg& leg, Real simpleDuration(const Leg& leg,
const InterestRate& y, const InterestRate& y,
bool includeSettlementDateFlows, bool includeSettlementDateFlows,
Expand All @@ -597,7 +633,6 @@ namespace QuantLib {
Real dPdy = 0.0; Real dPdy = 0.0;
Time t = 0.0; Time t = 0.0;
Date lastDate = npvDate; Date lastDate = npvDate;
Date refStartDate, refEndDate;
const DayCounter& dc = y.dayCounter(); const DayCounter& dc = y.dayCounter();
for (Size i=0; i<leg.size(); ++i) { for (Size i=0; i<leg.size(); ++i) {
if (leg[i]->hasOccurred(settlementDate, if (leg[i]->hasOccurred(settlementDate,
Expand All @@ -609,31 +644,12 @@ namespace QuantLib {
c = 0.0; c = 0.0;
} }


Date couponDate = leg[i]->date(); t += getStepwiseDiscountTime(leg[i], dc, npvDate, lastDate);
shared_ptr<Coupon> coupon =
boost::dynamic_pointer_cast<Coupon>(leg[i]);
if (coupon) {
refStartDate = coupon->referencePeriodStart();
refEndDate = coupon->referencePeriodEnd();
} else {
if (lastDate == npvDate) {
// we don't have a previous coupon date,
// so we fake it
refStartDate = couponDate - 1*Years;
} else {
refStartDate = lastDate;
}
refEndDate = couponDate;
}

t += dc.yearFraction(lastDate, couponDate,
refStartDate, refEndDate);

DiscountFactor B = y.discountFactor(t); DiscountFactor B = y.discountFactor(t);
P += c * B; P += c * B;
dPdy += t * c * B; dPdy += t * c * B;


lastDate = couponDate; lastDate = leg[i]->date();
} }
if (P == 0.0) // no cashflows if (P == 0.0) // no cashflows
return 0.0; return 0.0;
Expand All @@ -660,7 +676,6 @@ namespace QuantLib {
Rate r = y.rate(); Rate r = y.rate();
Natural N = y.frequency(); Natural N = y.frequency();
Date lastDate = npvDate; Date lastDate = npvDate;
Date refStartDate, refEndDate;
const DayCounter& dc = y.dayCounter(); const DayCounter& dc = y.dayCounter();
for (Size i=0; i<leg.size(); ++i) { for (Size i=0; i<leg.size(); ++i) {
if (leg[i]->hasOccurred(settlementDate, if (leg[i]->hasOccurred(settlementDate,
Expand All @@ -672,26 +687,7 @@ namespace QuantLib {
c = 0.0; c = 0.0;
} }


Date couponDate = leg[i]->date(); t += getStepwiseDiscountTime(leg[i], dc, npvDate, lastDate);
shared_ptr<Coupon> coupon =
boost::dynamic_pointer_cast<Coupon>(leg[i]);
if (coupon) {
refStartDate = coupon->referencePeriodStart();
refEndDate = coupon->referencePeriodEnd();
} else {
if (lastDate == npvDate) {
// we don't have a previous coupon date,
// so we fake it
refStartDate = couponDate - 1*Years;
} else {
refStartDate = lastDate;
}
refEndDate = couponDate;
}

t += dc.yearFraction(lastDate, couponDate,
refStartDate, refEndDate);

DiscountFactor B = y.discountFactor(t); DiscountFactor B = y.discountFactor(t);
P += c * B; P += c * B;
switch (y.compounding()) { switch (y.compounding()) {
Expand Down Expand Up @@ -720,7 +716,7 @@ namespace QuantLib {
QL_FAIL("unknown compounding convention (" << QL_FAIL("unknown compounding convention (" <<
Integer(y.compounding()) << ")"); Integer(y.compounding()) << ")");
} }
lastDate = couponDate; lastDate = leg[i]->date();
} }


if (P == 0.0) // no cashflows if (P == 0.0) // no cashflows
Expand Down Expand Up @@ -857,38 +853,20 @@ namespace QuantLib {
Real npv = 0.0; Real npv = 0.0;
DiscountFactor discount = 1.0; DiscountFactor discount = 1.0;
Date lastDate = npvDate; Date lastDate = npvDate;
Date refStartDate, refEndDate; const DayCounter& dc = y.dayCounter();

for (Size i=0; i<leg.size(); ++i) { for (Size i=0; i<leg.size(); ++i) {
if (leg[i]->hasOccurred(settlementDate, if (leg[i]->hasOccurred(settlementDate,
includeSettlementDateFlows)) includeSettlementDateFlows))
continue; continue;


Date couponDate = leg[i]->date();
Real amount = leg[i]->amount(); Real amount = leg[i]->amount();
if (leg[i]->tradingExCoupon(settlementDate)) { if (leg[i]->tradingExCoupon(settlementDate)) {
amount = 0.0; amount = 0.0;
} }


shared_ptr<Coupon> coupon = DiscountFactor b = y.discountFactor(getStepwiseDiscountTime(leg[i], dc, npvDate, lastDate));
boost::dynamic_pointer_cast<Coupon>(leg[i]);
if (coupon) {
refStartDate = coupon->referencePeriodStart();
refEndDate = coupon->referencePeriodEnd();
} else {
if (lastDate == npvDate) {
// we don't have a previous coupon date,
// so we fake it
refStartDate = couponDate - 1*Years;
} else {
refStartDate = lastDate;
}
refEndDate = couponDate;
}
DiscountFactor b = y.discountFactor(lastDate, couponDate,
refStartDate, refEndDate);
discount *= b; discount *= b;
lastDate = couponDate; lastDate = leg[i]->date();


npv += amount * discount; npv += amount * discount;
} }
Expand Down Expand Up @@ -1036,7 +1014,6 @@ namespace QuantLib {
Rate r = y.rate(); Rate r = y.rate();
Natural N = y.frequency(); Natural N = y.frequency();
Date lastDate = npvDate; Date lastDate = npvDate;
Date refStartDate, refEndDate;
for (Size i=0; i<leg.size(); ++i) { for (Size i=0; i<leg.size(); ++i) {
if (leg[i]->hasOccurred(settlementDate, if (leg[i]->hasOccurred(settlementDate,
includeSettlementDateFlows)) includeSettlementDateFlows))
Expand All @@ -1047,26 +1024,7 @@ namespace QuantLib {
c = 0.0; c = 0.0;
} }


Date couponDate = leg[i]->date(); t += getStepwiseDiscountTime(leg[i], dc, npvDate, lastDate);
shared_ptr<Coupon> coupon =
boost::dynamic_pointer_cast<Coupon>(leg[i]);
if (coupon) {
refStartDate = coupon->referencePeriodStart();
refEndDate = coupon->referencePeriodEnd();
} else {
if (lastDate == npvDate) {
// we don't have a previous coupon date,
// so we fake it
refStartDate = couponDate - 1*Years;
} else {
refStartDate = lastDate;
}
refEndDate = couponDate;
}

t += dc.yearFraction(lastDate, couponDate,
refStartDate, refEndDate);

DiscountFactor B = y.discountFactor(t); DiscountFactor B = y.discountFactor(t);
P += c * B; P += c * B;
switch (y.compounding()) { switch (y.compounding()) {
Expand Down Expand Up @@ -1095,7 +1053,7 @@ namespace QuantLib {
QL_FAIL("unknown compounding convention (" << QL_FAIL("unknown compounding convention (" <<
Integer(y.compounding()) << ")"); Integer(y.compounding()) << ")");
} }
lastDate = couponDate; lastDate = leg[i]->date();
} }


if (P == 0.0) if (P == 0.0)
Expand Down
44 changes: 44 additions & 0 deletions test-suite/bonds.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -1378,6 +1378,49 @@ void BondTest::testBondFromScheduleWithDateVector()
} }
} }


void BondTest::testThiry360BondWithSettlementOn31st(){
BOOST_TEST_MESSAGE("Testing Thiry360Bond With Settlement On 31st of the month");

// cusip 3130A0X70, data is from Bloomberg
SavedSettings backup;
Settings::instance().evaluationDate() = Date(28, July, 2017);

Date datedDate(13, February, 2014);
Date settlement(31, July, 2017);
Date maturity(13, August, 2018);

DayCounter dayCounter = Thirty360(Thirty360::USA);
Compounding compounding = Compounded;

Schedule fixedBondSchedule(datedDate,
maturity,
Period(Semiannual),
UnitedStates(UnitedStates::GovernmentBond),
Unadjusted, Unadjusted, DateGeneration::Forward, false);

FixedRateBond fixedRateBond(
1,
100,
fixedBondSchedule,
std::vector<Rate>(1, 0.015),
dayCounter,
Unadjusted,
100.0);

double cleanPrice = 100;

Real yield = BondFunctions::yield(fixedRateBond, cleanPrice, dayCounter, compounding, Semiannual, settlement);
ASSERT_CLOSE("yield", settlement, yield, 0.015, 1e-4);

Real duration = BondFunctions::duration(fixedRateBond, InterestRate(yield, dayCounter, compounding, Semiannual), Duration::Macaulay, settlement);
ASSERT_CLOSE("duration", settlement, duration, 1.022, 1e-3);

Real convexity = BondFunctions::convexity(fixedRateBond, InterestRate(yield, dayCounter, compounding, Semiannual), settlement)/100;
ASSERT_CLOSE("convexity", settlement, convexity, 0.015, 1e-3);

Real accrued = BondFunctions::accruedAmount(fixedRateBond, settlement);
ASSERT_CLOSE("accrued", settlement, accrued, 0.7, 1e-6);
}


test_suite* BondTest::suite() { test_suite* BondTest::suite() {
test_suite* suite = BOOST_TEST_SUITE("Bond tests"); test_suite* suite = BOOST_TEST_SUITE("Bond tests");
Expand All @@ -1394,6 +1437,7 @@ test_suite* BondTest::suite() {
suite->add(QUANTLIB_TEST_CASE(&BondTest::testExCouponGilt)); suite->add(QUANTLIB_TEST_CASE(&BondTest::testExCouponGilt));
suite->add(QUANTLIB_TEST_CASE(&BondTest::testExCouponAustralianBond)); suite->add(QUANTLIB_TEST_CASE(&BondTest::testExCouponAustralianBond));
suite->add(QUANTLIB_TEST_CASE(&BondTest::testBondFromScheduleWithDateVector)); suite->add(QUANTLIB_TEST_CASE(&BondTest::testBondFromScheduleWithDateVector));
suite->add(QUANTLIB_TEST_CASE(&BondTest::testThiry360BondWithSettlementOn31st));
return suite; return suite;
} }


1 change: 1 addition & 0 deletions test-suite/bonds.hpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class BondTest {
static void testExCouponGilt(); static void testExCouponGilt();
static void testExCouponAustralianBond(); static void testExCouponAustralianBond();
static void testBondFromScheduleWithDateVector(); static void testBondFromScheduleWithDateVector();
static void testThiry360BondWithSettlementOn31st();
static boost::unit_test_framework::test_suite* suite(); static boost::unit_test_framework::test_suite* suite();
}; };


Expand Down