Skip to content

Commit

Permalink
More functions to support both clean and dirty prices as input parame…
Browse files Browse the repository at this point in the history
…ter (#1813)
  • Loading branch information
lballabio committed Mar 6, 2024
2 parents 028c76e + f1ed886 commit 6d5892e
Show file tree
Hide file tree
Showing 11 changed files with 419 additions and 194 deletions.
7 changes: 3 additions & 4 deletions Examples/Bonds/Bonds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -541,10 +541,9 @@ int main(int, char* []) {
<< floatingRateBond.cleanPrice(floatingRateBond.yield(Actual360(),Compounded,Annual),Actual360(),Compounded,Annual,settlementDate) << std::endl;

std::cout << "Clean Price to Yield: "
<< io::rate(floatingRateBond.yield(floatingRateBond.cleanPrice(),Actual360(),Compounded,Annual,settlementDate)) << std::endl;

/* "Yield to Price"
"Price to Yield" */
<< io::rate(floatingRateBond.yield({floatingRateBond.cleanPrice(), Bond::Price::Clean},
Actual360(), Compounded, Annual, settlementDate))
<< std::endl;

return 0;

Expand Down
2 changes: 1 addition & 1 deletion Examples/FittedBondCurve/FittedBondCurve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ int main(int, char* []) {

Real P = instrumentsA[k]->quote()->value();
const Bond& b = *instrumentsA[k]->bond();
Rate ytm = BondFunctions::yield(b, P,
Rate ytm = BondFunctions::yield(b, {P, Bond::Price::Clean},
dc, Compounded, frequency,
today);
Time dur = BondFunctions::duration(b, ytm,
Expand Down
2 changes: 1 addition & 1 deletion Examples/Repo/Repo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ int main(int, char* []) {
// unknown what fincad is using. this may affect accrued calculation
Integer bondSettlementDays = 0;
BusinessDayConvention bondBusinessDayConvention = Unadjusted;
Real bondCleanPrice = 89.97693786;
Bond::Price bondCleanPrice(89.97693786, Bond::Price::Clean);
Real bondRedemption = 100.0;
Real faceAmount = 100.0;

Expand Down
17 changes: 14 additions & 3 deletions ql/instruments/bond.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,12 @@ namespace QuantLib {
if (currentNotional == 0.0)
return 0.0;

Real price = priceType == Bond::Price::Clean ? cleanPrice() : dirtyPrice();
Bond::Price price(priceType == Bond::Price::Clean ? cleanPrice() : dirtyPrice(), priceType);

return BondFunctions::yield(*this, price, dc, comp, freq,
settlementDate(),
accuracy, maxEvaluations,
guess, priceType);
guess);
}

Real Bond::cleanPrice(Rate y,
Expand Down Expand Up @@ -244,13 +244,24 @@ namespace QuantLib {
Size maxEvaluations,
Real guess,
Bond::Price::Type priceType) const {
return yield({price, priceType}, dc, comp, freq, settlement, accuracy,
maxEvaluations, guess);
}
Rate Bond::yield(Bond::Price price,
const DayCounter& dc,
Compounding comp,
Frequency freq,
Date settlement,
Real accuracy,
Size maxEvaluations,
Real guess) const {
Real currentNotional = notional(settlement);
if (currentNotional == 0.0)
return 0.0;

return BondFunctions::yield(*this, price, dc, comp, freq,
settlement, accuracy, maxEvaluations,
guess, priceType);
guess);
}

Real Bond::accruedAmount(Date settlement) const {
Expand Down
20 changes: 17 additions & 3 deletions ql/instruments/bond.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,14 @@ namespace QuantLib {
class Price {
public:
enum Type { Dirty, Clean };
Price() : amount_(Null<Real>()) {}
Price() : amount_(Null<Real>()), type_(Bond::Price::Clean) {}
Price(Real amount, Type type) : amount_(amount), type_(type) {}
Real amount() const {
QL_REQUIRE(amount_ != Null<Real>(), "no amount given");
return amount_;
}
Type type() const { return type_; }
bool isValid() const { return amount_ != Null<Real>(); }
private:
Real amount_;
Type type_;
Expand Down Expand Up @@ -194,8 +195,10 @@ namespace QuantLib {
/*! The default bond settlement date is used for calculation. */
Real settlementValue(Real cleanPrice) const;

//! yield given a price and settlement date
/*! The default bond settlement is used if no date is given. */
/*! \deprecated Use the overload taking a Bond::Price argument instead.
Deprecated in version 1.34.
*/
[[deprecated("Use the overload taking a Bond::Price argument instead")]]
Rate yield(Real price,
const DayCounter& dc,
Compounding comp,
Expand All @@ -206,6 +209,17 @@ namespace QuantLib {
Real guess = 0.05,
Bond::Price::Type priceType = Bond::Price::Clean) const;

//! yield given a price and settlement date
/*! The default bond settlement is used if no date is given. */
Rate yield(Bond::Price price,
const DayCounter& dc,
Compounding comp,
Frequency freq,
Date settlementDate = Date(),
Real accuracy = 1.0e-8,
Size maxEvaluations = 100,
Real guess = 0.05) const;

//! accrued amount at a given date
/*! The default bond settlement is used if no date is given. */
virtual Real accruedAmount(Date d = Date()) const;
Expand Down
15 changes: 7 additions & 8 deletions ql/instruments/bonds/btp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@ namespace QuantLib {
Date settlementDate,
Real accuracy,
Size maxEvaluations) const {
return Bond::yield(cleanPrice, ActualActual(ActualActual::ISMA),
Compounded, Annual,
settlementDate, accuracy, maxEvaluations);
return Bond::yield({cleanPrice, Bond::Price::Clean},
ActualActual(ActualActual::ISMA), Compounded, Annual, settlementDate,
accuracy, maxEvaluations);
}


Expand Down Expand Up @@ -160,9 +160,8 @@ namespace QuantLib {
Date bondSettlementDate = btps[0]->settlementDate();
for (Size i=0; i<basket_->size(); ++i) {
yields_[i] = BondFunctions::yield(
*btps[i], quotes[i]->value(),
ActualActual(ActualActual::ISMA), Compounded, Annual,
bondSettlementDate,
*btps[i], {quotes[i]->value(), Bond::Price::Clean},
ActualActual(ActualActual::ISMA), Compounded, Annual, bondSettlementDate,
// accuracy, maxIterations, guess
1.0e-10, 100, yields_[i]);
durations_[i] = BondFunctions::duration(
Expand All @@ -186,7 +185,7 @@ namespace QuantLib {
Following, // paymentConvention
100.0); // redemption
swapBondYields_[0] = BondFunctions::yield(swapBond,
100.0, // floating leg NPV including end payment
{100.0, Bond::Price::Clean}, // floating leg NPV including end payment
ActualActual(ActualActual::ISMA), Compounded, Annual,
bondSettlementDate,
// accuracy, maxIterations, guess
Expand All @@ -205,7 +204,7 @@ namespace QuantLib {
Following, // paymentConvention
100.0); // redemption
swapBondYields_[i] = BondFunctions::yield(swapBond,
100.0, // floating leg NPV including end payment
{100.0, Bond::Price::Clean}, // floating leg NPV including end payment
ActualActual(ActualActual::ISMA), Compounded, Annual,
bondSettlementDate,
// accuracy, maxIterations, guess
Expand Down
82 changes: 72 additions & 10 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 @@ -272,19 +281,30 @@ namespace QuantLib {
const YieldTermStructure& discountCurve,
Date settlement,
Real cleanPrice) {
return atmRate(bond, discountCurve, settlement, {cleanPrice, Bond::Price::Clean});
}

Rate BondFunctions::atmRate(const Bond& bond,
const YieldTermStructure& discountCurve,
Date settlement,
const Bond::Price price) {
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;
Real npv = Null<Real>();
if (price.isValid()) {
Real dirtyPrice =
price.amount() +
(price.type() == Bond::Price::Clean ? bond.accruedAmount(settlement) : 0);

Real currentNotional = bond.notional(settlement);
npv = dirtyPrice / 100.0 * currentNotional;

}
return CashFlows::atmRate(bond.cashflows(), discountCurve,
false, settlement, settlement,
npv);
Expand Down Expand Up @@ -367,11 +387,24 @@ namespace QuantLib {
Size maxIterations,
Rate guess,
Bond::Price::Type priceType) {
return yield(bond, {price, priceType}, dayCounter, compounding, frequency,
settlement, accuracy, maxIterations, guess);
}

Rate BondFunctions::yield(const Bond& bond,
Bond::Price price,
const DayCounter& dayCounter,
Compounding compounding,
Frequency frequency,
Date settlement,
Real accuracy,
Size maxIterations,
Rate guess) {
NewtonSafe solver;
solver.setMaxEvaluations(maxIterations);
return yield<NewtonSafe>(solver, bond, price, dayCounter,
compounding, frequency, settlement,
accuracy, guess, priceType);
accuracy, guess);
}

Time BondFunctions::duration(const Bond& bond,
Expand Down Expand Up @@ -483,6 +516,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 @@ -491,7 +537,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 @@ -504,14 +550,31 @@ 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,
Bond::Price price,
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.amount() +
(price.type() == Bond::Price::Clean ? bond.accruedAmount(settlement) : 0);

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

return CashFlows::zSpread(bond.cashflows(),
Expand All @@ -521,5 +584,4 @@ namespace QuantLib {
false, settlement, settlement,
accuracy, maxIterations, guess);
}

}
Loading

0 comments on commit 6d5892e

Please sign in to comment.