Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
Functions | Variables
commodityvolcurve.cpp File Reference
#include <boost/test/unit_test.hpp>
#include <boost/test/data/test_case.hpp>
#include <oret/datapaths.hpp>
#include <oret/toplevelfixture.hpp>
#include <boost/make_shared.hpp>
#include <cmath>
#include <ored/configuration/curveconfigurations.hpp>
#include <ored/marketdata/commodityvolcurve.hpp>
#include <ored/marketdata/csvloader.hpp>
#include <ored/marketdata/curvespec.hpp>
#include <ored/marketdata/loader.hpp>
#include <ored/marketdata/todaysmarket.hpp>
#include <ored/utilities/csvfilereader.hpp>
#include <ored/utilities/parsers.hpp>
#include <ored/utilities/to_string.hpp>
#include <ql/termstructures/volatility/equityfx/blackvariancesurface.hpp>
#include <qle/indexes/dividendmanager.hpp>
#include <qle/termstructures/aposurface.hpp>
#include <qle/termstructures/blackvariancesurfacesparse.hpp>
#include <qle/termstructures/blackvolsurfacedelta.hpp>
#include <qle/termstructures/blackvolsurfacewithatm.hpp>

Go to the source code of this file.

Functions

 BOOST_AUTO_TEST_CASE (testCommodityVolCurveTypeConstant)
 
 BOOST_AUTO_TEST_CASE (testCommodityVolCurveTypeCurve)
 
 BOOST_AUTO_TEST_CASE (testCommodityVolCurveTypeSurface)
 
 BOOST_AUTO_TEST_CASE (testCommodityVolSurfaceWildcardExpiriesWildcardStrikes)
 
 BOOST_AUTO_TEST_CASE (testCommodityVolSurfaceWildcardExpiriesExplicitStrikes)
 
 BOOST_AUTO_TEST_CASE (testCommodityVolSurfaceExplicitExpiriesWildcardStrikes)
 
 BOOST_AUTO_TEST_CASE (testCommodityVolSurfaceExplicitExpiriesExplicitStrikes)
 
 BOOST_DATA_TEST_CASE (testCommodityVolDeltaSurface, bdata::make(asofDates) *bdata::make(curveConfigs), asof, curveConfig)
 
 BOOST_DATA_TEST_CASE (testCommodityVolMoneynessSurface, bdata::make(asofDates) *bdata::make(curveConfigs), asof, curveConfig)
 
 BOOST_DATA_TEST_CASE (testCommodityApoSurface, bdata::make(asofDates), asof)
 
 BOOST_AUTO_TEST_CASE (testCommodityVolSurfaceMyrCrudePalmOil)
 

Variables

vector< Date > asofDates {Date(13, Jan, 2020), Date(15, Jan, 2020)}
 
vector< string > curveConfigs {"curveconfig_explicit_expiries.xml", "curveconfig_wildcard_expiries.xml"}
 

Function Documentation

◆ BOOST_AUTO_TEST_CASE() [1/8]

BOOST_AUTO_TEST_CASE ( testCommodityVolCurveTypeConstant  )

Definition at line 168 of file commodityvolcurve.cpp.

168 {
169
170 BOOST_TEST_MESSAGE("Testing commodity vol curve building with a single configured volatility");
171
172 // As of date
173 Date asof(5, Feb, 2016);
174
175 // Constant volatility config
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"));
178
179 // Volatility configuration with a single quote
180 QuantLib::ext::shared_ptr<CommodityVolatilityConfig> curveConfig =
181 QuantLib::ext::make_shared<CommodityVolatilityConfig>("GOLD_USD_VOLS", "", "USD", cvc, "A365", "NullCalendar");
182
183 // Curve configurations
185 curveConfigs.add(CurveSpec::CurveType::CommodityVolatility, "GOLD_USD_VOLS", curveConfig);
186
187 // Commodity curve spec
188 CommodityVolatilityCurveSpec curveSpec("USD", "GOLD_USD_VOLS");
189
190 // Market data loader
191 MockLoader loader;
192
193 // Empty Conventions
194 Conventions conventions;
195
196 // Check commodity volatility construction works
197 QuantLib::ext::shared_ptr<CommodityVolCurve> curve;
198 BOOST_CHECK_NO_THROW(curve = QuantLib::ext::make_shared<CommodityVolCurve>(asof, curveSpec, loader, curveConfigs));
199
200 // Check volatilities are all equal to the configured volatility regardless of strike and expiry
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);
211}
Commodity volatility description.
Definition: curvespec.hpp:440
Repository for currency dependent market conventions.
Container class for all Curve Configurations.
vector< string > curveConfigs

◆ BOOST_AUTO_TEST_CASE() [2/8]

BOOST_AUTO_TEST_CASE ( testCommodityVolCurveTypeCurve  )

Definition at line 213 of file commodityvolcurve.cpp.

213 {
214
215 BOOST_TEST_MESSAGE("Testing commodity vol curve building with time dependent volatilities");
216
217 // As of date
218 Date asof(5, Feb, 2016);
219
220 // Quotes for the volatility curve
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"};
224
225 // Volatility curve config with linear interpolation and flat extrapolation.
226 vector<QuantLib::ext::shared_ptr<VolatilityConfig>> vcc;
227 vcc.push_back(QuantLib::ext::make_shared<VolatilityCurveConfig>(quotes, "Linear", "Flat"));
228
229 // Commodity volatility configuration with time dependent volatilities
230 QuantLib::ext::shared_ptr<CommodityVolatilityConfig> curveConfig =
231 QuantLib::ext::make_shared<CommodityVolatilityConfig>("GOLD_USD_VOLS", "", "USD", vcc, "A365", "NullCalendar");
232
233 // Curve configurations
235 curveConfigs.add(CurveSpec::CurveType::CommodityVolatility,"GOLD_USD_VOLS", curveConfig);
236
237 // Commodity curve spec
238 CommodityVolatilityCurveSpec curveSpec("USD", "GOLD_USD_VOLS");
239
240 // Market data loader
241 MockLoader loader;
242
243 // Empty Conventions
244 Conventions conventions;
245
246 // Check commodity volatility construction works
247 QuantLib::ext::shared_ptr<CommodityVolCurve> curve;
248 BOOST_CHECK_NO_THROW(curve = QuantLib::ext::make_shared<CommodityVolCurve>(asof, curveSpec, loader, curveConfigs));
249
250 // Check time depending volatilities are as expected
251 QuantLib::ext::shared_ptr<BlackVolTermStructure> volatility = curve->volatility();
252 Real configuredVolatility;
253
254 // Check configured pillar points: { (1Y, 0.11), (2Y, 0.10), (5Y, 0.09) }
255 // Check also strike independence
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);
259
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);
263
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);
267
268 // Check briefly the default linear interpolation and extrapolation
269 Time t_s = volatility->dayCounter().yearFraction(asof, asof + 2 * Years);
270 Real v_s = 0.10;
271 Time t_e = volatility->dayCounter().yearFraction(asof, asof + 5 * Years);
272 Real v_e = 0.09;
273 // at 3 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);
278 // at 6 years, extrapolation is with a flat vol
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);
281}
RandomVariable sqrt(RandomVariable x)
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [3/8]

BOOST_AUTO_TEST_CASE ( testCommodityVolCurveTypeSurface  )

Definition at line 283 of file commodityvolcurve.cpp.

283 {
284
285 BOOST_TEST_MESSAGE("Testing commodity vol curve building with time and strike dependent volatilities");
286
287 // As of date
288 Date asof(5, Feb, 2016);
289
290 // Volatility configuration with expiry period vs. absolute strike matrix. Bilinear interpolation and flat
291 // extrapolation.
292 vector<string> strikes{"1150", "1190"};
293 vector<string> expiries{"1Y", "2Y", "5Y"};
294
295 vector<QuantLib::ext::shared_ptr<VolatilityConfig>> vssc;
296 vssc.push_back(
297 QuantLib::ext::make_shared<VolatilityStrikeSurfaceConfig>(strikes, expiries, "Linear", "Linear", true, "Flat", "Flat"));
298
299 // Commodity volatility configuration
300 QuantLib::ext::shared_ptr<CommodityVolatilityConfig> curveConfig =
301 QuantLib::ext::make_shared<CommodityVolatilityConfig>("GOLD_USD_VOLS", "", "USD", vssc, "A365", "NullCalendar");
302
303 // Curve configurations
305 curveConfigs.add(CurveSpec::CurveType::CommodityVolatility, "GOLD_USD_VOLS", curveConfig);
306
307 // Commodity curve spec
308 CommodityVolatilityCurveSpec curveSpec("USD", "GOLD_USD_VOLS");
309
310 // Market data loader
311 MockLoader loader;
312
313 // Empty Conventions
314 Conventions conventions;
315
316 // Check commodity volatility construction works
317 QuantLib::ext::shared_ptr<CommodityVolCurve> curve;
318 BOOST_CHECK_NO_THROW(curve = QuantLib::ext::make_shared<CommodityVolCurve>(asof, curveSpec, loader, curveConfigs));
319
320 // Check time and strike depending volatilities are as expected
321 QuantLib::ext::shared_ptr<BlackVolTermStructure> volatility = curve->volatility();
322
323 // Check configured pillar points
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);
330}
vector< Real > strikes

◆ BOOST_AUTO_TEST_CASE() [4/8]

BOOST_AUTO_TEST_CASE ( testCommodityVolSurfaceWildcardExpiriesWildcardStrikes  )

Definition at line 332 of file commodityvolcurve.cpp.

332 {
333
334 // Testing commodity volatility curve building wildcard expiries and strikes in configuration and more than one
335 // set of commodity volatility quotes in the market data. In particular, the market data in the wildcard_data
336 // folder has commodity volatility data for two surfaces NYMEX:CL and ICE:B. Check here that the commodity
337 // volatility curve building for NYMEX:CL uses only the 9 NYMEX:CL quotes - 3 tenors, each with 3 strikes.
338 BOOST_TEST_MESSAGE("Testing commodity volatility curve building wildcard expiries and strikes in configuration");
339
340 auto todaysMarket =
341 createTodaysMarket(Date(16, Sep, 2019), "wildcard_data", "curveconfig_surface_wc_expiries_wc_strikes.xml");
342
343 auto vts = todaysMarket->commodityVolatility("NYMEX:CL");
344
345 // Wildcards in configuration so we know that a BlackVarianceSurfaceSparse has been created and fed to a
346 // BlackVolatilityWithATM surface in TodaysMarket
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");
353
354 // The expected NYMEX CL volatility data
355 NymexVolatilityData expData;
356
357 // Check what is loaded against expected data as provided in market data file for NYMEX:CL.
358 // Note: the BlackVarianceSurfaceSparse adds a dummy expiry slice at time zero
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]);
362 }
363
364 BOOST_REQUIRE_EQUAL(surface->strikes().size() - 1, expData.strikes.size());
365 for (Size i = 0; i < expData.strikes.size(); i++) {
366
367 // Check the strikes against the input
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);
372 }
373
374 // Check the volatilities against the input
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);
378 }
379 }
380}
Size size(const ValueType &v)
Definition: value.cpp:145
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [5/8]

BOOST_AUTO_TEST_CASE ( testCommodityVolSurfaceWildcardExpiriesExplicitStrikes  )

Definition at line 382 of file commodityvolcurve.cpp.

382 {
383
384 BOOST_TEST_MESSAGE(
385 "Testing commodity volatility curve building wildcard expiries and explicit strikes in configuration");
386
387 auto todaysMarket = createTodaysMarket(Date(16, Sep, 2019), "wildcard_data",
388 "curveconfig_surface_wc_expiries_explicit_strikes.xml");
389
390 auto vts = todaysMarket->commodityVolatility("NYMEX:CL");
391
392 // Wildcards in configuration so we know that a BlackVarianceSurfaceSparse has been created and fed to a
393 // BlackVolatilityWithATM surface in TodaysMarket
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");
400
401 // The expected NYMEX CL volatility data
402 NymexVolatilityData expData;
403
404 // Check what is loaded against expected data as provided in market data file for NYMEX:CL. The explicit strikes
405 // that we have chosen lead have only two corresponding expiries i.e. 2019-10-17 and 2019-12-16
406 // Note: the BlackVarianceSurfaceSparse adds a dummy expiry slice at time zero
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]);
411 }
412
413 // The explicit strikes in the config are 60 and 61
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++) {
417
418 // Check the strikes against the expected explicit strikes
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);
422 }
423
424 // Check the volatilities against the input
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++) {
428 // Find the index of the explicit strike in the input data
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);
434
435 BOOST_CHECK_CLOSE(expData.volatilities[e][idx], surface->blackVol(e, surface->strikes()[i + 1][j]), 1e-12);
436 }
437 }
438}
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [6/8]

BOOST_AUTO_TEST_CASE ( testCommodityVolSurfaceExplicitExpiriesWildcardStrikes  )

Definition at line 440 of file commodityvolcurve.cpp.

440 {
441
442 BOOST_TEST_MESSAGE(
443 "Testing commodity volatility curve building explicit expiries and wildcard strikes in configuration");
444
445 auto todaysMarket = createTodaysMarket(Date(16, Sep, 2019), "wildcard_data",
446 "curveconfig_surface_explicit_expiries_wc_strikes.xml");
447
448 auto vts = todaysMarket->commodityVolatility("NYMEX:CL");
449
450 // Wildcards in configuration so we know that a BlackVarianceSurfaceSparse has been created and fed to a
451 // BlackVolatilityWithATM surface in TodaysMarket
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");
458
459 // The expected NYMEX CL volatility data
460 NymexVolatilityData expData;
461
462 // Check what is loaded against expected data as provided in market data file for NYMEX:CL.
463 // We have chosen the explicit expiries 2019-10-17 and 2019-12-16
464 // Note: the BlackVarianceSurfaceSparse adds a dummy expiry slice at time zero
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]);
469 }
470
471 BOOST_REQUIRE_EQUAL(surface->strikes().size() - 1, expExpiries.size());
472 for (Size i = 0; i < expExpiries.size(); i++) {
473
474 // Check the strikes against the input
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);
479 }
480
481 // Check the volatilities against the input
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);
485 }
486 }
487}
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [7/8]

BOOST_AUTO_TEST_CASE ( testCommodityVolSurfaceExplicitExpiriesExplicitStrikes  )

Definition at line 489 of file commodityvolcurve.cpp.

489 {
490
491 BOOST_TEST_MESSAGE(
492 "Testing commodity volatility curve building explicit expiries and explicit strikes in configuration");
493
494 auto todaysMarket = createTodaysMarket(Date(16, Sep, 2019), "wildcard_data",
495 "curveconfig_surface_explicit_expiries_explicit_strikes.xml");
496
497 auto vts = todaysMarket->commodityVolatility("NYMEX:CL");
498
499 // The expected NYMEX CL volatility data
500 NymexVolatilityData expData;
501
502 // We have provided two explicit expiries, 2019-10-17 and 2019-12-16, and two explicit strikes, 60 and 61.
503 // We check the volatility term structure at these 4 points against the input data
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) {
508 // Find the index of the explicit strike in the input data
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);
513
514 Real inputVol = expData.volatilities[e][idx];
515 BOOST_CHECK_CLOSE(inputVol, vts->blackVol(e, s), 1e-12);
516 }
517 }
518}

◆ BOOST_DATA_TEST_CASE() [1/3]

BOOST_DATA_TEST_CASE ( testCommodityVolDeltaSurface  ,
bdata::make(asofDates) *bdata::make(curveConfigs ,
asof  ,
curveConfig   
)

Definition at line 527 of file commodityvolcurve.cpp.

528 {
529
530 BOOST_TEST_MESSAGE("Testing commodity volatility delta surface building");
531
532 auto todaysMarket = createTodaysMarket(asof, "delta_surface", curveConfig, "market.txt");
533
534 // Get the built commodity volatility surface
535 auto vts = todaysMarket->commodityVolatility("NYMEX:CL");
536
537 // Cast to expected type and check that it succeeds
538 // For some reason, todaysmarket wraps the surface built in CommodityVolCurve in a BlackVolatilityWithATM.
539 auto bvwa = dynamic_pointer_cast<BlackVolatilityWithATM>(*vts);
540 BOOST_REQUIRE(bvwa);
541 auto bvsd = QuantLib::ext::dynamic_pointer_cast<BlackVolatilitySurfaceDelta>(bvwa->surface());
542 BOOST_REQUIRE(bvsd);
543
544 // Tolerance for float comparison
545 Real tol = 1e-12;
546
547 // Read in the expected on-grid results for the given date.
548 string filename = "delta_surface/expected_grid_" + to_string(io::iso_date(asof)) + ".csv";
549 CSVFileReader reader(TEST_INPUT_FILE(filename), true, ",");
550 BOOST_REQUIRE_EQUAL(reader.numberOfColumns(), 3);
551
552 while (reader.next()) {
553
554 // Get the expected expiry date, strike and volatility grid point
555 Date expiryDate = parseDate(reader.get(0));
556 Real strike = parseReal(reader.get(1));
557 Real volatility = parseReal(reader.get(2));
558
559 // Check that the expected grid expiry date is one of the surface dates
560 auto itExpiry = find(bvsd->dates().begin(), bvsd->dates().end(), expiryDate);
561 BOOST_REQUIRE(itExpiry != bvsd->dates().end());
562
563 // Get the smile section, cast to expected type and check cast succeeds.
564 auto fxss = bvsd->blackVolSmile(expiryDate);
565 auto iss = QuantLib::ext::dynamic_pointer_cast<InterpolatedSmileSection>(fxss);
566 BOOST_REQUIRE(iss);
567
568 // Check that the expected grid strike is one of the smile section strikes.
569 auto itStrike =
570 find_if(iss->strikes().begin(), iss->strikes().end(), [&](Real s) { return std::abs(s - strike) < tol; });
571 BOOST_REQUIRE(itStrike != iss->strikes().end());
572
573 // Check that the expected volatility is equal to that at the grid strike
574 auto pos = distance(iss->strikes().begin(), itStrike);
575 BOOST_CHECK_SMALL(volatility - iss->volatilities()[pos], tol);
576 }
577
578 // Check flat time extrapolation
579 auto fxss = bvsd->blackVolSmile(bvsd->dates().back());
580 auto iss = QuantLib::ext::dynamic_pointer_cast<InterpolatedSmileSection>(fxss);
581 BOOST_REQUIRE(iss);
582 vector<Real> lastVolatilities = iss->volatilities();
583
584 fxss = bvsd->blackVolSmile(bvsd->dates().back() + 1 * Years);
585 iss = QuantLib::ext::dynamic_pointer_cast<InterpolatedSmileSection>(fxss);
586 BOOST_REQUIRE(iss);
587 vector<Real> extrapVolatilities = iss->volatilities();
588
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);
592 }
593
594 // Check flat strike extrapolation.
595 Date testDate = asof + 1 * Years;
596
597 fxss = bvsd->blackVolSmile(testDate);
598 iss = QuantLib::ext::dynamic_pointer_cast<InterpolatedSmileSection>(fxss);
599 BOOST_REQUIRE(iss);
600
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);
605
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);
610}
Date parseDate(const string &s)
Convert std::string to QuantLib::Date.
Definition: parsers.cpp:51
Real parseReal(const string &s)
Convert text to Real.
Definition: parsers.cpp:112
std::string to_string(const LocationInfo &l)
Definition: ast.cpp:28
+ Here is the call graph for this function:

◆ BOOST_DATA_TEST_CASE() [2/3]

BOOST_DATA_TEST_CASE ( testCommodityVolMoneynessSurface  ,
bdata::make(asofDates) *bdata::make(curveConfigs ,
asof  ,
curveConfig   
)

Definition at line 612 of file commodityvolcurve.cpp.

613 {
614
615 BOOST_TEST_MESSAGE("Testing commodity volatility forward moneyness surface building");
616
617 Settings::instance().evaluationDate() = asof;
618 auto todaysMarket = createTodaysMarket(asof, "moneyness_surface", curveConfig, "market.txt");
619
620 // Get the built commodity volatility surface
621 auto vts = todaysMarket->commodityVolatility("NYMEX:CL");
622
623 // Tolerance for float comparison
624 Real tol = 1e-12;
625
626 // Read in the expected on-grid results for the given date.
627 string filename = "moneyness_surface/expected_grid_" + to_string(io::iso_date(asof)) + ".csv";
628 CSVFileReader reader(TEST_INPUT_FILE(filename), true, ",");
629 BOOST_REQUIRE_EQUAL(reader.numberOfColumns(), 3);
630
631 while (reader.next()) {
632
633 // Get the expected expiry date, strike and volatility grid point
634 Date expiryDate = parseDate(reader.get(0));
635 Real strike = parseReal(reader.get(1));
636 Real volatility = parseReal(reader.get(2));
637
638 // Check the surface on the grid point.
639 BOOST_CHECK_SMALL(volatility - vts->blackVol(expiryDate, strike), tol);
640 }
641
642 // Price term structure
643 auto pts = todaysMarket->commodityPriceCurve("NYMEX:CL");
644
645 // Pick two future expiries beyond max vol surface time and get their prices
646 // This should correspond to moneyness 1.0 and we should see flat vol extrapolation.
647 // This only passes because we have set sticky strike to false in CommodityVolCurve.
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 << ","
655 << vol_2 << ".");
656 BOOST_CHECK_SMALL(vol_1 - vol_2, tol);
657
658 // Test flat strike extrapolation at lower and upper strikes i.e. at 50% and 150% forward moneyness.
659 Date optionExpiry(14, Jan, 2021);
660 Date futureExpiry(20, Jan, 2021);
661 Real futurePrice = pts->price(futureExpiry);
662
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);
669
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);
676}
+ Here is the call graph for this function:

◆ BOOST_DATA_TEST_CASE() [3/3]

BOOST_DATA_TEST_CASE ( testCommodityApoSurface  ,
bdata::make(asofDates ,
asof   
)

Definition at line 678 of file commodityvolcurve.cpp.

678 {
679
680 BOOST_TEST_MESSAGE("Testing commodity volatility forward moneyness surface building");
681
682 Settings::instance().evaluationDate() = asof;
683
684 string fixingsFile = "fixings_" + to_string(io::iso_date(asof)) + ".txt";
685 auto todaysMarket = createTodaysMarket(asof, "apo_surface", "curveconfig.xml", "market.txt", fixingsFile);
686
687 // Get the built commodity volatility surface
688 auto vts = todaysMarket->commodityVolatility("NYMEX:FF");
689
690 // Tolerance for float comparison
691 Real tol = 1e-12;
692
693 // Read in the expected on-grid results for the given date.
694 string filename = "apo_surface/expected_grid_" + to_string(io::iso_date(asof)) + ".csv";
695 CSVFileReader reader(TEST_INPUT_FILE(filename), true, ",");
696 BOOST_REQUIRE_EQUAL(reader.numberOfColumns(), 3);
697
698 BOOST_TEST_MESSAGE("exp_vol,calc_vol,difference");
699 while (reader.next()) {
700
701 // Get the expected expiry date, strike and volatility grid point
702 Date expiryDate = parseDate(reader.get(0));
703 Real strike = parseReal(reader.get(1));
704 Real volatility = parseReal(reader.get(2));
705
706 // Check the surface on the grid point.
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);
712 }
713}
+ Here is the call graph for this function:

◆ BOOST_AUTO_TEST_CASE() [8/8]

BOOST_AUTO_TEST_CASE ( testCommodityVolSurfaceMyrCrudePalmOil  )

Definition at line 716 of file commodityvolcurve.cpp.

716 {
717
718 BOOST_TEST_MESSAGE("Testing commodity volatility delta surface building for MYR Crude Palm Oil");
719
720 Date asof(14, Oct, 2020);
721 auto todaysMarket = createTodaysMarket(asof, "myr_crude_palm_oil", "curveconfig.xml", "market.txt");
722
723 // Get the built commodity volatility surface
724 auto vts = todaysMarket->commodityVolatility("XKLS:FCPO");
725
726 // Check that it built ok i.e. can query a volatility.
727 BOOST_CHECK_NO_THROW(vts->blackVol(1.0, 2800));
728
729 // Cast to expected type and check that it succeeds
730 // For some reason, todaysmarket wraps the surface built in CommodityVolCurve in a BlackVolatilityWithATM.
731 auto bvwa = dynamic_pointer_cast<BlackVolatilityWithATM>(*vts);
732 BOOST_REQUIRE(bvwa);
733 auto bvsd = QuantLib::ext::dynamic_pointer_cast<BlackVolatilitySurfaceDelta>(bvwa->surface());
734 BOOST_REQUIRE(bvsd);
735
736 // Now check that the surface dates are as expected.
737
738 // Calculated surface dates.
739 const vector<Date>& surfaceDates = bvsd->dates();
740
741 // Read in the expected dates.
742 vector<Date> expectedDates;
743 string filename = "myr_crude_palm_oil/expected_expiries.csv";
744 CSVFileReader reader(TEST_INPUT_FILE(filename), true, ",");
745 BOOST_REQUIRE_EQUAL(reader.numberOfColumns(), 1);
746 while (reader.next()) {
747 expectedDates.push_back(parseDate(reader.get(0)));
748 }
749
750 // Check equal.
751 BOOST_CHECK_EQUAL_COLLECTIONS(surfaceDates.begin(), surfaceDates.end(), expectedDates.begin(), expectedDates.end());
752}
+ Here is the call graph for this function:

Variable Documentation

◆ asofDates

vector<Date> asofDates {Date(13, Jan, 2020), Date(15, Jan, 2020)}

Definition at line 522 of file commodityvolcurve.cpp.

◆ curveConfigs

vector<string> curveConfigs {"curveconfig_explicit_expiries.xml", "curveconfig_wildcard_expiries.xml"}

Definition at line 525 of file commodityvolcurve.cpp.