20#include <boost/test/unit_test.hpp>
21#include <boost/test/data/test_case.hpp>
23#include <oret/datapaths.hpp>
24#include <oret/toplevelfixture.hpp>
26#include <boost/make_shared.hpp>
38#include <ql/termstructures/volatility/equityfx/blackvariancesurface.hpp>
46using namespace boost::unit_test_framework;
56Real testTolerance = 1e-10;
58class MockLoader :
public Loader {
61 vector<QuantLib::ext::shared_ptr<MarketDatum>> loadQuotes(
const Date&)
const override {
return data_; }
62 set<Fixing> loadFixings()
const override {
return dummyFixings_; }
63 set<QuantExt::Dividend> loadDividends()
const override {
return dummyDividends_; }
64 void add(QuantLib::Date date,
const string&
name, QuantLib::Real
value) {}
65 void addFixing(QuantLib::Date date,
const string&
name, QuantLib::Real
value) {}
69 vector<QuantLib::ext::shared_ptr<MarketDatum>> data_;
70 QuantLib::ext::shared_ptr<MarketDatum> dummyDatum_;
71 set<Fixing> dummyFixings_;
72 set<QuantExt::Dividend> dummyDividends_;
75MockLoader::MockLoader() {
76 Date asof(5, Feb, 2016);
78 QuantLib::ext::make_shared<CommodityOptionQuote>(0.11, asof,
"COMMODITY_OPTION/RATE_LNVOL/GOLD/USD/1Y/ATM/AtmFwd",
79 MarketDatum::QuoteType::RATE_LNVOL,
"GOLD",
"USD",
80 QuantLib::ext::make_shared<ExpiryPeriod>(1 * Years),
81 QuantLib::ext::make_shared<AtmStrike>(DeltaVolQuote::AtmFwd)),
82 QuantLib::ext::make_shared<CommodityOptionQuote>(0.10, asof,
"COMMODITY_OPTION/RATE_LNVOL/GOLD/USD/2Y/ATM/AtmFwd",
83 MarketDatum::QuoteType::RATE_LNVOL,
"GOLD",
"USD",
84 QuantLib::ext::make_shared<ExpiryPeriod>(2 * Years),
85 QuantLib::ext::make_shared<AtmStrike>(DeltaVolQuote::AtmFwd)),
86 QuantLib::ext::make_shared<CommodityOptionQuote>(0.09, asof,
"COMMODITY_OPTION/RATE_LNVOL/GOLD/USD/5Y/ATM/AtmFwd",
87 MarketDatum::QuoteType::RATE_LNVOL,
"GOLD",
"USD",
88 QuantLib::ext::make_shared<ExpiryPeriod>(5 * Years),
89 QuantLib::ext::make_shared<AtmStrike>(DeltaVolQuote::AtmFwd)),
90 QuantLib::ext::make_shared<CommodityOptionQuote>(
91 0.105, asof,
"COMMODITY_OPTION/RATE_LNVOL/GOLD_USD_VOLS/USD/1Y/1150", MarketDatum::QuoteType::RATE_LNVOL,
92 "GOLD",
"USD", QuantLib::ext::make_shared<ExpiryPeriod>(1 * Years), QuantLib::ext::make_shared<AbsoluteStrike>(1150)),
93 QuantLib::ext::make_shared<CommodityOptionQuote>(
94 0.115, asof,
"COMMODITY_OPTION/RATE_LNVOL/GOLD_USD_VOLS/USD/1Y/1190", MarketDatum::QuoteType::RATE_LNVOL,
95 "GOLD",
"USD", QuantLib::ext::make_shared<ExpiryPeriod>(1 * Years), QuantLib::ext::make_shared<AbsoluteStrike>(1190)),
96 QuantLib::ext::make_shared<CommodityOptionQuote>(
97 0.095, asof,
"COMMODITY_OPTION/RATE_LNVOL/GOLD_USD_VOLS/USD/2Y/1150", MarketDatum::QuoteType::RATE_LNVOL,
98 "GOLD",
"USD", QuantLib::ext::make_shared<ExpiryPeriod>(2 * Years), QuantLib::ext::make_shared<AbsoluteStrike>(1150)),
99 QuantLib::ext::make_shared<CommodityOptionQuote>(
100 0.105, asof,
"COMMODITY_OPTION/RATE_LNVOL/GOLD_USD_VOLS/USD/2Y/1190", MarketDatum::QuoteType::RATE_LNVOL,
101 "GOLD",
"USD", QuantLib::ext::make_shared<ExpiryPeriod>(2 * Years), QuantLib::ext::make_shared<AbsoluteStrike>(1190)),
102 QuantLib::ext::make_shared<CommodityOptionQuote>(
103 0.085, asof,
"COMMODITY_OPTION/RATE_LNVOL/GOLD_USD_VOLS/USD/5Y/1150", MarketDatum::QuoteType::RATE_LNVOL,
104 "GOLD",
"USD", QuantLib::ext::make_shared<ExpiryPeriod>(5 * Years), QuantLib::ext::make_shared<AbsoluteStrike>(1150)),
105 QuantLib::ext::make_shared<CommodityOptionQuote>(
106 0.095, asof,
"COMMODITY_OPTION/RATE_LNVOL/GOLD_USD_VOLS/USD/5Y/1190", MarketDatum::QuoteType::RATE_LNVOL,
107 "GOLD",
"USD", QuantLib::ext::make_shared<ExpiryPeriod>(5 * Years), QuantLib::ext::make_shared<AbsoluteStrike>(1190))};
110QuantLib::ext::shared_ptr<TodaysMarket> createTodaysMarket(
const Date& asof,
const string& inputDir,
111 const string& curveConfigFile,
112 const string& marketFile =
"market.txt",
113 const string& fixingsFile =
"fixings.txt") {
115 auto conventions = QuantLib::ext::make_shared<Conventions>();
116 conventions->fromFile(TEST_INPUT_FILE(
string(inputDir +
"/conventions.xml")));
117 InstrumentConventions::instance().setConventions(conventions);
119 auto curveConfigs = QuantLib::ext::make_shared<CurveConfigurations>();
120 curveConfigs->fromFile(TEST_INPUT_FILE(
string(inputDir +
"/" + curveConfigFile)));
122 auto todaysMarketParameters = QuantLib::ext::make_shared<TodaysMarketParameters>();
123 todaysMarketParameters->fromFile(TEST_INPUT_FILE(
string(inputDir +
"/todaysmarket.xml")));
125 auto loader = QuantLib::ext::make_shared<CSVLoader>(TEST_INPUT_FILE(
string(inputDir +
"/" + marketFile)),
126 TEST_INPUT_FILE(
string(inputDir +
"/" + fixingsFile)),
false);
128 return QuantLib::ext::make_shared<TodaysMarket>(asof, todaysMarketParameters, loader,
curveConfigs);
133struct NymexVolatilityData {
135 vector<Date> expiries;
136 map<Date, vector<Real>>
strikes;
137 map<Date, vector<Real>> volatilities;
138 map<Date, Real> atmVolatilities;
140 NymexVolatilityData() {
141 expiries = { Date(17, Oct, 2019), Date(16, Dec, 2019), Date(17, Mar, 2020) };
143 { Date(17, Oct, 2019), { 60, 61, 62 } },
144 { Date(16, Dec, 2019), { 59, 60, 61 } },
145 { Date(17, Mar, 2020), { 57, 58, 59 } }
148 { Date(17, Oct, 2019), { 0.4516, 0.4558, 0.4598 } },
149 { Date(16, Dec, 2019), { 0.4050, 0.4043, 0.4041 } },
150 { Date(17, Mar, 2020), { 0.3599, 0.3573, 0.3545 } }
153 { Date(17, Oct, 2019), 0.4678 },
154 { Date(16, Dec, 2019), 0.4353 },
155 { Date(17, Mar, 2020), 0.3293 }
164BOOST_FIXTURE_TEST_SUITE(OREDataTestSuite, ore::test::TopLevelFixture)
166BOOST_AUTO_TEST_SUITE(CommodityVolCurveTests)
170 BOOST_TEST_MESSAGE(
"Testing commodity vol curve building with a single configured volatility");
173 Date asof(5, Feb, 2016);
176 vector<QuantLib::ext::shared_ptr<VolatilityConfig>> cvc;
177 cvc.push_back(QuantLib::ext::make_shared<ConstantVolatilityConfig>(
"COMMODITY_OPTION/RATE_LNVOL/GOLD/USD/2Y/ATM/AtmFwd"));
180 QuantLib::ext::shared_ptr<CommodityVolatilityConfig> curveConfig =
181 QuantLib::ext::make_shared<CommodityVolatilityConfig>(
"GOLD_USD_VOLS",
"",
"USD", cvc,
"A365",
"NullCalendar");
185 curveConfigs.add(CurveSpec::CurveType::CommodityVolatility,
"GOLD_USD_VOLS", curveConfig);
197 QuantLib::ext::shared_ptr<CommodityVolCurve> curve;
198 BOOST_CHECK_NO_THROW(curve = QuantLib::ext::make_shared<CommodityVolCurve>(asof, curveSpec, loader,
curveConfigs));
201 Real configuredVolatility = 0.10;
202 QuantLib::ext::shared_ptr<BlackVolTermStructure> volatility = curve->volatility();
203 BOOST_CHECK_CLOSE(volatility->blackVol(0.25, 1000.0), configuredVolatility, testTolerance);
204 BOOST_CHECK_CLOSE(volatility->blackVol(0.25, 1200.0), configuredVolatility, testTolerance);
205 BOOST_CHECK_CLOSE(volatility->blackVol(asof + 3 * Months, 1000.0), configuredVolatility, testTolerance);
206 BOOST_CHECK_CLOSE(volatility->blackVol(asof + 3 * Months, 1200.0), configuredVolatility, testTolerance);
207 BOOST_CHECK_CLOSE(volatility->blackVol(50.0, 1000.0), configuredVolatility, testTolerance);
208 BOOST_CHECK_CLOSE(volatility->blackVol(50.0, 1200.0), configuredVolatility, testTolerance);
209 BOOST_CHECK_CLOSE(volatility->blackVol(asof + 50 * Years, 1000.0), configuredVolatility, testTolerance);
210 BOOST_CHECK_CLOSE(volatility->blackVol(asof + 50 * Years, 1200.0), configuredVolatility, testTolerance);
215 BOOST_TEST_MESSAGE(
"Testing commodity vol curve building with time dependent volatilities");
218 Date asof(5, Feb, 2016);
221 vector<string> quotes{
"COMMODITY_OPTION/RATE_LNVOL/GOLD/USD/1Y/ATM/AtmFwd",
222 "COMMODITY_OPTION/RATE_LNVOL/GOLD/USD/2Y/ATM/AtmFwd",
223 "COMMODITY_OPTION/RATE_LNVOL/GOLD/USD/5Y/ATM/AtmFwd"};
226 vector<QuantLib::ext::shared_ptr<VolatilityConfig>> vcc;
227 vcc.push_back(QuantLib::ext::make_shared<VolatilityCurveConfig>(quotes,
"Linear",
"Flat"));
230 QuantLib::ext::shared_ptr<CommodityVolatilityConfig> curveConfig =
231 QuantLib::ext::make_shared<CommodityVolatilityConfig>(
"GOLD_USD_VOLS",
"",
"USD", vcc,
"A365",
"NullCalendar");
235 curveConfigs.add(CurveSpec::CurveType::CommodityVolatility,
"GOLD_USD_VOLS", curveConfig);
247 QuantLib::ext::shared_ptr<CommodityVolCurve> curve;
248 BOOST_CHECK_NO_THROW(curve = QuantLib::ext::make_shared<CommodityVolCurve>(asof, curveSpec, loader,
curveConfigs));
251 QuantLib::ext::shared_ptr<BlackVolTermStructure> volatility = curve->volatility();
252 Real configuredVolatility;
256 configuredVolatility = 0.11;
257 BOOST_CHECK_CLOSE(volatility->blackVol(asof + 1 * Years, 1000.0), configuredVolatility, testTolerance);
258 BOOST_CHECK_CLOSE(volatility->blackVol(asof + 1 * Years, 1200.0), configuredVolatility, testTolerance);
260 configuredVolatility = 0.10;
261 BOOST_CHECK_CLOSE(volatility->blackVol(asof + 2 * Years, 1000.0), configuredVolatility, testTolerance);
262 BOOST_CHECK_CLOSE(volatility->blackVol(asof + 2 * Years, 1200.0), configuredVolatility, testTolerance);
264 configuredVolatility = 0.09;
265 BOOST_CHECK_CLOSE(volatility->blackVol(asof + 5 * Years, 1000.0), configuredVolatility, testTolerance);
266 BOOST_CHECK_CLOSE(volatility->blackVol(asof + 5 * Years, 1200.0), configuredVolatility, testTolerance);
269 Time t_s = volatility->dayCounter().yearFraction(asof, asof + 2 * Years);
271 Time t_e = volatility->dayCounter().yearFraction(asof, asof + 5 * Years);
274 Time t = volatility->dayCounter().yearFraction(asof, asof + 3 * Years);
275 Real v =
sqrt((v_s * v_s * t_s + (v_e * v_e * t_e - v_s * v_s * t_s) * (t - t_s) / (t_e - t_s)) / t);
276 BOOST_CHECK_CLOSE(volatility->blackVol(asof + 3 * Years, 1000.0), v, testTolerance);
277 BOOST_CHECK_CLOSE(volatility->blackVol(asof + 3 * Years, 1200.0), v, testTolerance);
279 BOOST_CHECK_CLOSE(volatility->blackVol(asof + 6 * Years, 1000.0), v_e, testTolerance);
280 BOOST_CHECK_CLOSE(volatility->blackVol(asof + 6 * Years, 1200.0), v_e, testTolerance);
285 BOOST_TEST_MESSAGE(
"Testing commodity vol curve building with time and strike dependent volatilities");
288 Date asof(5, Feb, 2016);
292 vector<string>
strikes{
"1150",
"1190"};
293 vector<string> expiries{
"1Y",
"2Y",
"5Y"};
295 vector<QuantLib::ext::shared_ptr<VolatilityConfig>> vssc;
297 QuantLib::ext::make_shared<VolatilityStrikeSurfaceConfig>(
strikes, expiries,
"Linear",
"Linear",
true,
"Flat",
"Flat"));
300 QuantLib::ext::shared_ptr<CommodityVolatilityConfig> curveConfig =
301 QuantLib::ext::make_shared<CommodityVolatilityConfig>(
"GOLD_USD_VOLS",
"",
"USD", vssc,
"A365",
"NullCalendar");
305 curveConfigs.add(CurveSpec::CurveType::CommodityVolatility,
"GOLD_USD_VOLS", curveConfig);
317 QuantLib::ext::shared_ptr<CommodityVolCurve> curve;
318 BOOST_CHECK_NO_THROW(curve = QuantLib::ext::make_shared<CommodityVolCurve>(asof, curveSpec, loader,
curveConfigs));
321 QuantLib::ext::shared_ptr<BlackVolTermStructure> volatility = curve->volatility();
324 BOOST_CHECK_CLOSE(volatility->blackVol(asof + 1 * Years, 1150.0), 0.105, testTolerance);
325 BOOST_CHECK_CLOSE(volatility->blackVol(asof + 1 * Years, 1190.0), 0.115, testTolerance);
326 BOOST_CHECK_CLOSE(volatility->blackVol(asof + 2 * Years, 1150.0), 0.095, testTolerance);
327 BOOST_CHECK_CLOSE(volatility->blackVol(asof + 2 * Years, 1190.0), 0.105, testTolerance);
328 BOOST_CHECK_CLOSE(volatility->blackVol(asof + 5 * Years, 1150.0), 0.085, testTolerance);
329 BOOST_CHECK_CLOSE(volatility->blackVol(asof + 5 * Years, 1190.0), 0.095, testTolerance);
338 BOOST_TEST_MESSAGE(
"Testing commodity volatility curve building wildcard expiries and strikes in configuration");
341 createTodaysMarket(Date(16, Sep, 2019),
"wildcard_data",
"curveconfig_surface_wc_expiries_wc_strikes.xml");
343 auto vts = todaysMarket->commodityVolatility(
"NYMEX:CL");
347 auto tmSurface = QuantLib::ext::dynamic_pointer_cast<BlackVolatilityWithATM>(*vts);
348 BOOST_REQUIRE_MESSAGE(tmSurface,
"Expected the commodity vol structure in TodaysMarket"
349 <<
" to be of type BlackVolatilityWithATM");
350 auto surface = QuantLib::ext::dynamic_pointer_cast<BlackVarianceSurfaceSparse>(tmSurface->surface());
351 BOOST_REQUIRE_MESSAGE(tmSurface,
"Expected the commodity vol structure in TodaysMarket to contain"
352 <<
" a surface of type BlackVarianceSurfaceSparse");
355 NymexVolatilityData expData;
359 BOOST_REQUIRE_EQUAL(surface->expiries().size() - 1, expData.expiries.size());
360 for (Size i = 0; i < expData.strikes.size(); i++) {
361 BOOST_CHECK_EQUAL(surface->expiries()[i + 1], expData.expiries[i]);
364 BOOST_REQUIRE_EQUAL(surface->strikes().size() - 1, expData.strikes.size());
365 for (Size i = 0; i < expData.strikes.size(); i++) {
368 Date e = expData.expiries[i];
369 BOOST_REQUIRE_EQUAL(surface->strikes()[i + 1].size(), expData.strikes[e].size());
370 for (Size j = 0; j < surface->strikes()[i + 1].
size(); j++) {
371 BOOST_CHECK_CLOSE(expData.strikes[e][j], surface->strikes()[i + 1][j], 1e-12);
375 BOOST_REQUIRE_EQUAL(surface->values()[i + 1].size(), expData.volatilities[e].size());
376 for (Size j = 0; j < surface->values()[i + 1].
size(); j++) {
377 BOOST_CHECK_CLOSE(expData.volatilities[e][j], surface->blackVol(e, surface->strikes()[i + 1][j]), 1e-12);
385 "Testing commodity volatility curve building wildcard expiries and explicit strikes in configuration");
387 auto todaysMarket = createTodaysMarket(Date(16, Sep, 2019),
"wildcard_data",
388 "curveconfig_surface_wc_expiries_explicit_strikes.xml");
390 auto vts = todaysMarket->commodityVolatility(
"NYMEX:CL");
394 auto tmSurface = QuantLib::ext::dynamic_pointer_cast<BlackVolatilityWithATM>(*vts);
395 BOOST_REQUIRE_MESSAGE(tmSurface,
"Expected the commodity vol structure in TodaysMarket"
396 <<
" to be of type BlackVolatilityWithATM");
397 auto surface = QuantLib::ext::dynamic_pointer_cast<BlackVarianceSurfaceSparse>(tmSurface->surface());
398 BOOST_REQUIRE_MESSAGE(tmSurface,
"Expected the commodity vol structure in TodaysMarket to contain"
399 <<
" a surface of type BlackVarianceSurfaceSparse");
402 NymexVolatilityData expData;
407 vector<Date> expExpiries{Date(17, Oct, 2019), Date(16, Dec, 2019)};
408 BOOST_REQUIRE_EQUAL(surface->expiries().size() - 1, expExpiries.size());
409 for (Size i = 0; i < expExpiries.size(); i++) {
410 BOOST_CHECK_EQUAL(surface->expiries()[i + 1], expExpiries[i]);
414 vector<Real> expStrikes{60, 61};
415 BOOST_REQUIRE_EQUAL(surface->strikes().size() - 1, expExpiries.size());
416 for (Size i = 0; i < expExpiries.size(); i++) {
419 BOOST_REQUIRE_EQUAL(surface->strikes()[i + 1].size(), expStrikes.size());
420 for (Size j = 0; j < surface->strikes()[i + 1].
size(); j++) {
421 BOOST_CHECK_CLOSE(expStrikes[j], surface->strikes()[i + 1][j], 1e-12);
425 Date e = expExpiries[i];
426 BOOST_REQUIRE_EQUAL(surface->values()[i + 1].size(), expStrikes.size());
427 for (Size j = 0; j < surface->values()[i + 1].
size(); j++) {
429 Real expStrike = expStrikes[j];
430 auto it = find_if(expData.strikes[e].begin(), expData.strikes[e].end(),
431 [expStrike](Real s) { return close(expStrike, s); });
432 BOOST_REQUIRE_MESSAGE(it != expData.strikes[e].end(),
"Strike not found in input strikes");
433 auto idx = distance(expData.strikes[e].begin(), it);
435 BOOST_CHECK_CLOSE(expData.volatilities[e][idx], surface->blackVol(e, surface->strikes()[i + 1][j]), 1e-12);
443 "Testing commodity volatility curve building explicit expiries and wildcard strikes in configuration");
445 auto todaysMarket = createTodaysMarket(Date(16, Sep, 2019),
"wildcard_data",
446 "curveconfig_surface_explicit_expiries_wc_strikes.xml");
448 auto vts = todaysMarket->commodityVolatility(
"NYMEX:CL");
452 auto tmSurface = QuantLib::ext::dynamic_pointer_cast<BlackVolatilityWithATM>(*vts);
453 BOOST_REQUIRE_MESSAGE(tmSurface,
"Expected the commodity vol structure in TodaysMarket"
454 <<
" to be of type BlackVolatilityWithATM");
455 auto surface = QuantLib::ext::dynamic_pointer_cast<BlackVarianceSurfaceSparse>(tmSurface->surface());
456 BOOST_REQUIRE_MESSAGE(tmSurface,
"Expected the commodity vol structure in TodaysMarket to contain"
457 <<
" a surface of type BlackVarianceSurfaceSparse");
460 NymexVolatilityData expData;
465 vector<Date> expExpiries{Date(17, Oct, 2019), Date(16, Dec, 2019)};
466 BOOST_REQUIRE_EQUAL(surface->expiries().size() - 1, expExpiries.size());
467 for (Size i = 0; i < expExpiries.size(); i++) {
468 BOOST_CHECK_EQUAL(surface->expiries()[i + 1], expExpiries[i]);
471 BOOST_REQUIRE_EQUAL(surface->strikes().size() - 1, expExpiries.size());
472 for (Size i = 0; i < expExpiries.size(); i++) {
475 Date e = expExpiries[i];
476 BOOST_REQUIRE_EQUAL(surface->strikes()[i + 1].size(), expData.strikes[e].size());
477 for (Size j = 0; j < surface->strikes()[i + 1].
size(); j++) {
478 BOOST_CHECK_CLOSE(expData.strikes[e][j], surface->strikes()[i + 1][j], 1e-12);
482 BOOST_REQUIRE_EQUAL(surface->values()[i + 1].size(), expData.volatilities[e].size());
483 for (Size j = 0; j < surface->values()[i + 1].
size(); j++) {
484 BOOST_CHECK_CLOSE(expData.volatilities[e][j], surface->blackVol(e, surface->strikes()[i + 1][j]), 1e-12);
492 "Testing commodity volatility curve building explicit expiries and explicit strikes in configuration");
494 auto todaysMarket = createTodaysMarket(Date(16, Sep, 2019),
"wildcard_data",
495 "curveconfig_surface_explicit_expiries_explicit_strikes.xml");
497 auto vts = todaysMarket->commodityVolatility(
"NYMEX:CL");
500 NymexVolatilityData expData;
504 vector<Date> expExpiries{Date(17, Oct, 2019), Date(16, Dec, 2019)};
505 vector<Real> expStrikes{60, 61};
506 for (
const Date& e : expExpiries) {
507 for (Real s : expStrikes) {
509 auto it = find_if(expData.strikes[e].begin(), expData.strikes[e].end(),
510 [s](Real strike) { return close(strike, s); });
511 BOOST_REQUIRE_MESSAGE(it != expData.strikes[e].end(),
"Strike not found in input strikes");
512 auto idx = distance(expData.strikes[e].begin(), it);
514 Real inputVol = expData.volatilities[e][idx];
515 BOOST_CHECK_CLOSE(inputVol, vts->blackVol(e, s), 1e-12);
522vector<Date>
asofDates{Date(13, Jan, 2020), Date(15, Jan, 2020)};
525vector<string>
curveConfigs{
"curveconfig_explicit_expiries.xml",
"curveconfig_wildcard_expiries.xml"};
530 BOOST_TEST_MESSAGE(
"Testing commodity volatility delta surface building");
532 auto todaysMarket = createTodaysMarket(asof,
"delta_surface", curveConfig,
"market.txt");
535 auto vts = todaysMarket->commodityVolatility(
"NYMEX:CL");
539 auto bvwa = dynamic_pointer_cast<BlackVolatilityWithATM>(*vts);
541 auto bvsd = QuantLib::ext::dynamic_pointer_cast<BlackVolatilitySurfaceDelta>(bvwa->surface());
548 string filename =
"delta_surface/expected_grid_" +
to_string(io::iso_date(asof)) +
".csv";
552 while (reader.
next()) {
560 auto itExpiry = find(bvsd->dates().begin(), bvsd->dates().end(), expiryDate);
561 BOOST_REQUIRE(itExpiry != bvsd->dates().end());
564 auto fxss = bvsd->blackVolSmile(expiryDate);
565 auto iss = QuantLib::ext::dynamic_pointer_cast<InterpolatedSmileSection>(fxss);
570 find_if(iss->strikes().begin(), iss->strikes().end(), [&](Real s) { return std::abs(s - strike) < tol; });
571 BOOST_REQUIRE(itStrike != iss->strikes().end());
574 auto pos = distance(iss->strikes().begin(), itStrike);
575 BOOST_CHECK_SMALL(volatility - iss->volatilities()[pos], tol);
579 auto fxss = bvsd->blackVolSmile(bvsd->dates().back());
580 auto iss = QuantLib::ext::dynamic_pointer_cast<InterpolatedSmileSection>(fxss);
582 vector<Real> lastVolatilities = iss->volatilities();
584 fxss = bvsd->blackVolSmile(bvsd->dates().back() + 1 * Years);
585 iss = QuantLib::ext::dynamic_pointer_cast<InterpolatedSmileSection>(fxss);
587 vector<Real> extrapVolatilities = iss->volatilities();
589 BOOST_REQUIRE_EQUAL(lastVolatilities.size(), extrapVolatilities.size());
590 for (Size i = 0; i < lastVolatilities.size(); i++) {
591 BOOST_CHECK_SMALL(lastVolatilities[i] - extrapVolatilities[i], tol);
595 Date testDate = asof + 1 * Years;
597 fxss = bvsd->blackVolSmile(testDate);
598 iss = QuantLib::ext::dynamic_pointer_cast<InterpolatedSmileSection>(fxss);
601 Volatility volAtMinStrike = iss->volatilities().front();
602 Real minStrike = iss->strikes().front();
603 Real extrapStrike = minStrike / 2.0;
604 BOOST_CHECK_SMALL(volAtMinStrike - bvsd->blackVol(testDate, extrapStrike), tol);
606 Volatility volAtMaxStrike = iss->volatilities().back();
607 Real maxStrike = iss->strikes().back();
608 extrapStrike = maxStrike * 2.0;
609 BOOST_CHECK_SMALL(volAtMaxStrike - bvsd->blackVol(testDate, extrapStrike), tol);
615 BOOST_TEST_MESSAGE(
"Testing commodity volatility forward moneyness surface building");
617 Settings::instance().evaluationDate() = asof;
618 auto todaysMarket = createTodaysMarket(asof,
"moneyness_surface", curveConfig,
"market.txt");
621 auto vts = todaysMarket->commodityVolatility(
"NYMEX:CL");
627 string filename =
"moneyness_surface/expected_grid_" +
to_string(io::iso_date(asof)) +
".csv";
631 while (reader.
next()) {
639 BOOST_CHECK_SMALL(volatility - vts->blackVol(expiryDate, strike), tol);
643 auto pts = todaysMarket->commodityPriceCurve(
"NYMEX:CL");
648 Date extrapDate_1(20, Mar, 2024);
649 Date extrapDate_2(22, Apr, 2024);
650 Real strike_1 = pts->price(extrapDate_1);
651 Real strike_2 = pts->price(extrapDate_2);
652 Volatility vol_1 = vts->blackVol(extrapDate_1, strike_1);
653 Volatility vol_2 = vts->blackVol(extrapDate_2, strike_2);
654 BOOST_TEST_MESSAGE(
"The two time extrapolated volatilities are: " << fixed << setprecision(12) << vol_1 <<
","
656 BOOST_CHECK_SMALL(vol_1 - vol_2, tol);
659 Date optionExpiry(14, Jan, 2021);
660 Date futureExpiry(20, Jan, 2021);
661 Real futurePrice = pts->price(futureExpiry);
663 Real lowerStrike = 0.5 * futurePrice;
664 Volatility volLowerStrike = vts->blackVol(optionExpiry, lowerStrike);
665 Volatility volLowerExtrapStrike = vts->blackVol(optionExpiry, lowerStrike / 2.0);
666 BOOST_TEST_MESSAGE(
"The two lower strike extrapolated volatilities are: "
667 << fixed << setprecision(12) << volLowerStrike <<
"," << volLowerExtrapStrike <<
".");
668 BOOST_CHECK_SMALL(volLowerStrike - volLowerExtrapStrike, tol);
670 Real upperStrike = 1.5 * futurePrice;
671 Volatility volUpperStrike = vts->blackVol(optionExpiry, upperStrike);
672 Volatility volUpperExtrapStrike = vts->blackVol(optionExpiry, upperStrike * 2.0);
673 BOOST_TEST_MESSAGE(
"The two upper strike extrapolated volatilities are: "
674 << fixed << setprecision(12) << volUpperStrike <<
"," << volUpperExtrapStrike <<
".");
675 BOOST_CHECK_SMALL(volUpperStrike - volUpperExtrapStrike, tol);
680 BOOST_TEST_MESSAGE(
"Testing commodity volatility forward moneyness surface building");
682 Settings::instance().evaluationDate() = asof;
684 string fixingsFile =
"fixings_" +
to_string(io::iso_date(asof)) +
".txt";
685 auto todaysMarket = createTodaysMarket(asof,
"apo_surface",
"curveconfig.xml",
"market.txt", fixingsFile);
688 auto vts = todaysMarket->commodityVolatility(
"NYMEX:FF");
694 string filename =
"apo_surface/expected_grid_" +
to_string(io::iso_date(asof)) +
".csv";
698 BOOST_TEST_MESSAGE(
"exp_vol,calc_vol,difference");
699 while (reader.
next()) {
707 auto calcVolatility = vts->blackVol(expiryDate, strike);
708 auto difference = volatility - calcVolatility;
709 BOOST_TEST_MESSAGE(std::fixed << std::setprecision(12) << strike <<
"," << volatility <<
"," << calcVolatility
710 <<
"," << difference);
711 BOOST_CHECK_SMALL(difference, tol);
718 BOOST_TEST_MESSAGE(
"Testing commodity volatility delta surface building for MYR Crude Palm Oil");
720 Date asof(14, Oct, 2020);
721 auto todaysMarket = createTodaysMarket(asof,
"myr_crude_palm_oil",
"curveconfig.xml",
"market.txt");
724 auto vts = todaysMarket->commodityVolatility(
"XKLS:FCPO");
727 BOOST_CHECK_NO_THROW(vts->blackVol(1.0, 2800));
731 auto bvwa = dynamic_pointer_cast<BlackVolatilityWithATM>(*vts);
733 auto bvsd = QuantLib::ext::dynamic_pointer_cast<BlackVolatilitySurfaceDelta>(bvwa->surface());
739 const vector<Date>& surfaceDates = bvsd->dates();
742 vector<Date> expectedDates;
743 string filename =
"myr_crude_palm_oil/expected_expiries.csv";
746 while (reader.
next()) {
751 BOOST_CHECK_EQUAL_COLLECTIONS(surfaceDates.begin(), surfaceDates.end(), expectedDates.begin(), expectedDates.end());
754BOOST_AUTO_TEST_SUITE_END()
756BOOST_AUTO_TEST_SUITE_END()
Size numberOfColumns() const
std::string get(const std::string &field) const
Commodity volatility description.
Repository for currency dependent market conventions.
Container class for all Curve Configurations.
Market data loader base class.
Wrapper class for building commodity volatility structures.
SafeStack< ValueType > value
utility class to access CSV files
Market Datum Loader Implementation.
Curve configuration repository.
Curve requirements specification.
Date parseDate(const string &s)
Convert std::string to QuantLib::Date.
Real parseReal(const string &s)
Convert text to Real.
Market Datum Loader Interface.
RandomVariable sqrt(RandomVariable x)
Size size(const ValueType &v)
std::string to_string(const LocationInfo &l)
Map text representations to QuantLib/QuantExt types.
BOOST_AUTO_TEST_CASE(testCommodityVolCurveTypeConstant)
BOOST_DATA_TEST_CASE(testCommodityVolDeltaSurface, bdata::make(asofDates) *bdata::make(curveConfigs), asof, curveConfig)
vector< string > curveConfigs
string conversion utilities
An concrete implementation of the Market class that loads todays market and builds the required curve...