19#include <ql/instruments/impliedvolatility.hpp>
20#include <ql/instruments/vanillaoption.hpp>
21#include <ql/math/solvers1d/brent.hpp>
22#include <ql/pricingengines/blackformula.hpp>
23#include <ql/processes/blackscholesprocess.hpp>
24#include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
25#include <ql/termstructures/yield/flatforward.hpp>
37 PriceError(
const VanillaOption& option, SimpleQuote& vol, Real targetValue);
38 Real operator()(Volatility x)
const;
41 const VanillaOption& option_;
46PriceError::PriceError(
const VanillaOption& option, SimpleQuote& vol, Real targetValue)
49Real PriceError::operator()(Volatility x)
const {
66 const QuantLib::ext::shared_ptr<OptionPriceSurface>& putSurface,
67 Handle<YieldTermStructure>& forecastCurve,
68 Handle<QuantLib::Quote>& equitySpot, Exercise::Type type)
69 : callSurface_(callSurface), putSurface_(putSurface), forecastCurve_(forecastCurve), equitySpot_(equitySpot),
70 type_(type), forwards_(callSurface_->expiries().size()) {
75 "Mismatch between Call and Put strikes in EquityForwardCurveStripper");
77 "Mismatch between Call and Put expiries in EquityForwardCurveStripper");
79 "Mismatch between Call and Put reference dates in EquityForwardCurveStripper");
81 "Mismatch between Call and Put day counters in EquityForwardCurveStripper");
84 registerWith(callSurface);
85 registerWith(putSurface);
86 registerWith(forecastCurve);
87 registerWith(equitySpot);
88 registerWith(Settings::instance().evaluationDate());
93 vector<vector<Real> > allStrikes =
callSurface_->strikes();
97 for (Size i = 0; i <
expiries().size(); i++) {
100 vector<Real>
strikes = allStrikes[i];
101 QL_REQUIRE(
strikes.size() > 0,
"No strikes for expiry " << expiry);
113 for (Size k = 0; k <
strikes.size(); k++) {
129 bool isForward =
false;
130 while (!isForward && j < maxIter) {
132 if (
type_ == Exercise::American) {
136 vector<Real> amerStrikes(2);
137 auto it_lower = std::lower_bound(
strikes.begin(),
strikes.end(), forward);
139 it_lower = std::prev(it_lower);
140 amerStrikes[1] = *it_lower;
141 amerStrikes[0] = (it_lower ==
strikes.begin()) ? *it_lower : *std::prev(it_lower);
150 Time t = dc.yearFraction(asof, expiry);
156 QuantLib::ext::shared_ptr<SimpleQuote> volQuote = QuantLib::ext::make_shared<SimpleQuote>(0.1);
157 Handle<BlackVolTermStructure> volTs(
158 QuantLib::ext::make_shared<BlackConstantVol>(asof, cal, Handle<Quote>(volQuote), dc));
159 Handle<YieldTermStructure> divTs(QuantLib::ext::make_shared<FlatForward>(asof, q, dc));
162 QuantLib::ext::shared_ptr<GeneralizedBlackScholesProcess> gbsp =
164 QuantLib::ext::shared_ptr<PricingEngine> engine =
165 QuantLib::ext::make_shared<QuantExt::BaroneAdesiWhaleyApproximationEngine>(gbsp);
167 vector<vector<Volatility> > vols(2, vector<Volatility>(amerStrikes.size()));
168 vector<Option::Type> types;
169 types.push_back(Option::Call);
170 types.push_back(Option::Put);
172 for (Size l = 0; l < types.size(); l++) {
173 for (Size k = 0; k < amerStrikes.size(); k++) {
175 QuantLib::ext::shared_ptr<StrikedTypePayoff> payoff(
new PlainVanillaPayoff(types[l], amerStrikes[k]));
176 QuantLib::ext::shared_ptr<Exercise> exercise = QuantLib::ext::make_shared<AmericanExercise>(expiry);
177 VanillaOption option(payoff, exercise);
178 option.setPricingEngine(engine);
181 Real targetPrice = types[l] == Option::Call ?
callSurface_->price(expiry, amerStrikes[k])
186 PriceError f(option, *volQuote, targetPrice);
188 solver.setMaxEvaluations(100);
189 solver.setLowerBound(0.0001);
190 vols[l][k] = solver.solve(f, 0.0001, 0.2, 0.01);
197 vector<Real> newStrikes;
199 vector<Real> callPremiums, putPremiums;
201 for (Size k = 0; k < amerStrikes.size(); k++) {
202 if (vols[0][k] != 0.0 && vols[1][k] != 0.0) {
204 Real call = blackFormula(Option::Call, amerStrikes[k], forward, vols[0][k] *
sqrt(t),
206 Real put = blackFormula(Option::Put, amerStrikes[k], forward, vols[1][k] *
sqrt(t),
209 if (call != 0.0 && put != 0.0) {
210 newStrikes.push_back(amerStrikes[k]);
211 dates.push_back(expiry);
212 callPremiums.push_back(call);
213 putPremiums.push_back(put);
219 if (newStrikes.size() > 0) {
221 callSurface = QuantLib::ext::make_shared<OptionPriceSurface>(asof, dates, newStrikes, callPremiums, dc);
222 putSurface = QuantLib::ext::make_shared<OptionPriceSurface>(asof, dates, newStrikes, putPremiums, dc);
226 Real newForward = 0.0;
228 if (forward <=
strikes.front()) {
231 isForward = newForward <=
strikes.front();
232 }
else if (forward >=
strikes.back()) {
235 isForward = newForward >=
strikes.back();
240 isForward = fabs((newForward - forward) / forward) < 0.001;
242 forward = newForward;
251 Real C = callSurface.
price(d, strike);
252 Real
P = putSurface.
price(d, strike);
255 return strike + (C -
P) / D;
Barone-Adesi and Whaley approximation engine.
QuantLib::ext::shared_ptr< SimpleQuote > vol_
QuantLib::Exercise::Type type_
void performCalculations() const override
QuantLib::Handle< QuantLib::YieldTermStructure > forecastCurve_
const QuantLib::ext::shared_ptr< OptionPriceSurface > callSurface_
const QuantLib::ext::shared_ptr< OptionPriceSurface > putSurface_
QuantLib::Real forwardFromPutCallParity(QuantLib::Date d, QuantLib::Real call, const OptionPriceSurface &callSurface, const OptionPriceSurface &putSurface) const
const std::vector< QuantLib::Date > expiries() const
return the expiries
QuantLib::Handle< QuantLib::Quote > equitySpot_
EquityForwardCurveStripper(const QuantLib::ext::shared_ptr< OptionPriceSurface > &callSurface, const QuantLib::ext::shared_ptr< OptionPriceSurface > &putSurface, QuantLib::Handle< QuantLib::YieldTermStructure > &forecastCurve, QuantLib::Handle< QuantLib::Quote > &equitySpot, QuantLib::Exercise::Type type=QuantLib::Exercise::European)
std::vector< QuantLib::Real > forwards_
store the stripped forward rates
const std::vector< QuantLib::Real > forwards() const
return the stripped forwards
QuantLib::Real price(QuantLib::Time t, QuantLib::Real strike) const
Imply equity forwards from option put/call parity.
const P2_< E1, E2 > P(const E1 &e1, const E2 &e2)
RandomVariable sqrt(RandomVariable x)
CompiledFormula log(CompiledFormula x)