Skip to content

Commit 30d4db5

Browse files
author
Sjogren
committed
Merge remote-tracking branch 'origin/master' into feature/martin_docs1
2 parents a706727 + 1daf6d0 commit 30d4db5

16 files changed

Lines changed: 96 additions & 42 deletions

File tree

Docs/UserGuide/parameterisation/conventions.tex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,8 @@ \subsubsection{Inflation Swap Conventions}
641641
<AdjustInflationObservationDates>false</AdjustInflationObservationDates>
642642
<InflationCalendar>TARGET</InflationCalendar>
643643
<InflationConvention>MF</InflationConvention>
644+
<StartDelay>2</StartDelay>
645+
<StartDelayConvention>Following</StartDelayConvention>
644646
</InflationSwap>
645647
\end{minted}
646648
\caption{Inflation swap conventions}
@@ -659,6 +661,8 @@ \subsubsection{Inflation Swap Conventions}
659661
\item \lstinline!AdjustInflationObservationDates!: Flag indicating whether index observation dates should be adjusted or not.
660662
\item \lstinline!InflationCalendar!: The calendar for the inflation leg of the swap.
661663
\item \lstinline!InflationConvention!: The rolling convention for the inflation leg of the swap.
664+
\item \lstinline!StartDelay!: [Optional] The inflation swap starts n business days after today, defaults to zero if omitted.
665+
\item \lstinline!StartDelayConvention!: [Optional] BusinessDayConvention to adjust the start day, defaults to Following if omitted.
662666

663667
\item \lstinline!PublicationRoll!:
664668
This is an optional node taking the values \lstinline!None!, \lstinline!OnPublicationDate! or \lstinline!AfterPublicationDate!. If omitted, the value \lstinline!None! is used. Currently, our only known use case for a value other than \lstinline!None! is for Australian zero coupon inflation indexed swaps (ZCIIS). Here, the index is published quarterly on the last Wednesday of the month following the end of the reference quarter. The start date and maturity date of the market quoted ZCIIS roll to the next quarterly date after the publication date of the index. For example, the AU CPI value for Q3 2020, i.e.\ 1 Jul 2020 to 30 Sep 2020 was released on 28 Oct 2020. On 27 Oct 2020, before the index publication date, the market 5Y ZCIIS would start on 15 Sep 2020 and end on 15 Sep 2025 and reference the Q2 inflation index value. On 29 Oct 2020, after the index publication date, the market 5Y ZCIIS would start on 15 Dec 2020 and end on 15 Dec 2025 and reference the Q3 inflation index value. On the release date, i.e. 28 Oct 2020, the market ZCIIS that is set up is determined by whether the \lstinline!PublicationRoll! value is \lstinline!OnPublicationDate! or \lstinline!AfterPublicationDate!. If it is set to \lstinline!OnPublicationDate!, the swap rolls on this date and hence the market 5Y ZCIIS would start on 15 Dec 2020 and end on 15 Dec 2025 and reference the Q3 inflation index value. If it is set to \lstinline!AfterPublicationDate!, the swap does not roll on the publication date and instead rolls on the next day, and hence the market 5Y ZCIIS would start on 15 Sep 2020 and end on 15 Sep 2025 and reference the Q2 inflation index value. The publication schedule for the index must be provided in the \lstinline!PublicationSchedule! node if \lstinline!PublicationRoll! is not \lstinline!None!. An example of the AU CPI conventions set up is given in Listing \ref{lst:aucpi_inflation_conventions}.

OREData/ored/configuration/conventions.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,7 +1313,6 @@ void CdsConvention::fromXML(XMLNode* node) {
13131313
strPaysAtDefaultTime_ = XMLUtils::getChildValue(node, "PaysAtDefaultTime", true);
13141314
strUpfrontSettlementDays_ = XMLUtils::getChildValue(node, "UpfrontSettlementDays", false);
13151315
strLastPeriodDayCounter_ = XMLUtils::getChildValue(node, "LastPeriodDayCounter", false);
1316-
13171316
build();
13181317
}
13191318

@@ -1366,6 +1365,8 @@ void InflationSwapConvention::build() {
13661365
adjustInfObsDates_ = parseBool(strAdjustInfObsDates_);
13671366
infCalendar_ = parseCalendar(strInfCalendar_);
13681367
infConvention_ = parseBusinessDayConvention(strInfConvention_);
1368+
startDelayConvention_ =
1369+
strStartDelayConvention_.empty() ? Following : parseBusinessDayConvention(strStartDelayConvention_);
13691370
if (publicationRoll_ != PublicationRoll::None) {
13701371
QL_REQUIRE(publicationScheduleData_, "Publication roll is " << publicationRoll_ << " for " << id() <<
13711372
" so expect non-null publication schedule data.");
@@ -1389,7 +1390,9 @@ void InflationSwapConvention::fromXML(XMLNode* node) {
13891390
strAdjustInfObsDates_ = XMLUtils::getChildValue(node, "AdjustInflationObservationDates", true);
13901391
strInfCalendar_ = XMLUtils::getChildValue(node, "InflationCalendar", true);
13911392
strInfConvention_ = XMLUtils::getChildValue(node, "InflationConvention", true);
1392-
1393+
startDelay_ = XMLUtils::getChildValueAsInt(node, "StartDelay", false, 0);
1394+
strStartDelayConvention_ = XMLUtils::getChildValue(node, "StartDelayConvention", false);
1395+
13931396
publicationRoll_ = PublicationRoll::None;
13941397
if (XMLNode* n = XMLUtils::getChildNode(node, "PublicationRoll")) {
13951398
publicationRoll_ = parseInflationSwapPublicationRoll(XMLUtils::getNodeValue(n));
@@ -1419,7 +1422,12 @@ XMLNode* InflationSwapConvention::toXML(XMLDocument& doc) const {
14191422
XMLUtils::addChild(doc, node, "AdjustInflationObservationDates", strAdjustInfObsDates_);
14201423
XMLUtils::addChild(doc, node, "InflationCalendar", strInfCalendar_);
14211424
XMLUtils::addChild(doc, node, "InflationConvention", strInfConvention_);
1422-
1425+
if (startDelay_ != 0) {
1426+
XMLUtils::addChild(doc, node, "StartDelay", startDelay_);
1427+
}
1428+
if (!strStartDelayConvention_.empty()) {
1429+
XMLUtils::addChild(doc, node, "StartDelayConvention", strStartDelayConvention_);
1430+
}
14231431
if (publicationRoll_ != PublicationRoll::None) {
14241432
XMLUtils::addChild(doc, node, "PublicationRoll", to_string(publicationRoll_));
14251433
QL_REQUIRE(publicationScheduleData_, "PublicationRoll is " << publicationRoll_ << " for "

OREData/ored/configuration/conventions.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1157,7 +1157,7 @@ class InflationSwapConvention : public Convention {
11571157
InflationSwapConvention(const string& id, const string& strFixCalendar, const string& strFixConvention,
11581158
const string& strDayCounter, const string& strIndex, const string& strInterpolated,
11591159
const string& strObservationLag, const string& strAdjustInfObsDates,
1160-
const string& strInfCalendar, const string& strInfConvention,
1160+
const string& strInfCalendar, const string& strInfConvention,
11611161
PublicationRoll publicationRoll = PublicationRoll::None,
11621162
const QuantLib::ext::shared_ptr<ScheduleData>& publicationScheduleData = nullptr);
11631163

@@ -1173,6 +1173,8 @@ class InflationSwapConvention : public Convention {
11731173
BusinessDayConvention infConvention() const { return infConvention_; }
11741174
PublicationRoll publicationRoll() const { return publicationRoll_; }
11751175
const Schedule& publicationSchedule() const { return publicationSchedule_; }
1176+
int startDelay() const { return startDelay_; }
1177+
BusinessDayConvention startDelayConvention() const { return startDelayConvention_; }
11761178

11771179
virtual void fromXML(XMLNode* node) override;
11781180
virtual XMLNode* toXML(XMLDocument& doc) const override;
@@ -1189,6 +1191,8 @@ class InflationSwapConvention : public Convention {
11891191
Calendar infCalendar_;
11901192
BusinessDayConvention infConvention_;
11911193
Schedule publicationSchedule_;
1194+
int startDelay_ = 0;
1195+
BusinessDayConvention startDelayConvention_ = BusinessDayConvention::Following;
11921196

11931197
// Store the inputs
11941198
string strFixCalendar_;
@@ -1200,6 +1204,8 @@ class InflationSwapConvention : public Convention {
12001204
string strAdjustInfObsDates_;
12011205
string strInfCalendar_;
12021206
string strInfConvention_;
1207+
string strStartDelayConvention_;
1208+
12031209
PublicationRoll publicationRoll_;
12041210
QuantLib::ext::shared_ptr<ScheduleData> publicationScheduleData_;
12051211
};

OREData/ored/portfolio/builders/deltagammaengines.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,15 +170,16 @@ class FxForwardEngineBuilderDeltaGamma : public FxForwardEngineBuilderBase {
170170
: FxForwardEngineBuilderBase("DiscountedCashflows", "DiscountingFxForwardEngineDeltaGamma") {}
171171

172172
protected:
173-
virtual QuantLib::ext::shared_ptr<PricingEngine> engineImpl(const Currency& forCcy, const Currency& domCcy) override {
173+
virtual QuantLib::ext::shared_ptr<PricingEngine> engineImpl(const Currency& forCcy, const Currency& domCcy,
174+
const std::string& discountCurve) override {
174175

175176
std::vector<Time> bucketTimes = parseListOfValues<Time>(engineParameter("BucketTimes"), &parseReal);
176177
bool computeDelta = parseBool(engineParameter("ComputeDelta"));
177178
bool computeGamma = parseBool(engineParameter("ComputeGamma"));
178179
bool linearInZero = parseBool(engineParameter("LinearInZero", {}, false, "True")); // FIXME: Add to pricing engine parameters?
179180
bool applySimmExemptions = parseBool(engineParameter("ApplySimmExemptions", {}, false, "false"));
180181

181-
string pair = keyImpl(forCcy, domCcy);
182+
string pair = forCcy.code() + domCcy.code();
182183
Handle<YieldTermStructure> domCcyCurve =
183184
market_->discountCurve(domCcy.code(), configuration(MarketContext::pricing));
184185
Handle<YieldTermStructure> forCcyCurve =

OREData/ored/portfolio/builders/fxforward.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ using namespace QuantLib;
2929
using namespace QuantExt;
3030

3131
QuantLib::ext::shared_ptr<PricingEngine> CamAmcFxForwardEngineBuilder::engineImpl(const Currency& forCcy,
32-
const Currency& domCcy) {
32+
const Currency& domCcy,
33+
const std::string& discountCurve) {
3334

3435
QL_REQUIRE(domCcy != forCcy, "CamAmcFxForwardEngineBuilder: domCcy = forCcy = " << domCcy.code());
3536

@@ -76,7 +77,8 @@ QuantLib::ext::shared_ptr<PricingEngine> CamAmcFxForwardEngineBuilder::engineImp
7677
}
7778

7879
QuantLib::ext::shared_ptr<PricingEngine> AmcCgFxForwardEngineBuilder::engineImpl(const Currency& forCcy,
79-
const Currency& domCcy) {
80+
const Currency& domCcy,
81+
const std::string& discountCurve) {
8082

8183
QL_REQUIRE(domCcy != forCcy, "AmcCgFxForwardEngineBuilder: domCcy = forCcy = " << domCcy.code());
8284

OREData/ored/portfolio/builders/fxforward.hpp

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,14 @@
2323

2424
#pragma once
2525

26-
#include <boost/make_shared.hpp>
2726
#include <ored/portfolio/builders/cachingenginebuilder.hpp>
2827
#include <ored/portfolio/enginefactory.hpp>
28+
#include <ored/utilities/marketdata.hpp>
29+
2930
#include <qle/pricingengines/discountingfxforwardengine.hpp>
3031

32+
#include <boost/make_shared.hpp>
33+
3134
namespace ore {
3235
namespace data {
3336

@@ -36,15 +39,17 @@ namespace data {
3639
\ingroup builders
3740
*/
3841
class FxForwardEngineBuilderBase
39-
: public CachingPricingEngineBuilder<string, const Currency&, const Currency&> {
42+
: public CachingPricingEngineBuilder<string, const Currency&, const Currency&, const string&> {
4043
public:
4144
FxForwardEngineBuilderBase(const std::string& model, const std::string& engine)
4245
: CachingEngineBuilder(model, engine, {"FxForward"}) {}
4346

4447
protected:
45-
string keyImpl(const Currency& forCcy, const Currency& domCcy) override {
46-
return forCcy.code() + domCcy.code();
48+
string keyImpl(const Currency& forCcy, const Currency& domCcy, const std::string& discountCurve) override {
49+
return forCcy.code() + domCcy.code() + "_" + discountCurve;
4750
}
51+
52+
std::string discountCurve_;
4853
};
4954

5055
//! Engine Builder for FX Forwards
@@ -56,12 +61,16 @@ class FxForwardEngineBuilder : public FxForwardEngineBuilderBase {
5661
FxForwardEngineBuilder() : FxForwardEngineBuilderBase("DiscountedCashflows", "DiscountingFxForwardEngine") {}
5762

5863
protected:
59-
QuantLib::ext::shared_ptr<PricingEngine> engineImpl(const Currency& forCcy, const Currency& domCcy) override {
60-
string pair = keyImpl(forCcy, domCcy);
64+
QuantLib::ext::shared_ptr<PricingEngine> engineImpl(const Currency& forCcy, const Currency& domCcy,
65+
const std::string& discountCurve) override {
66+
std::cout << "getting curve '" << discountCurve << "'" << std::endl;
67+
string pair = forCcy.code() + domCcy.code();
6168
return QuantLib::ext::make_shared<QuantExt::DiscountingFxForwardEngine>(
6269
domCcy, market_->discountCurve(domCcy.code(), configuration(MarketContext::pricing)), forCcy,
6370
market_->discountCurve(forCcy.code(), configuration(MarketContext::pricing)),
64-
market_->fxRate(pair, configuration(MarketContext::pricing)));
71+
market_->fxRate(pair, configuration(MarketContext::pricing)), QuantLib::Date(), QuantLib::Date(),
72+
discountCurve.empty() ? Handle<YieldTermStructure>{}
73+
: indexOrYieldCurve(market_, discountCurve, configuration(MarketContext::pricing)));
6574
}
6675
};
6776

@@ -75,7 +84,8 @@ class CamAmcFxForwardEngineBuilder : public FxForwardEngineBuilderBase {
7584
stickyCloseOutDates_(stickyCloseOutDates) {}
7685

7786
protected:
78-
QuantLib::ext::shared_ptr<PricingEngine> engineImpl(const Currency& forCcy, const Currency& domCcy) override;
87+
QuantLib::ext::shared_ptr<PricingEngine> engineImpl(const Currency& forCcy, const Currency& domCcy,
88+
const std::string& discountCurve) override;
7989

8090
private:
8191
const QuantLib::ext::shared_ptr<QuantExt::CrossAssetModel> cam_;
@@ -93,7 +103,8 @@ class AmcCgFxForwardEngineBuilder : public FxForwardEngineBuilderBase {
93103
}
94104

95105
protected:
96-
QuantLib::ext::shared_ptr<PricingEngine> engineImpl(const Currency& forCcy, const Currency& domCcy) override;
106+
QuantLib::ext::shared_ptr<PricingEngine> engineImpl(const Currency& forCcy, const Currency& domCcy,
107+
const std::string& discountCurve) override;
97108

98109
private:
99110
const QuantLib::ext::shared_ptr<ore::data::ModelCG> modelCg_;

OREData/ored/portfolio/fxforward.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,8 @@ void FxForward::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFact
156156
instrument_.reset(new VanillaInstrument(instrument));
157157

158158
// set pricing engine
159-
instrument_->qlInstrument()->setPricingEngine(fxBuilder->engine(boughtCcy, soldCcy));
159+
instrument_->qlInstrument()->setPricingEngine(
160+
fxBuilder->engine(boughtCcy, soldCcy, envelope().additionalField("discount_curve", false)));
160161
setSensitivityTemplate(*fxBuilder);
161162
addProductModelEngine(*fxBuilder);
162163

OREData/ored/portfolio/fxoption.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ void FxOption::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFacto
178178
QuantLib::ext::shared_ptr<EngineBuilder> builder = engineFactory->builder("FxForward");
179179
auto fxBuilder = QuantLib::ext::dynamic_pointer_cast<FxForwardEngineBuilderBase>(builder);
180180
QL_REQUIRE(fxBuilder, "FxOption::build(): internal error: could not cast to FxForwardEngineBuilderBase");
181-
qlinstr->setPricingEngine(fxBuilder->engine(boughtCcy,soldCcy));
181+
qlinstr->setPricingEngine(fxBuilder->engine(boughtCcy, soldCcy, {}));
182182
auto configuration = fxBuilder->configuration(MarketContext::pricing);
183183

184184
maturity_ = expiryDate_;

OREData/ored/portfolio/fxswap.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,12 @@ void FxSwap::build(const QuantLib::ext::shared_ptr<EngineFactory>& engineFactory
6363
QL_REQUIRE(builder, "No builder found for " << tradeType_);
6464
QuantLib::ext::shared_ptr<FxForwardEngineBuilderBase> fxBuilder =
6565
QuantLib::ext::dynamic_pointer_cast<FxForwardEngineBuilderBase>(builder);
66-
instNear_->setPricingEngine(fxBuilder->engine(nearSoldCcy, nearBoughtCcy));
66+
instNear_->setPricingEngine(fxBuilder->engine(nearSoldCcy, nearBoughtCcy, {}));
6767
setSensitivityTemplate(*fxBuilder);
6868
addProductModelEngine(*fxBuilder);
6969
instFar_.reset(
7070
new QuantExt::FxForward(farBoughtAmount_, nearSoldCcy, farSoldAmount_, nearBoughtCcy, farDate, false));
71-
instFar_->setPricingEngine(fxBuilder->engine(nearSoldCcy, nearBoughtCcy));
71+
instFar_->setPricingEngine(fxBuilder->engine(nearSoldCcy, nearBoughtCcy, {}));
7272

7373
DLOG("FxSwap::build(): Near NPV = " << instNear_->NPV());
7474
DLOG("FxSwap::build(): Far NPV = " << instFar_->NPV());

OREData/ored/utilities/inflationstartdate.cpp

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,29 @@ using QuantLib::Days;
2727
std::pair<QuantLib::Date, QuantLib::Period> getStartAndLag(const QuantLib::Date& asof,
2828
const InflationSwapConvention& conv) {
2929

30+
Date adjustedAsOf = asof;
31+
if (conv.startDelay() != 0) {
32+
adjustedAsOf = conv.fixCalendar().advance(asof, conv.startDelay() * Days, conv.startDelayConvention());
33+
}
34+
3035
using IPR = InflationSwapConvention::PublicationRoll;
3136

3237
// If no roll schedule, just return (as of, convention's obs lag).
3338
if (conv.publicationRoll() == IPR::None) {
34-
return make_pair(asof, Period());
39+
return make_pair(adjustedAsOf, Period());
3540
}
3641

3742
// If there is a publication roll, call getStart to retrieve the date.
38-
Date d = getInflationSwapStart(asof, conv);
43+
Date d = getInflationSwapStart(adjustedAsOf, conv);
3944

4045
// Date in inflation period related to the inflation index value.
4146
Date dateInPeriod = d - Period(conv.index()->frequency());
4247

43-
// Find period between dateInPeriod and asof. This will be the inflation curve's obsLag.
44-
QL_REQUIRE(dateInPeriod < asof, "InflationCurve: expected date in inflation period ("
45-
<< io::iso_date(dateInPeriod) << ") to be before the as of date ("
46-
<< io::iso_date(asof) << ").");
47-
Period curveObsLag = (asof - dateInPeriod) * Days;
48+
// Find period between dateInPeriod and adjustedAsOf. This will be the inflation curve's obsLag.
49+
QL_REQUIRE(dateInPeriod < adjustedAsOf, "InflationCurve: expected date in inflation period ("
50+
<< io::iso_date(dateInPeriod) << ") to be before the as of date ("
51+
<< io::iso_date(adjustedAsOf) << ").");
52+
Period curveObsLag = (adjustedAsOf - dateInPeriod) * Days;
4853

4954
return make_pair(d, curveObsLag);
5055
}

0 commit comments

Comments
 (0)