Skip to content

Commit 1b25192

Browse files
damienbarkerjenkins
authored andcommitted
Merge branch 'QPR-13367' into 'master'
QPR-13367 enable openEndDateReplacement for forward bonds Closes QPR-13367 See merge request qs/oreplus!2757
1 parent c0f81c8 commit 1b25192

7 files changed

Lines changed: 49 additions & 3 deletions

File tree

Docs/UserGuide/pricing/pricingengines.tex

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ \subsection{Product Type: Bond}
8181
The parameters have the following meaning:
8282

8383
\begin{itemize}
84+
\item OpenEndDateReplacement: if end date is missing in bond schedule (perpetual bond), valuation date plus this period is used as a replacement date
8485
\item TimestepPeriod: discretization interval for zero bond pricing
8586
\item SensitivityTemplate [optional]: the sensitivity template to use
8687
\item IncludePastCashflows [optional]: include past cashflows in the cashflow report, defaults to false (ignored in the DiscountingRiskyBondEngineMultiState)
@@ -90,7 +91,9 @@ \subsection{Product Type: Bond}
9091
\begin{minted}[fontsize=\footnotesize]{xml}
9192
<Product type="Bond">
9293
<Model>DiscountedCashflows</Model>
93-
<ModelParameters/>
94+
<ModelParameters>
95+
<Parameter name="OpenEndDateReplacement">50Y</Parameter>
96+
</ModelParameters>
9497
<Engine>DiscountingRiskyBondEngine</Engine>
9598
<EngineParameters>
9699
<Parameter name="TimestepPeriod">3M</Parameter>
@@ -3865,6 +3868,7 @@ \subsection{Product Type: ForwardBond}
38653868
The parameters have the following meaning:
38663869

38673870
\begin{itemize}
3871+
\item OpenEndDateReplacement: if end date is missing in bond schedule (perpetual bond), valuation date plus this period is used as a replacement date
38683872
\item TimestepPeriod: discretization interval for zero bond pricing
38693873
\item SensitivityTemplate [optional]: the sensitivity template to use
38703874
\item SpreadOnIncomeCurve [optional]: whether to apply the security spread on the income curve for compounding, default to false
@@ -3874,7 +3878,9 @@ \subsection{Product Type: ForwardBond}
38743878
\begin{minted}[fontsize=\footnotesize]{xml}
38753879
<Product type="ForwardBond">
38763880
<Model>DiscountedCashflows</Model>
3877-
<ModelParameters/>
3881+
<ModelParameters>
3882+
<Parameter name="OpenEndDateReplacement">50Y</Parameter>
3883+
</ModelParameters>
38783884
<Engine>DiscountingForwardBondEngine</Engine>
38793885
<EngineParameters>
38803886
<Parameter name="TimestepPeriod">3M</Parameter>

OREData/ored/portfolio/bondoption.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ void BondOption::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFac
5858
const QuantLib::ext::shared_ptr<Market> market = engineFactory->market();
5959
QuantLib::ext::shared_ptr<EngineBuilder> builder = engineFactory->builder("BondOption");
6060
bondData_ = originalBondData_;
61+
auto bondType = getBondReferenceDatumType(bondData_.securityId(), engineFactory->referenceData());
62+
QL_REQUIRE(bondType.empty() || bondType == BondReferenceDatum::TYPE,
63+
"BondOption: bond type " << bondType << " is not supported.");
6164
bondData_.populateFromBondReferenceData(engineFactory->referenceData());
6265

6366
Calendar calendar = parseCalendar(bondData_.calendar());

OREData/ored/portfolio/bondrepo.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ void BondRepo::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFacto
4949
// build security leg (as a bond)
5050

5151
securityLegData_ = originalSecurityLegData_;
52+
auto bondType = getBondReferenceDatumType(securityLegData_.securityId(), engineFactory->referenceData());
53+
QL_REQUIRE(bondType.empty() || bondType == BondReferenceDatum::TYPE,
54+
"BondRepo: bond type " << bondType << " is not supported.");
5255
securityLegData_.populateFromBondReferenceData(engineFactory->referenceData());
5356
securityLeg_ = QuantLib::ext::make_shared<ore::data::Bond>(Envelope(), securityLegData_);
5457
securityLeg_->id() = id() + "_SecurityLeg";

OREData/ored/portfolio/bondtotalreturnswap.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ void BondTRS::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFactor
5959
const QuantLib::ext::shared_ptr<Market> market = engineFactory->market();
6060
QuantLib::ext::shared_ptr<EngineBuilder> builder_trs = engineFactory->builder("BondTRS");
6161
bondData_ = originalBondData_;
62+
auto bondType = getBondReferenceDatumType(bondData_.securityId(), engineFactory->referenceData());
63+
QL_REQUIRE(bondType.empty() || bondType == BondReferenceDatum::TYPE,
64+
"BondTRS: bond type " << bondType << " is not supported. Consider using TotalReturnSwap.");
6265
bondData_.populateFromBondReferenceData(engineFactory->referenceData());
6366

6467
additionalData_["underlyingSecurityId"] = bondData_.securityId();

OREData/ored/portfolio/bondutils.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <ored/portfolio/bondutils.hpp>
2020
#include <ored/portfolio/structuredtradeerror.hpp>
2121
#include <ored/utilities/log.hpp>
22+
#include <ored/portfolio/convertiblebondreferencedata.hpp>
2223

2324
namespace ore {
2425
namespace data {
@@ -139,5 +140,19 @@ Date getOpenEndDateReplacement(const std::string& replacementPeriodStr, const Ca
139140
return result;
140141
}
141142

143+
std::string getBondReferenceDatumType(const std::string& id,
144+
const QuantLib::ext::shared_ptr<ReferenceDataManager>& refData) {
145+
if (refData == nullptr)
146+
return std::string();
147+
148+
if (refData->hasData(BondReferenceDatum::TYPE, id)) {
149+
return BondReferenceDatum::TYPE;
150+
} else if (refData->hasData(ConvertibleBondReferenceDatum::TYPE, id)) {
151+
return ConvertibleBondReferenceDatum::TYPE;
152+
}
153+
154+
return std::string();
155+
}
156+
142157
} // namespace data
143158
} // namespace ore

OREData/ored/portfolio/bondutils.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,14 @@ void populateFromBondReferenceData(std::string& subType, std::string& issuerId,
5858

5959
Date getOpenEndDateReplacement(const std::string& replacementPeriodStr, const Calendar& calendar = NullCalendar());
6060

61+
/* Returns the type of the bond reference data
62+
63+
- BondReferenceDatum::TYPE ("Bond")
64+
- ConvertibleBondReferenceDatum:TYPE ("ConvertibleBond")
65+
66+
or an empty string if no reference data was found. */
67+
std::string getBondReferenceDatumType(const std::string& id,
68+
const QuantLib::ext::shared_ptr<ReferenceDataManager>& refData);
69+
6170
} // namespace data
6271
} // namespace ore

OREData/ored/portfolio/forwardbond.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
1818
*/
1919

20+
#include <ored/portfolio/bondutils.hpp>
2021
#include <ored/portfolio/builders/bond.hpp>
2122
#include <ored/portfolio/builders/forwardbond.hpp>
2223
#include <ored/portfolio/fixingdates.hpp>
@@ -62,6 +63,9 @@ void ForwardBond::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFa
6263
QuantLib::ext::shared_ptr<EngineBuilder> builder_bd = engineFactory->builder("Bond");
6364

6465
bondData_ = originalBondData_;
66+
auto bondType = getBondReferenceDatumType(bondData_.securityId(), engineFactory->referenceData());
67+
QL_REQUIRE(bondType.empty() || bondType == BondReferenceDatum::TYPE,
68+
"ForwardBond: bond type " << bondType << " is not supported.");
6569
bondData_.populateFromBondReferenceData(engineFactory->referenceData());
6670

6771
npvCurrency_ = currency_ = bondData_.coupons().front().currency();
@@ -76,6 +80,9 @@ void ForwardBond::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFa
7680
Calendar calendar = parseCalendar(bondData_.calendar());
7781
Natural settlementDays = boost::lexical_cast<Natural>(bondData_.settlementDays());
7882

83+
std::string openEndDateStr = builder_fwd->modelParameter("OpenEndDateReplacement", {}, false, "");
84+
Date openEndDateReplacement = getOpenEndDateReplacement(openEndDateStr, calendar);
85+
7986
Date fwdMaturityDate = parseDate(fwdMaturityDate_);
8087
Date fwdSettlementDate = fwdSettlementDate_.empty() ? fwdMaturityDate : parseDate(fwdSettlementDate_);
8188
bool isPhysicallySettled;
@@ -130,7 +137,7 @@ void ForwardBond::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFa
130137
auto configuration = builder_bd->configuration(MarketContext::pricing);
131138
auto legBuilder = engineFactory->legBuilder(bondData_.coupons()[i].legType());
132139
leg = legBuilder->buildLeg(bondData_.coupons()[i], engineFactory, requiredFixings_, configuration,
133-
QuantLib::Date(), false, true, &productModelEngines);
140+
openEndDateReplacement, false, true, &productModelEngines);
134141
separateLegs.push_back(leg);
135142
addProductModelEngine(productModelEngines);
136143
}

0 commit comments

Comments
 (0)