/src/quantlib/ql/experimental/commodities/energybasisswap.cpp
Line | Count | Source |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2008 J. Erik Radmall |
5 | | |
6 | | This file is part of QuantLib, a free-software/open-source library |
7 | | for financial quantitative analysts and developers - http://quantlib.org/ |
8 | | |
9 | | QuantLib is free software: you can redistribute it and/or modify it |
10 | | under the terms of the QuantLib license. You should have received a |
11 | | copy of the license along with this program; if not, please email |
12 | | <quantlib-dev@lists.sf.net>. The license is also available online at |
13 | | <https://www.quantlib.org/license.shtml>. |
14 | | |
15 | | This program is distributed in the hope that it will be useful, but WITHOUT |
16 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
17 | | FOR A PARTICULAR PURPOSE. See the license for more details. |
18 | | */ |
19 | | |
20 | | #include <ql/experimental/commodities/commoditysettings.hpp> |
21 | | #include <ql/experimental/commodities/energybasisswap.hpp> |
22 | | #include <utility> |
23 | | |
24 | | namespace QuantLib { |
25 | | |
26 | | EnergyBasisSwap::EnergyBasisSwap(const Calendar& calendar, |
27 | | ext::shared_ptr<CommodityIndex> spreadIndex, |
28 | | ext::shared_ptr<CommodityIndex> payIndex, |
29 | | ext::shared_ptr<CommodityIndex> receiveIndex, |
30 | | bool spreadToPayLeg, |
31 | | const Currency& payCurrency, |
32 | | const Currency& receiveCurrency, |
33 | | const PricingPeriods& pricingPeriods, |
34 | | CommodityUnitCost basis, |
35 | | const CommodityType& commodityType, |
36 | | const ext::shared_ptr<SecondaryCosts>& secondaryCosts, |
37 | | Handle<YieldTermStructure> payLegTermStructure, |
38 | | Handle<YieldTermStructure> receiveLegTermStructure, |
39 | | Handle<YieldTermStructure> discountTermStructure) |
40 | 0 | : EnergySwap( |
41 | 0 | calendar, payCurrency, receiveCurrency, pricingPeriods, commodityType, secondaryCosts), |
42 | 0 | spreadIndex_(std::move(spreadIndex)), payIndex_(std::move(payIndex)), |
43 | 0 | receiveIndex_(std::move(receiveIndex)), spreadToPayLeg_(spreadToPayLeg), |
44 | 0 | basis_(std::move(basis)), payLegTermStructure_(std::move(payLegTermStructure)), |
45 | 0 | receiveLegTermStructure_(std::move(receiveLegTermStructure)), |
46 | 0 | discountTermStructure_(std::move(discountTermStructure)) { |
47 | 0 | QL_REQUIRE(!pricingPeriods_.empty(), "no payment dates"); |
48 | 0 | registerWith(spreadIndex_); |
49 | 0 | registerWith(payIndex_); |
50 | 0 | registerWith(receiveIndex_); |
51 | 0 | } Unexecuted instantiation: QuantLib::EnergyBasisSwap::EnergyBasisSwap(QuantLib::Calendar const&, boost::shared_ptr<QuantLib::CommodityIndex>, boost::shared_ptr<QuantLib::CommodityIndex>, boost::shared_ptr<QuantLib::CommodityIndex>, bool, QuantLib::Currency const&, QuantLib::Currency const&, std::__1::vector<boost::shared_ptr<QuantLib::PricingPeriod>, std::__1::allocator<boost::shared_ptr<QuantLib::PricingPeriod> > > const&, QuantLib::CommodityUnitCost, QuantLib::CommodityType const&, boost::shared_ptr<std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::any, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::any> > > > const&, QuantLib::Handle<QuantLib::YieldTermStructure>, QuantLib::Handle<QuantLib::YieldTermStructure>, QuantLib::Handle<QuantLib::YieldTermStructure>) Unexecuted instantiation: QuantLib::EnergyBasisSwap::EnergyBasisSwap(QuantLib::Calendar const&, boost::shared_ptr<QuantLib::CommodityIndex>, boost::shared_ptr<QuantLib::CommodityIndex>, boost::shared_ptr<QuantLib::CommodityIndex>, bool, QuantLib::Currency const&, QuantLib::Currency const&, std::__1::vector<boost::shared_ptr<QuantLib::PricingPeriod>, std::__1::allocator<boost::shared_ptr<QuantLib::PricingPeriod> > > const&, QuantLib::CommodityUnitCost, QuantLib::CommodityType const&, boost::shared_ptr<std::__1::map<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::any, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::pair<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::any> > > > const&, QuantLib::Handle<QuantLib::YieldTermStructure>, QuantLib::Handle<QuantLib::YieldTermStructure>, QuantLib::Handle<QuantLib::YieldTermStructure>) |
52 | | |
53 | 0 | void EnergyBasisSwap::performCalculations() const { |
54 | |
|
55 | 0 | try { |
56 | |
|
57 | 0 | if (payIndex_->empty()) { |
58 | 0 | if (payIndex_->forwardCurveEmpty()) { |
59 | 0 | QL_FAIL("index [" + payIndex_->name() + |
60 | 0 | "] does not have any quotes or forward prices"); |
61 | 0 | } else { |
62 | 0 | addPricingError(PricingError::Warning, |
63 | 0 | "index [" + payIndex_->name() + |
64 | 0 | "] does not have any quotes; " |
65 | 0 | "using forward prices from [" + |
66 | 0 | payIndex_->forwardCurve()->name() + "]"); |
67 | 0 | } |
68 | 0 | } |
69 | 0 | if (receiveIndex_->empty()) { |
70 | 0 | if (receiveIndex_->forwardCurveEmpty()) { |
71 | 0 | QL_FAIL("index [" + receiveIndex_->name() + |
72 | 0 | "] does not have any quotes or forward prices"); |
73 | 0 | } else { |
74 | 0 | addPricingError(PricingError::Warning, |
75 | 0 | "index [" + receiveIndex_->name() + |
76 | 0 | "] does not have any quotes; " |
77 | 0 | "using forward prices from [" + |
78 | 0 | receiveIndex_->forwardCurve()->name() + |
79 | 0 | "]"); |
80 | 0 | } |
81 | 0 | } |
82 | | |
83 | 0 | NPV_ = 0.0; |
84 | 0 | additionalResults_.clear(); |
85 | 0 | dailyPositions_.clear(); |
86 | 0 | paymentCashFlows_.clear(); |
87 | |
|
88 | 0 | Date evaluationDate = Settings::instance().evaluationDate(); |
89 | |
|
90 | 0 | const Currency& baseCurrency = |
91 | 0 | CommoditySettings::instance().currency(); |
92 | 0 | const UnitOfMeasure baseUnitOfMeasure = |
93 | 0 | CommoditySettings::instance().unitOfMeasure(); |
94 | |
|
95 | 0 | Real quantityUomConversionFactor = |
96 | 0 | calculateUomConversionFactor( |
97 | 0 | pricingPeriods_[0]->quantity().commodityType(), |
98 | 0 | baseUnitOfMeasure, |
99 | 0 | pricingPeriods_[0]->quantity().unitOfMeasure()); |
100 | 0 | Real payIndexUomConversionFactor = |
101 | 0 | calculateUomConversionFactor(payIndex_->commodityType(), |
102 | 0 | payIndex_->unitOfMeasure(), |
103 | 0 | baseUnitOfMeasure); |
104 | 0 | Real receiveIndexUomConversionFactor = |
105 | 0 | calculateUomConversionFactor(receiveIndex_->commodityType(), |
106 | 0 | receiveIndex_->unitOfMeasure(), |
107 | 0 | baseUnitOfMeasure); |
108 | |
|
109 | 0 | Real payIndexFxConversionFactor = |
110 | 0 | calculateFxConversionFactor(payIndex_->currency(), |
111 | 0 | baseCurrency, evaluationDate); |
112 | 0 | Real receiveIndexFxConversionFactor = |
113 | 0 | calculateFxConversionFactor(receiveIndex_->currency(), |
114 | 0 | baseCurrency, evaluationDate); |
115 | 0 | Real payLegFxConversionFactor = |
116 | 0 | calculateFxConversionFactor(baseCurrency, payCurrency_, |
117 | 0 | evaluationDate); |
118 | 0 | Real receiveLegFxConversionFactor = |
119 | 0 | calculateFxConversionFactor(baseCurrency, receiveCurrency_, |
120 | 0 | evaluationDate); |
121 | |
|
122 | 0 | Real basisUomConversionFactor = |
123 | 0 | calculateUomConversionFactor( |
124 | 0 | pricingPeriods_[0]->quantity().commodityType(), |
125 | 0 | basis_.unitOfMeasure(), baseUnitOfMeasure); |
126 | 0 | Real basisFxConversionFactor = |
127 | 0 | calculateFxConversionFactor(baseCurrency, |
128 | 0 | basis_.amount().currency(), |
129 | 0 | evaluationDate); |
130 | |
|
131 | 0 | Real basisValue = basis_.amount().value() * |
132 | 0 | basisUomConversionFactor * basisFxConversionFactor; |
133 | |
|
134 | 0 | Date lastPayIndexQuoteDate = payIndex_->lastQuoteDate(); |
135 | 0 | Date lastReceiveIndexQuoteDate = receiveIndex_->lastQuoteDate(); |
136 | |
|
137 | 0 | if (lastPayIndexQuoteDate < evaluationDate - 1) { |
138 | 0 | std::ostringstream message; |
139 | 0 | message << "index [" << payIndex_->name() |
140 | 0 | << "] has last quote date of " |
141 | 0 | << io::iso_date(lastPayIndexQuoteDate); |
142 | 0 | addPricingError(PricingError::Warning, message.str()); |
143 | 0 | } |
144 | 0 | if (lastReceiveIndexQuoteDate < evaluationDate - 1) { |
145 | 0 | std::ostringstream message; |
146 | 0 | message << "index [" << receiveIndex_->name() |
147 | 0 | << "] has last quote date of " |
148 | 0 | << io::iso_date(lastReceiveIndexQuoteDate); |
149 | 0 | addPricingError(PricingError::Warning, message.str()); |
150 | 0 | } |
151 | |
|
152 | 0 | Date lastQuoteDate = std::min(lastPayIndexQuoteDate, |
153 | 0 | lastReceiveIndexQuoteDate); |
154 | |
|
155 | 0 | Real totalQuantityAmount = 0; |
156 | | |
157 | | // price each period |
158 | 0 | for (const auto& pricingPeriod : pricingPeriods_) { |
159 | 0 | Integer periodDayCount = 0; |
160 | | |
161 | | // get the index quotes |
162 | 0 | Date periodStartDate = |
163 | 0 | calendar_.adjust(pricingPeriod->startDate()); |
164 | 0 | for (Date stepDate = periodStartDate; |
165 | 0 | stepDate <= pricingPeriod->endDate(); |
166 | 0 | stepDate = calendar_.advance(stepDate, 1*Days)) { |
167 | |
|
168 | 0 | bool unrealized = stepDate > evaluationDate; |
169 | 0 | Real payQuoteValue = 0; |
170 | 0 | Real receiveQuoteValue = 0; |
171 | |
|
172 | 0 | if (stepDate <= lastQuoteDate) { |
173 | 0 | payQuoteValue = payIndex_->fixing(stepDate); |
174 | 0 | receiveQuoteValue = receiveIndex_->fixing(stepDate); |
175 | 0 | } else { |
176 | 0 | payQuoteValue = payIndex_->forwardPrice(stepDate); |
177 | 0 | receiveQuoteValue = |
178 | 0 | receiveIndex_->forwardPrice(stepDate); |
179 | 0 | } |
180 | |
|
181 | 0 | if (payQuoteValue == 0) { |
182 | 0 | std::ostringstream message; |
183 | 0 | message << "pay quote value for curve [" |
184 | 0 | << payIndex_->name() << "] is 0 for date " |
185 | 0 | << io::iso_date(stepDate); |
186 | 0 | addPricingError(PricingError::Warning, message.str()); |
187 | 0 | } |
188 | 0 | if (receiveQuoteValue == 0) { |
189 | 0 | std::ostringstream message; |
190 | 0 | message << "receive quote value for curve [" |
191 | 0 | << receiveIndex_->name() << "] is 0 for date " |
192 | 0 | << io::iso_date(stepDate); |
193 | 0 | addPricingError(PricingError::Warning, message.str()); |
194 | 0 | } |
195 | |
|
196 | 0 | QL_REQUIRE(payQuoteValue != Null<Real>(), |
197 | 0 | "curve [" << payIndex_->name() << |
198 | 0 | "] missing value for pricing date: " |
199 | 0 | << stepDate); |
200 | 0 | QL_REQUIRE(receiveQuoteValue != Null<Real>(), |
201 | 0 | "curve [" << receiveIndex_->name() << |
202 | 0 | "] missing value for pricing date: " |
203 | 0 | << stepDate); |
204 | | |
205 | 0 | Real payLegPriceValue = |
206 | 0 | payQuoteValue * payIndexUomConversionFactor * |
207 | 0 | payIndexFxConversionFactor; |
208 | 0 | Real receiveLegPriceValue = |
209 | 0 | receiveQuoteValue * receiveIndexUomConversionFactor * |
210 | 0 | receiveIndexFxConversionFactor; |
211 | |
|
212 | 0 | if (spreadToPayLeg_) |
213 | 0 | payLegPriceValue += basisValue; |
214 | 0 | else |
215 | 0 | receiveLegPriceValue += basisValue; |
216 | |
|
217 | 0 | dailyPositions_[stepDate] = |
218 | 0 | EnergyDailyPosition(stepDate, payLegPriceValue, |
219 | 0 | receiveLegPriceValue, unrealized); |
220 | 0 | periodDayCount++; |
221 | 0 | } |
222 | | |
223 | 0 | Real periodQuantityAmount = |
224 | 0 | pricingPeriod->quantity().amount() * |
225 | 0 | quantityUomConversionFactor; |
226 | 0 | totalQuantityAmount += periodQuantityAmount; |
227 | |
|
228 | 0 | Real avgDailyQuantityAmount = |
229 | 0 | periodDayCount == 0 ? Real(0) : |
230 | 0 | periodQuantityAmount / periodDayCount; |
231 | |
|
232 | 0 | Real payLegValue = 0; |
233 | 0 | Real receiveLegValue = 0; |
234 | 0 | for (auto dpi = dailyPositions_.find(periodStartDate); |
235 | 0 | dpi != dailyPositions_.end() && dpi->first <= pricingPeriod->endDate(); |
236 | 0 | ++dpi) { |
237 | 0 | EnergyDailyPosition& dailyPosition = dpi->second; |
238 | 0 | dailyPosition.quantityAmount = avgDailyQuantityAmount; |
239 | 0 | dailyPosition.riskDelta = |
240 | 0 | (-dailyPosition.payLegPrice + dailyPosition.receiveLegPrice) * avgDailyQuantityAmount; |
241 | 0 | payLegValue += -dailyPosition.payLegPrice * avgDailyQuantityAmount; |
242 | 0 | receiveLegValue += dailyPosition.receiveLegPrice * avgDailyQuantityAmount; |
243 | 0 | } |
244 | |
|
245 | 0 | Real discountFactor = 1; |
246 | 0 | Real payLegDiscountFactor = 1; |
247 | 0 | Real receiveLegDiscountFactor = 1; |
248 | 0 | if (pricingPeriod->paymentDate() >= evaluationDate + 2 /* settlement days*/) { |
249 | 0 | discountFactor = |
250 | 0 | discountTermStructure_->discount( |
251 | 0 | pricingPeriod->paymentDate()); |
252 | 0 | payLegDiscountFactor = |
253 | 0 | payLegTermStructure_->discount( |
254 | 0 | pricingPeriod->paymentDate()); |
255 | 0 | receiveLegDiscountFactor = |
256 | 0 | receiveLegTermStructure_->discount( |
257 | 0 | pricingPeriod->paymentDate()); |
258 | 0 | } |
259 | |
|
260 | 0 | Real uDelta = receiveLegValue + payLegValue; |
261 | 0 | Real dDelta = (receiveLegValue * receiveLegDiscountFactor) + |
262 | 0 | (payLegValue * payLegDiscountFactor); |
263 | 0 | Real pmtFxConversionFactor = |
264 | 0 | (dDelta > 0) ? payLegFxConversionFactor : receiveLegFxConversionFactor; |
265 | 0 | Currency pmtCurrency = |
266 | 0 | (dDelta > 0) ? receiveCurrency_ : payCurrency_; |
267 | 0 | Real pmtDiscountFactor = |
268 | 0 | (dDelta > 0) ? receiveLegDiscountFactor : payLegDiscountFactor; |
269 | |
|
270 | 0 | paymentCashFlows_[pricingPeriod->paymentDate()] = |
271 | 0 | ext::make_shared<CommodityCashFlow> ( |
272 | 0 | pricingPeriod->paymentDate(), |
273 | 0 | Money(baseCurrency, |
274 | 0 | uDelta * discountFactor), |
275 | 0 | Money(baseCurrency, uDelta), |
276 | 0 | Money(pmtCurrency, |
277 | 0 | dDelta * pmtFxConversionFactor), |
278 | 0 | Money(pmtCurrency, |
279 | 0 | uDelta * pmtFxConversionFactor), |
280 | 0 | discountFactor, |
281 | 0 | pmtDiscountFactor, |
282 | 0 | pricingPeriod->paymentDate() <= evaluationDate); |
283 | |
|
284 | 0 | calculateSecondaryCostAmounts( |
285 | 0 | pricingPeriods_[0]->quantity().commodityType(), |
286 | 0 | totalQuantityAmount, evaluationDate); |
287 | |
|
288 | 0 | NPV_ += dDelta; |
289 | 0 | } |
290 | | |
291 | 0 | QL_REQUIRE(!paymentCashFlows_.empty(), "no cashflows"); |
292 | | |
293 | 0 | for (auto & secondaryCostAmount : secondaryCostAmounts_) { |
294 | 0 | Real amount = secondaryCostAmount.second.value(); |
295 | 0 | NPV_ -= amount; |
296 | 0 | } |
297 | |
|
298 | 0 | additionalResults_["dailyPositions"] = dailyPositions_; |
299 | | |
300 | 0 | } catch (const QuantLib::Error& e) { |
301 | 0 | addPricingError(PricingError::Error, e.what()); |
302 | 0 | throw; |
303 | 0 | } catch (const std::exception& e) { |
304 | 0 | addPricingError(PricingError::Error, e.what()); |
305 | 0 | throw; |
306 | 0 | } |
307 | 0 | } |
308 | | |
309 | | } |
310 | | |