Skip to content

Commit 1700242

Browse files
author
jenkins
committed
git subrepo pull (merge) ore
subrepo: subdir: "ore" merged: "b5911700a4" upstream: origin: "git@gitlab.acadiasoft.net:qs/ore.git" branch: "master" commit: "79989cbad1" git-subrepo: version: "0.4.6" origin: "https://github.com/ingydotnet/git-subrepo" commit: "110b9eb"
2 parents c9374a4 + 79989cb commit 1700242

9 files changed

Lines changed: 127 additions & 15 deletions

File tree

Docs/UserGuide/tradecomponents/legdatanotionals.tex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ \subsubsection{Leg Data and Notionals}
5252

5353
Allowable values: See Table \ref{tab:currency} \lstinline!Currency!. When \lstinline!LegType! is \emph{Equity}, Minor Currencies in Table \ref{tab:currency} are also allowable.
5454

55-
\item PaymentCalendar [Optional]: The payment calendar of the leg coupons. The \lstinline!PaymentCalendar! is used in conjuction with the \lstinline!PaymentConvention! and the \lstinline!PaymentLag! to determine the payments dates, unless the \lstinline!PaymentDates! node is used which defines the payment dates explicitly.
55+
\item PaymentCalendar [Optional]: The payment calendar of the leg coupons. The \lstinline!PaymentCalendar! is used in conjunction with the \lstinline!PaymentConvention! and the \lstinline!PaymentLag! to determine the payments dates, unless the \lstinline!PaymentDates! node is used which defines the payment dates explicitly.
5656

5757
Allowable values: See Table \ref{tab:calendar} \lstinline!Calendar!. If left blank or omitted, defaults to the calendar in the \lstinline!ScheduleData! node, unless \lstinline!LegType! is \emph{Floating} and \lstinline!Index! is OIS, in which case this defaults to the index calendar.
5858

@@ -62,7 +62,7 @@ \subsubsection{Leg Data and Notionals}
6262

6363
Allowable values: See Table \ref{tab:convention}.
6464

65-
\item PaymentLag [optional]: The payment lag applies to Fixed legs, Equity legs, and Floating legs with Ibor and OIS indices (but not to BMA/SIFMA indices). \\
65+
\item PaymentLag [optional]: The payment lag applies to Fixed legs, Equity legs, and Floating legs with Ibor and OIS indices (but not to BMA/SIFMA indices), as well as CPI legs and Zero Coupon Fixed legs. \\
6666
PaymentLag is also not supported for CapFloor Floating legs that have Ibor coupons with sub periods (HasSubPeriods = \emph{true}), nor for CapFloor Floating legs with averaged ON coupons (IsAveraged = \emph{true}).
6767

6868
Allowable values: Any valid period, i.e. a non-negative whole number, optionally followed by \emph{D} (days), \emph{W} (weeks), \emph{M} (months),

OREData/ored/marketdata/fxvolcurve.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
22
Copyright (C) 2016 Quaternion Risk Management Ltd
3+
Copyright (C) 2023 Skandinaviska Enskilda Banken AB (publ)
34
All rights reserved.
45
56
This file is part of ORE, a free-software/open-source library
@@ -207,6 +208,15 @@ void FXVolCurve::buildSmileDeltaCurve(Date asof, FXVolatilityCurveSpec spec, con
207208
}
208209
}
209210

211+
QuantExt::InterpolatedSmileSection::InterpolationMethod interp;
212+
if (config->smileInterpolation() == FXVolatilityCurveConfig::SmileInterpolation::Linear)
213+
interp = QuantExt::InterpolatedSmileSection::InterpolationMethod::Linear;
214+
else if (config->smileInterpolation() == FXVolatilityCurveConfig::SmileInterpolation::Cubic)
215+
interp = QuantExt::InterpolatedSmileSection::InterpolationMethod::CubicSpline;
216+
else {
217+
QL_FAIL("Delta FX vol surface: invalid interpolation, expected Linear, Cubic");
218+
}
219+
210220
// daycounter used for interpolation in time.
211221
// TODO: push into conventions or config
212222
DayCounter dc = config->dayCounter();
@@ -217,7 +227,7 @@ void FXVolCurve::buildSmileDeltaCurve(Date asof, FXVolatilityCurveSpec spec, con
217227
[](const std::pair<Real, string>& x) { return x.first; });
218228
vol_ = boost::make_shared<QuantExt::BlackVolatilitySurfaceDelta>(
219229
asof, dates, putDeltasNum, callDeltasNum, hasATM, blackVolMatrix, dc, cal, fxSpot_, domYts_, forYts_,
220-
deltaType_, atmType_, boost::none, switchTenor_, longTermDeltaType_, longTermAtmType_);
230+
deltaType_, atmType_, boost::none, switchTenor_, longTermDeltaType_, longTermAtmType_, boost::none, interp);
221231

222232
vol_->enableExtrapolation();
223233
}

OREData/ored/portfolio/legdata.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,8 @@ Leg makeZCFixedLeg(const LegData& data, const QuantLib::Date& openEndDateReplace
10581058
paymentCalendar = parseCalendar(data.paymentCalendar());
10591059

10601060
BusinessDayConvention payConvention = parseBusinessDayConvention(data.paymentConvention());
1061+
PaymentLag paymentLag = parsePaymentLag(data.paymentLag());
1062+
Natural paymentLagDays = boost::apply_visitor(PaymentLagInteger(), paymentLag);
10611063

10621064
DayCounter dc = parseDayCounter(data.dayCounter());
10631065

@@ -1088,7 +1090,7 @@ Leg makeZCFixedLeg(const LegData& data, const QuantLib::Date& openEndDateReplace
10881090
double currentNotional = i < notionals.size() ? notionals[i] : notionals.back();
10891091
double currentRate = i < rates.size() ? rates[i] : rates.back();
10901092
cpnDates.push_back(dates[i + 1]);
1091-
Date paymentDate = paymentCalendar.adjust(dates[i + 1], payConvention);
1093+
Date paymentDate = paymentCalendar.advance(dates[i + 1], paymentLagDays, Days, payConvention);
10921094
leg.push_back(boost::make_shared<ZeroFixedCoupon>(paymentDate, currentNotional, currentRate, dc, cpnDates, comp,
10931095
zcFixedLegData->subtractNotional()));
10941096
}
@@ -1663,6 +1665,7 @@ Leg makeCPILeg(const LegData& data, const boost::shared_ptr<ZeroInflationIndex>&
16631665
bool finalFlowCapFloor = cpiLegData->finalFlowCap() != Null<Real>() || cpiLegData->finalFlowFloor() != Null<Real>();
16641666

16651667
applyAmortization(notionals, data, schedule, false);
1668+
PaymentLag paymentLag = parsePaymentLag(data.paymentLag());
16661669

16671670
QuantExt::CPILeg cpiLeg =
16681671
QuantExt::CPILeg(schedule, index,
@@ -1673,6 +1676,7 @@ Leg makeCPILeg(const LegData& data, const boost::shared_ptr<ZeroInflationIndex>&
16731676
.withPaymentDayCounter(dc)
16741677
.withPaymentAdjustment(bdc)
16751678
.withPaymentCalendar(paymentCalendar)
1679+
.withPaymentLag(boost::apply_visitor(PaymentLagInteger(), paymentLag))
16761680
.withFixedRates(rates)
16771681
.withObservationInterpolation(interpolationMethod)
16781682
.withSubtractInflationNominal(cpiLegData->subtractInflationNominal())

QuantExt/qle/cashflows/cpicoupon.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,11 @@ CPILeg& CPILeg::withPaymentCalendar(const Calendar& cal) {
277277
return *this;
278278
}
279279

280+
CPILeg& CPILeg::withPaymentLag(Natural lag) {
281+
paymentLag_ = lag;
282+
return *this;
283+
}
284+
280285
CPILeg& CPILeg::withFixingDays(Natural fixingDays) {
281286
fixingDays_ = std::vector<Natural>(1, fixingDays);
282287
return *this;
@@ -356,7 +361,7 @@ CPILeg::operator Leg() const {
356361
for (Size i = 0; i < n; ++i) {
357362
refStart = start = schedule_.date(i);
358363
refEnd = end = schedule_.date(i + 1);
359-
Date paymentDate = paymentCalendar_.adjust(end, paymentAdjustment_);
364+
Date paymentDate = paymentCalendar_.advance(end, paymentLag_, Days, paymentAdjustment_);
360365

361366
Date exCouponDate;
362367
if (exCouponPeriod_ != Period()) {
@@ -395,10 +400,13 @@ CPILeg::operator Leg() const {
395400
}
396401

397402
// in CPI legs you always have a notional flow of some sort
398-
Date paymentDate = paymentCalendar_.adjust(schedule_.date(n), paymentAdjustment_);
399403

404+
// Previous implementations didn't differentiate the observation and payment dates
405+
Date observationDate = paymentCalendar_.adjust(schedule_.date(n), paymentAdjustment_);
406+
Date paymentDate = paymentCalendar_.advance(schedule_.date(n), paymentLag_, Days, paymentAdjustment_);
407+
400408
ext::shared_ptr<CPICashFlow> xnl = ext::make_shared<CPICashFlow>(
401-
detail::get(notionals_, n, 0.0), index_, baseDate, baseCPI_, paymentDate, observationLag_,
409+
detail::get(notionals_, n, 0.0), index_, baseDate, baseCPI_, observationDate, observationLag_,
402410
observationInterpolation_, paymentDate, subtractInflationNominal_);
403411

404412
if (finalFlowCap_ == Null<Real>() && finalFlowFloor_ == Null<Real>()) {

QuantExt/qle/cashflows/cpicoupon.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ class CPILeg {
146146
CPILeg& withPaymentDayCounter(const DayCounter&);
147147
CPILeg& withPaymentAdjustment(BusinessDayConvention);
148148
CPILeg& withPaymentCalendar(const Calendar&);
149+
CPILeg& withPaymentLag(Natural lag);
149150
CPILeg& withFixingDays(Natural fixingDays);
150151
CPILeg& withFixingDays(const std::vector<Natural>& fixingDays);
151152
CPILeg& withObservationInterpolation(CPI::InterpolationType);
@@ -174,6 +175,7 @@ class CPILeg {
174175
DayCounter paymentDayCounter_;
175176
BusinessDayConvention paymentAdjustment_;
176177
Calendar paymentCalendar_;
178+
Natural paymentLag_;
177179
std::vector<Natural> fixingDays_;
178180
CPI::InterpolationType observationInterpolation_;
179181
bool subtractInflationNominal_;

QuantExt/test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ commodityspreadoption.cpp
2222
computeenvironment.cpp
2323
correlationtermstructure.cpp
2424
cpicapfloor.cpp
25+
cpileg.cpp
2526
crcirpp.cpp
2627
crossassetmodel.cpp
2728
crossassetmodel2.cpp

QuantExt/test/cpileg.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
Copyright (C) 2023 Skandinaviska Enskilda Banken AB (publ)
3+
All rights reserved.
4+
5+
This file is part of ORE, a free-software/open-source library
6+
for transparent pricing and risk analysis - http://opensourcerisk.org
7+
8+
ORE is free software: you can redistribute it and/or modify it
9+
under the terms of the Modified BSD License. You should have received a
10+
copy of the license along with this program.
11+
The license is also available online at <http://opensourcerisk.org>
12+
13+
This program is distributed on the basis that it will form a useful
14+
contribution to risk analytics and model standardisation, but WITHOUT
15+
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16+
FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17+
*/
18+
19+
#include "toplevelfixture.hpp"
20+
#include <boost/test/unit_test.hpp>
21+
#include <ql/indexes/inflation/ukrpi.hpp>
22+
#include <ql/termstructures/yield/flatforward.hpp>
23+
#include <ql/time/calendars/all.hpp>
24+
#include <ql/time/daycounters/actualactual.hpp>
25+
#include <qle/cashflows/cpicoupon.hpp>
26+
27+
using namespace QuantLib;
28+
using namespace boost::unit_test_framework;
29+
30+
BOOST_FIXTURE_TEST_SUITE(QuantExtTestSuite, qle::test::TopLevelFixture)
31+
32+
BOOST_AUTO_TEST_SUITE(CpiLegTest)
33+
34+
BOOST_AUTO_TEST_CASE(testCpiLegPaymentLag) {
35+
36+
BOOST_TEST_MESSAGE("Testing QuantExt::CPILeg for payment lag...");
37+
38+
Date evaluationDate(6, October, 2023);
39+
Settings::instance().evaluationDate() = evaluationDate;
40+
Calendar calendar = WeekendsOnly();
41+
DayCounter dayCounter = ActualActual(ActualActual::ISDA);
42+
43+
Date startDate(6, October, 2023);
44+
Date endDate(6, October, 2026);
45+
Schedule fixedSchedule = MakeSchedule()
46+
.from(startDate)
47+
.to(endDate)
48+
.withTenor(Period(6, Months))
49+
.withCalendar(calendar)
50+
.withConvention(ModifiedFollowing)
51+
.backwards();
52+
53+
auto flatYts = ext::shared_ptr<YieldTermStructure>(new FlatForward(evaluationDate, 0.025, dayCounter));
54+
RelinkableHandle<YieldTermStructure> yTS(flatYts);
55+
56+
auto ukrpi = ext::make_shared<UKRPI>();
57+
Leg cpiLeg = QuantExt::CPILeg(fixedSchedule, ukrpi, yTS, 100, Period(3, Months))
58+
.withNotionals(1e6)
59+
.withFixedRates(0.01)
60+
.withPaymentCalendar(calendar)
61+
.withPaymentLag(2);
62+
63+
for (auto& coupon : cpiLeg) {
64+
if (auto cpiCoupon = ext::dynamic_pointer_cast<CPICoupon>(coupon))
65+
// The setup leg will have six regular coupons
66+
BOOST_CHECK_EQUAL(cpiCoupon->date(), cpiCoupon->accrualEndDate() + 2 * Days);
67+
else if (auto cpiNotionalCashflow = ext::dynamic_pointer_cast<CPICashFlow>(coupon))
68+
// and one of these flows
69+
BOOST_CHECK_EQUAL(cpiNotionalCashflow->date(), fixedSchedule.endDate() + 2 * Days);
70+
}
71+
}
72+
73+
BOOST_AUTO_TEST_SUITE_END()
74+
75+
BOOST_AUTO_TEST_SUITE_END()

xsd/curveconfig.xsd

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,6 +1192,13 @@
11921192
</xs:all>
11931193
</xs:complexType>
11941194
</xs:element>
1195+
<xs:element name="CommodityVolatilities" minOccurs="0">
1196+
<xs:complexType>
1197+
<xs:all>
1198+
<xs:element type="reportConfiguration" name="Report" minOccurs="0"/>
1199+
</xs:all>
1200+
</xs:complexType>
1201+
</xs:element>
11951202
<xs:element name="IRSwaptionVolatilities" minOccurs="0">
11961203
<xs:complexType>
11971204
<xs:all>
@@ -1213,9 +1220,14 @@
12131220
<xs:all>
12141221
<xs:element type="bool" name="ReportOnDeltaGrid" minOccurs="0"/>
12151222
<xs:element type="bool" name="ReportOnMoneynessGrid" minOccurs="0"/>
1223+
<xs:element type="bool" name="ReportOnStrikeGrid" minOccurs="0"/>
1224+
<xs:element type="bool" name="ReportOnStrikeSpreadGrid" minOccurs="0"/>
12161225
<xs:element type="xs:string" name="Deltas" minOccurs="0"/>
12171226
<xs:element type="xs:string" name="Moneyness" minOccurs="0"/>
1227+
<xs:element type="xs:string" name="Strikes" minOccurs="0"/>
1228+
<xs:element type="xs:string" name="StrikeSpreads" minOccurs="0"/>
12181229
<xs:element type="xs:string" name="Expiries" minOccurs="0"/>
1230+
<xs:element type="xs:string" name="UnderlyingTenors" minOccurs="0"/>
12191231
</xs:all>
12201232
</xs:complexType>
12211233

xsd/instruments.xsd

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,8 +1007,8 @@
10071007
</xs:element>
10081008
<xs:element type="xs:boolean" name="IsAveraged" minOccurs="0" maxOccurs="1"/>
10091009
<xs:element type="xs:boolean" name="IsInArrears" minOccurs="0" maxOccurs="1"/>
1010-
<xs:element type="xs:integer" name="FutureMonthOffset" minOccurs="0" maxOccurs="1"/>
1011-
<xs:element type="xs:integer" name="DeliveryRollDays" minOccurs="0" maxOccurs="1"/>
1010+
<xs:element type="xs:nonNegativeInteger" name="FutureMonthOffset" minOccurs="0" maxOccurs="1"/>
1011+
<xs:element type="xs:nonNegativeInteger" name="DeliveryRollDays" minOccurs="0" maxOccurs="1"/>
10121012
<xs:element type="xs:nonNegativeInteger" name="DailyExpiryOffset" minOccurs="0" maxOccurs="1"/>
10131013
<xs:element type="xs:boolean" name="IncludePeriodEnd" minOccurs="0" maxOccurs="1"/>
10141014
<xs:element type="xs:boolean" name="ExcludePeriodStart" minOccurs="0" maxOccurs="1"/>
@@ -1073,8 +1073,8 @@
10731073
<xs:element type="xs:float" name="Spread" minOccurs="0" maxOccurs="1"/>
10741074
<xs:element type="commodityQuantityFrequencyType" name="CommodityQuantityFrequency" minOccurs="0" maxOccurs="1"/>
10751075
<xs:element type="commodityPayRelativeToType" name="CommodityPayRelativeTo" minOccurs="0" maxOccurs="1"/>
1076-
<xs:element type="xs:integer" name="FutureMonthOffset" minOccurs="0" maxOccurs="1"/>
1077-
<xs:element type="xs:integer" name="DeliveryRollDays" minOccurs="0" maxOccurs="1"/>
1076+
<xs:element type="xs:nonNegativeInteger" name="FutureMonthOffset" minOccurs="0" maxOccurs="1"/>
1077+
<xs:element type="xs:nonNegativeInteger" name="DeliveryRollDays" minOccurs="0" maxOccurs="1"/>
10781078
<xs:element type="xs:boolean" name="IncludePeriodEnd" minOccurs="0" maxOccurs="1"/>
10791079
<xs:element type="xs:string" name="FXIndex" minOccurs="0"/>
10801080
</xs:all>
@@ -1100,8 +1100,8 @@
11001100
<xs:element type="xs:float" name="Spread" minOccurs="0" maxOccurs="1"/>
11011101
<xs:element type="commodityQuantityFrequencyType" name="CommodityQuantityFrequency" minOccurs="0" maxOccurs="1"/>
11021102
<xs:element type="commodityPayRelativeToType" name="CommodityPayRelativeTo" minOccurs="0" maxOccurs="1"/>
1103-
<xs:element type="xs:integer" name="FutureMonthOffset" minOccurs="0" maxOccurs="1"/>
1104-
<xs:element type="xs:integer" name="DeliveryRollDays" minOccurs="0" maxOccurs="1"/>
1103+
<xs:element type="xs:nonNegativeInteger" name="FutureMonthOffset" minOccurs="0" maxOccurs="1"/>
1104+
<xs:element type="xs:nonNegativeInteger" name="DeliveryRollDays" minOccurs="0" maxOccurs="1"/>
11051105
<xs:element type="xs:boolean" name="IncludePeriodEnd" minOccurs="0" maxOccurs="1"/>
11061106
<xs:element type="xs:string" name="FXIndex" minOccurs="0"/>
11071107
</xs:all>
@@ -1794,8 +1794,8 @@
17941794
<xs:element type="xs:string" name="Exchange" minOccurs="0"/>
17951795
<xs:element type="xs:float" name="Weight" minOccurs="0"/>
17961796
<xs:element type="xs:string" name="PriceType" minOccurs="0"/>
1797-
<xs:element type="xs:int" name="FutureMonthOffset" minOccurs="0"/>
1798-
<xs:element type="xs:int" name="DeliveryRollDays" minOccurs="0"/>
1797+
<xs:element type="xs:nonNegativeInteger" name="FutureMonthOffset" minOccurs="0"/>
1798+
<xs:element type="xs:nonNegativeInteger" name="DeliveryRollDays" minOccurs="0"/>
17991799
<xs:element type="xs:string" name="DeliveryRollCalendar" minOccurs="0"/>
18001800
<xs:element type="xs:string" name="Interpolation" minOccurs="0"/>
18011801
<xs:element type="xs:float" name="BidAskAdjustment" minOccurs="0"/>

0 commit comments

Comments
 (0)