22#include <ql/instruments/creditdefaultswap.hpp>
23#include <ql/instruments/forwardrateagreement.hpp>
24#include <ql/instruments/makecapfloor.hpp>
25#include <ql/instruments/makeois.hpp>
26#include <ql/instruments/makevanillaswap.hpp>
27#include <ql/instruments/yearonyearinflationswap.hpp>
28#include <ql/instruments/zerocouponinflationswap.hpp>
29#include <ql/math/solvers1d/newtonsafe.hpp>
30#include <ql/pricingengine.hpp>
31#include <ql/pricingengines/capfloor/bacheliercapfloorengine.hpp>
32#include <ql/pricingengines/capfloor/blackcapfloorengine.hpp>
33#include <ql/quotes/simplequote.hpp>
34#include <ql/termstructures/volatility/inflation/yoyinflationoptionletvolatilitystructure.hpp>
57class ImpliedCapFloorVolHelper {
59 ImpliedCapFloorVolHelper(
60 const QuantLib::Instrument& cap,
61 const std::function<QuantLib::ext::shared_ptr<QuantLib::PricingEngine>(
const QuantLib::Handle<Quote>)>
63 const Real targetValue);
64 Real operator()(Volatility x)
const;
65 Real derivative(Volatility x)
const;
69 QuantLib::ext::shared_ptr<PricingEngine>
engine_;
70 QuantLib::ext::shared_ptr<QuantLib::SimpleQuote>
vol_;
74ImpliedCapFloorVolHelper::ImpliedCapFloorVolHelper(
75 const QuantLib::Instrument& cap,
76 const std::function<QuantLib::ext::shared_ptr<PricingEngine>(
const Handle<Quote>)> engineGenerator,
77 const Real targetValue)
81 vol_ = QuantLib::ext::shared_ptr<SimpleQuote>(
new SimpleQuote(-1));
83 cap.setupArguments(
engine_->getArguments());
84 results_ =
dynamic_cast<const Instrument::results*
>(
engine_->getResults());
87Real ImpliedCapFloorVolHelper::operator()(Volatility x)
const {
88 if (x !=
vol_->value()) {
95Real ImpliedCapFloorVolHelper::derivative(Volatility x)
const {
96 if (x !=
vol_->value()) {
100 std::map<std::string, boost::any>::const_iterator vega_ =
results_->additionalResults.find(
"vega");
101 QL_REQUIRE(vega_ !=
results_->additionalResults.end(),
"vega not provided");
102 return boost::any_cast<Real>(vega_->second);
105std::function<QuantLib::ext::shared_ptr<PricingEngine>(
const Handle<Quote>&)>
106pricingEngineGeneratorFactory(
const CapFloor& cap,
const Handle<YieldTermStructure>& d, VolatilityType type,
107 Real displacement,
const Handle<Index>& index) {
108 std::function<QuantLib::ext::shared_ptr<PricingEngine>(
const Handle<Quote>)> engineGenerator;
109 if (type == ShiftedLognormal)
110 engineGenerator = [&d, displacement](
const Handle<Quote>& h) {
111 return QuantLib::ext::make_shared<BlackCapFloorEngine>(d, h, Actual365Fixed(), displacement);
113 else if (type == Normal)
114 engineGenerator = [&d](
const Handle<Quote>& h) {
115 return QuantLib::ext::make_shared<BachelierCapFloorEngine>(d, h, Actual365Fixed());
118 QL_FAIL(
"volatility type " << type <<
" not implemented");
119 return engineGenerator;
122std::function<QuantLib::ext::shared_ptr<PricingEngine>(
const Handle<Quote>&)>
123pricingEngineGeneratorFactory(
const YoYInflationCapFloor& cap,
const Handle<YieldTermStructure>& d, VolatilityType type,
124 Real displacement,
const Handle<YoYInflationIndex>& index) {
125 std::function<QuantLib::ext::shared_ptr<PricingEngine>(
const Handle<Quote>)> engineGenerator;
126 if (type == ShiftedLognormal) {
128 engineGenerator = [&d, &index](
const Handle<Quote>& h) {
132 auto c = Handle<QuantLib::YoYOptionletVolatilitySurface>(
133 QuantLib::ext::make_shared<QuantExt::ConstantYoYOptionletVolatility>(
134 h, 0, NullCalendar(), Unadjusted, Actual365Fixed(),
135 index->yoyInflationTermStructure()->observationLag(), index->frequency(),
136 index->interpolated()));
137 return QuantLib::ext::make_shared<QuantExt::YoYInflationBlackCapFloorEngine>(*index, c, d);
140 engineGenerator = [&d, &index](
const Handle<Quote>& h) {
141 auto c = Handle<QuantLib::YoYOptionletVolatilitySurface>(
142 QuantLib::ext::make_shared<QuantExt::ConstantYoYOptionletVolatility>(
143 h, 0, NullCalendar(), Unadjusted, Actual365Fixed(),
144 index->yoyInflationTermStructure()->observationLag(), index->frequency(),
145 index->interpolated()));
146 return QuantLib::ext::make_shared<QuantExt::YoYInflationUnitDisplacedBlackCapFloorEngine>(*index, c, d);
148 }
else if (type == Normal)
149 engineGenerator = [&d, &index](
const Handle<Quote>& h) {
150 auto c = Handle<QuantLib::YoYOptionletVolatilitySurface>(
151 QuantLib::ext::make_shared<QuantExt::ConstantYoYOptionletVolatility>(
152 h, 0, NullCalendar(), Unadjusted, Actual365Fixed(),
153 index->yoyInflationTermStructure()->observationLag(), index->frequency(), index->interpolated()));
154 return QuantLib::ext::make_shared<QuantExt::YoYInflationBachelierCapFloorEngine>(*index, c, d);
157 QL_FAIL(
"volatility type " << type <<
" not implemented");
158 return engineGenerator;
161template <
typename CapFloorType,
typename IndexType>
162Volatility impliedVolatilityImpl(
const CapFloorType& cap, Real targetValue,
const Handle<YieldTermStructure>& d,
163 Volatility guess, VolatilityType type, Real displacement, Real accuracy,
164 Natural maxEvaluations, Volatility minVolLognormal, Volatility maxVolLognormal,
165 Volatility minVolNormal, Volatility maxVolNormal,
166 const Handle<IndexType>& index = Handle<IndexType>()) {
167 QL_REQUIRE(!cap.isExpired(),
"instrument expired");
168 auto engineGenerator = pricingEngineGeneratorFactory(cap, d, type, displacement, index);
169 ImpliedCapFloorVolHelper f(cap, engineGenerator, targetValue);
171 solver.setMaxEvaluations(maxEvaluations);
172 Real minVol = type == Normal ? minVolNormal : minVolLognormal;
173 Real maxVol = type == Normal ? maxVolNormal : maxVolLognormal;
174 return solver.solve(f, accuracy, guess, minVol, maxVol);
178template <
typename CapFloorType,
typename IndexType>
179Volatility impliedVolatilityWrapper(
const CapFloorType& cap, Real targetValue,
const Handle<YieldTermStructure>& d,
180 Volatility guess, VolatilityType type, Real displacement,
181 const Handle<IndexType>& index = Handle<IndexType>()) {
182 string strikeStr =
"?";
185 Real accuracy = 1.0e-6;
186 Natural maxEvaluations = 100;
193 std::ostringstream oss;
194 if (!cap.capRates().empty()) {
195 oss <<
"Cap: " << cap.capRates().size() <<
" strikes, starting with " << cap.capRates().front()
198 if (!cap.floorRates().empty()) {
199 oss <<
"Floor: " << cap.floorRates().size() <<
" strikes, starting with " << cap.floorRates().front()
202 strikeStr = oss.str();
205 TLOG(
"Getting impliedVolatility for cap (" << cap.maturityDate() <<
" strike " << strikeStr <<
")");
208 impliedVolatilityImpl(cap, targetValue, d, guess, type, displacement, accuracy, maxEvaluations,
209 minVolLognormal, maxVolLognormal, minVolNormal, maxVolNormal, index);
210 TLOG(
"Got vol " << vol <<
" on first attempt");
212 }
catch (std::exception& e) {
213 ALOG(
"Exception getting implied Vol for Cap (" << cap.maturityDate() <<
" strike " << strikeStr <<
") "
219 Volatility vol = impliedVolatilityImpl(cap, targetValue, d, guess, type, displacement, accuracy,
220 maxEvaluations, minVolLognormal / 100.0, maxVolLognormal * 100.0,
221 minVolNormal / 100.0, maxVolNormal * 100.0, index);
222 TLOG(
"Got vol " << vol <<
" on second attempt");
224 }
catch (std::exception& e) {
225 ALOG(
"Exception getting implied Vol for Cap (" << cap.maturityDate() <<
" strike " << strikeStr <<
") "
233 ALOG(
"Cap impliedVolatility() failed for Cap (" << cap.type() <<
", maturity " << cap.maturityDate() <<
", strike "
234 << strikeStr <<
" for target " << targetValue
235 <<
". Returning Initial guess " << guess <<
" and continuing");
241 if (QuantLib::ext::dynamic_pointer_cast<VanillaSwap>(i))
242 return QuantLib::ext::dynamic_pointer_cast<VanillaSwap>(i)->fairRate();
243 if (QuantLib::ext::dynamic_pointer_cast<Deposit>(i))
244 return QuantLib::ext::dynamic_pointer_cast<Deposit>(i)->fairRate();
245 if (QuantLib::ext::dynamic_pointer_cast<QuantLib::ForwardRateAgreement>(i))
246 return QuantLib::ext::dynamic_pointer_cast<QuantLib::ForwardRateAgreement>(i)->forwardRate();
247 if (QuantLib::ext::dynamic_pointer_cast<OvernightIndexedSwap>(i))
248 return QuantLib::ext::dynamic_pointer_cast<OvernightIndexedSwap>(i)->fairRate();
249 if (QuantLib::ext::dynamic_pointer_cast<CrossCcyBasisMtMResetSwap>(i))
250 return QuantLib::ext::dynamic_pointer_cast<CrossCcyBasisMtMResetSwap>(i)->fairSpread();
251 if (QuantLib::ext::dynamic_pointer_cast<CrossCcyBasisSwap>(i))
252 return QuantLib::ext::dynamic_pointer_cast<CrossCcyBasisSwap>(i)->fairPaySpread();
253 if (QuantLib::ext::dynamic_pointer_cast<FxForward>(i))
254 return QuantLib::ext::dynamic_pointer_cast<FxForward>(i)->fairForwardRate().rate();
255 if (QuantLib::ext::dynamic_pointer_cast<QuantExt::CreditDefaultSwap>(i))
256 return QuantLib::ext::dynamic_pointer_cast<QuantExt::CreditDefaultSwap>(i)->fairSpreadClean();
257 if (QuantLib::ext::dynamic_pointer_cast<ZeroCouponInflationSwap>(i))
258 return QuantLib::ext::dynamic_pointer_cast<ZeroCouponInflationSwap>(i)->fairRate();
259 if (QuantLib::ext::dynamic_pointer_cast<YearOnYearInflationSwap>(i))
260 return QuantLib::ext::dynamic_pointer_cast<YearOnYearInflationSwap>(i)->fairRate();
261 if (QuantLib::ext::dynamic_pointer_cast<TenorBasisSwap>(i)) {
262 if (QuantLib::ext::dynamic_pointer_cast<TenorBasisSwap>(i)->spreadOnRec())
263 return QuantLib::ext::dynamic_pointer_cast<TenorBasisSwap>(i)->fairRecLegSpread();
265 return QuantLib::ext::dynamic_pointer_cast<TenorBasisSwap>(i)->fairPayLegSpread();
267 if (QuantLib::ext::dynamic_pointer_cast<FixedBMASwap>(i))
268 return QuantLib::ext::dynamic_pointer_cast<FixedBMASwap>(i)->fairRate();
269 if (QuantLib::ext::dynamic_pointer_cast<SubPeriodsSwap>(i))
270 return QuantLib::ext::dynamic_pointer_cast<SubPeriodsSwap>(i)->fairRate();
271 QL_FAIL(
"SensitivityAnalysis: impliedQuote: unknown instrument (is null = " << std::boolalpha << (i ==
nullptr)
276 Volatility guess, VolatilityType type, Real displacement) {
277 return impliedVolatilityWrapper(cap, targetValue, d, guess, type, displacement, Handle<Index>());
281 const Handle<YieldTermStructure>& d, Volatility guess, VolatilityType type,
282 Real displacement,
const Handle<YoYInflationIndex>& index) {
283 return impliedVolatilityWrapper(cap, targetValue, d, guess, type, displacement, index);
291 if (key.
keytype == RiskFactorKey::KeyType::OptionletVolatility) {
292 QL_REQUIRE(instruments.
parCaps_.count(key) == 1,
"Can not convert capFloor par shifts to zero Vols");
294 "getTodaysAndTargetQuotes: no cap yts found for key " << key);
296 "getTodaysAndTargetQuotes: no cap vts found for key " << key);
297 const auto cap = instruments.
parCaps_.at(key);
298 Real price = cap->NPV();
303 }
else if (key.
keytype == RiskFactorKey::KeyType::YoYInflationCapFloorVolatility) {
304 QL_REQUIRE(instruments.
parYoYCaps_.count(key) == 1,
"Can not convert capFloor par shifts to zero Vols");
306 "getTodaysAndTargetQuotes: no cap yts found for key " << key);
308 "getTodaysAndTargetQuotes: no cap vts found for key " << key);
310 "getTodaysAndTargetQuotes: no cap index found for key " << key);
312 Real price = cap->NPV();
318 QL_FAIL(
"impliedCapVolatility: Unsupported risk factor key "
319 << key.
keytype <<
". Support OptionletVolatility and YoYInflationCapFloorVolatility");
boost::shared_ptr< SimpleQuote > vol_
boost::shared_ptr< PricingEngine > engine_
const Instrument::results * results_
Data types stored in the scenario class.
std::string name
Key name.
Filter close_enough(const RandomVariable &x, const RandomVariable &y)
Volatility impliedVolatility(const QuantLib::CapFloor &cap, Real targetValue, const Handle< YieldTermStructure > &d, Volatility guess, VolatilityType type, Real displacement)
bool riskFactorKeysAreSimilar(const ore::analytics::RiskFactorKey &x, const ore::analytics::RiskFactorKey &y)
true if key type and name are equal, do not care about the index though
Real impliedQuote(const QuantLib::ext::shared_ptr< Instrument > &i)
QuantLib::ext::shared_ptr< QuantLib::SimpleQuote > vol_
QuantLib::ext::shared_ptr< PricingEngine > engine_
const Instrument::results * results_
std::map< ore::analytics::RiskFactorKey, QuantLib::Handle< QuantExt::YoYOptionletVolatilitySurface > > parYoYCapsVts_
std::map< ore::analytics::RiskFactorKey, QuantLib::Handle< QuantLib::YieldTermStructure > > parYoYCapsYts_
par helpers: YoY cap / floors
std::map< ore::analytics::RiskFactorKey, QuantLib::Handle< QuantLib::YieldTermStructure > > parCapsYts_
std::map< ore::analytics::RiskFactorKey, QuantLib::ext::shared_ptr< QuantLib::CapFloor > > parCaps_
par helpers: IR cap / floors
std::map< ore::analytics::RiskFactorKey, QuantLib::Handle< QuantLib::OptionletVolatilityStructure > > parCapsVts_
std::map< ore::analytics::RiskFactorKey, QuantLib::ext::shared_ptr< QuantLib::YoYInflationCapFloor > > parYoYCaps_
std::map< ore::analytics::RiskFactorKey, QuantLib::Handle< QuantLib::YoYInflationIndex > > parYoYCapsIndex_