Skip to content

Commit

Permalink
More functions to support both clean and dirty prices as input parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
igitur committed Nov 18, 2020
1 parent a2fb7df commit 4db733f
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 21 deletions.
74 changes: 62 additions & 12 deletions ql/pricingengines/bond/bondfunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,15 @@ namespace QuantLib {
if (settlement == Date())
settlement = bond.settlementDate();

return dirtyPrice(bond, discountCurve, settlement) - bond.accruedAmount(settlement);
}

Real BondFunctions::dirtyPrice(const Bond& bond,
const YieldTermStructure& discountCurve,
Date settlement) {
if (settlement == Date())
settlement = bond.settlementDate();

QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
"non tradable at " << settlement <<
" settlement date (maturity being " <<
Expand All @@ -250,7 +259,7 @@ namespace QuantLib {
Real dirtyPrice = CashFlows::npv(bond.cashflows(), discountCurve,
false, settlement) *
100.0 / bond.notional(settlement);
return dirtyPrice - bond.accruedAmount(settlement);
return dirtyPrice;
}

Real BondFunctions::bps(const Bond& bond,
Expand All @@ -271,23 +280,32 @@ namespace QuantLib {
Rate BondFunctions::atmRate(const Bond& bond,
const YieldTermStructure& discountCurve,
Date settlement,
Real cleanPrice) {
Real price,
const Bond::Price::Type priceType) {
if (settlement == Date())
settlement = bond.settlementDate();

QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
"non tradable at " << settlement <<
" (maturity being " << bond.maturityDate() << ")");

Real dirtyPrice = cleanPrice==Null<Real>() ? Null<Real>() :
cleanPrice + bond.accruedAmount(settlement);
Real currentNotional = bond.notional(settlement);
Real npv = dirtyPrice==Null<Real>() ? Null<Real>() :
dirtyPrice/100.0 * currentNotional;
if (price == Null<Real>())
return CashFlows::atmRate(bond.cashflows(), discountCurve,
false, settlement, settlement,
Null<Real>());
else {
Real dirtyPrice = price;

if (priceType == Bond::Price::Clean)
dirtyPrice += bond.accruedAmount(settlement);

return CashFlows::atmRate(bond.cashflows(), discountCurve,
false, settlement, settlement,
npv);
Real currentNotional = bond.notional(settlement);
Real npv = dirtyPrice / 100.0 * currentNotional;

return CashFlows::atmRate(bond.cashflows(), discountCurve,
false, settlement, settlement,
npv);
}
}

Real BondFunctions::cleanPrice(const Bond& bond,
Expand Down Expand Up @@ -485,6 +503,19 @@ namespace QuantLib {
if (settlement == Date())
settlement = bond.settlementDate();

return dirtyPrice(bond, d, zSpread, dc, comp, freq, settlement) - bond.accruedAmount(settlement);
}

Real BondFunctions::dirtyPrice(const Bond& bond,
const ext::shared_ptr<YieldTermStructure>& d,
Spread zSpread,
const DayCounter& dc,
Compounding comp,
Frequency freq,
Date settlement) {
if (settlement == Date())
settlement = bond.settlementDate();

QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
"non tradable at " << settlement <<
" (maturity being " << bond.maturityDate() << ")");
Expand All @@ -493,7 +524,7 @@ namespace QuantLib {
zSpread, dc, comp, freq,
false, settlement) *
100.0 / bond.notional(settlement);
return dirtyPrice - bond.accruedAmount(settlement);
return dirtyPrice;
}

Spread BondFunctions::zSpread(const Bond& bond,
Expand All @@ -506,14 +537,33 @@ namespace QuantLib {
Real accuracy,
Size maxIterations,
Rate guess) {

return zSpread(bond, cleanPrice, Bond::Price::Clean, d, dayCounter, compounding, frequency,
settlement, accuracy, maxIterations, guess);
}


Spread BondFunctions::zSpread(const Bond& bond,
Real price,
const Bond::Price::Type priceType,
const ext::shared_ptr<YieldTermStructure>& d,
const DayCounter& dayCounter,
Compounding compounding,
Frequency frequency,
Date settlement,
Real accuracy,
Size maxIterations,
Rate guess) {
if (settlement == Date())
settlement = bond.settlementDate();

QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
"non tradable at " << settlement <<
" (maturity being " << bond.maturityDate() << ")");

Real dirtyPrice = cleanPrice + bond.accruedAmount(settlement);
Real dirtyPrice = price +
(priceType == Bond::Price::Clean ? bond.accruedAmount(settlement) : 0);

dirtyPrice /= 100.0 / bond.notional(settlement);

return CashFlows::zSpread(bond.cashflows(),
Expand Down
27 changes: 26 additions & 1 deletion ql/pricingengines/bond/bondfunctions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,17 @@ namespace QuantLib {
static Real cleanPrice(const Bond& bond,
const YieldTermStructure& discountCurve,
Date settlementDate = Date());
static Real dirtyPrice(const Bond& bond,
const YieldTermStructure& discountCurve,
Date settlementDate = Date());
static Real bps(const Bond& bond,
const YieldTermStructure& discountCurve,
Date settlementDate = Date());
static Rate atmRate(const Bond& bond,
const YieldTermStructure& discountCurve,
Date settlementDate = Date(),
Real cleanPrice = Null<Real>());
Real price = Null<Real>(),
const Bond::Price::Type priceType = Bond::Price::Clean);
//@}

//! \name Yield (a.k.a. Internal Rate of Return, i.e. IRR) functions
Expand Down Expand Up @@ -235,6 +239,15 @@ namespace QuantLib {
Compounding compounding,
Frequency frequency,
Date settlementDate = Date());
static Real dirtyPrice(const Bond& bond,
const ext::shared_ptr<YieldTermStructure>& discount,
Spread zSpread,
const DayCounter& dayCounter,
Compounding compounding,
Frequency frequency,
Date settlementDate = Date());

QL_DEPRECATED
static Spread zSpread(const Bond& bond,
Real cleanPrice,
const ext::shared_ptr<YieldTermStructure>&,
Expand All @@ -245,6 +258,18 @@ namespace QuantLib {
Real accuracy = 1.0e-10,
Size maxIterations = 100,
Rate guess = 0.0);

static Spread zSpread(const Bond& bond,
Real price,
const Bond::Price::Type priceType,
const ext::shared_ptr<YieldTermStructure>&,
const DayCounter& dayCounter,
Compounding compounding,
Frequency frequency,
Date settlementDate = Date(),
Real accuracy = 1.0e-10,
Size maxIterations = 100,
Rate guess = 0.0);
//@}

};
Expand Down
58 changes: 50 additions & 8 deletions test-suite/bonds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,12 +331,14 @@ void BondTest::testZspread() {

for (Size m=0; m<LENGTH(spreads); m++) {

// Clean price
Real price = BondFunctions::cleanPrice(bond, *discountCurve,
spreads[m],
bondDayCount,
compounding[n],
frequencies[l]);
Spread calculated = BondFunctions::zSpread(bond, price,
Bond::Price::Clean,
*discountCurve,
bondDayCount,
compounding[n],
Expand All @@ -354,19 +356,59 @@ void BondTest::testZspread() {
frequencies[l]);
if (std::fabs(price-price2)/price > tolerance) {
BOOST_ERROR("\nZ-spread recalculation failed:"
"\n issue: " << issue <<
"\n maturity: " << maturity <<
"\n coupon: " << io::rate(coupons[k]) <<
"\n frequency: " << frequencies[l] <<
"\n Z-spread: " << io::rate(spreads[m]) <<
"\n issue: " << issue <<
"\n maturity: " << maturity <<
"\n coupon: " << io::rate(coupons[k]) <<
"\n frequency: " << frequencies[l] <<
"\n Z-spread: " << io::rate(spreads[m]) <<
(compounding[n] == Compounded ?
" compounded" : " continuous") <<
std::setprecision(7) <<
"\n price: " << price <<
"\n Z-spread': " << io::rate(calculated) <<
"\n price': " << price2);
"\n clean price: " << price <<
"\n Z-spread': " << io::rate(calculated) <<
"\n clean price': " << price2);
}
}

// Dirty price
price = BondFunctions::dirtyPrice(bond, *discountCurve,
spreads[m],
bondDayCount,
compounding[n],
frequencies[l]);

calculated = BondFunctions::zSpread(bond, price,
Bond::Price::Dirty,
*discountCurve,
bondDayCount,
compounding[n],
frequencies[l],
Date(),
tolerance,
maxEvaluations);

if (std::fabs(spreads[m] - calculated) > tolerance) {
// the difference might not matter
Real price2 = BondFunctions::dirtyPrice(bond, *discountCurve,
calculated,
bondDayCount,
compounding[n],
frequencies[l]);
if (std::fabs(price - price2) / price > tolerance) {
BOOST_ERROR("\nZ-spread recalculation failed:"
"\n issue: " << issue <<
"\n maturity: " << maturity <<
"\n coupon: " << io::rate(coupons[k]) <<
"\n frequency: " << frequencies[l] <<
"\n Z-spread: " << io::rate(spreads[m]) <<
(compounding[n] == Compounded ?
" compounded" : " continuous") <<
std::setprecision(7) <<
"\n dirty price: " << price <<
"\n Z-spread': " << io::rate(calculated) <<
"\n dirty price': " << price2);
}
}
}
}
}
Expand Down

0 comments on commit 4db733f

Please sign in to comment.