Skip to content

Commit b7d832f

Browse files
mgronckijenkins
authored andcommitted
QPR-12130 commodity future of a specific contract instead of the prompt future
1 parent b930562 commit b7d832f

3 files changed

Lines changed: 22 additions & 8 deletions

File tree

OREData/ored/portfolio/commodityposition.cpp

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,24 @@ void CommodityPosition::build(const boost::shared_ptr<ore::data::EngineFactory>&
6464
auto convention = boost::dynamic_pointer_cast<CommodityFutureConvention>(p.second);
6565
ConventionsBasedFutureExpiry feCalc(*convention);
6666
Date expiry = Settings::instance().evaluationDate();
67-
if (u.deliveryRollDays() != Null<Size>()) {
68-
auto cal = u.deliveryRollCalendar().empty() ? convention->calendar() : parseCalendar(u.deliveryRollCalendar());
69-
expiry = cal.advance(expiry, u.deliveryRollDays() * Days, convention->businessDayConvention());
70-
}
71-
7267
Size nOffset = u.futureMonthOffset() == Null<Size>() ? 0 : u.futureMonthOffset();
73-
74-
expiry = feCalc.nextExpiry(true, expiry, nOffset);
75-
68+
if (u.futureExpiryDate().empty()) {
69+
if (u.deliveryRollDays() != Null<Size>()) {
70+
auto cal = u.deliveryRollCalendar().empty() ? convention->calendar() : parseCalendar(u.deliveryRollCalendar());
71+
expiry = cal.advance(expiry, u.deliveryRollDays() * Days, convention->businessDayConvention());
72+
}
73+
74+
expiry = feCalc.nextExpiry(true, expiry, nOffset);
75+
} else if (u.futureExpiryDate().size() == 7) {
76+
// parse MONTHYYYY (e.g. NOV2023) format into date
77+
auto month = parseMonth(u.futureExpiryDate().substr(0, 3));
78+
auto year = parseInteger(u.futureExpiryDate().substr(3, 4));
79+
Date contractDate(1, month, year);
80+
expiry = feCalc.expiryDate(contractDate, nOffset, false);
81+
} else {
82+
Date contractDate = parseDate(u.futureExpiryDate());
83+
expiry = feCalc.expiryDate(contractDate, nOffset, false);
84+
}
7685
index = index->clone(expiry, pts);
7786
}
7887
indices_.push_back(index);

OREData/ored/portfolio/underlying.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ void CommodityUnderlying::fromXML(XMLNode* node) {
133133
deliveryRollDays_ = Null<Size>();
134134
deliveryRollCalendar_ = XMLUtils::getChildValue(node, "DeliveryRollCalendar", false);
135135
isBasic_ = false;
136+
futureExpiryDate_ = XMLUtils::getChildValue(node, "FutureExpiryDate", false);
136137
} else {
137138
QL_FAIL("Need either a Name or Underlying node for CommodityUnderlying.");
138139
}
@@ -153,6 +154,8 @@ XMLNode* CommodityUnderlying::toXML(XMLDocument& doc) {
153154
XMLUtils::addChild(doc, node, "DeliveryRollDays", (int)deliveryRollDays_);
154155
if (!deliveryRollCalendar_.empty())
155156
XMLUtils::addChild(doc, node, "DeliveryRollCalendar", deliveryRollCalendar_);
157+
if (!futureExpiryDate_.empty())
158+
XMLUtils::addChild(doc, node, "FutureExpiryDate", futureExpiryDate_);
156159
}
157160
return node;
158161
}

OREData/ored/portfolio/underlying.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ class CommodityUnderlying : public Underlying {
140140
QuantLib::Size futureMonthOffset() const { return futureMonthOffset_; }
141141
QuantLib::Size deliveryRollDays() const { return deliveryRollDays_; }
142142
const std::string& deliveryRollCalendar() const { return deliveryRollCalendar_; }
143+
const std::string& futureExpiryDate() const { return futureExpiryDate_; }
143144

144145
//! \name Serialisation
145146
//@{
@@ -152,6 +153,7 @@ class CommodityUnderlying : public Underlying {
152153
QuantLib::Size futureMonthOffset_ = QuantLib::Null<QuantLib::Size>();
153154
QuantLib::Size deliveryRollDays_ = QuantLib::Null<QuantLib::Size>();
154155
std::string deliveryRollCalendar_;
156+
std::string futureExpiryDate_;
155157
};
156158

157159
class FXUnderlying : public Underlying {

0 commit comments

Comments
 (0)