Skip to content

Commit 5dbc9c0

Browse files
author
jenkins
committed
git subrepo pull (merge) ore
subrepo: subdir: "ore" merged: "be2d321491" upstream: origin: "git@gitlab.acadiasoft.net:qs/ore.git" branch: "master" commit: "daa48ca4ed" git-subrepo: version: "0.4.6" origin: "https://github.com/ingydotnet/git-subrepo" commit: "110b9eb"
2 parents 864b6a3 + daa48ca commit 5dbc9c0

5 files changed

Lines changed: 128 additions & 23 deletions

File tree

.gitmodules

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[submodule "QuantLib"]
22
path = QuantLib
3-
url = https://github.com/OpenSourceRisk/QuantLib.git
3+
url = git@gitlab.acadiasoft.net:qs/quantlib.git
44
branch = master
55
ignore = dirty

Docs/UserGuide/curve_configurations/yieldcurves.tex

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ \subsubsection*{Direct Segment}
128128
or discount factor. The \lstinline!Conventions! node contains the ID of a node in the {\tt conventions.xml} file
129129
described in section \ref{sec:conventions}. The \lstinline!Conventions! node associates conventions with the quotes.
130130
131+
For \emph{Discount} type segments, the quotes can be given using a wildcard. Any valid and matching quotes will then be loaded from the provided market data. An example wildcard is:
132+
\begin{itemize}
133+
\item {DISCOUNT/RATE/EUR/EUR3M/*}
134+
\end{itemize}
135+
131136
\begin{listing}[H]
132137
%\hrule\medskip
133138
\begin{minted}[fontsize=\footnotesize]{xml}

Docs/UserGuide/marketdata.tex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ \subsection{Discount Factor}\label{ss:discount_rate}
144144
Examples with a Term and with a DiscountDate:
145145
\begin{itemize}
146146
\item {DISCOUNT/RATE/EUR/EUR3M/3Y}
147-
\item {DISCOUNT/RATE/EUR/EUR3M/A365F/12-05-2018}
147+
\item {DISCOUNT/RATE/EUR/EUR3M/12-05-2018}
148148
\end{itemize}
149149

150150
%- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

OREData/ored/marketdata/yieldcurve.cpp

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,39 +1062,70 @@ void YieldCurve::buildDiscountCurve() {
10621062
boost::shared_ptr<Conventions> conventions = InstrumentConventions::instance().conventions();
10631063
boost::shared_ptr<Convention> convention;
10641064

1065-
for (Size i = 0; i < discountQuoteIDs.size(); ++i) {
1066-
boost::shared_ptr<MarketDatum> marketQuote = loader_.get(discountQuoteIDs[i], asofDate_);
1067-
if (marketQuote) {
1068-
QL_REQUIRE(marketQuote->instrumentType() == MarketDatum::InstrumentType::DISCOUNT,
1069-
"Market quote not of type Discount.");
1070-
boost::shared_ptr<DiscountQuote> discountQuote = boost::dynamic_pointer_cast<DiscountQuote>(marketQuote);
1065+
vector<string> quotes;
1066+
quotes.reserve(discountQuoteIDs.size()); // Reserve space for efficiency
10711067

1072-
if(discountQuote->date() != Date()){
1068+
std::transform(discountQuoteIDs.begin(), discountQuoteIDs.end(), std::back_inserter(quotes),
1069+
[](const std::pair<string, bool>& pair) {
1070+
return pair.first; // Extract only the quote part
1071+
});
10731072

1074-
data[discountQuote->date()] = discountQuote->quote()->value();
1073+
auto wildcard = getUniqueWildcard(quotes);
10751074

1076-
} else if (discountQuote->tenor() != Period()){
1075+
std::set<boost::shared_ptr<MarketDatum>> marketData;
1076+
if (wildcard) {
1077+
marketData = loader_.get(*wildcard, asofDate_);
1078+
} else {
1079+
std::ostringstream ss;
1080+
ss << MarketDatum::InstrumentType::DISCOUNT << "/" << MarketDatum::QuoteType::RATE << "/" << currency_ << "/*";
1081+
Wildcard w(ss.str());
1082+
marketData = loader_.get(w, asofDate_);
1083+
}
1084+
1085+
for (const auto& marketQuote : marketData) {
1086+
QL_REQUIRE(marketQuote->instrumentType() == MarketDatum::InstrumentType::DISCOUNT,
1087+
"Market quote not of type Discount.");
1088+
boost::shared_ptr<DiscountQuote> discountQuote = boost::dynamic_pointer_cast<DiscountQuote>(marketQuote);
1089+
1090+
// filtering
1091+
if (!wildcard) {
1092+
vector<string>::const_iterator it = find(quotes.begin(), quotes.end(), discountQuote->name());
1093+
if (it == quotes.end())
1094+
continue;
1095+
}
10771096

1078-
if(!convention)
1079-
convention = conventions->get(discountCurveSegment->conventionsID());
1080-
boost::shared_ptr<ZeroRateConvention> zeroConvention = boost::dynamic_pointer_cast<ZeroRateConvention>(convention);
1081-
QL_REQUIRE(zeroConvention, "could not cast to ZeroRateConvention");
1097+
if (discountQuote->date() != Date()) {
10821098

1083-
Calendar cal = zeroConvention->tenorCalendar();
1084-
BusinessDayConvention rollConvention = zeroConvention->rollConvention();
1085-
Date date = cal.adjust(cal.adjust(asofDate_, rollConvention) + discountQuote->tenor(), rollConvention);
1086-
DLOG("YieldCurve::buildDiscountCurve - tenor " << discountQuote->tenor() << " to date " << io::iso_date(date));
1087-
data[date] = discountQuote->quote()->value();
1099+
data[discountQuote->date()] = discountQuote->quote()->value();
10881100

1089-
} else {
1090-
QL_FAIL("YieldCurve::buildDiscountCurve - neither date nor tenor recognised");
1091-
}
1101+
} else if (discountQuote->tenor() != Period()) {
10921102

1103+
if (!convention)
1104+
convention = conventions->get(discountCurveSegment->conventionsID());
1105+
boost::shared_ptr<ZeroRateConvention> zeroConvention =
1106+
boost::dynamic_pointer_cast<ZeroRateConvention>(convention);
1107+
QL_REQUIRE(zeroConvention, "could not cast to ZeroRateConvention");
1108+
1109+
Calendar cal = zeroConvention->tenorCalendar();
1110+
BusinessDayConvention rollConvention = zeroConvention->rollConvention();
1111+
Date date = cal.adjust(cal.adjust(asofDate_, rollConvention) + discountQuote->tenor(), rollConvention);
1112+
DLOG("YieldCurve::buildDiscountCurve - tenor " << discountQuote->tenor() << " to date "
1113+
<< io::iso_date(date));
1114+
data[date] = discountQuote->quote()->value();
1115+
1116+
} else {
1117+
QL_FAIL("YieldCurve::buildDiscountCurve - neither date nor tenor recognised");
10931118
}
10941119
}
10951120

1121+
// Some logging and checks
10961122
QL_REQUIRE(data.size() > 0, "No market data found for curve spec " << curveSpec_.name() << " with as of date "
10971123
<< io::iso_date(asofDate_));
1124+
if (!wildcard) {
1125+
QL_REQUIRE(data.size() == quotes.size(), "Found " << data.size() << " quotes, but "
1126+
<< quotes.size()
1127+
<< " quotes given in config " << curveConfig_->curveID());
1128+
}
10981129

10991130
if (data.begin()->first > asofDate_) {
11001131
DLOG("Insert discount curve point at time zero for " << curveSpec_.name());

OREData/test/yieldcurve.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,75 @@ BOOST_AUTO_TEST_CASE(testBootstrapAndFixings) {
246246
BOOST_CHECK_NO_THROW(YieldCurve jpyYieldCurve(asof, spec, curveConfigs, loader));
247247
}
248248

249+
BOOST_AUTO_TEST_CASE(testBuildDiscountCurveDirectSegment) {
250+
251+
Date asof(13, October, 2023);
252+
Settings::instance().evaluationDate() = asof;
253+
254+
YieldCurveSpec spec("EUR", "EUR-CURVE");
255+
256+
CurveConfigurations curveConfigs;
257+
258+
vector<string> quotes;
259+
quotes.emplace_back("DISCOUNT/RATE/EUR/EUR-CURVE/2023-10-14");
260+
quotes.emplace_back("DISCOUNT/RATE/EUR/EUR-CURVE/2023-10-15");
261+
262+
vector<boost::shared_ptr<YieldCurveSegment>> segments{boost::make_shared<DirectYieldCurveSegment>(
263+
"Discount", "", quotes)};
264+
265+
boost::shared_ptr<YieldCurveConfig> yCConfig =
266+
boost::make_shared<YieldCurveConfig>("EUR-CURVE", "ORE YieldCurve built from EUR-CURVE", "EUR", "", segments);
267+
curveConfigs.add(CurveSpec::CurveType::Yield, "EUR-CURVE", yCConfig);
268+
269+
vector<string> data{"2023-10-12 DISCOUNT/RATE/SEK/STINA-CURVE/2023-10-13 0.77",
270+
"2023-10-12 DISCOUNT/RATE/EUR/EUR-ANOTHER-CURVE/2023-10-13 0.95",
271+
"2023-10-13 DISCOUNT/RATE/EUR/EUR-ANOTHER-CURVE/2023-10-14 0.95",
272+
"2023-10-12 DISCOUNT/RATE/EUR/EUR-CURVE/2023-10-12 0.88",
273+
"2023-10-13 DISCOUNT/RATE/EUR/EUR-CURVE/2023-10-13 1.0",
274+
"2023-10-13 DISCOUNT/RATE/EUR/EUR-CURVE/2023-10-14 0.99",
275+
"2023-10-13 DISCOUNT/RATE/EUR/EUR-CURVE/2023-10-15 0.98",
276+
"2023-10-13 COMMODITY_FWD/PRICE/GOLD/USD/2023-10-31 1158.8",
277+
"2023-10-13 COMMODITY_FWD/PRICE/GOLD/USD/2023-11-01 1160.9",
278+
"2023-10-13 COMMODITY_FWD/PRICE/GOLD/USD/2023-11-02 1163.4"};
279+
MarketDataLoader loader(data);
280+
281+
BOOST_CHECK_NO_THROW(YieldCurve yieldCurve(asof, spec, curveConfigs, loader));
282+
}
283+
284+
BOOST_AUTO_TEST_CASE(testBuildDiscountCurveDirectSegmentWildcard) {
285+
286+
Date asof(13, October, 2023);
287+
Settings::instance().evaluationDate() = asof;
288+
289+
YieldCurveSpec spec("EUR", "EUR-CURVE");
290+
291+
CurveConfigurations curveConfigs;
292+
293+
vector<string> quotes;
294+
quotes.emplace_back("DISCOUNT/RATE/EUR/EUR-CURVE/*");
295+
296+
vector<boost::shared_ptr<YieldCurveSegment>> segments{
297+
boost::make_shared<DirectYieldCurveSegment>("Discount", "", quotes)};
298+
299+
boost::shared_ptr<YieldCurveConfig> yCConfig = boost::make_shared<YieldCurveConfig>(
300+
"EUR-CURVE", "ORE YieldCurve built from EUR-CURVE", "EUR", "", segments);
301+
curveConfigs.add(CurveSpec::CurveType::Yield, "EUR-CURVE", yCConfig);
302+
303+
vector<string> data{"2023-10-12 DISCOUNT/RATE/SEK/STINA-CURVE/2023-10-13 0.77",
304+
"2023-10-12 DISCOUNT/RATE/EUR/EUR-ANOTHER-CURVE/2023-10-13 0.95",
305+
"2023-10-13 DISCOUNT/RATE/EUR/EUR-ANOTHER-CURVE/2023-10-14 0.95",
306+
"2023-10-13 DISCOUNT/RATE/EUR/EUR-CURVE/2023-10-13 1.0",
307+
"2023-10-13 DISCOUNT/RATE/EUR/EUR-CURVE/2023-10-14 0.99",
308+
"2023-10-13 DISCOUNT/RATE/EUR/EUR-CURVE/2023-10-15 0.98",
309+
"2023-10-13 EQUITY_FWD/PRICE/SP5/USD/1Y 1500.00",
310+
"2023-10-13 EQUITY_FWD/PRICE/SP5/USD/20231014 1500.00",
311+
"2023-10-13 EQUITY_DIVIDEND/RATE/SP5/USD/20231015 0.00",
312+
"2023-10-13 EQUITY_DIVIDEND/RATE/SP5/USD/2Y 0.00"};
313+
MarketDataLoader loader(data);
314+
315+
BOOST_CHECK_NO_THROW(YieldCurve yieldCurve(asof, spec, curveConfigs, loader));
316+
}
317+
249318
// Test ARS-IN-USD failures using the old QuantLib::IterativeBootstrap parameters
250319
BOOST_DATA_TEST_CASE(testBootstrapARSinUSDFailures, bdata::make(curveConfigFiles), curveConfigFile) {
251320

0 commit comments

Comments
 (0)