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