Skip to content

Commit 557cf6d

Browse files
authored
Notify observers when an inflation volatility quote changes (#2747)
2 parents 8174e99 + 4f9239d commit 557cf6d

4 files changed

Lines changed: 84 additions & 2 deletions

File tree

ql/termstructures/volatility/inflation/constantcpivolatility.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ namespace QuantLib {
3232
bool indexIsInterpolated)
3333
: CPIVolatilitySurface(settlementDays, cal, bdc, dc,
3434
observationLag, frequency, indexIsInterpolated),
35-
volatility_(vol) {}
35+
volatility_(vol) {
36+
registerWith(volatility_);
37+
}
3638

3739
ConstantCPIVolatility:: ConstantCPIVolatility(Volatility vol,
3840
Natural settlementDays,

ql/termstructures/volatility/inflation/yoyinflationoptionletvolatilitystructure.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,9 @@ namespace QuantLib {
220220
indexIsInterpolated,
221221
volType,
222222
displacement),
223-
volatility_(std::move(v)), minStrike_(minStrike), maxStrike_(maxStrike) {}
223+
volatility_(std::move(v)), minStrike_(minStrike), maxStrike_(maxStrike) {
224+
registerWith(volatility_);
225+
}
224226

225227
Volatility ConstantYoYOptionletVolatility::volatilityImpl(Time, Rate) const {
226228
return volatility_->value();

test-suite/inflationcapfloor.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,49 @@ BOOST_AUTO_TEST_CASE(testParity) {
449449
vars.hy.reset();
450450
}
451451

452+
BOOST_AUTO_TEST_CASE(testVolatilityQuoteObservability) {
453+
454+
BOOST_TEST_MESSAGE("Testing yoy inflation cap repricing when its volatility quote moves...");
455+
456+
CommonVars vars;
457+
458+
auto quote = ext::make_shared<SimpleQuote>(0.01);
459+
Handle<YoYOptionletVolatilitySurface> volatility(
460+
ext::make_shared<ConstantYoYOptionletVolatility>(Handle<Quote>(quote),
461+
vars.settlementDays,
462+
vars.calendar,
463+
vars.convention,
464+
vars.dc,
465+
vars.observationLag,
466+
vars.frequency,
467+
false));
468+
469+
Leg leg = vars.makeYoYLeg(vars.evaluationDate, 2);
470+
auto cap = ext::make_shared<YoYInflationCap>(leg, std::vector<Rate>(1, 0.0295));
471+
cap->setPricingEngine(ext::make_shared<YoYInflationBlackCapFloorEngine>(
472+
vars.iir, volatility, vars.nominalTS));
473+
474+
Real quoted = cap->NPV();
475+
quote->setValue(0.02);
476+
Real moved = cap->NPV();
477+
478+
BOOST_CHECK_MESSAGE(moved != quoted,
479+
"yoy cap NPV did not move when its volatility quote did: "
480+
<< quoted << " at vol 0.01 and " << moved << " at vol 0.02");
481+
482+
Leg other = vars.makeYoYLeg(vars.evaluationDate, 2);
483+
auto fixed = ext::make_shared<YoYInflationCap>(other, std::vector<Rate>(1, 0.0295));
484+
fixed->setPricingEngine(vars.makeEngine(0.02, 0));
485+
486+
Real expected = fixed->NPV();
487+
BOOST_CHECK_MESSAGE(relativeError(moved, expected, expected) < 1.0e-10,
488+
"yoy cap priced off a quote of 0.02 gives " << moved
489+
<< ", off a value of 0.02 gives " << expected);
490+
491+
// remove circular refernce
492+
vars.hy.reset();
493+
}
494+
452495
BOOST_AUTO_TEST_CASE(testCachedValue) {
453496

454497
BOOST_TEST_MESSAGE("Testing Black yoy inflation cap/floor price"

test-suite/inflationvolatility.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
#include <ql/pricingengines/inflation/inflationcapfloorengines.hpp>
3030
#include <ql/experimental/inflation/yoyoptionletstripper.hpp>
3131
#include <ql/experimental/inflation/kinterpolatedyoyoptionletvolatilitysurface.hpp>
32+
#include <ql/quotes/simplequote.hpp>
33+
#include <ql/termstructures/volatility/inflation/constantcpivolatility.hpp>
34+
#include <ql/termstructures/volatility/inflation/yoyinflationoptionletvolatilitystructure.hpp>
3235
#include <ql/experimental/inflation/interpolatedyoyoptionletstripper.hpp>
3336
#include <ql/cashflows/capflooredinflationcoupon.hpp>
3437
#include <ql/indexes/inflation/euhicp.hpp>
@@ -390,6 +393,38 @@ BOOST_AUTO_TEST_CASE(testYoYPriceSurfaceToATM) {
390393
reset();
391394
}
392395

396+
BOOST_AUTO_TEST_CASE(testConstantVolatilityQuoteIsObserved) {
397+
398+
BOOST_TEST_MESSAGE("Testing that constant inflation volatility surfaces observe their quote...");
399+
400+
auto quote = ext::make_shared<SimpleQuote>(0.01);
401+
Handle<Quote> handle(quote);
402+
403+
auto cpi = ext::make_shared<ConstantCPIVolatility>(
404+
handle, 0, TARGET(), Following, Actual365Fixed(),
405+
Period(3, Months), Monthly, false);
406+
auto yoy = ext::make_shared<ConstantYoYOptionletVolatility>(
407+
handle, 0, TARGET(), Following, Actual365Fixed(),
408+
Period(3, Months), Monthly, false);
409+
410+
Flag cpiFlag, yoyFlag;
411+
cpiFlag.registerWith(cpi);
412+
yoyFlag.registerWith(yoy);
413+
cpiFlag.lower();
414+
yoyFlag.lower();
415+
416+
quote->setValue(0.02);
417+
418+
BOOST_CHECK_MESSAGE(cpiFlag.isUp(),
419+
"ConstantCPIVolatility did not notify when its quote moved");
420+
BOOST_CHECK_MESSAGE(yoyFlag.isUp(),
421+
"ConstantYoYOptionletVolatility did not notify when its quote moved");
422+
423+
// volatilityImpl reads the quote on every call, so these pass either way.
424+
BOOST_CHECK_CLOSE(cpi->volatility(1.0, 0.02), 0.02, 1.0e-10);
425+
BOOST_CHECK_CLOSE(yoy->volatility(1.0, 0.02), 0.02, 1.0e-10);
426+
}
427+
393428
BOOST_AUTO_TEST_SUITE_END()
394429

395430
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)