Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
commodityindexedcashflow.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2019 Quaternion Risk Management Ltd
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 <ql/utilities/vectors.hpp>
21
22using namespace QuantLib;
23using std::vector;
24
25namespace QuantExt {
26
27CommodityIndexedCashFlow::CommodityIndexedCashFlow(Real quantity, const Date& pricingDate, const Date& paymentDate,
28 const ext::shared_ptr<CommodityIndex>& index, Real spread,
29 Real gearing, bool useFuturePrice, const Date& contractDate,
30 const ext::shared_ptr<FutureExpiryCalculator>& calc,
31 QuantLib::Natural dailyExpiryOffset, const ext::shared_ptr<FxIndex>& fxIndex)
32 : CommodityCashFlow(quantity, spread, gearing, useFuturePrice, index, fxIndex), pricingDate_(pricingDate),
33 paymentDate_(paymentDate), futureMonthOffset_(0), periodQuantity_(quantity), dailyExpiryOffset_(dailyExpiryOffset),
34 isAveraging_(false) {
35
36 QL_REQUIRE(paymentDate != Date(), "CommodityIndexedCashFlow: payment date is null");
37 init(calc, contractDate);
38}
39
40CommodityIndexedCashFlow::CommodityIndexedCashFlow(
41 Real quantity, const Date& startDate, const Date& endDate, const ext::shared_ptr<CommodityIndex>& index,
42 Natural paymentLag, const Calendar& paymentCalendar, BusinessDayConvention paymentConvention, Natural pricingLag,
43 const Calendar& pricingLagCalendar, Real spread, Real gearing, PaymentTiming paymentTiming, bool isInArrears,
44 bool useFuturePrice, bool useFutureExpiryDate, Natural futureMonthOffset,
45 const ext::shared_ptr<FutureExpiryCalculator>& calc, const QuantLib::Date& paymentDateOverride,
46 const QuantLib::Date& pricingDateOverride, QuantLib::Natural dailyExpiryOffset,
47 const ext::shared_ptr<FxIndex>& fxIndex, const bool isAveragingWithBalanceMonth,
48 const QuantLib::Calendar& pricingCalendar, bool includeEndDate, bool excludeStartDate)
49 : CommodityCashFlow(quantity, spread, gearing, useFuturePrice, index, fxIndex), pricingDate_(pricingDateOverride),
50 paymentDate_(paymentDateOverride), useFutureExpiryDate_(useFutureExpiryDate),
51 futureMonthOffset_(futureMonthOffset), periodQuantity_(quantity), dailyExpiryOffset_(dailyExpiryOffset),
52 isAveraging_(isAveragingWithBalanceMonth) {
53
54 // Derive the pricing date if an explicit override has not been provided
55 if (pricingDate_ == Date()) {
56 pricingDate_ = isInArrears ? endDate : startDate;
57 if (!useFuturePrice_ || !useFutureExpiryDate_) {
58 // We just use the pricing date rules to get the pricing date
59 pricingDate_ = pricingLagCalendar.advance(pricingDate_, -static_cast<Integer>(pricingLag), Days, Preceding);
60 } else {
61 // We need to use the expiry date of the future contract
62 QL_REQUIRE(calc, "CommodityIndexedCashFlow needs a valid future "
63 << "expiry calculator when using first future");
64 Date expiry = calc->expiryDate(pricingDate_, futureMonthOffset_);
65 if (dailyExpiryOffset_ != Null<Natural>()) {
66 expiry = index_->fixingCalendar().advance(expiry, dailyExpiryOffset_ * Days);
67 }
68 pricingDate_ = expiry;
69 }
70 }
71
72 // We may not need the month and year if we are not using a future settlement price but get them
73 // and pass them here in any case to init
74 Date ref = isInArrears ? endDate : startDate;
75
76 init(calc, ref, paymentTiming, startDate, endDate, paymentLag, paymentConvention, paymentCalendar, pricingCalendar,
77 includeEndDate, excludeStartDate);
78}
79
80void CommodityIndexedCashFlow::performCalculations() const {
81 Date today = Settings::instance().evaluationDate();
82 price_ = 0.0;
83 if (isAveragingFrontMonthCashflow(today)) {
84 for (const auto& pd : spotAveragingPricingDates_) {
85 double fxRate = (fxIndex_) ? this->fxIndex()->fixing(pricingDate_) : 1.0;
86 if (pd <= today) {
87 price_ += fxRate * spotIndex_->fixing(pd);
88 } else {
89 price_ += fxRate * index_->fixing(pricingDate_);
90 }
91 }
92 price_ /= static_cast<double>(spotAveragingPricingDates_.size());
93 } else {
94 double fxRate = (fxIndex_) ? this->fxIndex()->fixing(pricingDate_) : 1.0;
95 price_ = fxRate * index_->fixing(pricingDate_);
96 }
97 amount_ = periodQuantity_ * gearing_ * (price_ + spread_);
98}
99
100Real CommodityIndexedCashFlow::amount() const {
101 calculate();
102 return amount_;
103}
104
105Real CommodityIndexedCashFlow::fixing() const {
106 calculate();
107 return price_;
108}
109
110bool CommodityIndexedCashFlow::isAveragingFrontMonthCashflow(const QuantLib::Date& asof) const {
111 return (isAveraging_ && useFuturePrice_ && !spotAveragingPricingDates_.empty() &&
112 *spotAveragingPricingDates_.begin() <= asof && spotIndex_ != nullptr);
113}
114
115void CommodityIndexedCashFlow::accept(AcyclicVisitor& v) {
116 if (Visitor<CommodityIndexedCashFlow>* v1 = dynamic_cast<Visitor<CommodityIndexedCashFlow>*>(&v))
117 v1->visit(*this);
118 else
119 CommodityCashFlow::accept(v);
120}
121
122void CommodityIndexedCashFlow::setPeriodQuantity(Real periodQuantity) { periodQuantity_ = periodQuantity; }
123
124void CommodityIndexedCashFlow::init(const ext::shared_ptr<FutureExpiryCalculator>& calc, const Date& contractDate,
125 const PaymentTiming paymentTiming, const Date& startDate, const Date& endDate,
126 const Natural paymentLag, const BusinessDayConvention paymentConvention,
127 const Calendar& paymentCalendar,
128 const QuantLib::Calendar& pricingCalendar,
129 bool includeEndDate, bool excludeStartDate) {
130
131 pricingDate_ = index_->fixingCalendar().adjust(pricingDate_, Preceding);
132
133 // If we are using the future settlement price as the reference price, then we need to create the
134 // relevant "future index" here and update the cashflow's index with it.
135 Date expiry;
136 if (useFuturePrice_) {
137 QL_REQUIRE(calc, "CommodityIndexedCashFlow needs a valid future expiry calculator when using "
138 << "the future settlement price as reference price");
139 expiry = calc->expiryDate(contractDate, futureMonthOffset_);
140 if (dailyExpiryOffset_ != Null<Natural>()) {
141 expiry = index_->fixingCalendar().advance(expiry, dailyExpiryOffset_ * Days);
142 }
143 index_ = index_->clone(expiry);
144 isAveraging_ = isAveraging_ && startDate != Date() && endDate != Date();
145 if (isAveraging_) {
146 // If pricing calendar is not set, use the index fixing calendar
147 Calendar cal = pricingCalendar;
148 if (pricingCalendar == Calendar()) {
149 cal = index_->fixingCalendar();
150 }
151
152 // Create Spot Index
153 spotIndex_ = ext::make_shared<CommoditySpotIndex>(index_->underlyingName(), cal,
154 index_->priceCurve());
155 if (spotIndex_) {
156 registerWith(spotIndex_);
157 spotAveragingPricingDates_ =
158 pricingDates(startDate, endDate, cal, excludeStartDate, includeEndDate);
159 for (const auto& d : spotAveragingPricingDates_) {
160 indices_.push_back({d, spotIndex_});
161 }
162 }
163 }
164 }
165
166 // Derive the payment date if an explicit override has not been provided
167 if (paymentDate_ == Date()) {
168 if (paymentTiming == PaymentTiming::InAdvance) {
169 QL_REQUIRE(startDate != Date(), "CommodityIndexedCashFlow: startDate is null, can not derive paymentDate.");
170 paymentDate_ = startDate;
171 } else if (paymentTiming == PaymentTiming::InArrears) {
172 QL_REQUIRE(endDate != Date(), "CommodityIndexedCashFlow: endDate is null, can not derive paymentDate.");
173 paymentDate_ = endDate;
174 } else if (paymentTiming == PaymentTiming::RelativeToExpiry) {
175 QL_REQUIRE(
176 expiry != Date(),
177 "CommodityIndexedCashFlow: payment relative to expiry date only possibly when future price is used.");
178 paymentDate_ = expiry;
179 }
180 paymentDate_ = paymentCalendar.advance(paymentDate_, paymentLag, Days, paymentConvention);
181 }
182
183 // the pricing date has to lie on or before the payment date
184 pricingDate_ = index_->fixingCalendar().adjust(std::min(paymentDate_, pricingDate_), Preceding);
185
186 indices_.push_back({pricingDate_, index_});
187
188 registerWith(index_);
189}
190
191CommodityIndexedLeg::CommodityIndexedLeg(const Schedule& schedule, const ext::shared_ptr<CommodityIndex>& index)
192 : schedule_(schedule), index_(index), paymentLag_(0), paymentCalendar_(NullCalendar()),
193 paymentConvention_(Unadjusted), pricingLag_(0), pricingLagCalendar_(NullCalendar()),
194 paymentTiming_(CommodityIndexedCashFlow::PaymentTiming::InArrears), inArrears_(true), useFuturePrice_(false),
195 useFutureExpiryDate_(true), futureMonthOffset_(0), payAtMaturity_(false), dailyExpiryOffset_(Null<Natural>()),
196 isAveraging_(false), pricingCalendar_(NullCalendar()), includeEndDate_(true), excludeStartDate_(true) {}
197
199 quantities_ = vector<Real>(1, quantity);
200 return *this;
201}
202
203CommodityIndexedLeg& CommodityIndexedLeg::withQuantities(const vector<Real>& quantities) {
204 quantities_ = quantities;
205 return *this;
206}
207
209 paymentLag_ = paymentLag;
210 return *this;
211}
212
214 paymentCalendar_ = paymentCalendar;
215 return *this;
216}
217
218CommodityIndexedLeg& CommodityIndexedLeg::withPaymentConvention(BusinessDayConvention paymentConvention) {
219 paymentConvention_ = paymentConvention;
220 return *this;
221}
222
224 pricingLag_ = pricingLag;
225 return *this;
226}
227
229 pricingLagCalendar_ = pricingLagCalendar;
230 return *this;
231}
232
234 spreads_ = vector<Real>(1, spread);
235 return *this;
236}
237
238CommodityIndexedLeg& CommodityIndexedLeg::withSpreads(const vector<Real>& spreads) {
239 spreads_ = spreads;
240 return *this;
241}
242
243CommodityIndexedLeg& CommodityIndexedLeg::withGearings(Real gearing) {
244 gearings_ = vector<Real>(1, gearing);
245 return *this;
246}
247
248CommodityIndexedLeg& CommodityIndexedLeg::withGearings(const vector<Real>& gearings) {
249 gearings_ = gearings;
250 return *this;
251}
252
255 return *this;
256}
257
259 inArrears_ = flag;
260 return *this;
261}
262
264 useFuturePrice_ = flag;
265 return *this;
266}
267
270 return *this;
271}
272
274 futureMonthOffset_ = futureMonthOffset;
275 return *this;
276}
277
279CommodityIndexedLeg::withFutureExpiryCalculator(const ext::shared_ptr<FutureExpiryCalculator>& calc) {
280 calc_ = calc;
281 return *this;
282}
283
285 payAtMaturity_ = flag;
286 return *this;
287}
288
291 return *this;
292}
293
295 paymentDates_ = paymentDates;
296 return *this;
297}
298
300 dailyExpiryOffset_ = dailyExpiryOffset;
301 return *this;
302}
303
304CommodityIndexedLeg& CommodityIndexedLeg::withFxIndex(const ext::shared_ptr<FxIndex>& fxIndex) {
305 fxIndex_ = fxIndex;
306 return *this;
307}
308
310 isAveraging_ = isAveraging;
311 return *this;
312}
313
315 pricingCalendar_ = pricingCalendar;
316 return *this;
317}
318
321 return *this;
322}
323
326 return *this;
327}
328
329CommodityIndexedLeg::operator Leg() const {
330
331 // Number of commodity indexed cashflows
332 Size numberCashflows = schedule_.size() - 1;
333
334 // Initial consistency checks
335 QL_REQUIRE(!quantities_.empty(), "No quantities given");
336 QL_REQUIRE(quantities_.size() <= numberCashflows,
337 "Too many quantities (" << quantities_.size() << "), only " << numberCashflows << " required");
338 if (useFuturePrice_) {
339 QL_REQUIRE(calc_, "CommodityIndexedCashFlow needs a valid future expiry calculator when using first future");
340 }
341 if (!pricingDates_.empty()) {
342 QL_REQUIRE(pricingDates_.size() == numberCashflows, "Expected the number of explicit pricing dates ("
343 << pricingDates_.size()
344 << ") to equal the number of calculation periods ("
345 << numberCashflows << ")");
346 }
347 if (!paymentDates_.empty()) {
348 QL_REQUIRE(paymentDates_.size() == numberCashflows, "Expected the number of explicit payment dates ("
349 << paymentDates_.size()
350 << ") to equal the number of calculation periods ("
351 << numberCashflows << ")");
352 }
353
354 // If pay at maturity, populate payment date.
355 Date paymentDate;
356 if (payAtMaturity_) {
357 paymentDate = paymentCalendar_.advance(schedule_.dates().back(), paymentLag_ * Days, paymentConvention_);
358 }
359
360 // Leg to hold the result
361 Leg leg;
362 leg.reserve(numberCashflows);
363 for (Size i = 0; i < numberCashflows; ++i) {
364
365 Date start = schedule_.date(i);
366 Date end = schedule_.date(i + 1);
367 Real quantity = detail::get(quantities_, i, 1.0);
368 Real spread = detail::get(spreads_, i, 0.0);
369 Real gearing = detail::get(gearings_, i, 1.0);
370 Date pricingDate = detail::get(pricingDates_, i, Date());
371 bool excludeStart = i == 0 ? false : excludeStartDate_;
372 bool includeEnd = i == numberCashflows - 1 ? true : includeEndDate_;
373
374 // If explicit payment dates provided, use them.
375 if (!paymentDates_.empty()) {
376 paymentDate = paymentDates_[i];
377 }
378
379 leg.push_back(ext::make_shared<CommodityIndexedCashFlow>(
380 quantity, start, end, index_, paymentLag_, paymentCalendar_, paymentConvention_, pricingLag_,
381 pricingLagCalendar_, spread, gearing, paymentTiming_, inArrears_, useFuturePrice_, useFutureExpiryDate_,
382 futureMonthOffset_, calc_, paymentDate, pricingDate, dailyExpiryOffset_, fxIndex_, isAveraging_,
383 pricingCalendar_, includeEnd, excludeStart));
384 }
385
386 return leg;
387}
388
389} // namespace QuantExt
Cash flow dependent on a single commodity spot price or futures settlement price on a given pricing d...
CommodityIndexedCashFlow(QuantLib::Real quantity, const QuantLib::Date &pricingDate, const QuantLib::Date &paymentDate, const ext::shared_ptr< CommodityIndex > &index, QuantLib::Real spread=0.0, QuantLib::Real gearing=1.0, bool useFuturePrice=false, const Date &contractDate=Date(), const ext::shared_ptr< FutureExpiryCalculator > &calc=nullptr, QuantLib::Natural dailyExpiryOffset=QuantLib::Null< QuantLib::Natural >(), const ext::shared_ptr< FxIndex > &fxIndex=nullptr)
Constructor taking an explicit pricingDate and paymentDate.
Helper class building a sequence of commodity indexed cashflows.
CommodityIndexedLeg & excludeStartDate(bool flag=true)
CommodityIndexedLeg & withPricingLagCalendar(const QuantLib::Calendar &pricingLagCalendar)
CommodityIndexedLeg & paymentTiming(CommodityIndexedCashFlow::PaymentTiming paymentTiming)
QuantLib::BusinessDayConvention paymentConvention_
CommodityIndexedLeg & payAtMaturity(bool flag=false)
CommodityIndexedLeg & withIsAveraging(const bool isAveraging)
CommodityIndexedCashFlow::PaymentTiming paymentTiming_
CommodityIndexedLeg & includeEndDate(bool flag=true)
ext::shared_ptr< FutureExpiryCalculator > calc_
std::vector< QuantLib::Date > paymentDates_
CommodityIndexedLeg & withFutureMonthOffset(QuantLib::Natural futureMonthOffset)
CommodityIndexedLeg & withQuantities(QuantLib::Real quantity)
CommodityIndexedLeg & withPaymentDates(const std::vector< QuantLib::Date > &paymentDates)
CommodityIndexedLeg & withFxIndex(const ext::shared_ptr< FxIndex > &fxIndex)
CommodityIndexedLeg & withFutureExpiryCalculator(const ext::shared_ptr< FutureExpiryCalculator > &calc=nullptr)
CommodityIndexedLeg & withPaymentLag(QuantLib::Natural paymentLag)
CommodityIndexedLeg & inArrears(bool flag=true)
CommodityIndexedLeg & withGearings(QuantLib::Real gearing)
CommodityIndexedLeg & withDailyExpiryOffset(QuantLib::Natural dailyExpiryOffset)
std::vector< QuantLib::Date > pricingDates_
CommodityIndexedLeg & withPaymentCalendar(const QuantLib::Calendar &paymentCalendar)
CommodityIndexedLeg & withPricingDates(const std::vector< QuantLib::Date > &pricingDates)
std::vector< QuantLib::Real > quantities_
CommodityIndexedLeg & withSpreads(QuantLib::Real spread)
CommodityIndexedLeg & useFuturePrice(bool flag=false)
CommodityIndexedLeg & withPricingCalendar(const QuantLib::Calendar &pricingCalendar)
std::vector< QuantLib::Real > spreads_
CommodityIndexedLeg & withPaymentConvention(QuantLib::BusinessDayConvention paymentConvention)
std::vector< QuantLib::Real > gearings_
CommodityIndexedLeg & withPricingLag(QuantLib::Natural pricingLag)
CommodityIndexedLeg & useFutureExpiryDate(bool flag=true)
Cash flow dependent on a single commodity spot price or future's settlement price.
SimpleQuote & spread_
set< Date > pricingDates(const Date &s, const Date &e, const Calendar &pricingCalendar, bool excludeStart, bool includeEnd, bool useBusinessDays)