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  <http://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/energyvanillaswap.hpp>
21 #include <ql/experimental/commodities/commoditysettings.hpp>
22 
23 namespace QuantLib {
24 
EnergyVanillaSwap(bool payer,const Calendar & calendar,const Money & fixedPrice,const UnitOfMeasure & fixedPriceUnitOfMeasure,const ext::shared_ptr<CommodityIndex> & index,const Currency & payCurrency,const Currency & receiveCurrency,const PricingPeriods & pricingPeriods,const CommodityType & commodityType,const ext::shared_ptr<SecondaryCosts> & secondaryCosts,const Handle<YieldTermStructure> & payLegTermStructure,const Handle<YieldTermStructure> & receiveLegTermStructure,const Handle<YieldTermStructure> & discountTermStructure)25     EnergyVanillaSwap::EnergyVanillaSwap(
26                     bool payer, const Calendar& calendar,
27                     const Money& fixedPrice,
28                     const UnitOfMeasure& fixedPriceUnitOfMeasure,
29                     const ext::shared_ptr<CommodityIndex>& index,
30                     const Currency& payCurrency,
31                     const Currency& receiveCurrency,
32                     const PricingPeriods& pricingPeriods,
33                     const CommodityType& commodityType,
34                     const ext::shared_ptr<SecondaryCosts>& secondaryCosts,
35                     const Handle<YieldTermStructure>& payLegTermStructure,
36                     const Handle<YieldTermStructure>& receiveLegTermStructure,
37                     const Handle<YieldTermStructure>& discountTermStructure)
38     : EnergySwap(calendar, payCurrency, receiveCurrency, pricingPeriods,
39                  commodityType, secondaryCosts),
40       payReceive_(payer ? 1 : 0), fixedPrice_(fixedPrice),
41       fixedPriceUnitOfMeasure_(fixedPriceUnitOfMeasure),
42       index_(index), payLegTermStructure_(payLegTermStructure),
43       receiveLegTermStructure_(receiveLegTermStructure),
44       discountTermStructure_(discountTermStructure) {
45 
46         QL_REQUIRE(!pricingPeriods_.empty(), "no pricing periods");
47         registerWith(index_);
48     }
49 
isExpired() const50     bool EnergyVanillaSwap::isExpired() const {
51         return detail::simple_event(pricingPeriods_.back()->endDate())
52                .hasOccurred();
53     }
54 
performCalculations() const55     void EnergyVanillaSwap::performCalculations() const {
56 
57         try {
58             if (index_->empty()) {
59                 if (index_->forwardCurveEmpty()) {
60                     QL_FAIL("index [" << index_->name()
61                             << "] does not have any quotes");
62                 } else {
63                     addPricingError(PricingError::Warning,
64                                     "index [" + index_->name() +
65                                     "] does not have any quotes; "
66                                     "using forward prices from ["
67                                     + index_->forwardCurve()->name() + "]");
68                 }
69             }
70 
71             NPV_ = 0.0;
72             additionalResults_.clear();
73             dailyPositions_.clear();
74             paymentCashFlows_.clear();
75 
76             Date evaluationDate = Settings::instance().evaluationDate();
77 
78             const Currency& baseCurrency =
79                 CommoditySettings::instance().currency();
80             const UnitOfMeasure baseUnitOfMeasure =
81                 CommoditySettings::instance().unitOfMeasure();
82 
83             Real quantityUomConversionFactor =
84                 calculateUomConversionFactor(
85                                pricingPeriods_[0]->quantity().commodityType(),
86                                baseUnitOfMeasure,
87                                pricingPeriods_[0]->quantity().unitOfMeasure());
88             Real fixedPriceUomConversionFactor =
89                 calculateUomConversionFactor(
90                                pricingPeriods_[0]->quantity().commodityType(),
91                                fixedPriceUnitOfMeasure_, baseUnitOfMeasure);
92             Real indexUomConversionFactor =
93                 calculateUomConversionFactor(index_->commodityType(),
94                                              index_->unitOfMeasure(),
95                                              baseUnitOfMeasure);
96 
97             Real fixedPriceFxConversionFactor =
98                 calculateFxConversionFactor(fixedPrice_.currency(),
99                                             baseCurrency, evaluationDate);
100             Real indexPriceFxConversionFactor =
101                 calculateFxConversionFactor(index_->currency(),
102                                             baseCurrency, evaluationDate);
103             Real payLegFxConversionFactor =
104                 calculateFxConversionFactor(
105                             baseCurrency,
106                             payReceive_ > 0 ? payCurrency_ : receiveCurrency_,
107                             evaluationDate);
108             Real receiveLegFxConversionFactor =
109                 calculateFxConversionFactor(
110                             baseCurrency,
111                             payReceive_ > 0 ? receiveCurrency_ : payCurrency_,
112                             evaluationDate);
113 
114             Date lastQuoteDate = index_->lastQuoteDate();
115             if (lastQuoteDate < evaluationDate - 1) {
116                 std::ostringstream message;
117                 message << "index [" << index_->name()
118                         << "] has last quote date of "
119                         << io::iso_date(lastQuoteDate);
120                 addPricingError(PricingError::Warning, message.str());
121             }
122 
123             Real totalQuantityAmount = 0;
124 
125             // price each period
126             for (PricingPeriods::const_iterator pi = pricingPeriods_.begin();
127                  pi != pricingPeriods_.end(); ++pi) {
128                 const ext::shared_ptr<PricingPeriod>& pricingPeriod = *pi;
129 
130                 QL_REQUIRE(pricingPeriod->quantity().amount() != 0,
131                            "quantity is zero");
132 
133                 Integer periodDayCount = 0;
134 
135                 // get the futures quotes or everything after
136                 Date periodStartDate =
137                     calendar_.adjust(pricingPeriod->startDate());
138                 for (Date stepDate = periodStartDate;
139                      stepDate <= pricingPeriod->endDate();
140                      stepDate = calendar_.advance(stepDate, 1*Days)) {
141 
142                     bool unrealized = stepDate > evaluationDate;
143                     Real quoteValue = 0;
144 
145                     if (stepDate <= lastQuoteDate) {
146                         quoteValue = index_->price(stepDate);
147                     } else {
148                         quoteValue = index_->forwardPrice(stepDate);
149                     }
150 
151                     if (quoteValue == 0) {
152                         std::ostringstream message;
153                         message << "pay quote value for curve ["
154                                 << index_->name() << "] is 0 for date "
155                                 << io::iso_date(stepDate);
156                         addPricingError(PricingError::Warning, message.str());
157                     }
158 
159                     QL_REQUIRE(quoteValue != Null<Real>(),
160                                "curve [" << index_->name() <<
161                                "] missing value for pricing date: "
162                                << stepDate);
163 
164                     Real fixedLegPriceValue =
165                         fixedPrice_.value() * fixedPriceUomConversionFactor *
166                         fixedPriceFxConversionFactor;
167                     Real floatingLegPriceValue =
168                         quoteValue * indexUomConversionFactor *
169                         indexPriceFxConversionFactor;
170                     Real payLegPriceValue =
171                         payReceive_ > 0 ? fixedLegPriceValue :
172                                           floatingLegPriceValue;
173                     Real receiveLegPriceValue =
174                         payReceive_ > 0 ? floatingLegPriceValue :
175                                           fixedLegPriceValue;
176 
177                     dailyPositions_[stepDate] =
178                         EnergyDailyPosition(stepDate, payLegPriceValue,
179                                             receiveLegPriceValue, unrealized);
180                     periodDayCount++;
181                 }
182 
183                 Real periodQuantityAmount =
184                     pricingPeriod->quantity().amount() *
185                     quantityUomConversionFactor;
186                 totalQuantityAmount += periodQuantityAmount;
187 
188                 Real avgDailyQuantityAmount =
189                     periodDayCount == 0 ? 0 :
190                                           periodQuantityAmount / periodDayCount;
191 
192                 Real payLegValue = 0;
193                 Real receiveLegValue = 0;
194                 for (std::map<Date, EnergyDailyPosition>::iterator dpi =
195                          dailyPositions_.find(periodStartDate);
196                      dpi != dailyPositions_.end() &&
197                          dpi->first <= pricingPeriod->endDate(); ++dpi) {
198                     EnergyDailyPosition& dailyPosition = dpi->second;
199                     dailyPosition.quantityAmount = avgDailyQuantityAmount;
200                     dailyPosition.riskDelta =
201                         (-dailyPosition.payLegPrice + dailyPosition.receiveLegPrice) * avgDailyQuantityAmount;
202                     payLegValue += -dailyPosition.payLegPrice * avgDailyQuantityAmount;
203                     receiveLegValue += dailyPosition.receiveLegPrice * avgDailyQuantityAmount;
204                 }
205 
206                 Real discountFactor = 1;
207                 Real payLegDiscountFactor = 1;
208                 Real receiveLegDiscountFactor = 1;
209                 if (pricingPeriod->paymentDate() >= evaluationDate + 2) {
210                     discountFactor =
211                         discountTermStructure_->discount(
212                                                 pricingPeriod->paymentDate());
213                     payLegDiscountFactor =
214                         payLegTermStructure_->discount(
215                                                 pricingPeriod->paymentDate());
216                     receiveLegDiscountFactor =
217                         receiveLegTermStructure_->discount(
218                                                 pricingPeriod->paymentDate());
219                 }
220 
221                 Real uDelta = receiveLegValue + payLegValue;
222                 Real dDelta = (receiveLegValue * receiveLegDiscountFactor) +
223                     (payLegValue * payLegDiscountFactor);
224                 Real pmtFxConversionFactor =
225                     ((dDelta * payReceive_) > 0) ? payLegFxConversionFactor :
226                                                    receiveLegFxConversionFactor;
227                 Currency pmtCurrency =
228                     ((dDelta * payReceive_) > 0) ? receiveCurrency_ :
229                                                    payCurrency_;
230                 Real pmtDiscountFactor =
231                     (dDelta  > 0) ? receiveLegDiscountFactor :
232                                     payLegDiscountFactor;
233 
234                 paymentCashFlows_[pricingPeriod->paymentDate()] =
235                     ext::make_shared<CommodityCashFlow>(
236                            pricingPeriod->paymentDate(),
237                                                  Money(baseCurrency,
238                                                        uDelta * discountFactor),
239                                                  Money(baseCurrency, uDelta),
240                                                  Money(pmtCurrency,
241                                                        dDelta * pmtFxConversionFactor),
242                                                  Money(pmtCurrency,
243                                                        uDelta * pmtFxConversionFactor),
244                                                  discountFactor,
245                                                  pmtDiscountFactor,
246                                                  pricingPeriod->paymentDate() <= evaluationDate);
247 
248                 calculateSecondaryCostAmounts(
249                                pricingPeriods_[0]->quantity().commodityType(),
250                                totalQuantityAmount, evaluationDate);
251 
252                 NPV_ += dDelta;
253             }
254 
255             QL_REQUIRE(!paymentCashFlows_.empty(), "no cashflows");
256 
257             for (SecondaryCostAmounts::const_iterator i =
258                      secondaryCostAmounts_.begin();
259                  i != secondaryCostAmounts_.end(); ++i) {
260                 Real amount = i->second.value();
261                 NPV_ -= amount;
262             }
263 
264             additionalResults_["dailyPositions"] = dailyPositions_;
265 
266         } catch (const QuantLib::Error& e) {
267             addPricingError(PricingError::Error, e.what());
268             throw;
269         } catch (const std::exception& e) {
270             addPricingError(PricingError::Error, e.what());
271             throw;
272         }
273     }
274 
275 }
276 
277