Skip to content

Commit fcdf518

Browse files
pcaspersjenkins
authored andcommitted
Merge branch 'QPR-13369' into 'master'
QPR-13369 add rounding to average commodity cashflows Closes QPR-13369 See merge request qs/oreplus!2750
1 parent 60eef0f commit fcdf518

8 files changed

Lines changed: 47 additions & 20 deletions

File tree

Docs/UserGuide/tradecomponents/commodityfloatingleg.tex

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,9 @@ \subsubsection{Commodity Floating Leg Data}
242242
\item \lstinline!FXIndex! [Optional]: If \lstinline!IsAveraged! is \emph{true} this node allows the fx conversion to be applied daily in the computation of averaged cash flows. It cannot be used with the \lstinline!Indexing! node.
243243

244244
Allowable values: See Table \ref{tab:fxindex_data} for supported fx indices.
245+
246+
\item \lstinline|AvgPricePrecision| [Optional]: This is only applicable when averaging is enabled. Allowed values: non-negative integer. Specifies the number of decimal places to which the average price should be rounded.
247+
245248
\end{itemize}
246249

247250
\begin{listing}[h!]

OREData/ored/portfolio/commoditylegbuilder.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,8 @@ Leg CommodityFloatingLegBuilder::buildLeg(
619619
.withDailyExpiryOffset(dailyExpOffset)
620620
.unrealisedQuantity(floatingLegData->unrealisedQuantity())
621621
.withOffPeakPowerData(offPeakPowerData)
622-
.withFxIndex(fxIndex);
622+
.withFxIndex(fxIndex)
623+
.withAvgPricePrecision(floatingLegData->avgPricePrecision());
623624
} else {
624625
CommodityIndexedCashFlow::PaymentTiming paymentTiming = CommodityIndexedCashFlow::PaymentTiming::InArrears;
625626
if (floatingLegData->commodityPayRelativeTo() == CommodityPayRelativeTo::CalculationPeriodStartDate) {

OREData/ored/portfolio/commoditylegdata.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ CommodityFloatingLegData::CommodityFloatingLegData()
177177
pricingLag_(0), isAveraged_(false), isInArrears_(true), futureMonthOffset_(0),
178178
deliveryRollDays_(0), includePeriodEnd_(true), excludePeriodStart_(true),
179179
hoursPerDay_(Null<Natural>()), useBusinessDays_(true), dailyExpiryOffset_(0),
180-
unrealisedQuantity_(false), lastNDays_(Null<Natural>()), fxIndex_("") {}
180+
unrealisedQuantity_(false), lastNDays_(Null<Natural>()), fxIndex_(""), avgPricePrecision_(QuantLib::Null<QuantLib::Natural>()) {}
181181

182182
CommodityFloatingLegData::CommodityFloatingLegData(
183183
const string& name, CommodityPriceType priceType, const vector<Real>& quantities,
@@ -187,7 +187,8 @@ CommodityFloatingLegData::CommodityFloatingLegData(
187187
const string& pricingCalendar, Natural pricingLag, const vector<string>& pricingDates, bool isAveraged,
188188
bool isInArrears, Natural futureMonthOffset, Natural deliveryRollDays, bool includePeriodEnd,
189189
bool excludePeriodStart, Natural hoursPerDay, bool useBusinessDays, const string& tag,
190-
Natural dailyExpiryOffset, bool unrealisedQuantity, QuantLib::Natural lastNDays, std::string fxIndex)
190+
Natural dailyExpiryOffset, bool unrealisedQuantity, QuantLib::Natural lastNDays, std::string fxIndex,
191+
QuantLib::Natural avgPricePrecision)
191192
: LegAdditionalData(LegType::CommodityFloating, false), name_(name), priceType_(priceType), quantities_(quantities),
192193
quantityDates_(quantityDates), commodityQuantityFrequency_(commodityQuantityFrequency),
193194
commodityPayRelativeTo_(commodityPayRelativeTo), spreads_(spreads), spreadDates_(spreadDates),
@@ -196,7 +197,8 @@ CommodityFloatingLegData::CommodityFloatingLegData(
196197
isInArrears_(isInArrears), futureMonthOffset_(futureMonthOffset), deliveryRollDays_(deliveryRollDays),
197198
includePeriodEnd_(includePeriodEnd), excludePeriodStart_(excludePeriodStart), hoursPerDay_(hoursPerDay),
198199
useBusinessDays_(useBusinessDays), tag_(tag), dailyExpiryOffset_(dailyExpiryOffset),
199-
unrealisedQuantity_(unrealisedQuantity), lastNDays_(lastNDays), fxIndex_(fxIndex) {
200+
unrealisedQuantity_(unrealisedQuantity), lastNDays_(lastNDays), fxIndex_(fxIndex),
201+
avgPricePrecision_(avgPricePrecision) {
200202
indices_.insert("COMM-" + name_);
201203
}
202204

@@ -287,6 +289,12 @@ void CommodityFloatingLegData::fromXML(XMLNode* node) {
287289
if (XMLNode* n = XMLUtils::getChildNode(node, "FXIndex")) {
288290
fxIndex_ = XMLUtils::getNodeValue(n);
289291
}
292+
293+
if (XMLNode* n = XMLUtils::getChildNode(node, "AvgPricePrecision")) {
294+
int precision = parseInteger(XMLUtils::getNodeValue(n));
295+
QL_REQUIRE(precision >= 0, "CommodityFloatingLegData: avgPricePrecision must be non-negative, got " << precision);
296+
avgPricePrecision_ = static_cast<QuantLib::Natural>(precision);
297+
}
290298
}
291299

292300
XMLNode* CommodityFloatingLegData::toXML(XMLDocument& doc) const {
@@ -337,7 +345,9 @@ XMLNode* CommodityFloatingLegData::toXML(XMLDocument& doc) const {
337345
XMLUtils::addChild(doc, node, "LastNDays", static_cast<int>(lastNDays_));
338346
if (!fxIndex_.empty())
339347
XMLUtils::addChild(doc, node, "FXIndex", fxIndex_);
340-
348+
if (avgPricePrecision_ != Null<Natural>()) {
349+
XMLUtils::addChild(doc, node, "AvgPricePrecision", static_cast<int>(avgPricePrecision_));
350+
}
341351
return node;
342352
}
343353

OREData/ored/portfolio/commoditylegdata.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ class CommodityFloatingLegData : public ore::data::LegAdditionalData {
115115
bool excludePeriodStart = true, QuantLib::Natural hoursPerDay = QuantLib::Null<QuantLib::Natural>(),
116116
bool useBusinessDays = true, const std::string& tag = "", QuantLib::Natural dailyExpiryOffset =
117117
QuantLib::Null<QuantLib::Natural>(), bool unrealisedQuantity = false,
118-
QuantLib::Natural lastNDays = QuantLib::Null<QuantLib::Natural>(), std::string fxIndex = "");
118+
QuantLib::Natural lastNDays = QuantLib::Null<QuantLib::Natural>(), std::string fxIndex = "",
119+
QuantLib::Natural avgPricePrecision = QuantLib::Null<QuantLib::Natural>());
119120

120121
//! \name Inspectors
121122
//@{
@@ -146,6 +147,7 @@ class CommodityFloatingLegData : public ore::data::LegAdditionalData {
146147
bool unrealisedQuantity() const { return unrealisedQuantity_; }
147148
QuantLib::Natural lastNDays() const { return lastNDays_; }
148149
std::string const& fxIndex() const { return fxIndex_; }
150+
QuantLib::Natural avgPricePrecision() const { return avgPricePrecision_; }
149151
//@}
150152

151153
//! \name Serialisation
@@ -182,6 +184,7 @@ class CommodityFloatingLegData : public ore::data::LegAdditionalData {
182184
bool unrealisedQuantity_;
183185
QuantLib::Natural lastNDays_;
184186
std::string fxIndex_;
187+
QuantLib::Natural avgPricePrecision_;
185188
};
186189

187190
} // namespace data

OREData/ored/portfolio/commodityswap.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -249,19 +249,16 @@ const std::map<std::string,boost::any>& CommoditySwap::additionalData() const {
249249
std::vector<Real> priceVec;
250250
std::vector<std::string> indexVec;
251251
std::vector<Date> indexExpiryVec, pricingDateVec;
252-
double averagePrice = 0;
253252
for (const auto& kv : indexedAvgFlow->indices()) {
254253
indexVec.push_back(kv.second->name());
255254
indexExpiryVec.push_back(kv.second->expiryDate());
256255
pricingDateVec.push_back(kv.first);
257256
priceVec.push_back(kv.second->fixing(kv.first));
258-
averagePrice += priceVec.back();
259257
}
260-
averagePrice /= indexedAvgFlow->indices().size();
261258
additionalData_["index[" + label + "]"] = indexVec;
262259
additionalData_["indexExpiry[" + label + "]"] = indexExpiryVec;
263260
additionalData_["price[" + label + "]"] = priceVec;
264-
additionalData_["averagePrice[" + label + "]"] = averagePrice;
261+
additionalData_["averagePrice[" + label + "]"] = indexedAvgFlow->fixing();
265262
additionalData_["pricingDate[" + label + "]"] = pricingDateVec;
266263
additionalData_["paymentDate[" + label + "]"] = to_string(indexedAvgFlow->date());
267264
}

QuantExt/qle/cashflows/commodityindexedaveragecashflow.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,14 @@ CommodityIndexedAverageCashFlow::CommodityIndexedAverageCashFlow(
4545
const ext::shared_ptr<FutureExpiryCalculator>& calc, bool includeEndDate, bool excludeStartDate,
4646
bool useBusinessDays, CommodityQuantityFrequency quantityFrequency, Natural hoursPerDay, Natural dailyExpiryOffset,
4747
bool unrealisedQuantity, const boost::optional<pair<Calendar, Real>>& offPeakPowerData,
48-
const ext::shared_ptr<FxIndex>& fxIndex)
48+
const ext::shared_ptr<FxIndex>& fxIndex, QuantLib::Natural avgPricePrecision)
4949
: CommodityCashFlow(quantity, spread, gearing, useFuturePrice, index, fxIndex), startDate_(startDate),
5050
endDate_(endDate),
5151
paymentDate_(paymentDate), pricingCalendar_(pricingCalendar), deliveryDateRoll_(deliveryDateRoll),
5252
futureMonthOffset_(futureMonthOffset), includeEndDate_(includeEndDate), excludeStartDate_(excludeStartDate),
5353
useBusinessDays_(useBusinessDays), quantityFrequency_(quantityFrequency), hoursPerDay_(hoursPerDay),
5454
dailyExpiryOffset_(dailyExpiryOffset), unrealisedQuantity_(unrealisedQuantity),
55-
offPeakPowerData_(offPeakPowerData) {
55+
offPeakPowerData_(offPeakPowerData), avgPricePrecision_(avgPricePrecision) {
5656
init(calc);
5757
}
5858

@@ -64,13 +64,14 @@ CommodityIndexedAverageCashFlow::CommodityIndexedAverageCashFlow(
6464
const ext::shared_ptr<FutureExpiryCalculator>& calc, bool includeEndDate, bool excludeStartDate,
6565
const QuantLib::Date& paymentDateOverride, bool useBusinessDays, CommodityQuantityFrequency quantityFrequency,
6666
Natural hoursPerDay, Natural dailyExpiryOffset, bool unrealisedQuantity,
67-
const boost::optional<pair<Calendar, Real>>& offPeakPowerData, const ext::shared_ptr<FxIndex>& fxIndex)
67+
const boost::optional<pair<Calendar, Real>>& offPeakPowerData, const ext::shared_ptr<FxIndex>& fxIndex,
68+
QuantLib::Natural avgPricePrecision)
6869
: CommodityCashFlow(quantity, spread, gearing, useFuturePrice, index, fxIndex), startDate_(startDate), endDate_(endDate),
6970
paymentDate_(paymentDateOverride), pricingCalendar_(pricingCalendar),
7071
deliveryDateRoll_(deliveryDateRoll), futureMonthOffset_(futureMonthOffset), includeEndDate_(includeEndDate),
7172
excludeStartDate_(excludeStartDate), useBusinessDays_(useBusinessDays), quantityFrequency_(quantityFrequency),
7273
hoursPerDay_(hoursPerDay), dailyExpiryOffset_(dailyExpiryOffset), unrealisedQuantity_(unrealisedQuantity),
73-
offPeakPowerData_(offPeakPowerData) {
74+
offPeakPowerData_(offPeakPowerData), avgPricePrecision_(avgPricePrecision) {
7475

7576
// Derive the payment date
7677
if (paymentDate_ == Date()) {
@@ -100,6 +101,11 @@ void CommodityIndexedAverageCashFlow::performCalculations() const {
100101
}
101102
}
102103

104+
if (avgPricePrecision_ != QuantLib::Null<QuantLib::Natural>()) {
105+
QuantLib::ClosestRounding round(avgPricePrecision_);
106+
averagePrice_ = round(averagePrice_);
107+
}
108+
103109
// Amount is just average price times quantity
104110
// In case of Foreign currency settlement, the spread must be expressed in Foreign currency units
105111
amount_ = periodQuantity_ * (gearing_ * averagePrice_ + spread_);
@@ -284,7 +290,7 @@ CommodityIndexedAverageLeg::CommodityIndexedAverageLeg(const Schedule& schedule,
284290
deliveryDateRoll_(0), futureMonthOffset_(0), payAtMaturity_(false), includeEndDate_(true),
285291
excludeStartDate_(true), useBusinessDays_(true),
286292
quantityFrequency_(CommodityQuantityFrequency::PerCalculationPeriod), hoursPerDay_(Null<Natural>()),
287-
dailyExpiryOffset_(Null<Natural>()), unrealisedQuantity_(false) {}
293+
dailyExpiryOffset_(Null<Natural>()), unrealisedQuantity_(false), avgPricePrecision_(Null<Natural>()) {}
288294

289295
CommodityIndexedAverageLeg& CommodityIndexedAverageLeg::withQuantities(Real quantity) {
290296
quantities_ = vector<Real>(1, quantity);
@@ -419,6 +425,11 @@ CommodityIndexedAverageLeg& CommodityIndexedAverageLeg::withOffPeakPowerData(con
419425
return *this;
420426
}
421427

428+
CommodityIndexedAverageLeg& CommodityIndexedAverageLeg::withAvgPricePrecision(QuantLib::Natural precision){
429+
avgPricePrecision_ = precision;
430+
return *this;
431+
}
432+
422433
CommodityIndexedAverageLeg::operator Leg() const {
423434

424435
// Number of commodity indexed average cashflows
@@ -469,7 +480,7 @@ CommodityIndexedAverageLeg::operator Leg() const {
469480
quantity, start, end, paymentLag_, paymentCalendar_, paymentConvention_, index_, pricingCalendar_, spread,
470481
gearing, paymentTiming_, useFuturePrice_, deliveryDateRoll_, futureMonthOffset_, calc_, includeEnd,
471482
excludeStart, paymentDate, useBusinessDays_, quantityFrequency_, hoursPerDay_, dailyExpiryOffset_,
472-
unrealisedQuantity_, offPeakPowerData_, fxIndex_));
483+
unrealisedQuantity_, offPeakPowerData_, fxIndex_, avgPricePrecision_));
473484
}
474485

475486
return leg;

QuantExt/qle/cashflows/commodityindexedaveragecashflow.hpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class CommodityIndexedAverageCashFlow : public CommodityCashFlow {
6060
QuantLib::Natural hoursPerDay = QuantLib::Null<QuantLib::Natural>(),
6161
QuantLib::Natural dailyExpiryOffset = QuantLib::Null<QuantLib::Natural>(), bool unrealisedQuantity = false,
6262
const boost::optional<std::pair<QuantLib::Calendar, QuantLib::Real>>& offPeakPowerData = boost::none,
63-
const ext::shared_ptr<FxIndex>& fxIndex = nullptr);
63+
const ext::shared_ptr<FxIndex>& fxIndex = nullptr, QuantLib::Natural avgPricePrecision = QuantLib::Null<QuantLib::Natural>());
6464

6565
//! Constructor that deduces payment date from \p endDate using payment conventions
6666
CommodityIndexedAverageCashFlow(
@@ -76,7 +76,7 @@ class CommodityIndexedAverageCashFlow : public CommodityCashFlow {
7676
QuantLib::Natural hoursPerDay = QuantLib::Null<QuantLib::Natural>(),
7777
QuantLib::Natural dailyExpiryOffset = QuantLib::Null<QuantLib::Natural>(), bool unrealisedQuantity = false,
7878
const boost::optional<std::pair<QuantLib::Calendar, QuantLib::Real>>& offPeakPowerData = boost::none,
79-
const ext::shared_ptr<FxIndex>& fxIndex = nullptr);
79+
const ext::shared_ptr<FxIndex>& fxIndex = nullptr, QuantLib::Natural avgPricePrecision = QuantLib::Null<QuantLib::Natural>());
8080

8181
//! \name Inspectors
8282
//@{
@@ -159,7 +159,7 @@ class CommodityIndexedAverageCashFlow : public CommodityCashFlow {
159159
QuantLib::Real periodQuantity_;
160160
boost::optional<std::pair<QuantLib::Calendar, QuantLib::Real>> offPeakPowerData_;
161161
mutable QuantLib::Real averagePrice_;
162-
162+
QuantLib::Natural avgPricePrecision_;
163163
// Populated only when offPeakPowerData_ is provided.
164164
std::map<QuantLib::Date, QuantLib::Real> weights_;
165165

@@ -203,7 +203,7 @@ class CommodityIndexedAverageLeg {
203203
CommodityIndexedAverageLeg&
204204
withOffPeakPowerData(const boost::optional<std::pair<QuantLib::Calendar, QuantLib::Real>>& offPeakPowerData);
205205
CommodityIndexedAverageLeg& withFxIndex(const ext::shared_ptr<FxIndex>& fxIndex);
206-
206+
CommodityIndexedAverageLeg& withAvgPricePrecision(QuantLib::Natural precision = QuantLib::Null<QuantLib::Natural>());
207207
operator Leg() const;
208208

209209
private:
@@ -232,6 +232,7 @@ class CommodityIndexedAverageLeg {
232232
bool unrealisedQuantity_;
233233
boost::optional<std::pair<QuantLib::Calendar, QuantLib::Real>> offPeakPowerData_;
234234
ext::shared_ptr<FxIndex> fxIndex_;
235+
QuantLib::Natural avgPricePrecision_;
235236
};
236237

237238
} // namespace QuantExt

xsd/instruments.xsd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,7 @@
10431043
<xs:element type="xs:positiveInteger" name="LastNDays" minOccurs="0" maxOccurs="1"/>
10441044
<xs:element type="xs:string" name="Tag" minOccurs="0" maxOccurs="1"/>
10451045
<xs:element type="xs:string" name="FXIndex" minOccurs="0"/>
1046+
<xs:element type="xs:nonNegativeInteger" name="AvgPricePrecision" minOccurs="0"/>
10461047
</xs:all>
10471048
</xs:complexType>
10481049
</xs:element>

0 commit comments

Comments
 (0)