Skip to content

Commit 4c68b18

Browse files
pcaspersjenkins
authored andcommitted
QPR-11524 lazy build of conventions
1 parent cc265cd commit 4c68b18

2 files changed

Lines changed: 227 additions & 116 deletions

File tree

OREData/ored/configuration/conventions.cpp

Lines changed: 221 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -2413,87 +2413,57 @@ XMLNode* ZeroInflationIndexConvention::toXML(XMLDocument& doc) {
24132413
}
24142414

24152415
void Conventions::fromXML(XMLNode* node) {
2416-
24172416
XMLUtils::checkNode(node, "Conventions");
24182417

24192418
for (XMLNode* child = XMLUtils::getChildNode(node); child; child = XMLUtils::getNextSibling(child)) {
24202419

2420+
string type = XMLUtils::getNodeName(child);
24212421
boost::shared_ptr<Convention> convention;
2422-
string childName = XMLUtils::getNodeName(child);
2423-
2424-
// Some conventions depend on the already read conventions, since they parse an ibor or overnight
2425-
// index which may be convention based. In this case we require the index convention to appear
2426-
// before the convention that depends on it in the input.
2427-
2428-
if (childName == "Zero") {
2429-
convention.reset(new ZeroRateConvention());
2430-
} else if (childName == "Deposit") {
2431-
convention.reset(new DepositConvention());
2432-
} else if (childName == "Future") {
2433-
convention.reset(new FutureConvention());
2434-
} else if (childName == "FRA") {
2435-
convention.reset(new FraConvention());
2436-
} else if (childName == "OIS") {
2437-
convention.reset(new OisConvention());
2438-
} else if (childName == "Swap") {
2439-
convention.reset(new IRSwapConvention());
2440-
} else if (childName == "AverageOIS") {
2441-
convention.reset(new AverageOisConvention());
2442-
} else if (childName == "TenorBasisSwap") {
2443-
convention.reset(new TenorBasisSwapConvention());
2444-
} else if (childName == "TenorBasisTwoSwap") {
2445-
convention.reset(new TenorBasisTwoSwapConvention());
2446-
} else if (childName == "BMABasisSwap") {
2447-
convention.reset(new BMABasisSwapConvention());
2448-
} else if (childName == "FX") {
2449-
convention.reset(new FXConvention());
2450-
} else if (childName == "CrossCurrencyBasis") {
2451-
convention.reset(new CrossCcyBasisSwapConvention());
2452-
} else if (childName == "CrossCurrencyFixFloat") {
2453-
convention.reset(new CrossCcyFixFloatSwapConvention());
2454-
} else if (childName == "CDS") {
2455-
convention.reset(new CdsConvention());
2456-
} else if (childName == "SwapIndex") {
2457-
convention.reset(new SwapIndexConvention());
2458-
} else if (childName == "InflationSwap") {
2459-
convention.reset(new InflationSwapConvention());
2460-
} else if (childName == "CmsSpreadOption") {
2461-
convention.reset(new CmsSpreadOptionConvention());
2462-
} else if (childName == "CommodityForward") {
2463-
convention = boost::make_shared<CommodityForwardConvention>();
2464-
} else if (childName == "CommodityFuture") {
2465-
convention = boost::make_shared<CommodityFutureConvention>();
2466-
} else if (childName == "FxOption") {
2467-
convention = boost::make_shared<FxOptionConvention>();
2468-
} else if (childName == "IborIndex") {
2422+
2423+
/* we need to build conventions of type
2424+
2425+
- IborIndex
2426+
- OvernightIndex
2427+
- FX
2428+
2429+
immediately because
2430+
2431+
- for IborIndex other conventions depend on it via parseIborIndex() calls
2432+
- the id of IborIndex convention is changed during build (period is normalized)
2433+
- FX conventions are searched by currencies, not id */
2434+
2435+
if (type == "IborIndex") {
24692436
convention = boost::make_shared<IborIndexConvention>();
2470-
} else if (childName == "OvernightIndex") {
2437+
} else if (type == "FX") {
2438+
convention = boost::make_shared<FXConvention>();
2439+
} else if (type == "OvernightIndex") {
24712440
convention = boost::make_shared<OvernightIndexConvention>();
2472-
} else if (childName == "ZeroInflationIndex") {
2473-
convention = boost::make_shared<ZeroInflationIndexConvention>();
2474-
} else if (childName == "BondYield") {
2475-
convention = boost::make_shared<BondYieldConvention>();
2476-
} else {
2477-
// No need to QL_FAIL here, just go to the next one
2478-
WLOG("Convention name, " << childName << ", not recognized.");
2479-
continue;
24802441
}
24812442

2482-
string id = XMLUtils::getChildValue(child, "Id", true);
2483-
2484-
try {
2485-
DLOG("Loading Convention " << id);
2486-
convention->fromXML(child);
2487-
add(convention);
2488-
} catch (exception& e) {
2489-
WLOG("Exception parsing convention "
2490-
"XML Node (id = "
2491-
<< id << ") : " << e.what());
2443+
string id = "unknown";
2444+
if (convention) {
2445+
try {
2446+
id = XMLUtils::getChildValue(child, "Id", true);
2447+
convention->fromXML(child);
2448+
add(convention);
2449+
} catch (const std::exception& e) {
2450+
WLOG("Exception parsing convention "
2451+
<< id << ": " << e.what() << ". This is only a problem if this convention is used later on.");
2452+
}
2453+
} else {
2454+
try {
2455+
id = XMLUtils::getChildValue(child, "Id", true);
2456+
unparsed_[id] = std::make_pair(type, XMLUtils::toString(child));
2457+
} catch (const std::exception& e) {
2458+
WLOG("Exception during retrieval of convention "
2459+
<< id << ": " << e.what() << ". This is only a problem if this convention is used later on.");
2460+
}
24922461
}
24932462
}
24942463
}
24952464

24962465
XMLNode* Conventions::toXML(XMLDocument& doc) {
2466+
boost::unique_lock<boost::shared_mutex> lock(mutex_);
24972467

24982468
XMLNode* conventionsNode = doc.allocNode("Conventions");
24992469

@@ -2509,33 +2479,109 @@ void Conventions::clear() {
25092479
data_.clear();
25102480
}
25112481

2512-
std::string flip(const std::string& id, const std::string& sep) {
2513-
boost::tokenizer<boost::escaped_list_separator<char>> tokenSplit(id, boost::escaped_list_separator<char>("\\", sep, "\""));
2482+
namespace {
2483+
std::string flip(const std::string& id, const std::string& sep = "-") {
2484+
boost::tokenizer<boost::escaped_list_separator<char>> tokenSplit(
2485+
id, boost::escaped_list_separator<char>("\\", sep, "\""));
25142486
std::vector<std::string> tokens(tokenSplit.begin(), tokenSplit.end());
2515-
if (tokens.size() >= 2 && tokens[0].size() == 3 && tokens[1].size() == 3) {
2516-
std::string id2 = tokens[1] + sep + tokens[0];
2517-
for (Size i = 2; i < tokens.size(); ++i)
2518-
id2 += sep + tokens[i];
2519-
return id2; // flipped id
2487+
2488+
bool eligible = false;
2489+
for (auto const& t : tokens) {
2490+
eligible = eligible || (t == "XCCY" || t == "FX" || t == "FXOPTION");
25202491
}
2521-
else return id; // original id
2492+
2493+
if (eligible && (tokens.size() > 2 && tokens[0].size() == 3 && tokens[1].size() == 3)) {
2494+
std::string id2 = tokens[1] + sep + tokens[0];
2495+
for (Size i = 2; i < tokens.size(); ++i)
2496+
id2 += sep + tokens[i];
2497+
return id2;
2498+
}
2499+
2500+
return id;
25222501
}
2523-
2502+
}
2503+
25242504
boost::shared_ptr<Convention> Conventions::get(const string& id) const {
2525-
boost::shared_lock<boost::shared_mutex> lock(mutex_);
2526-
auto it = data_.find(id);
2527-
if (it != data_.end())
2528-
return it->second;
2529-
else {
2530-
std::string id2 = flip(id);
2531-
auto it = data_.find(id2);
2532-
if (it != data_.end() && boost::dynamic_pointer_cast<CrossCcyBasisSwapConvention>(it->second)) {
2533-
return it->second;
2534-
}
2535-
else {
2536-
QL_FAIL("Cannot find conventions for id " << id);
2537-
}
2505+
2506+
{
2507+
boost::shared_lock<boost::shared_mutex> lock(mutex_);
2508+
if (auto it = data_.find(id); it != data_.end())
2509+
return it->second;
2510+
if (auto it = data_.find(flip(id)); it != data_.end())
2511+
return it->second;
25382512
}
2513+
2514+
boost::unique_lock<boost::shared_mutex> lock(mutex_);
2515+
2516+
std::string type, unparsed;
2517+
if (auto it = unparsed_.find(id); it != unparsed_.end()) {
2518+
std::tie(type, unparsed) = it->second;
2519+
unparsed_.erase(id);
2520+
} else if (auto it = unparsed_.find(flip(id)); it != unparsed_.end()) {
2521+
std::tie(type, unparsed) = it->second;
2522+
unparsed_.erase(flip(id));
2523+
}
2524+
2525+
if (unparsed.empty()) {
2526+
QL_FAIL("Required convention '" << id << "' not found.");
2527+
}
2528+
2529+
boost::shared_ptr<Convention> convention;
2530+
if (type == "Zero") {
2531+
convention = boost::make_shared<ZeroRateConvention>();
2532+
} else if (type == "Deposit") {
2533+
convention = boost::make_shared<DepositConvention>();
2534+
} else if (type == "Future") {
2535+
convention = boost::make_shared<FutureConvention>();
2536+
} else if (type == "FRA") {
2537+
convention = boost::make_shared<FraConvention>();
2538+
} else if (type == "OIS") {
2539+
convention = boost::make_shared<OisConvention>();
2540+
} else if (type == "Swap") {
2541+
convention = boost::make_shared<IRSwapConvention>();
2542+
} else if (type == "AverageOIS") {
2543+
convention = boost::make_shared<AverageOisConvention>();
2544+
} else if (type == "TenorBasisSwap") {
2545+
convention = boost::make_shared<TenorBasisSwapConvention>();
2546+
} else if (type == "TenorBasisTwoSwap") {
2547+
convention=boost::make_shared<TenorBasisTwoSwapConvention>();
2548+
} else if (type == "BMABasisSwap") {
2549+
convention = boost::make_shared<BMABasisSwapConvention>();
2550+
} else if (type == "CrossCurrencyBasis") {
2551+
convention=boost::make_shared<CrossCcyBasisSwapConvention>();
2552+
} else if (type == "CrossCurrencyFixFloat") {
2553+
convention=boost::make_shared<CrossCcyFixFloatSwapConvention>();
2554+
} else if (type == "CDS") {
2555+
convention=boost::make_shared<CdsConvention>();
2556+
} else if (type == "SwapIndex") {
2557+
convention=boost::make_shared<SwapIndexConvention>();
2558+
} else if (type == "InflationSwap") {
2559+
convention=boost::make_shared<InflationSwapConvention>();
2560+
} else if (type == "CmsSpreadOption") {
2561+
convention=boost::make_shared<CmsSpreadOptionConvention>();
2562+
} else if (type == "CommodityForward") {
2563+
convention = boost::make_shared<CommodityForwardConvention>();
2564+
} else if (type == "CommodityFuture") {
2565+
convention = boost::make_shared<CommodityFutureConvention>();
2566+
} else if (type == "FxOption") {
2567+
convention = boost::make_shared<FxOptionConvention>();
2568+
} else if (type == "ZeroInflationIndex") {
2569+
convention = boost::make_shared<ZeroInflationIndexConvention>();
2570+
} else if (type == "BondYield") {
2571+
convention = boost::make_shared<BondYieldConvention>();
2572+
} else {
2573+
QL_FAIL("Required convention '" << id << "' has unknown type '" + type + "' not recognized.");
2574+
}
2575+
2576+
try {
2577+
DLOG("Loading Convention " << id);
2578+
convention->fromXMLString(unparsed);
2579+
addInternal(convention);
2580+
} catch (exception& e) {
2581+
QL_FAIL("Required convention '" << id << "' could not be built: " << e.what());
2582+
}
2583+
2584+
return convention;
25392585
}
25402586

25412587
boost::shared_ptr<Convention> Conventions::getFxConvention(const string& ccy1, const string& ccy2) const {
@@ -2549,39 +2595,44 @@ boost::shared_ptr<Convention> Conventions::getFxConvention(const string& ccy1, c
25492595
return fxCon;
25502596
}
25512597
}
2552-
QL_FAIL("Cannot find FX conventions for ccys " << ccy1 << " and " << ccy2);
2598+
QL_FAIL("Required FX convention for ccys '" << ccy1 << "' and '" << ccy2 << "' not found.");
25532599
}
25542600

25552601
pair<bool, boost::shared_ptr<Convention>> Conventions::get(const string& id, const Convention::Type& type) const {
2556-
boost::shared_lock<boost::shared_mutex> lock(mutex_);
2557-
auto c = data_.find(id);
2558-
if (c == data_.end() || c->second->type() != type)
2559-
return make_pair(false, nullptr);
2560-
else
2561-
return make_pair(true, c->second);
2602+
try {
2603+
auto c = get(id);
2604+
if (c->type() == type)
2605+
return std::make_pair(true, c);
2606+
} catch (...) {
2607+
}
2608+
return make_pair(false, nullptr);
25622609
}
25632610

25642611
std::set<boost::shared_ptr<Convention>> Conventions::get(const Convention::Type& type) const {
2565-
boost::shared_lock<boost::shared_mutex> lock(mutex_);
25662612
std::set<boost::shared_ptr<Convention>> result;
2567-
for (auto const& d : data_) {
2568-
if (d.second->type() == type)
2569-
result.insert(d.second);
2613+
std::set<std::string> unparsedIds;
2614+
std::string typeStr = ore::data::to_string(type);
2615+
{
2616+
boost::shared_lock<boost::shared_mutex> lock(mutex_);
2617+
for (auto const& d : data_) {
2618+
if (d.second->type() == type)
2619+
result.insert(d.second);
2620+
}
2621+
for (auto const& u : unparsed_) {
2622+
if (u.second.first == typeStr)
2623+
unparsedIds.insert(u.first);
2624+
}
2625+
}
2626+
for (auto const& id : unparsedIds) {
2627+
result.insert(get(id));
25702628
}
25712629
return result;
25722630
}
25732631

25742632
bool Conventions::has(const string& id) const {
25752633
boost::shared_lock<boost::shared_mutex> lock(mutex_);
2576-
if (data_.find(id) != data_.end())
2577-
return true;
2578-
else {
2579-
std::string id2 = flip(id);
2580-
if (data_.find(id2) != data_.end())
2581-
return true;
2582-
else
2583-
return false;
2584-
}
2634+
return data_.find(id) != data_.end() || unparsed_.find(id) != unparsed_.end() ||
2635+
data_.find(flip(id)) != data_.end() || unparsed_.find(flip(id)) != unparsed_.end();
25852636
}
25862637

25872638
bool Conventions::has(const std::string& id, const Convention::Type& type) const {
@@ -2590,10 +2641,71 @@ bool Conventions::has(const std::string& id, const Convention::Type& type) const
25902641

25912642
void Conventions::add(const boost::shared_ptr<Convention>& convention) {
25922643
boost::unique_lock<boost::shared_mutex> lock(mutex_);
2644+
addInternal(convention);
2645+
}
2646+
2647+
void Conventions::addInternal(const boost::shared_ptr<Convention>& convention) const {
25932648
const string& id = convention->id();
25942649
QL_REQUIRE(data_.find(id) == data_.end(), "Convention already exists for id " << id);
25952650
data_[id] = convention;
25962651
}
25972652

2653+
std::ostream& operator<<(std::ostream& out, Convention::Type type) {
2654+
switch (type) {
2655+
case Zero:
2656+
return out << "Zero";
2657+
case Deposit:
2658+
return out << "Deposit";
2659+
case Future:
2660+
return out << "Future";
2661+
case FRA:
2662+
return out << "FRA";
2663+
case OIS:
2664+
return out << "OIS";
2665+
case Swap:
2666+
return out << "Swap";
2667+
case AverageOIS:
2668+
return out << "AverageOIS";
2669+
case TenorBasisSwap:
2670+
return out << "TenorBasisSwap";
2671+
case TenorBasisTwoSwap:
2672+
return out << "TenorBasisTwoSwap";
2673+
case BMABasisSwap:
2674+
return out << "BMABasisSwap";
2675+
case FX:
2676+
return out << "FX";
2677+
case CrossCcyBasis:
2678+
return out << "CrossCcyBasis";
2679+
case CrossCcyFixFloat:
2680+
return out << "CrossCcyFixFloat";
2681+
case CDS:
2682+
return out << "CDS";
2683+
case IborIndex:
2684+
return out << "IborIndex";
2685+
case OvernightIndex:
2686+
return out << "OvernightIndex";
2687+
case SwapIndex:
2688+
return out << "SwapIndex";
2689+
case ZeroInflationIndex:
2690+
return out << "ZeroInflationIndex";
2691+
case InflationSwap:
2692+
return out << "InflationSwap";
2693+
case SecuritySpread:
2694+
return out << "SecuritySpread";
2695+
case CMSSpreadOption:
2696+
return out << "CMSSpreadOption";
2697+
case CommodityForward:
2698+
return out << "CommodityForward";
2699+
case CommodityFuture:
2700+
return out << "CommodityFuture";
2701+
case FxOption:
2702+
return out << "FxOption";
2703+
case BondYield:
2704+
return out << "BondYield";
2705+
default:
2706+
return out << "unknown convention type (" << static_cast<int>(type) << ")";
2707+
}
2708+
}
2709+
25982710
} // namespace data
25992711
} // namespace ore

0 commit comments

Comments
 (0)