Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
Public Member Functions | List of all members
EquityForwardCurveStripper Class Reference

#include <qle/termstructures/equityforwardcurvestripper.hpp>

+ Inheritance diagram for EquityForwardCurveStripper:
+ Collaboration diagram for EquityForwardCurveStripper:

Public Member Functions

 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)
 
const std::vector< QuantLib::Date > expiries () const
 return the expiries More...
 
const std::vector< QuantLib::Real > forwards () const
 return the stripped forwards More...
 

LazyObject interface

const QuantLib::ext::shared_ptr< OptionPriceSurfacecallSurface_
 
const QuantLib::ext::shared_ptr< OptionPriceSurfaceputSurface_
 
QuantLib::Handle< QuantLib::YieldTermStructure > forecastCurve_
 
QuantLib::Handle< QuantLib::Quote > equitySpot_
 
QuantLib::Exercise::Type type_
 
std::vector< QuantLib::Real > forwards_
 store the stripped forward rates More...
 
void performCalculations () const override
 
QuantLib::Real forwardFromPutCallParity (QuantLib::Date d, QuantLib::Real call, const OptionPriceSurface &callSurface, const OptionPriceSurface &putSurface) const
 

Detailed Description

Definition at line 34 of file equityforwardcurvestripper.hpp.

Constructor & Destructor Documentation

◆ EquityForwardCurveStripper()

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 
)

Definition at line 65 of file equityforwardcurvestripper.cpp.

69 : callSurface_(callSurface), putSurface_(putSurface), forecastCurve_(forecastCurve), equitySpot_(equitySpot),
70 type_(type), forwards_(callSurface_->expiries().size()) {
71
72 // the call and put surfaces should have the same expiries/strikes/reference date/day counters, some checks to
73 // ensure this
74 QL_REQUIRE(callSurface_->strikes() == putSurface_->strikes(),
75 "Mismatch between Call and Put strikes in EquityForwardCurveStripper");
76 QL_REQUIRE(callSurface_->expiries() == putSurface_->expiries(),
77 "Mismatch between Call and Put expiries in EquityForwardCurveStripper");
78 QL_REQUIRE(callSurface_->referenceDate() == putSurface_->referenceDate(),
79 "Mismatch between Call and Put reference dates in EquityForwardCurveStripper");
80 QL_REQUIRE(callSurface_->dayCounter() == putSurface_->dayCounter(),
81 "Mismatch between Call and Put day counters in EquityForwardCurveStripper");
82
83 // register with all market data
84 registerWith(callSurface);
85 registerWith(putSurface);
86 registerWith(forecastCurve);
87 registerWith(equitySpot);
88 registerWith(Settings::instance().evaluationDate());
89}
QuantLib::Handle< QuantLib::YieldTermStructure > forecastCurve_
const QuantLib::ext::shared_ptr< OptionPriceSurface > callSurface_
const QuantLib::ext::shared_ptr< OptionPriceSurface > putSurface_
QuantLib::Handle< QuantLib::Quote > equitySpot_
std::vector< QuantLib::Real > forwards_
store the stripped forward rates

Member Function Documentation

◆ expiries()

const vector< Date > expiries ( ) const

return the expiries

Definition at line 258 of file equityforwardcurvestripper.cpp.

258 {
259 calculate();
260 return callSurface_->expiries();
261}
+ Here is the caller graph for this function:

◆ forwards()

const vector< Real > forwards ( ) const

return the stripped forwards

Definition at line 263 of file equityforwardcurvestripper.cpp.

263 {
264 calculate();
265 return forwards_;
266}

◆ performCalculations()

void performCalculations ( ) const
override

Definition at line 91 of file equityforwardcurvestripper.cpp.

91 {
92
93 vector<vector<Real> > allStrikes = callSurface_->strikes();
94 forwards_.resize(callSurface_->expiries().size());
95
96 // at each option expiry time we calculate a forward
97 for (Size i = 0; i < expiries().size(); i++) {
98 Date expiry = expiries()[i];
99 // get the relevant strikes at this expiry
100 vector<Real> strikes = allStrikes[i];
101 QL_REQUIRE(strikes.size() > 0, "No strikes for expiry " << expiry);
102
103 // if we only have one strike we just use that to get the forward
104 if (strikes.size() == 1) {
106 continue;
107 }
108
109 // we make a first guess at the forward price
110 // strikes are ordered, lowest to highest, we take the first guess as midpoint of 2 strikes
111 // where (C-P) goes from positive to negative
112 Real forward = strikes.back();
113 for (Size k = 0; k < strikes.size(); k++) {
114 if (callSurface_->price(expiry, strikes[k]) <= putSurface_->price(expiry, strikes[k])) {
115 if (k == 0)
116 forward = strikes.front();
117 else
118 forward = (strikes[k] + strikes[k - 1]) / 2;
119 break;
120 }
121 }
122
123 // call and put surface to be used to find forward - updated for American
124 auto callSurface = callSurface_;
125 auto putSurface = putSurface_;
126
127 Size maxIter = 100;
128 Size j = 0;
129 bool isForward = false;
130 while (!isForward && j < maxIter) {
131
132 if (type_ == Exercise::American) {
133
134 // we take the strike either side of our forward guess, this is sufficient because
135 // we construct new price surfaces but only ask for the price at the forward from these
136 vector<Real> amerStrikes(2);
137 auto it_lower = std::lower_bound(strikes.begin(), strikes.end(), forward);
138 if (it_lower == strikes.end())
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);
142
143 // for American options we first get the implied vol from the American premiums
144 // we use these to construct the European prices in order to apply put call parity
145
146 // get date and daycounter from the prices surface
147 Date asof = callSurface_->referenceDate();
148 DayCounter dc = callSurface_->dayCounter();
149 Calendar cal = callSurface_->calendar();
150 Time t = dc.yearFraction(asof, expiry);
151
152 // dividend rate from S_t = S * exp((r - q) * t)
153 Real q = forecastCurve_->zeroRate(t, Continuous) - log(forward / equitySpot_->value()) / t;
154
155 // term structures needed to get implied vol
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));
160
161 // a black scholes process
162 QuantLib::ext::shared_ptr<GeneralizedBlackScholesProcess> gbsp =
163 QuantLib::ext::make_shared<BlackScholesMertonProcess>(equitySpot_, divTs, forecastCurve_, volTs);
164 QuantLib::ext::shared_ptr<PricingEngine> engine =
165 QuantLib::ext::make_shared<QuantExt::BaroneAdesiWhaleyApproximationEngine>(gbsp);
166
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);
171
172 for (Size l = 0; l < types.size(); l++) {
173 for (Size k = 0; k < amerStrikes.size(); k++) {
174 // create an american option for current strike/expiry and type
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);
179
180 // option.setPricingEngine(engine);
181 Real targetPrice = types[l] == Option::Call ? callSurface_->price(expiry, amerStrikes[k])
182 : putSurface_->price(expiry, amerStrikes[k]);
183
184 // calculate the implied volatility using a solver
185 try {
186 PriceError f(option, *volQuote, targetPrice);
187 Brent solver;
188 solver.setMaxEvaluations(100);
189 solver.setLowerBound(0.0001);
190 vols[l][k] = solver.solve(f, 0.0001, 0.2, 0.01);
191 } catch (...) {
192 vols[l][k] = 0.0;
193 }
194 }
195 }
196
197 vector<Real> newStrikes;
198 vector<Date> dates;
199 vector<Real> callPremiums, putPremiums;
200
201 for (Size k = 0; k < amerStrikes.size(); k++) {
202 if (vols[0][k] != 0.0 && vols[1][k] != 0.0) {
203 // get the european option prices for each strike
204 Real call = blackFormula(Option::Call, amerStrikes[k], forward, vols[0][k] * sqrt(t),
205 forecastCurve_->discount(t));
206 Real put = blackFormula(Option::Put, amerStrikes[k], forward, vols[1][k] * sqrt(t),
207 forecastCurve_->discount(t));
208
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);
214 }
215 }
216 }
217 // throw away any strikes where the vol is zero for either put or call
218 // must have at least one new strike otherwise continue with current price surfaces
219 if (newStrikes.size() > 0) {
220 // build call/put price surfaces with the new European prices
221 callSurface = QuantLib::ext::make_shared<OptionPriceSurface>(asof, dates, newStrikes, callPremiums, dc);
222 putSurface = QuantLib::ext::make_shared<OptionPriceSurface>(asof, dates, newStrikes, putPremiums, dc);
223 }
224 }
225
226 Real newForward = 0.0;
227 // if our guess is below the first strike or after the last strike we just take the relevant strike
228 if (forward <= strikes.front()) {
229 newForward = forwardFromPutCallParity(expiry, strikes.front(), *callSurface, *putSurface);
230 // if forward is still less than first strike we accept this
231 isForward = newForward <= strikes.front();
232 } else if (forward >= strikes.back()) {
233 newForward = forwardFromPutCallParity(expiry, strikes.back(), *callSurface, *putSurface);
234 // if forward is still greater than last strike we accept this
235 isForward = newForward >= strikes.back();
236 } else {
237 newForward = forwardFromPutCallParity(expiry, forward, *callSurface, *putSurface);
238
239 // check - has it moved by less that 0.1%
240 isForward = fabs((newForward - forward) / forward) < 0.001;
241 }
242 forward = newForward;
243 j++;
244 }
245 forwards_[i] = forward;
246 }
247}
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
RandomVariable sqrt(RandomVariable x)
CompiledFormula log(CompiledFormula x)
vector< Real > strikes
+ Here is the call graph for this function:

◆ forwardFromPutCallParity()

Real forwardFromPutCallParity ( QuantLib::Date  d,
QuantLib::Real  call,
const OptionPriceSurface callSurface,
const OptionPriceSurface putSurface 
) const
private

Definition at line 249 of file equityforwardcurvestripper.cpp.

250 {
251 Real C = callSurface.price(d, strike);
252 Real P = putSurface.price(d, strike);
253 Real D = forecastCurve_->discount(d);
254
255 return strike + (C - P) / D;
256}
const P2_< E1, E2 > P(const E1 &e1, const E2 &e2)
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Member Data Documentation

◆ callSurface_

const QuantLib::ext::shared_ptr<OptionPriceSurface> callSurface_
private

Definition at line 54 of file equityforwardcurvestripper.hpp.

◆ putSurface_

const QuantLib::ext::shared_ptr<OptionPriceSurface> putSurface_
private

Definition at line 55 of file equityforwardcurvestripper.hpp.

◆ forecastCurve_

QuantLib::Handle<QuantLib::YieldTermStructure> forecastCurve_
private

Definition at line 56 of file equityforwardcurvestripper.hpp.

◆ equitySpot_

QuantLib::Handle<QuantLib::Quote> equitySpot_
private

Definition at line 57 of file equityforwardcurvestripper.hpp.

◆ type_

QuantLib::Exercise::Type type_
private

Definition at line 58 of file equityforwardcurvestripper.hpp.

◆ forwards_

std::vector<QuantLib::Real> forwards_
mutableprivate

store the stripped forward rates

Definition at line 61 of file equityforwardcurvestripper.hpp.