Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
commodityaveragebasispricecurve.hpp
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/*! \file qle/termstructures/commodityaveragebasispricecurve.hpp
20 \brief A commodity price curve created from an averaged base curve and a collection of basis quotes.
21 \ingroup termstructures
22*/
23
24#ifndef quantext_commodity_average_basis_price_curve_hpp
25#define quantext_commodity_average_basis_price_curve_hpp
26
27#include <ql/math/comparison.hpp>
28#include <ql/patterns/lazyobject.hpp>
29#include <ql/quote.hpp>
30#include <ql/termstructures/interpolatedcurve.hpp>
31#include <ql/time/calendars/nullcalendar.hpp>
32#include <ql/utilities/dataformatters.hpp>
36
37namespace QuantExt {
38
39//! Commodity average basis price curve.
40/*! Class representing an outright commodity price curve created from a base price curve and a collection of basis
41 quotes that are added to or subtracted from the base curve. This class is intended to be used only for commodity
42 future basis price curves. The base curve is averaged over the period defined the basis quote.
43
44 \ingroup termstructures
45*/
46template <class Interpolator>
48 public QuantLib::LazyObject,
49 protected QuantLib::InterpolatedCurve<Interpolator> {
50public:
51 //! \name Constructors
52 //@{
53 //! Curve constructed from dates and quotes
54 CommodityAverageBasisPriceCurve(const QuantLib::Date& referenceDate,
55 const std::map<QuantLib::Date, QuantLib::Handle<QuantLib::Quote>>& basisData,
56 const QuantLib::ext::shared_ptr<FutureExpiryCalculator>& basisFec,
57 const QuantLib::ext::shared_ptr<CommodityIndex>& index,
58 const QuantLib::ext::shared_ptr<FutureExpiryCalculator>& baseFec, bool addBasis = true,
59 bool priceAsHistFixing = true, const Interpolator& interpolator = Interpolator());
60 //@}
61
62 //! \name Observer interface
63 //@{
64 void update() override;
65 //@}
66
67 //! \name LazyObject interface
68 //@{
69 void performCalculations() const override;
70 //@}
71
72 //! \name TermStructure interface
73 //@{
74 QuantLib::Date maxDate() const override;
75 QuantLib::Time maxTime() const override;
76 //@}
77
78 //! \name PriceTermStructure interface
79 //@{
80 QuantLib::Time minTime() const override;
81 std::vector<QuantLib::Date> pillarDates() const override;
82 const QuantLib::Currency& currency() const override { return baseIndex_->priceCurve()->currency(); }
83 //@}
84
85 //! \name Inspectors
86 //@{
87 const std::vector<QuantLib::Time>& times() const { return this->times_; }
88 const std::vector<QuantLib::Real>& prices() const { return this->data_; }
89 //@}
90
91protected:
92 //! \name PriceTermStructure implementation
93 //@{
94 QuantLib::Real priceImpl(QuantLib::Time t) const override;
95 //@}
96
97private:
98 std::map<QuantLib::Date, QuantLib::Handle<QuantLib::Quote> > basisData_;
99
100 std::vector<QuantLib::Date> dates_;
101
102 /*! Interpolator used for interpolating the basis if needed. Basis interpolation uses the same interpolator as
103 the curve itself. A second template parameter could be added for this in future if it needs to be relaxed.
104 */
105 mutable std::vector<Time> basisTimes_;
106 mutable std::vector<Real> basisValues_;
107 mutable Interpolation basisInterpolation_;
108
109 //! The averaging cashflows will give the base curve prices.
110 QuantLib::Leg baseLeg_;
111
112 /*! Map where the key is the index of a time in the times_ vector and the value is the index of the
113 cashflow in the baseLeg_ to associate with that time.
114 */
115 std::map<QuantLib::Size, QuantLib::Size> legIndexMap_;
116};
117
118template <class Interpolator>
120 const QuantLib::Date& referenceDate, const std::map<QuantLib::Date, QuantLib::Handle<QuantLib::Quote> >& basisData,
121 const QuantLib::ext::shared_ptr<FutureExpiryCalculator>& basisFec, const QuantLib::ext::shared_ptr<CommodityIndex>& index,
122 const QuantLib::ext::shared_ptr<FutureExpiryCalculator>& baseFec, bool addBasis, bool priceAsHistFixing,
123 const Interpolator& interpolator)
124 : CommodityBasisPriceTermStructure(referenceDate, basisFec, index, baseFec, addBasis, 0, true, priceAsHistFixing),
125 QuantLib::InterpolatedCurve<Interpolator>(interpolator), basisData_(basisData) {
126 QL_REQUIRE(baseIndex_ != nullptr && !baseIndex_->priceCurve().empty(),
127 "CommodityAverageBasisPriceCurve requires baseIndex with priceCurve");
128 using QuantLib::Date;
129 using QuantLib::Schedule;
130 using QuantLib::io::iso_date;
131 using QuantLib::io::ordinal;
132 using std::distance;
133 using std::find;
134 using std::max;
135 using std::sort;
136 using std::vector;
137
138 // Observe the quotes
139 for (auto it = basisData_.cbegin(); it != basisData_.cend();) {
140 if (it->first < referenceDate) {
141 // If the basis quote's date is before the reference date, remove it.
142 basisData_.erase(it++);
143 } else {
144 // Otherwise, process the basis quote
145 dates_.push_back(it->first);
146 basisTimes_.push_back(timeFromReference(it->first));
147 if (addBasis_)
148 basisValues_.push_back(it->second->value());
149 else
150 basisValues_.push_back(-it->second->value());
151 registerWith(it->second);
152 ++it;
153 }
154 }
155
156 // Set up the interpolation to be used on the basis
157 basisInterpolation_ = interpolator.interpolate(basisTimes_.begin(), basisTimes_.end(), basisValues_.begin());
158
159 // Initialise this curve's times with the basis pillars. We will add more pillars below.
160 this->times_ = basisTimes_;
161
162 // Get the first basis contract expiry date strictly prior to the curve reference date.
163 Date start = basisFec_->priorExpiry(false, referenceDate);
164
165 // Get the first basis contract expiry date on or after the max date. Here, max date is defined as the maximum of
166 // 1) last pillar date of base price curve and 2) basis curve data.
167 Date maxDate = max(baseIndex_->priceCurve()->maxDate(), basisData_.rbegin()->first);
168 Date end = basisFec_->nextExpiry(true, maxDate);
169
170 // Create the leg schedule using a vector of dates which are the successive basis contract expiry dates
171 QL_REQUIRE(start < end, "Expected that the start date, " << io::iso_date(start)
172 << ", would be strictly less than the end date, "
173 << io::iso_date(end) << ".");
174 vector<Date> expiries{ start + 1 * Days };
175 vector<Time> scheduleTimes;
176 while (start < end) {
177 start = basisFec_->nextExpiry(true, start + 1 * Days);
178 expiries.push_back(start);
179 Time t = timeFromReference(start);
180 // Only add to this->times_ if it is not already there. We can use dates_ for this check.
181 if (find(dates_.begin(), dates_.end(), start) == dates_.end()) {
182 this->times_.push_back(t);
183 dates_.push_back(start);
184 }
185 scheduleTimes.push_back(t);
186 }
187 QL_REQUIRE(start == end, "Expected that the start date, " << io::iso_date(start) << ", to equal the end date, "
188 << io::iso_date(end)
189 << ", after creating the sequence of expiry dates.");
190
191 // Sort the times and dates vector and ensure no duplicates in the times vector.
192 sort(this->times_.begin(), this->times_.end());
193 sort(dates_.begin(), dates_.end());
194 auto it = unique(this->times_.begin(), this->times_.end(), [](double s, double t) { return close(s, t); });
195 QL_REQUIRE(it == this->times_.end(), "Unexpected duplicate time, " << *it << ", in the times vector.");
196 this->data_.resize(this->times_.size());
197
198 // Populate the leg of cashflows.
199 baseLeg_ = CommodityIndexedAverageLeg(Schedule(expiries), baseIndex_)
201 .useFuturePrice(true)
202 .withQuantities(1.0);
203 QL_REQUIRE(baseLeg_.size() == scheduleTimes.size(), "Unexpected number of averaging cashflows in the leg: "
204 << "got " << baseLeg_.size() << " but expected "
205 << scheduleTimes.size());
206
207 // Populate the legIndexMap_
208 for (Size i = 0; i < this->times_.size(); i++) {
209 for (Size j = 0; j < scheduleTimes.size(); j++) {
210
211 if (this->times_[i] < scheduleTimes[j] || close(this->times_[i], scheduleTimes[j])) {
212 QL_REQUIRE(legIndexMap_.find(i) == legIndexMap_.end(),
213 "Should not already have a mapping for the " << ordinal(i) << " time.");
214 legIndexMap_[i] = j;
215 break;
216 }
217
218 if (j == scheduleTimes.size()) {
219 QL_FAIL("Could not map the " << ordinal(i) << " time, " << this->times_[i] << ", to a cashflow.");
220 }
221 }
222 }
223
224 // Set up the underlying interpolation on times_ and data_
225 QuantLib::InterpolatedCurve<Interpolator>::setupInterpolation();
226}
227
228template <class Interpolator> void CommodityAverageBasisPriceCurve<Interpolator>::update() {
229 QuantLib::LazyObject::update();
230}
231
233
234 // Update the basis interpolation object
235 Size basisIdx = 0;
236 for (const auto& kv : basisData_) {
237 basisValues_[basisIdx] = addBasis_ ? kv.second->value() : -kv.second->value();
238 basisIdx++;
239 }
240 basisInterpolation_.update();
241
242 // Update this curve's interpolation
243 for (Size i = 0; i < this->times_.size(); i++) {
244
245 Real baseValue = baseLeg_[legIndexMap_.at(i)]->amount();
246
247 // Get the basis with flat extrapolation
248 Real basis = 0.0;
249 if (this->times_[i] < basisTimes_.front()) {
250 basis = basisValues_.front();
251 } else if (this->times_[i] > basisTimes_.back()) {
252 basis = basisValues_.back();
253 } else {
254 basis = basisInterpolation_(this->times_[i], true);
255 }
256
257 // Update the outright value
258 this->data_[i] = baseValue + basis;
259 }
260 this->interpolation_.update();
261}
262
263template <class Interpolator> QuantLib::Date CommodityAverageBasisPriceCurve<Interpolator>::maxDate() const {
264 return dates_.back();
265}
266
267template <class Interpolator> QuantLib::Time CommodityAverageBasisPriceCurve<Interpolator>::maxTime() const {
268 return this->times_.back();
269}
270
271template <class Interpolator> QuantLib::Time CommodityAverageBasisPriceCurve<Interpolator>::minTime() const {
272 return this->times_.front();
273}
274
275template <class Interpolator>
277 return dates_;
278}
279
280template <class Interpolator>
281QuantLib::Real CommodityAverageBasisPriceCurve<Interpolator>::priceImpl(QuantLib::Time t) const {
282 calculate();
283 return this->interpolation_(t, true);
284}
285
286} // namespace QuantExt
287
288#endif
QuantLib::Time minTime() const override
The minimum time for which the curve can return values.
CommodityAverageBasisPriceCurve(const QuantLib::Date &referenceDate, const std::map< QuantLib::Date, QuantLib::Handle< QuantLib::Quote > > &basisData, const QuantLib::ext::shared_ptr< FutureExpiryCalculator > &basisFec, const QuantLib::ext::shared_ptr< CommodityIndex > &index, const QuantLib::ext::shared_ptr< FutureExpiryCalculator > &baseFec, bool addBasis=true, bool priceAsHistFixing=true, const Interpolator &interpolator=Interpolator())
Curve constructed from dates and quotes.
const QuantLib::Currency & currency() const override
The currency in which prices are expressed.
std::map< QuantLib::Size, QuantLib::Size > legIndexMap_
const std::vector< QuantLib::Time > & times() const
std::map< QuantLib::Date, QuantLib::Handle< QuantLib::Quote > > basisData_
QuantLib::Leg baseLeg_
The averaging cashflows will give the base curve prices.
const std::vector< QuantLib::Real > & prices() const
std::vector< QuantLib::Date > pillarDates() const override
The pillar dates for the PriceTermStructure.
QuantLib::Real priceImpl(QuantLib::Time t) const override
Price calculation.
QuantLib::ext::shared_ptr< FutureExpiryCalculator > baseFec_
QuantLib::ext::shared_ptr< FutureExpiryCalculator > basisFec_
QuantLib::ext::shared_ptr< CommodityIndex > baseIndex_
Helper class building a sequence of commodity indexed average cashflows.
CommodityIndexedAverageLeg & useFuturePrice(bool flag=false)
CommodityIndexedAverageLeg & withQuantities(QuantLib::Real quantity)
CommodityIndexedAverageLeg & withFutureExpiryCalculator(const ext::shared_ptr< FutureExpiryCalculator > &calc=nullptr)
An interface for a commodity price curve created from a base price curve and a collection of basis qu...
Cash flow dependent on the average commodity spot price or future's settlement price over a period....
Base class for classes that perform date calculations for future contracts.
CompiledFormula max(CompiledFormula x, const CompiledFormula &y)