Skip to content

Commit 1bb2f4e

Browse files
committed
Merge branch 'feature/QPR-13659' into 'master'
Resolve QPR-13659 Start Delay for inflation swaps conventions Closes QPR-13659 See merge request qs/oreplus!3096
2 parents a8e44a8 + 5969ef4 commit 1bb2f4e

6 files changed

Lines changed: 38 additions & 14 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/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
}

OREData/ored/utilities/inflationstartdate.hpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,8 @@ namespace data {
3333
// convention. In general, we take this simply to be (as of date, Period()). However, for AU CPI for
3434
// example, this is more complicated and we need to account for this here if the inflation swap conventions provide
3535
// us with a publication schedule and tell us to roll on that schedule.
36-
std::pair<QuantLib::Date, QuantLib::Period> getStartAndLag(const QuantLib::Date& asof,
37-
const InflationSwapConvention& conv);
38-
36+
std::pair<QuantLib::Date, QuantLib::Period>
37+
getStartAndLag(const QuantLib::Date& asof, const InflationSwapConvention& conv);
3938

4039
QuantLib::Date getInflationSwapStart(const QuantLib::Date& asof, const InflationSwapConvention& conv);
4140

xsd/conventions.xsd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,8 @@
293293
<xs:element type="businessDayConvention" name="InflationConvention" minOccurs="1" maxOccurs="1"/>
294294
<xs:element type="publicationRoll" name="PublicationRoll" minOccurs="0" maxOccurs="1"/>
295295
<xs:element type="scheduleData" name="PublicationSchedule" minOccurs="0" maxOccurs="1"/>
296+
<xs:element type="xs:string" name="StartDelay" minOccurs="0" maxOccurs="1"/>
297+
<xs:element type="businessDayConvention" name="StartDelayConvention" minOccurs="0" maxOccurs="1"/>
296298
</xs:all>
297299
</xs:complexType>
298300

0 commit comments

Comments
 (0)