Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
commoditybasispricecurve.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/commoditybasispricecurve.hpp
20 \brief A commodity price curve created from a base price curve and a collection of basis quotes
21 \ingroup termstructures
22*/
23
24#ifndef quantext_commodity_basis_price_curve_hpp
25#define quantext_commodity_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>
39namespace QuantExt {
40
41//! Commodity basis price curve.
42/*! Class representing an outright commodity price curve created from a base price curve and a collection of basis
43 quotes that are added to or subtracted from the base curve. This class is intended to be used only for commodity
44 future basis price curves.
45
46 There is an assumption in the curve construction that the frequency of the base future contract is the same as
47 the frequency of the basis future contract. In other words, if the base future contract is monthly then the basis
48 future contract is monthly for example.
49
50 \ingroup termstructures
51*/
52template <class Interpolator>
54 public QuantLib::LazyObject,
55 protected QuantLib::InterpolatedCurve<Interpolator> {
56public:
57 //! \name Constructors
58 //@{
59 //! Curve constructed from dates and quotes
60 CommodityBasisPriceCurve(const QuantLib::Date& referenceDate,
61 const std::map<QuantLib::Date, QuantLib::Handle<QuantLib::Quote>>& basisData,
62 const QuantLib::ext::shared_ptr<FutureExpiryCalculator>& basisFec,
63 const QuantLib::ext::shared_ptr<CommodityIndex>& baseIndex,
64 const QuantLib::ext::shared_ptr<FutureExpiryCalculator>& baseFec, bool addBasis = true,
65 QuantLib::Size monthOffset = 0, bool priceAsHistFixing = true,
66 const Interpolator& interpolator = Interpolator());
67 //@}
68
69 //! \name Observer interface
70 //@{
71 void update() override;
72 //@}
73
74 //! \name LazyObject interface
75 //@{
76 void performCalculations() const override;
77 //@}
78
79 //! \name TermStructure interface
80 //@{
81 QuantLib::Date maxDate() const override;
82 QuantLib::Time maxTime() const override;
83 //@}
84
85 //! \name PriceTermStructure interface
86 //@{
87 QuantLib::Time minTime() const override;
88 std::vector<QuantLib::Date> pillarDates() const override;
89 const QuantLib::Currency& currency() const override { return baseIndex_->priceCurve()->currency(); }
90 //@}
91
92 //! \name Inspectors
93 //@{
94 const std::vector<QuantLib::Time>& times() const { return this->times_; }
95 const std::vector<QuantLib::Real>& prices() const { return this->data_; }
96 //@}
97
98protected:
99 //! \name PriceTermStructure implementation
100 //@{
101 QuantLib::Real priceImpl(QuantLib::Time t) const override;
102 //@}
103
104private:
105 std::map<QuantLib::Date, QuantLib::Handle<QuantLib::Quote>> basisData_;
106 std::vector<QuantLib::Date> dates_;
107
108 /*! Interpolator used for interpolating the basis if needed. Basis interpolation uses the same interpolator as
109 the curve itself. A second template parameter could be added for this in future if it needs to be relaxed.
110 */
111 mutable std::vector<Time> basisTimes_;
112 mutable std::vector<Real> basisValues_;
113 mutable Interpolation basisInterpolation_;
114
115 //! The commodity cashflows will give the base curve prices.
116 std::map<QuantLib::Date, QuantLib::ext::shared_ptr<CashFlow>> baseLeg_;
117
118 /*! Map where the key is the index of a time in the times_ vector and the value is the index of the
119 cashflow in the baseLeg_ to associate with that time.
120 */
121 std::map<QuantLib::Size, QuantLib::Size> legIndexMap_;
122};
123
124template <class Interpolator>
126 const QuantLib::Date& referenceDate, const std::map<QuantLib::Date, QuantLib::Handle<QuantLib::Quote>>& basisData,
127 const QuantLib::ext::shared_ptr<FutureExpiryCalculator>& basisFec, const QuantLib::ext::shared_ptr<CommodityIndex>& baseIndex,
128 const QuantLib::ext::shared_ptr<FutureExpiryCalculator>& baseFec, bool addBasis, QuantLib::Size monthOffset,
129 bool priceAsHistFixing, const Interpolator& interpolator)
130 : CommodityBasisPriceTermStructure(referenceDate, basisFec, baseIndex, baseFec, addBasis, monthOffset, false,
131 priceAsHistFixing),
132 QuantLib::InterpolatedCurve<Interpolator>(interpolator), basisData_(basisData) {
133 QL_REQUIRE(baseIndex != nullptr && !baseIndex->priceCurve().empty(),
134 "CommodityBasisPriceCurve requires baseIndex with priceCurve");
135 using QuantLib::Date;
136 using QuantLib::Schedule;
137 using QuantLib::io::iso_date;
138 using QuantLib::io::ordinal;
139 using std::distance;
140 using std::find;
141 using std::max;
142 using std::min;
143 using std::sort;
144 using std::vector;
145
146 // Observe the quotes
147 for (auto it = basisData_.cbegin(); it != basisData_.cend();) {
148 if (it->first < referenceDate) {
149 // If the basis quote's date is before the reference date, remove it.
150 basisData_.erase(it++);
151 } else {
152 // Otherwise, process the basis quote
153 dates_.push_back(it->first);
154 basisTimes_.push_back(timeFromReference(it->first));
155 if (addBasis_)
156 basisValues_.push_back(it->second->value());
157 else
158 basisValues_.push_back(-it->second->value());
159 registerWith(it->second);
160 ++it;
161 }
162 }
163
164 // Set up the interpolation to be used on the basis
165 basisInterpolation_ = interpolator.interpolate(basisTimes_.begin(), basisTimes_.end(), basisValues_.begin());
166
167 // Initialise this curve's times with the basis pillars. We will add more pillars below.
168 this->times_ = basisTimes_;
169
170 // Get the first basis contract expiry date on or after the curve reference date.
171 Date basisExpiry = basisFec_->nextExpiry(true, referenceDate);
172
173 // Get the first basis contract expiry date on or after the max date. Here, max date is defined as the maximum of
174 // 1) last pillar date of base price curve and 2) basis curve data.
175 Date maxDate = max(baseIndex_->priceCurve()->maxDate(), basisData_.rbegin()->first);
176 Date end = basisFec_->nextExpiry(true, maxDate);
177
178 // Populate the base cashflows.
179 while (basisExpiry <= end) {
180 Date basisContractDate = basisFec_->contractDate(basisExpiry);
181 basisContractDate = Date(1, basisContractDate.month(), basisContractDate.year()) - monthOffset_ * Months;
182 Date periodStart = basisContractDate - monthOffset_ * Months;
183 Date periodEnd = (periodStart + 1 * Months) - 1 * Days;
185 periodStart, periodEnd, baseIndex_, baseFec_, averagingBaseCashflow_);
186
187 // Only add to this->times_ if it is not already there. We can use dates_ for this check.
188 if (find(dates_.begin(), dates_.end(), basisExpiry) == dates_.end()) {
189 this->times_.push_back(timeFromReference(basisExpiry));
190 dates_.push_back(basisExpiry);
191 }
192
193 basisExpiry = basisFec_->nextExpiry(true, basisExpiry + 1 * Days);
194 }
195
196 // Sort the times and dates vector and ensure no duplicates in the times vector.
197 sort(this->times_.begin(), this->times_.end());
198 sort(dates_.begin(), dates_.end());
199 auto it = unique(this->times_.begin(), this->times_.end(), [](double s, double t) { return close(s, t); });
200 QL_REQUIRE(it == this->times_.end(), "Unexpected duplicate time, " << *it << ", in the times vector.");
201 this->data_.resize(this->times_.size());
202
203 // Set up the underlying interpolation on times_ and data_
204 QuantLib::InterpolatedCurve<Interpolator>::setupInterpolation();
205}
206
207template <class Interpolator> void CommodityBasisPriceCurve<Interpolator>::update() { QuantLib::LazyObject::update(); }
208
209template <class Interpolator> void CommodityBasisPriceCurve<Interpolator>::performCalculations() const {
210
211 // Update the basis interpolation object
212 Size basisIdx = 0;
213 for (const auto& kv : basisData_) {
214 basisValues_[basisIdx] = addBasis_ ? kv.second->value() : -kv.second->value();
215 basisIdx++;
216 }
217 basisInterpolation_.update();
218
219 // Update this curve's interpolation
220 for (Size i = 0; i < this->times_.size(); i++) {
221
222 Real baseValue = 0.0;
223 auto it = baseLeg_.find(dates_[i]);
224 if (it != baseLeg_.end()) {
225 // If we have associated the basis date with a base cashflow.
226 baseValue = it->second->amount();
227 } else {
228 // If we didn't associate the basis date with a base cashflow, just ask the base pts at t. This will have
229 // happened if the basis date that was passed in was not a basis contract expiry date wrt basisFec_.
230 baseValue = baseIndex_->priceCurve()->price(this->times_[i], true);
231 }
232
233 // Get the basis with flat extrapolation
234 Real basis = 0.0;
235 if (this->times_[i] < basisTimes_.front()) {
236 basis = basisValues_.front();
237 } else if (this->times_[i] > basisTimes_.back()) {
238 basis = basisValues_.back();
239 } else {
240 basis = basisInterpolation_(this->times_[i], true);
241 }
242
243 // Update the outright value
244 this->data_[i] = baseValue + basis;
245 }
246 this->interpolation_.update();
247}
248
249template <class Interpolator> QuantLib::Date CommodityBasisPriceCurve<Interpolator>::maxDate() const {
250 return dates_.back();
251}
252
253template <class Interpolator> QuantLib::Time CommodityBasisPriceCurve<Interpolator>::maxTime() const {
254 return this->times_.back();
255}
256
257template <class Interpolator> QuantLib::Time CommodityBasisPriceCurve<Interpolator>::minTime() const {
258 return this->times_.front();
259}
260
261template <class Interpolator> std::vector<QuantLib::Date> CommodityBasisPriceCurve<Interpolator>::pillarDates() const {
262 return dates_;
263}
264
265template <class Interpolator> QuantLib::Real CommodityBasisPriceCurve<Interpolator>::priceImpl(QuantLib::Time t) const {
266 calculate();
267 return this->interpolation_(t, true);
268}
269
270} // namespace QuantExt
271
272#endif
QuantLib::Time minTime() const override
The minimum time for which the curve can return values.
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::ext::shared_ptr< CashFlow > > baseLeg_
The commodity cashflows will give the base curve prices.
std::map< QuantLib::Date, QuantLib::Handle< QuantLib::Quote > > basisData_
QuantLib::Date maxDate() const override
const std::vector< QuantLib::Real > & prices() const
std::vector< QuantLib::Date > pillarDates() const override
The pillar dates for the PriceTermStructure.
CommodityBasisPriceCurve(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 > &baseIndex, const QuantLib::ext::shared_ptr< FutureExpiryCalculator > &baseFec, bool addBasis=true, QuantLib::Size monthOffset=0, bool priceAsHistFixing=true, const Interpolator &interpolator=Interpolator())
Curve constructed from dates and quotes.
QuantLib::Real priceImpl(QuantLib::Time t) const override
Price calculation.
QuantLib::Time maxTime() const override
QuantLib::ext::shared_ptr< FutureExpiryCalculator > baseFec_
const QuantLib::ext::shared_ptr< CommodityIndex > & baseIndex() const
QuantLib::ext::shared_ptr< FutureExpiryCalculator > basisFec_
QuantLib::ext::shared_ptr< CommodityIndex > baseIndex_
some commodity related utilities.
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....
Cash flow dependent on a single commodity spot price or future's settlement price.
Base class for classes that perform date calculations for future contracts.
QuantLib::ext::shared_ptr< CashFlow > makeCommodityCashflowForBasisFuture(const QuantLib::Date &start, const QuantLib::Date &end, const QuantLib::ext::shared_ptr< CommodityIndex > &baseIndex, const QuantLib::ext::shared_ptr< FutureExpiryCalculator > &baseFec, bool baseIsAveraging, const QuantLib::Date &paymentDate)
Make a commodity indexed cashflow.
Definition: commodity.cpp:7
CompiledFormula max(CompiledFormula x, const CompiledFormula &y)
Term structure of prices.