20#include <ql/utilities/vectors.hpp>
23using QuantLib::AcyclicVisitor;
24using QuantLib::BusinessDayConvention;
25using QuantLib::Calendar;
26using QuantLib::CashFlow;
28using QuantLib::Integer;
29using QuantLib::MakeSchedule;
31using QuantLib::Schedule;
32using QuantLib::Visitor;
33using QuantLib::io::iso_date;
42 Real quantity,
const Date& startDate,
const Date& endDate,
const Date& paymentDate,
43 const ext::shared_ptr<CommodityIndex>& index,
const Calendar& pricingCalendar, QuantLib::Real spread,
44 QuantLib::Real gearing,
bool useFuturePrice, Natural deliveryDateRoll, Natural futureMonthOffset,
45 const ext::shared_ptr<FutureExpiryCalculator>& calc,
bool includeEndDate,
bool excludeStartDate,
47 bool unrealisedQuantity,
const boost::optional<pair<Calendar, Real>>& offPeakPowerData,
48 const ext::shared_ptr<FxIndex>& fxIndex)
49 : CommodityCashFlow(quantity, spread, gearing, useFuturePrice, index, fxIndex), startDate_(startDate),
51 paymentDate_(paymentDate), pricingCalendar_(pricingCalendar), deliveryDateRoll_(deliveryDateRoll),
52 futureMonthOffset_(futureMonthOffset), includeEndDate_(includeEndDate), excludeStartDate_(excludeStartDate),
53 useBusinessDays_(useBusinessDays), quantityFrequency_(quantityFrequency), hoursPerDay_(hoursPerDay),
54 dailyExpiryOffset_(dailyExpiryOffset), unrealisedQuantity_(unrealisedQuantity),
55 offPeakPowerData_(offPeakPowerData) {
59CommodityIndexedAverageCashFlow::CommodityIndexedAverageCashFlow(
60 Real quantity,
const Date& startDate,
const Date& endDate, Natural paymentLag, Calendar paymentCalendar,
61 BusinessDayConvention paymentConvention,
const ext::shared_ptr<CommodityIndex>& index,
62 const Calendar& pricingCalendar, QuantLib::Real spread, QuantLib::Real gearing, PaymentTiming paymentTiming,
63 bool useFuturePrice, Natural deliveryDateRoll, Natural futureMonthOffset,
64 const ext::shared_ptr<FutureExpiryCalculator>& calc,
bool includeEndDate,
bool excludeStartDate,
65 const QuantLib::Date& paymentDateOverride,
bool useBusinessDays, CommodityQuantityFrequency quantityFrequency,
66 Natural hoursPerDay, Natural dailyExpiryOffset,
bool unrealisedQuantity,
67 const boost::optional<pair<Calendar, Real>>& offPeakPowerData,
const ext::shared_ptr<FxIndex>& fxIndex)
68 : CommodityCashFlow(quantity, spread, gearing, useFuturePrice, index, fxIndex), startDate_(startDate), endDate_(endDate),
69 paymentDate_(paymentDateOverride), pricingCalendar_(pricingCalendar),
70 deliveryDateRoll_(deliveryDateRoll), futureMonthOffset_(futureMonthOffset), includeEndDate_(includeEndDate),
71 excludeStartDate_(excludeStartDate), useBusinessDays_(useBusinessDays), quantityFrequency_(quantityFrequency),
72 hoursPerDay_(hoursPerDay), dailyExpiryOffset_(dailyExpiryOffset), unrealisedQuantity_(unrealisedQuantity),
73 offPeakPowerData_(offPeakPowerData) {
76 if (paymentDate_ == Date()) {
77 paymentDate_ = paymentTiming == PaymentTiming::InArrears ? endDate : startDate;
78 paymentDate_ = paymentCalendar.advance(endDate, paymentLag, Days, paymentConvention);
84void CommodityIndexedAverageCashFlow::performCalculations()
const {
89 if (weights_.empty()) {
90 for (
const auto& kv : indices_) {
91 fxRate = (fxIndex_)? this->fxIndex()->fixing(kv.first):1.0;
92 averagePrice_ += fxRate * kv.second->fixing(kv.first);
94 averagePrice_ /= indices_.size();
97 for (
const auto& kv : indices_) {
98 fxRate = (fxIndex_)? this->fxIndex()->fixing(kv.first):1.0;
99 averagePrice_ += fxRate * kv.second->fixing(kv.first) * weights_.at(kv.first);
108Real CommodityIndexedAverageCashFlow::amount()
const {
113Real CommodityIndexedAverageCashFlow::fixing()
const {
115 return averagePrice_;
118void CommodityIndexedAverageCashFlow::accept(AcyclicVisitor& v) {
119 if (Visitor<CommodityIndexedAverageCashFlow>* v1 =
dynamic_cast<Visitor<CommodityIndexedAverageCashFlow>*
>(&v))
122 CommodityCashFlow::accept(v);
125void CommodityIndexedAverageCashFlow::init(
const ext::shared_ptr<FutureExpiryCalculator>& calc) {
128 if (pricingCalendar_ == Calendar()) {
129 pricingCalendar_ = index_->fixingCalendar();
133 if (useFuturePrice_) {
134 QL_REQUIRE(calc,
"CommodityIndexedCashFlow needs a valid future expiry calculator when using first future");
138 auto pds =
pricingDates(startDate_, endDate_, pricingCalendar_,
139 excludeStartDate_, includeEndDate_, useBusinessDays_);
141 QL_REQUIRE(!pds.empty(),
"CommodityIndexedAverageCashFlow: found no pricing dates between "
142 << io::iso_date(startDate_) <<
" and " << io::iso_date(endDate_) <<
".");
145 if (!useFuturePrice_) {
148 for (
const Date& pd : pds) {
149 indices_.push_back({pd,index_});
155 for (
const Date& pd : pds) {
156 auto expiry = calc->nextExpiry(
true, pd, futureMonthOffset_);
159 if (dailyExpiryOffset_ != Null<Natural>()) {
160 expiry = index_->fixingCalendar().advance(expiry, dailyExpiryOffset_ * Days);
163 indices_.push_back({pd, index_->clone(expiry)});
167 if (deliveryDateRoll_ > 0) {
173 for (
auto& kv : indices_) {
176 expiry = kv.second->expiryDate();
177 if (expiry != prevExpiry) {
178 rollDate = pricingCalendar_.advance(expiry,
179 -
static_cast<Integer
>(deliveryDateRoll_), Days);
184 if (kv.first > rollDate) {
185 expiry = calc->nextExpiry(
false, expiry);
186 kv.second = index_->clone(expiry);
194 for (
auto& kv : indices_) {
195 registerWith(kv.second);
199 if (offPeakPowerData_) {
200 Calendar peakCalendar = offPeakPowerData_->first;
201 Real offPeakHours = offPeakPowerData_->second;
203 for (
const auto& kv : indices_) {
204 if (peakCalendar.isHoliday(kv.first))
205 total += weights_[kv.first] = 24;
207 total += weights_[kv.first] = offPeakHours;
209 for (
auto& kv : weights_)
218void CommodityIndexedAverageCashFlow::updateQuantity() {
221 switch (quantityFrequency_)
223 case CQF::PerCalculationPeriod:
224 periodQuantity_ = quantity_;
225 if (unrealisedQuantity_) {
226 Date today = Settings::instance().evaluationDate();
227 if (startDate_ <= today && today < endDate_) {
229 if (offPeakPowerData_) {
230 QL_REQUIRE(!weights_.empty(),
"Expected to have weights when we have off-peak power data.");
231 auto unrealised = accumulate(weights_.begin(), weights_.end(), 0.0,
232 [&today](Real
sum,
const pair<Date, Real>& p) {
233 return p.first > today ? sum + p.second : sum; });
234 if (unrealised > 0) {
235 periodQuantity_ /= unrealised;
238 auto unrealised = count_if(indices_.begin(), indices_.end(),
239 [&today](
const pair<Date, ext::shared_ptr<CommodityIndex>>& p) { return p.first > today; });
240 if (unrealised > 0) {
241 periodQuantity_ = periodQuantity_ * indices_.size() / unrealised;
247 case CQF::PerPricingDay:
248 periodQuantity_ = quantity_ * indices_.size();
251 if (offPeakPowerData_) {
252 Calendar peakCalendar = offPeakPowerData_->first;
253 Real offPeakHours = offPeakPowerData_->second;
254 periodQuantity_ = 0.0;
255 for (
const auto& kv : indices_) {
256 if (peakCalendar.isHoliday(kv.first))
257 periodQuantity_ += 24.0 * quantity_;
259 periodQuantity_ += offPeakHours * quantity_;
262 QL_REQUIRE(hoursPerDay_ != Null<Natural>(),
"If a commodity quantity frequency of PerHour is used, " <<
263 "a valid hoursPerDay value should be supplied.");
264 periodQuantity_ = quantity_ * indices_.size() * hoursPerDay_;
267 case CQF::PerCalendarDay:
269 periodQuantity_ = quantity_ * ((endDate_ - startDate_ - 1.0) + (!excludeStartDate_ ? 1.0 : 0.0) +
270 (includeEndDate_ ? 1.0 : 0.0));
279CommodityIndexedAverageLeg::CommodityIndexedAverageLeg(
const Schedule& schedule,
280 const ext::shared_ptr<CommodityIndex>& index)
281 : schedule_(schedule), index_(index), paymentLag_(0), paymentCalendar_(NullCalendar()),
282 paymentConvention_(Unadjusted), pricingCalendar_(Calendar()),
284 deliveryDateRoll_(0), futureMonthOffset_(0), payAtMaturity_(false), includeEndDate_(true),
285 excludeStartDate_(true), useBusinessDays_(true),
287 dailyExpiryOffset_(Null<Natural>()), unrealisedQuantity_(false) {}
339CommodityIndexedAverageLeg&
422CommodityIndexedAverageLeg::operator Leg()
const {
425 Size numberCashflows = schedule_.size() - 1;
428 QL_REQUIRE(!quantities_.empty(),
"No quantities given");
429 QL_REQUIRE(quantities_.size() <= numberCashflows,
430 "Too many quantities (" << quantities_.size() <<
"), only " << numberCashflows <<
" required");
431 if (useFuturePrice_) {
432 QL_REQUIRE(calc_,
"CommodityIndexedCashFlow needs a valid future expiry calculator when using first future");
435 if (!paymentDates_.empty()) {
436 QL_REQUIRE(paymentDates_.size() == numberCashflows,
"Expected the number of explicit payment dates ("
437 << paymentDates_.size()
438 <<
") to equal the number of calculation periods ("
439 << numberCashflows <<
")");
444 if (payAtMaturity_) {
445 paymentDate = paymentCalendar_.advance(schedule_.dates().back(), paymentLag_ * Days, paymentConvention_);
452 leg.reserve(numberCashflows);
453 for (Size i = 0; i < numberCashflows; ++i) {
455 Date start = schedule_.date(i);
456 Date end = schedule_.date(i + 1);
457 bool excludeStart = i == 0 ? false : excludeStartDate_;
458 bool includeEnd = i == numberCashflows - 1 ? true : includeEndDate_;
459 Real quantity = detail::get(quantities_, i, 1.0);
460 Real spread = detail::get(spreads_, i, 0.0);
461 Real gearing = detail::get(gearings_, i, 1.0);
464 if (!paymentDates_.empty()) {
465 paymentDate = paymentDates_[i];
468 leg.push_back(ext::make_shared<CommodityIndexedAverageCashFlow>(
469 quantity, start, end, paymentLag_, paymentCalendar_, paymentConvention_, index_, pricingCalendar_, spread,
470 gearing, paymentTiming_, useFuturePrice_, deliveryDateRoll_, futureMonthOffset_, calc_, includeEnd,
471 excludeStart, paymentDate, useBusinessDays_, quantityFrequency_, hoursPerDay_, dailyExpiryOffset_,
472 unrealisedQuantity_, offPeakPowerData_, fxIndex_));
CommodityIndexedAverageCashFlow(QuantLib::Real quantity, const QuantLib::Date &startDate, const QuantLib::Date &endDate, const QuantLib::Date &paymentDate, const ext::shared_ptr< CommodityIndex > &index, const QuantLib::Calendar &pricingCalendar=QuantLib::Calendar(), QuantLib::Real spread=0.0, QuantLib::Real gearing=1.0, bool useFuturePrice=false, QuantLib::Natural deliveryDateRoll=0, QuantLib::Natural futureMonthOffset=0, const ext::shared_ptr< FutureExpiryCalculator > &calc=nullptr, bool includeEndDate=true, bool excludeStartDate=true, bool useBusinessDays=true, CommodityQuantityFrequency quantityFrequency=CommodityQuantityFrequency::PerCalculationPeriod, QuantLib::Natural hoursPerDay=QuantLib::Null< QuantLib::Natural >(), QuantLib::Natural dailyExpiryOffset=QuantLib::Null< QuantLib::Natural >(), bool unrealisedQuantity=false, const boost::optional< std::pair< QuantLib::Calendar, QuantLib::Real > > &offPeakPowerData=boost::none, const ext::shared_ptr< FxIndex > &fxIndex=nullptr)
Constructor taking an explicit paymentDate.
Helper class building a sequence of commodity indexed average cashflows.
CommodityIndexedAverageLeg & useFuturePrice(bool flag=false)
QuantLib::Calendar pricingCalendar_
CommodityIndexedAverageLeg & withPaymentLag(QuantLib::Natural paymentLag)
QuantLib::BusinessDayConvention paymentConvention_
CommodityIndexedAverageLeg & includeEndDate(bool flag=true)
CommodityIndexedAverageLeg & excludeStartDate(bool flag=true)
CommodityIndexedAverageLeg & withFutureMonthOffset(QuantLib::Natural futureMonthOffset)
CommodityIndexedAverageLeg & withGearings(QuantLib::Real gearing)
CommodityIndexedAverageLeg & paymentTiming(CommodityIndexedAverageCashFlow::PaymentTiming paymentTiming)
CommodityIndexedAverageLeg & withDeliveryDateRoll(QuantLib::Natural deliveryDateRoll)
ext::shared_ptr< FutureExpiryCalculator > calc_
CommodityIndexedAverageLeg & withQuantityFrequency(CommodityQuantityFrequency quantityFrequency)
std::vector< QuantLib::Date > paymentDates_
QuantLib::Natural futureMonthOffset_
QuantLib::Natural hoursPerDay_
CommodityIndexedAverageLeg & withPaymentConvention(QuantLib::BusinessDayConvention paymentConvention)
ext::shared_ptr< FxIndex > fxIndex_
CommodityQuantityFrequency quantityFrequency_
CommodityIndexedAverageLeg & withPaymentDates(const std::vector< QuantLib::Date > &paymentDates)
boost::optional< std::pair< QuantLib::Calendar, QuantLib::Real > > offPeakPowerData_
QuantLib::Natural dailyExpiryOffset_
CommodityIndexedAverageLeg & withDailyExpiryOffset(QuantLib::Natural dailyExpiryOffset)
CommodityIndexedAverageLeg & withQuantities(QuantLib::Real quantity)
QuantLib::Natural paymentLag_
CommodityIndexedAverageLeg & withPaymentCalendar(const QuantLib::Calendar &paymentCalendar)
std::vector< QuantLib::Real > quantities_
CommodityIndexedAverageLeg & withHoursPerDay(QuantLib::Natural hoursPerDay)
CommodityIndexedAverageLeg & useBusinessDays(bool flag=true)
QuantLib::Calendar paymentCalendar_
CommodityIndexedAverageLeg & withFutureExpiryCalculator(const ext::shared_ptr< FutureExpiryCalculator > &calc=nullptr)
CommodityIndexedAverageLeg & withPricingCalendar(const QuantLib::Calendar &pricingCalendar)
QuantLib::Natural deliveryDateRoll_
std::vector< QuantLib::Real > spreads_
std::vector< QuantLib::Real > gearings_
CommodityIndexedAverageLeg & withSpreads(QuantLib::Real spread)
CommodityIndexedAverageLeg & unrealisedQuantity(bool flag=false)
CommodityIndexedAverageCashFlow::PaymentTiming paymentTiming_
CommodityIndexedAverageLeg & withOffPeakPowerData(const boost::optional< std::pair< QuantLib::Calendar, QuantLib::Real > > &offPeakPowerData)
CommodityIndexedAverageLeg & withFxIndex(const ext::shared_ptr< FxIndex > &fxIndex)
CommodityIndexedAverageLeg & payAtMaturity(bool flag=false)
Cash flow dependent on the average commodity spot price or future's settlement price over a period....
CommodityQuantityFrequency
Enumeration indicating the frequency associated with a commodity quantity.
set< Date > pricingDates(const Date &s, const Date &e, const Calendar &pricingCalendar, bool excludeStart, bool includeEnd, bool useBusinessDays)
Real sum(const Cash &c, const Cash &d)