20#include <boost/make_shared.hpp>
21#include <ql/instruments/impliedvolatility.hpp>
22#include <ql/math/solver1d.hpp>
23#include <ql/pricingengines/vanilla/analyticeuropeanengine.hpp>
24#include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
39function<
bool(Real,Real)> comp = [](Real a, Real b) {
return !close(a, b) && a < b; };
41map<Real, Option::Type,
decltype(comp)> createStrikes(Real forward,
const vector<Real>& cStrikes,
42 const vector<Real>& pStrikes,
bool preferOutOfTheMoney) {
46 copy_if(cStrikes.begin(), cStrikes.end(), back_inserter(rcStks),[forward, preferOutOfTheMoney](Real stk) {
47 return (preferOutOfTheMoney && stk >= forward) || (!preferOutOfTheMoney && stk <= forward); });
49 copy_if(pStrikes.begin(), pStrikes.end(), back_inserter(rpStks), [forward, preferOutOfTheMoney](Real stk) {
50 return (preferOutOfTheMoney && stk <= forward) || (!preferOutOfTheMoney && stk >= forward); });
53 map<Real, Option::Type,
decltype(comp)> res(comp);
56 if (rcStks.empty() && rpStks.empty())
60 if (!rcStks.empty() && !rpStks.empty()) {
63 for (Real stk : rpStks)
64 res[stk] = Option::Put;
65 for (Real stk : rcStks)
66 res[stk] = Option::Call;
67 }
else if (rpStks.empty()) {
69 for (Real stk : cStrikes)
70 res[stk] = Option::Call;
71 }
else if (rcStks.empty()) {
73 for (Real stk : pStrikes)
74 res[stk] = Option::Put;
85 const QuantLib::ext::shared_ptr<OptionInterpolatorBase>& callSurface,
86 const QuantLib::ext::shared_ptr<OptionInterpolatorBase>& putSurface,
87 const Calendar& calendar,
88 const DayCounter& dayCounter,
90 bool lowerStrikeConstExtrap,
91 bool upperStrikeConstExtrap,
92 bool timeFlatExtrapolation,
93 bool preferOutOfTheMoney,
95 : callSurface_(callSurface),
96 putSurface_(putSurface),
98 dayCounter_(dayCounter),
100 lowerStrikeConstExtrap_(lowerStrikeConstExtrap),
101 upperStrikeConstExtrap_(upperStrikeConstExtrap),
102 timeFlatExtrapolation_(timeFlatExtrapolation),
103 preferOutOfTheMoney_(preferOutOfTheMoney),
104 solverOptions_(solverOptions),
108 "Mismatch between Call and Put reference dates in OptionSurfaceStripper");
110 registerWith(Settings::instance().evaluationDate());
116 QL_REQUIRE(QuantLib::ext::dynamic_pointer_cast<OptionPriceSurface>(
putSurface_),
117 "OptionSurfaceStripper: call price surface provided but no put price surface.");
124 : option_(option), volatility_(volatility), targetPrice_(targetPrice) {}
126Real OptionSurfaceStripper::PriceError::PriceError::operator()(Volatility x)
const {
128 volatility_.setValue(x);
138 return npv - targetPrice_;
145 set<Date> allExpiries(tmp.begin(), tmp.end());
147 allExpiries.insert(tmp.begin(), tmp.end());
149 QuantLib::ext::shared_ptr<BlackVarianceSurfaceSparse> callVolSurface;
150 QuantLib::ext::shared_ptr<BlackVarianceSurfaceSparse> putVolSurface;
153 QuantLib::ext::shared_ptr<PricingEngine> engine;
154 QuantLib::ext::shared_ptr<SimpleQuote> volQuote = QuantLib::ext::make_shared<SimpleQuote>(0.1);
158 QuantLib::ext::shared_ptr<GeneralizedBlackScholesProcess> gbsp =
process(volQuote);
161 if (
type_ == Exercise::American) {
162 engine = QuantLib::ext::make_shared<QuantExt::BaroneAdesiWhaleyApproximationEngine>(gbsp);
163 }
else if (
type_ == Exercise::European) {
164 engine = QuantLib::ext::make_shared<QuantExt::AnalyticEuropeanEngine>(gbsp);
166 QL_FAIL(
"Unsupported exercise type for option stripping");
171 callVolSurface = QuantLib::ext::dynamic_pointer_cast<BlackVarianceSurfaceSparse>(
callSurface_);
172 putVolSurface = QuantLib::ext::dynamic_pointer_cast<BlackVarianceSurfaceSparse>(
putSurface_);
176 vector<Real> volStrikes;
177 vector<Real> volData;
178 vector<Date> volExpiries;
181 for (
const Date& expiry : allExpiries) {
187 vector<Real> callStrikes =
strikes(expiry,
true);
188 vector<Real> putStrikes =
strikes(expiry,
false);
194 for (
const auto& kv : relevantStrikes) {
197 Real v =
implyVol(expiry, kv.first, kv.second, engine, *volQuote);
198 if (v != Null<Real>()) {
199 volExpiries.push_back(expiry);
200 volStrikes.push_back(kv.first);
201 volData.push_back(v);
204 volExpiries.push_back(expiry);
205 volStrikes.push_back(kv.first);
206 Real v = kv.second == Option::Call ? callVolSurface->blackVol(expiry, kv.first) :
207 putVolSurface->blackVol(expiry, kv.first);
208 volData.push_back(v);
214 volSurface_ = QuantLib::ext::make_shared<BlackVarianceSurfaceSparse>(
222 auto expiries = surface->expiries();
223 auto it = find(expiries.begin(), expiries.end(), expiry);
225 if (it != expiries.end()) {
226 return surface->strikes().at(distance(expiries.begin(), it));
234 QuantLib::ext::shared_ptr<PricingEngine> engine, SimpleQuote& volQuote)
const {
237 QuantLib::ext::shared_ptr<StrikedTypePayoff> payoff = QuantLib::ext::make_shared<PlainVanillaPayoff>(type, strike);
238 QuantLib::ext::shared_ptr<Exercise> exercise;
239 if (
type_ == Exercise::American) {
240 exercise = QuantLib::ext::make_shared<AmericanExercise>(expiry);
241 }
else if (
type_ == Exercise::European) {
242 exercise = QuantLib::ext::make_shared<EuropeanExercise>(expiry);
244 QL_FAIL(
"OptionSurfaceStripper: unsupported exercise type for option stripping.");
246 VanillaOption option(payoff, exercise);
247 option.setPricingEngine(engine);
250 Real targetPrice = type == Option::Call ?
callSurface_->getValue(expiry, strike)
254 Real vol = Null<Real>();
258 }
catch (
const Error&) {
268 QL_REQUIRE(guess != Null<Real>(),
"OptionSurfaceStripper: need a valid initial " <<
269 "guess for a price based surface.");
272 QL_REQUIRE(accuracy != Null<Real>(),
"OptionSurfaceStripper: need a valid accuracy " <<
273 "for a price based surface.");
294 using std::placeholders::_1;
295 if (
min != Null<Real>() &&
max != Null<Real>()) {
296 typedef Real (Brent::* MinMaxSolver)(
const PriceError&, Real, Real, Real, Real)
const;
297 solver_ = std::bind(
static_cast<MinMaxSolver
>(&Brent::solve), &
brent_, _1, accuracy, guess,
min,
max);
298 }
else if (step != Null<Real>()) {
299 typedef Real(Brent::* StepSolver)(
const PriceError&, Real, Real, Real)
const;
300 solver_ = std::bind(
static_cast<StepSolver
>(&Brent::solve), &
brent_, _1, accuracy, guess, step);
302 QL_FAIL(
"OptionSurfaceStripper: need a valid step size or (min, max) pair for a price based surface.");
313 const Handle<QuantExt::EquityIndex2>& equityIndex,
314 const QuantLib::ext::shared_ptr<OptionInterpolatorBase>& callSurface,
315 const QuantLib::ext::shared_ptr<OptionInterpolatorBase>& putSurface,
316 const Calendar& calendar,
317 const DayCounter& dayCounter,
319 bool lowerStrikeConstExtrap,
320 bool upperStrikeConstExtrap,
321 bool timeFlatExtrapolation,
322 bool preferOutOfTheMoney,
325 upperStrikeConstExtrap, timeFlatExtrapolation, preferOutOfTheMoney, solverOptions), equityIndex_(equityIndex) {
330 const QuantLib::ext::shared_ptr<QuantLib::SimpleQuote>& volatilityQuote)
const {
332 Handle<BlackVolTermStructure> vts(QuantLib::ext::make_shared<BlackConstantVol>(
335 return QuantLib::ext::make_shared<BlackScholesMertonProcess>(
equityIndex_->equitySpot(),
344 const Handle<PriceTermStructure>& priceCurve,
345 const Handle<YieldTermStructure>& discountCurve,
346 const QuantLib::ext::shared_ptr<OptionInterpolatorBase>& callSurface,
347 const QuantLib::ext::shared_ptr<OptionInterpolatorBase>& putSurface,
348 const Calendar& calendar,
349 const DayCounter& dayCounter,
351 bool lowerStrikeConstExtrap,
352 bool upperStrikeConstExtrap,
353 bool timeFlatExtrapolation,
354 bool preferOutOfTheMoney,
357 upperStrikeConstExtrap, timeFlatExtrapolation, preferOutOfTheMoney, solverOptions),
358 priceCurve_(priceCurve), discountCurve_(discountCurve) {
364 const QuantLib::ext::shared_ptr<QuantLib::SimpleQuote>& volatilityQuote)
const {
366 QL_REQUIRE(!
priceCurve_.empty(),
"CommodityOptionSurfaceStripper: price curve is empty");
367 QL_REQUIRE(!
discountCurve_.empty(),
"CommodityOptionSurfaceStripper: discount curve is empty");
370 Handle<BlackVolTermStructure> vts(QuantLib::ext::make_shared<BlackConstantVol>(
374 Handle<Quote> spot(QuantLib::ext::make_shared<DerivedPriceQuote>(
priceCurve_));
376 yield->enableExtrapolation();
378 return QuantLib::ext::make_shared<QuantLib::GeneralizedBlackScholesProcess>(spot, yield,
discountCurve_, vts);
382 QL_REQUIRE(!
priceCurve_.empty(),
"CommodityOptionSurfaceStripper: price curve is empty");
Barone-Adesi and Whaley approximation engine.
Black volatility surface modeled as variance surface.
QuantLib::Handle< QuantExt::PriceTermStructure > priceCurve_
QuantLib::Handle< QuantLib::YieldTermStructure > discountCurve_
CommodityOptionSurfaceStripper(const QuantLib::Handle< QuantExt::PriceTermStructure > &priceCurve, const QuantLib::Handle< QuantLib::YieldTermStructure > &discountCurve, const QuantLib::ext::shared_ptr< OptionInterpolatorBase > &callSurface, const QuantLib::ext::shared_ptr< OptionInterpolatorBase > &putSurface, const QuantLib::Calendar &calendar, const QuantLib::DayCounter &dayCounter, QuantLib::Exercise::Type type=QuantLib::Exercise::European, bool lowerStrikeConstExtrap=true, bool upperStrikeConstExtrap=true, bool timeFlatExtrapolation=false, bool preferOutOfTheMoney=false, Solver1DOptions solverOptions={})
QuantLib::Real forward(const QuantLib::Date &date) const override
Return the forward price at a given date.
QuantLib::ext::shared_ptr< QuantLib::GeneralizedBlackScholesProcess > process(const QuantLib::ext::shared_ptr< QuantLib::SimpleQuote > &volatilityQuote) const override
Generate the relevant Black Scholes process for the underlying.
QuantLib::Handle< QuantExt::EquityIndex2 > equityIndex_
QuantLib::Real forward(const QuantLib::Date &date) const override
Return the forward price at a given date.
QuantLib::ext::shared_ptr< QuantLib::GeneralizedBlackScholesProcess > process(const QuantLib::ext::shared_ptr< QuantLib::SimpleQuote > &volatilityQuote) const override
Generate the relevant Black Scholes process for the underlying.
EquityOptionSurfaceStripper(const QuantLib::Handle< QuantExt::EquityIndex2 > &equityIndex, const QuantLib::ext::shared_ptr< OptionInterpolatorBase > &callSurface, const QuantLib::ext::shared_ptr< OptionInterpolatorBase > &putSurface, const QuantLib::Calendar &calendar, const QuantLib::DayCounter &dayCounter, QuantLib::Exercise::Type type=QuantLib::Exercise::European, bool lowerStrikeConstExtrap=true, bool upperStrikeConstExtrap=true, bool timeFlatExtrapolation=false, bool preferOutOfTheMoney=false, Solver1DOptions solverOptions={})
Function object used in solving.
PriceError(const QuantLib::VanillaOption &option, QuantLib::SimpleQuote &volatility, QuantLib::Real targetPrice)
Abstract base class for the option stripper.
QuantLib::Exercise::Type type_
void performCalculations() const override
const QuantLib::DayCounter & dayCounter_
bool upperStrikeConstExtrap_
const QuantLib::Calendar & calendar_
virtual QuantLib::ext::shared_ptr< QuantLib::GeneralizedBlackScholesProcess > process(const QuantLib::ext::shared_ptr< QuantLib::SimpleQuote > &volatilityQuote) const =0
Generate the relevant Black Scholes process for the underlying.
QuantLib::ext::shared_ptr< QuantLib::BlackVolTermStructure > volSurface_
Solver1DOptions solverOptions_
OptionSurfaceStripper(const QuantLib::ext::shared_ptr< OptionInterpolatorBase > &callSurface, const QuantLib::ext::shared_ptr< OptionInterpolatorBase > &putSurface, const QuantLib::Calendar &calendar, const QuantLib::DayCounter &dayCounter, QuantLib::Exercise::Type type=QuantLib::Exercise::European, bool lowerStrikeConstExtrap=true, bool upperStrikeConstExtrap=true, bool timeFlatExtrapolation=false, bool preferOutOfTheMoney=false, Solver1DOptions solverOptions={})
bool havePrices_
Set to true if we must strip volatilities from prices.
virtual QuantLib::Real forward(const QuantLib::Date &date) const =0
Return the forward price at a given date.
std::vector< QuantLib::Real > strikes(const QuantLib::Date &expiry, bool isCall) const
Retrieve the vector of strikes at a given expiry date.
QuantLib::ext::shared_ptr< QuantLib::BlackVolTermStructure > volSurface()
Return the stripped volatility structure.
QuantLib::ext::shared_ptr< OptionInterpolatorBase > putSurface_
std::function< Real(const PriceError &)> solver_
Store the function that will be called each time to solve for volatility.
Brent brent_
Solver used when implying volatility from price.
bool timeFlatExtrapolation_
bool lowerStrikeConstExtrap_
QuantLib::Real implyVol(QuantLib::Date expiry, QuantLib::Real strike, QuantLib::Option::Type type, QuantLib::ext::shared_ptr< QuantLib::PricingEngine > engine, QuantLib::SimpleQuote &volQuote) const
QuantLib::ext::shared_ptr< OptionInterpolatorBase > callSurface_
bool preferOutOfTheMoney_
Imply equity or commodity volatility surface from put/call price surfaces.
CompiledFormula min(CompiledFormula x, const CompiledFormula &y)
CompiledFormula max(CompiledFormula x, const CompiledFormula &y)
PriceTermStructure adapter.
std::pair< QuantLib::Real, QuantLib::Real > minMax
Set the minimum and maximum search.
QuantLib::Real accuracy
The accuracy for the search.
QuantLib::Real initialGuess
The initial guess for the search.
QuantLib::Real upperBound
The upper bound of the search domain. A Null<Real>() indicates that the bound should not be set.
QuantLib::Real lowerBound
The lower bound of the search domain. A Null<Real>() indicates that the bound should not be set.
QuantLib::Real step
Set the step size for the search.
QuantLib::Size maxEvaluations
The maximum number of evaluations. Default used if not set.