@@ -30,13 +30,22 @@ void ReferenceDatum::fromXML(XMLNode* node) {
3030 XMLUtils::checkNode (node, " ReferenceDatum" );
3131 type_ = XMLUtils::getChildValue (node, " Type" , true );
3232 id_ = XMLUtils::getAttribute (node, " id" );
33+ std::string dateStr = XMLUtils::getAttribute (node, " validFrom" );
34+ if (!dateStr.empty ()) {
35+ validFrom_ = parseDate (dateStr);
36+ } else {
37+ validFrom_ = QuantLib::Date::minDate ();
38+ }
3339}
3440
3541XMLNode* ReferenceDatum::toXML (XMLDocument& doc) {
3642 XMLNode* node = doc.allocNode (" ReferenceDatum" );
3743 QL_REQUIRE (node, " Failed to create ReferenceDatum node" );
3844 XMLUtils::addAttribute (doc, node, " id" , id_);
3945 XMLUtils::addChild (doc, node, " Type" , type_);
46+ if (validFrom_ > QuantLib::Date::minDate ()) {
47+ XMLUtils::addAttribute (doc, node, " validFrom" , to_string (validFrom_));
48+ }
4049 return node;
4150}
4251
@@ -189,6 +198,9 @@ CreditIndexReferenceDatum::CreditIndexReferenceDatum() {}
189198
190199CreditIndexReferenceDatum::CreditIndexReferenceDatum (const string& name) : ReferenceDatum(TYPE, name) {}
191200
201+ CreditIndexReferenceDatum::CreditIndexReferenceDatum (const string& name, const QuantLib::Date& validFrom)
202+ : ReferenceDatum(TYPE, name, validFrom) {}
203+
192204void CreditIndexReferenceDatum::fromXML (XMLNode* node) {
193205
194206 ReferenceDatum::fromXML (node);
@@ -292,8 +304,6 @@ void CurrencyHedgedEquityIndexReferenceDatum::fromXML(XMLNode* node) {
292304
293305 underlyingIndexName_ = XMLUtils::getChildValue (innerNode, " UnderlyingIndex" , true );
294306
295- hedgeCurrency_ = XMLUtils::getChildValue (innerNode, " HedgeCurrency" , true );
296-
297307 auto rebalancingStr = XMLUtils::getChildValue (innerNode, " RebalancingStrategy" , false , " EndOfMonth" );
298308 if (rebalancingStr == " EndOfMonth" ) {
299309 rebalancingStrategy_ = CurrencyHedgedEquityIndexReferenceDatum::RebalancingDate::EndOfMonth;
@@ -313,7 +323,6 @@ void CurrencyHedgedEquityIndexReferenceDatum::fromXML(XMLNode* node) {
313323 fxIndexes_[currency] = indexFamily;
314324 }
315325 }
316-
317326 // Optional Fields
318327 referenceDateOffset_ = XMLUtils::getChildValueAsInt (innerNode, " ReferenceDateOffset" , false , 0 );
319328 auto hedgeAdjStr = XMLUtils::getChildValue (innerNode, " HedgeAdjustment" , false , " None" );
@@ -351,7 +360,6 @@ XMLNode* CurrencyHedgedEquityIndexReferenceDatum::toXML(XMLDocument& doc) {
351360 XMLNode* rdNode = XMLUtils::addChild (doc, node, type () + " ReferenceData" );
352361
353362 XMLUtils::addChild (doc, rdNode, " UnderlyingIndex" , underlyingIndexName_);
354- XMLUtils::addChild (doc, rdNode, " HedgeCurrency" , hedgeCurrency_);
355363 XMLUtils::addChild (doc, rdNode, " RebalancingStrategy" , " EndOfMonth" );
356364 XMLUtils::addChild (doc, rdNode, " HedgeCalendar" , to_string (hedgeCalendar_));
357365 if (referenceDateOffset_ != 0 )
@@ -485,10 +493,11 @@ void BasicReferenceDataManager::fromXML(XMLNode* node) {
485493
486494void BasicReferenceDataManager::add (const boost::shared_ptr<ReferenceDatum>& rd) {
487495 // Add reference datum, it is overwritten if it is already present.
488- data_[make_pair (rd->type (), rd->id ())] = rd;
496+ data_[make_pair (rd->type (), rd->id ())][rd-> validFrom ()] = rd;
489497}
490498
491- boost::shared_ptr<ReferenceDatum> BasicReferenceDataManager::addFromXMLNode (XMLNode* node, const std::string& inputId) {
499+ boost::shared_ptr<ReferenceDatum> BasicReferenceDataManager::addFromXMLNode (XMLNode* node, const std::string& inputId,
500+ const QuantLib::Date& inputValidFrom) {
492501 string refDataType = XMLUtils::getChildValue (node, " Type" , false );
493502 boost::shared_ptr<ReferenceDatum> refData;
494503
@@ -498,16 +507,29 @@ boost::shared_ptr<ReferenceDatum> BasicReferenceDataManager::addFromXMLNode(XMLN
498507 }
499508
500509 string id = inputId.empty () ? XMLUtils::getAttribute (node, " id" ) : inputId;
510+
511+ string validFromStr = XMLUtils::getAttribute (node, " validFrom" );
512+ QuantLib::Date validFrom;
513+ if (validFromStr.empty ()) {
514+ validFrom = QuantLib::Date::minDate ();
515+ } else {
516+ validFrom = ore::data::parseDate (validFromStr);
517+ }
518+
519+ validFrom = inputValidFrom == QuantLib::Null<QuantLib::Date>() ? validFrom : inputValidFrom;
501520
502521 if (id.empty ()) {
503522 ALOG (" Found referenceDatum without id - skipping" );
504523 return refData;
505524 }
506525
507- if (data_.find (make_pair (refDataType, id)) != data_.end ()) {
508- duplicates_.insert (make_pair (refDataType, id));
509- ALOG (" Found duplicate referenceDatum for type='" << refDataType << " ', id='" << id << " '" );
510- return refData;
526+ if (auto it = data_.find (make_pair (refDataType, id)); it != data_.end ()) {
527+ if (it->second .count (validFrom) > 0 ) {
528+ duplicates_.insert (make_tuple (refDataType, id, validFrom));
529+ ALOG (" Found duplicate referenceDatum for type='" << refDataType << " ', id='" << id << " ', validFrom='"
530+ << validFrom << " '" );
531+ return refData;
532+ }
511533 }
512534
513535 try {
@@ -516,11 +538,14 @@ boost::shared_ptr<ReferenceDatum> BasicReferenceDataManager::addFromXMLNode(XMLN
516538 // set the type and id at top level (is this needed?)
517539 refData->setType (refDataType);
518540 refData->setId (id);
519- data_[make_pair (refDataType, id)] = refData;
520- TLOG (" added referenceDatum for type='" << refDataType << " ', id='" << id << " '" );
541+ refData->setValidFrom (validFrom);
542+ data_[make_pair (refDataType, id)][validFrom] = refData;
543+ TLOG (" added referenceDatum for type='" << refDataType << " ', id='" << id << " ', validFrom='" << validFrom
544+ << " '" );
521545 } catch (const std::exception& e) {
522- buildErrors_[make_pair (refDataType, id)] = e.what ();
523- ALOG (" Error building referenceDatum for type='" << refDataType << " ', id='" << id << " ': " << e.what ());
546+ buildErrors_[make_pair (refDataType, id)][validFrom] = e.what ();
547+ ALOG (" Error building referenceDatum for type='" << refDataType << " ', id='" << id << " ', validFrom='" << validFrom
548+ << " ': " << e.what ());
524549 }
525550
526551 return refData;
@@ -536,31 +561,61 @@ boost::shared_ptr<ReferenceDatum> BasicReferenceDataManager::buildReferenceDatum
536561XMLNode* BasicReferenceDataManager::toXML (XMLDocument& doc) {
537562 XMLNode* node = doc.allocNode (" ReferenceData" );
538563 for (const auto & kv : data_) {
539- XMLUtils::appendNode (node, kv.second ->toXML (doc));
564+ for (const auto & [_, refData] : kv.second ) {
565+ XMLUtils::appendNode (node, refData->toXML (doc));
566+ }
540567 }
541568 return node;
542569}
543570
544- void BasicReferenceDataManager::check (const string& type, const string& id) const {
545- auto key = make_pair (type, id);
571+ std::tuple<QuantLib::Date, boost::shared_ptr<ReferenceDatum>> BasicReferenceDataManager::latestValidFrom (const string& type, const string& id,
572+ const QuantLib::Date& asof) const {
573+ auto it = data_.find (make_pair (type, id));
574+ if (it != data_.end () && !it->second .empty ()){
575+ auto uB = it->second .upper_bound (asof);
576+ if (uB != it->second .begin ()) {
577+ return *(--uB);
578+ }
579+ }
580+ return {QuantLib::Date (), nullptr };
581+ }
582+
583+ void BasicReferenceDataManager::check (const string& type, const string& id, const QuantLib::Date& validFrom) const {
584+ auto key = make_tuple (type, id, validFrom);
546585 if (duplicates_.find (key) != duplicates_.end ())
547- ALOG (" BasicReferenceDataManager: duplicate entries for type='" << type << " ', id='" << id << " '" );
548- auto err = buildErrors_.find (key);
549- if (err != buildErrors_.end ())
550- ALOG (" BasicReferenceDataManager: Build error for type='" << type << " ', id='" << id << " ': " << err->second );
586+ ALOG (" BasicReferenceDataManager: duplicate entries for type='" << type << " ', id='" << id << " ', validFrom='"
587+ << validFrom << " '" );
588+ auto err = buildErrors_.find (make_pair (type, id));
589+ if (err != buildErrors_.end ()) {
590+ for (const auto & [validFrom, error] : err->second ) {
591+ ALOG (" BasicReferenceDataManager: Build error for type='" << type << " ', id='" << id << " ', validFrom='"
592+ << validFrom << " ': " << error);
593+ }
594+ }
595+
551596}
552597
553- bool BasicReferenceDataManager::hasData (const string& type, const string& id) const {
554- check (type, id);
555- return data_.find (make_pair (type, id)) != data_.end ();
598+ bool BasicReferenceDataManager::hasData (const string& type, const string& id, const QuantLib::Date& asof) const {
599+ Date asofDate = asof;
600+ if (asofDate == QuantLib::Null<QuantLib::Date>()) {
601+ asofDate = Settings::instance ().evaluationDate ();
602+ }
603+ auto [validFrom, refData] = latestValidFrom (type, id, asofDate);
604+ check (type, id, validFrom);
605+ return refData != nullptr ;
556606}
557607
558- boost::shared_ptr<ReferenceDatum> BasicReferenceDataManager::getData (const string& type, const string& id) {
559- check (type, id);
560- auto it = data_.find (make_pair (type, id));
561- QL_REQUIRE (it != data_.end (),
562- " BasicReferenceDataManager::getData(): No Reference data for type='" << type << " ', id='" << id << " '" );
563- return it->second ;
608+ boost::shared_ptr<ReferenceDatum> BasicReferenceDataManager::getData (const string& type, const string& id,
609+ const QuantLib::Date& asof) {
610+ Date asofDate = asof;
611+ if (asofDate == QuantLib::Null<QuantLib::Date>()) {
612+ asofDate = Settings::instance ().evaluationDate ();
613+ }
614+ auto [validFrom, refData] = latestValidFrom (type, id, asofDate);
615+ check (type, id, validFrom);
616+ QL_REQUIRE (refData != nullptr , " BasicReferenceDataManager::getData(): No Reference data for type='"
617+ << type << " ', id='" << id << " ', asof='" << asof << " '" );
618+ return refData;
564619}
565620
566621} // namespace data
0 commit comments