@@ -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);
0 commit comments