QuantLib: a free/open-source library for quantitative finance
Fully annotated sources - version 1.32
Loading...
Searching...
No Matches
vannavolgadoublebarrierengine.hpp
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2013 Yue Tian
5
6 This file is part of QuantLib, a free-software/open-source library
7 for financial quantitative analysts and developers - http://quantlib.org/
8
9 QuantLib is free software: you can redistribute it and/or modify it
10 under the terms of the QuantLib license. You should have received a
11 copy of the license along with this program; if not, please email
12 <quantlib-dev@lists.sf.net>. The license is also available online at
13 <http://quantlib.org/license.shtml>.
14
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the license for more details.
18*/
19
24#ifndef quantlib_vanna_volga_double_barrier_engine_hpp
25#define quantlib_vanna_volga_double_barrier_engine_hpp
26
27#include <ql/instruments/doublebarrieroption.hpp>
28#include <ql/experimental/barrieroption/vannavolgainterpolation.hpp>
29#include <ql/experimental/fx/blackdeltacalculator.hpp>
30#include <ql/experimental/fx/deltavolquote.hpp>
31#include <ql/math/matrix.hpp>
32#include <ql/pricingengines/barrier/analyticbarrierengine.hpp>
33#include <ql/pricingengines/blackformula.hpp>
34#include <ql/processes/blackscholesprocess.hpp>
35#include <ql/quotes/simplequote.hpp>
36#include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp>
37#include <ql/time/calendars/nullcalendar.hpp>
38#include <utility>
39
40namespace QuantLib {
41
43
47 template <class DoubleBarrierEngine>
49 : public GenericEngine<DoubleBarrierOption::arguments,
50 DoubleBarrierOption::results> {
51 public:
52 // Constructor
54 Handle<DeltaVolQuote> vol25Put,
55 Handle<DeltaVolQuote> vol25Call,
56 Handle<Quote> spotFX,
59 const bool adaptVanDelta = false,
60 const Real bsPriceWithSmile = 0.0,
61 int series = 5)
63 atmVol_(std::move(atmVol)), vol25Put_(std::move(vol25Put)),
64 vol25Call_(std::move(vol25Call)), T_(atmVol_->maturity()), spotFX_(std::move(spotFX)),
65 domesticTS_(std::move(domesticTS)), foreignTS_(std::move(foreignTS)),
66 adaptVanDelta_(adaptVanDelta), bsPriceWithSmile_(bsPriceWithSmile), series_(series) {
67
68 QL_REQUIRE(vol25Put_->delta() == -0.25,
69 "25 delta put is required by vanna volga method");
70 QL_REQUIRE(vol25Call_->delta() == 0.25,
71 "25 delta call is required by vanna volga method");
72
73 QL_REQUIRE(vol25Put_->maturity() == vol25Call_->maturity() &&
74 vol25Put_->maturity() == atmVol_->maturity(),
75 "Maturity of 3 vols are not the same");
76
77 QL_REQUIRE(!domesticTS_.empty(), "domestic yield curve is not defined");
78 QL_REQUIRE(!foreignTS_.empty(), "foreign yield curve is not defined");
79
86 }
87
88 void calculate() const override {
89
90 using std::sqrt;
91
92 const Real sigmaShift_vega = 0.001;
93 const Real sigmaShift_volga = 0.0001;
94 const Real spotShift_delta = 0.0001 * spotFX_->value();
95 const Real sigmaShift_vanna = 0.0001;
96
99 "Only same type barrier supported");
100
101 Handle<Quote> x0Quote( // used for shift
102 ext::make_shared<SimpleQuote>(spotFX_->value()));
103 Handle<Quote> atmVolQuote( // used for shift
104 ext::make_shared<SimpleQuote>(atmVol_->value()));
105
106 ext::shared_ptr<BlackVolTermStructure> blackVolTS =
107 ext::make_shared<BlackConstantVol>(Settings::instance().evaluationDate(),
108 NullCalendar(), atmVolQuote,
110 ext::shared_ptr<BlackScholesMertonProcess> stochProcess =
111 ext::make_shared<BlackScholesMertonProcess>(
112 x0Quote, foreignTS_, domesticTS_,
114
115 ext::shared_ptr<PricingEngine> engineBS =
116 ext::make_shared<DoubleBarrierEngine>(stochProcess, series_);
117
118 BlackDeltaCalculator blackDeltaCalculatorAtm(
119 Option::Call, atmVol_->deltaType(), x0Quote->value(),
120 domesticTS_->discount(T_), foreignTS_->discount(T_),
121 atmVol_->value() * sqrt(T_));
122 Real atmStrike = blackDeltaCalculatorAtm.atmStrike(atmVol_->atmType());
123
124 Real call25Vol = vol25Call_->value();
125 Real put25Vol = vol25Put_->value();
126 BlackDeltaCalculator blackDeltaCalculatorPut25(
127 Option::Put, vol25Put_->deltaType(), x0Quote->value(),
128 domesticTS_->discount(T_), foreignTS_->discount(T_), put25Vol * sqrt(T_));
129 Real put25Strike = blackDeltaCalculatorPut25.strikeFromDelta(-0.25);
130 BlackDeltaCalculator blackDeltaCalculatorCall25(
131 Option::Call, vol25Call_->deltaType(), x0Quote->value(),
132 domesticTS_->discount(T_), foreignTS_->discount(T_), call25Vol * sqrt(T_));
133 Real call25Strike = blackDeltaCalculatorCall25.strikeFromDelta(0.25);
134
135 // here use vanna volga interpolated smile to price vanilla
136 std::vector<Real> strikes;
137 std::vector<Real> vols;
138 strikes.push_back(put25Strike);
139 vols.push_back(put25Vol);
140 strikes.push_back(atmStrike);
141 vols.push_back(atmVol_->value());
142 strikes.push_back(call25Strike);
143 vols.push_back(call25Vol);
144 VannaVolga vannaVolga(x0Quote->value(), foreignTS_->discount(T_),
145 foreignTS_->discount(T_), T_);
146 Interpolation interpolation =
147 vannaVolga.interpolate(strikes.begin(), strikes.end(), vols.begin());
148 interpolation.enableExtrapolation();
149 const ext::shared_ptr<StrikedTypePayoff> payoff =
150 ext::dynamic_pointer_cast<StrikedTypePayoff>(arguments_.payoff);
151 Real strikeVol = interpolation(payoff->strike());
152 // vanilla option price
153 Real vanillaOption = blackFormula(payoff->optionType(), payoff->strike(),
154 x0Quote->value() * foreignTS_->discount(T_) /
155 domesticTS_->discount(T_),
156 strikeVol * sqrt(T_), domesticTS_->discount(T_));
157
158 // already out
159 if ((x0Quote->value() > arguments_.barrier_hi ||
160 x0Quote->value() < arguments_.barrier_lo) &&
162 results_.value = 0.0;
163 results_.additionalResults["VanillaPrice"] =
164 adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption;
165 results_.additionalResults["BarrierInPrice"] =
166 adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption;
167 results_.additionalResults["BarrierOutPrice"] = Real(0.0);
168 }
169 // already in
170 else if ((x0Quote->value() > arguments_.barrier_hi ||
171 x0Quote->value() < arguments_.barrier_lo) &&
174 results_.additionalResults["VanillaPrice"] =
175 adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption;
176 results_.additionalResults["BarrierInPrice"] =
177 adaptVanDelta_ ? bsPriceWithSmile_ : vanillaOption;
178 results_.additionalResults["BarrierOutPrice"] = Real(0.0);
179 } else {
180
181 // set up BS barrier option pricing
182 // only calculate out barrier option price
183 // in barrier price = vanilla - out barrier
184 ext::shared_ptr<StrikedTypePayoff> payoff =
185 ext::static_pointer_cast<StrikedTypePayoff>(arguments_.payoff);
186 DoubleBarrierOption doubleBarrierOption(
188 arguments_.rebate, payoff, arguments_.exercise);
189
190 doubleBarrierOption.setPricingEngine(engineBS);
191
192 // BS price
193 Real priceBS = doubleBarrierOption.NPV();
194
195 Real priceAtmCallBS = blackFormula(
196 Option::Call, atmStrike,
197 x0Quote->value() * foreignTS_->discount(T_) / domesticTS_->discount(T_),
198 atmVol_->value() * sqrt(T_), domesticTS_->discount(T_));
199 Real price25CallBS = blackFormula(
200 Option::Call, call25Strike,
201 x0Quote->value() * foreignTS_->discount(T_) / domesticTS_->discount(T_),
202 atmVol_->value() * sqrt(T_), domesticTS_->discount(T_));
203 Real price25PutBS = blackFormula(
204 Option::Put, put25Strike,
205 x0Quote->value() * foreignTS_->discount(T_) / domesticTS_->discount(T_),
206 atmVol_->value() * sqrt(T_), domesticTS_->discount(T_));
207
208 // market price
209 Real priceAtmCallMkt = blackFormula(
210 Option::Call, atmStrike,
211 x0Quote->value() * foreignTS_->discount(T_) / domesticTS_->discount(T_),
212 atmVol_->value() * sqrt(T_), domesticTS_->discount(T_));
213 Real price25CallMkt = blackFormula(
214 Option::Call, call25Strike,
215 x0Quote->value() * foreignTS_->discount(T_) / domesticTS_->discount(T_),
216 call25Vol * sqrt(T_), domesticTS_->discount(T_));
217 Real price25PutMkt = blackFormula(
218 Option::Put, put25Strike,
219 x0Quote->value() * foreignTS_->discount(T_) / domesticTS_->discount(T_),
220 put25Vol * sqrt(T_), domesticTS_->discount(T_));
221
222 // Analytical Black Scholes formula
224 Real d1atm = (std::log(x0Quote->value() * foreignTS_->discount(T_) /
225 domesticTS_->discount(T_) / atmStrike) +
226 0.5 * std::pow(atmVolQuote->value(), 2.0) * T_) /
227 (atmVolQuote->value() * sqrt(T_));
228 Real vegaAtm_Analytical =
229 x0Quote->value() * norm(d1atm) * sqrt(T_) * foreignTS_->discount(T_);
230 Real vannaAtm_Analytical = vegaAtm_Analytical / x0Quote->value() *
231 (1.0 - d1atm / (atmVolQuote->value() * sqrt(T_)));
232 Real volgaAtm_Analytical = vegaAtm_Analytical * d1atm *
233 (d1atm - atmVolQuote->value() * sqrt(T_)) /
234 atmVolQuote->value();
235
236 Real d125call = (std::log(x0Quote->value() * foreignTS_->discount(T_) /
237 domesticTS_->discount(T_) / call25Strike) +
238 0.5 * std::pow(atmVolQuote->value(), 2.0) * T_) /
239 (atmVolQuote->value() * sqrt(T_));
240 Real vega25Call_Analytical =
241 x0Quote->value() * norm(d125call) * sqrt(T_) * foreignTS_->discount(T_);
242 Real vanna25Call_Analytical =
243 vega25Call_Analytical / x0Quote->value() *
244 (1.0 - d125call / (atmVolQuote->value() * sqrt(T_)));
245 Real volga25Call_Analytical = vega25Call_Analytical * d125call *
246 (d125call - atmVolQuote->value() * sqrt(T_)) /
247 atmVolQuote->value();
248
249 Real d125Put = (std::log(x0Quote->value() * foreignTS_->discount(T_) /
250 domesticTS_->discount(T_) / put25Strike) +
251 0.5 * std::pow(atmVolQuote->value(), 2.0) * T_) /
252 (atmVolQuote->value() * sqrt(T_));
253 Real vega25Put_Analytical =
254 x0Quote->value() * norm(d125Put) * sqrt(T_) * foreignTS_->discount(T_);
255 Real vanna25Put_Analytical =
256 vega25Put_Analytical / x0Quote->value() *
257 (1.0 - d125Put / (atmVolQuote->value() * sqrt(T_)));
258 Real volga25Put_Analytical = vega25Put_Analytical * d125Put *
259 (d125Put - atmVolQuote->value() * sqrt(T_)) /
260 atmVolQuote->value();
261
262
263 // BS vega
264 ext::static_pointer_cast<SimpleQuote>(atmVolQuote.currentLink())
265 ->setValue(atmVolQuote->value() + sigmaShift_vega);
266 doubleBarrierOption.recalculate();
267 Real vegaBarBS = (doubleBarrierOption.NPV() - priceBS) / sigmaShift_vega;
268 ext::static_pointer_cast<SimpleQuote>(atmVolQuote.currentLink())
269 ->setValue(atmVolQuote->value() - sigmaShift_vega); // setback
270
271 // BS volga
272
273 // vegaBar2
274 // base NPV
275 ext::static_pointer_cast<SimpleQuote>(atmVolQuote.currentLink())
276 ->setValue(atmVolQuote->value() + sigmaShift_volga);
277 doubleBarrierOption.recalculate();
278 Real priceBS2 = doubleBarrierOption.NPV();
279
280 // shifted npv
281 ext::static_pointer_cast<SimpleQuote>(atmVolQuote.currentLink())
282 ->setValue(atmVolQuote->value() + sigmaShift_vega);
283 doubleBarrierOption.recalculate();
284 Real vegaBarBS2 = (doubleBarrierOption.NPV() - priceBS2) / sigmaShift_vega;
285 Real volgaBarBS = (vegaBarBS2 - vegaBarBS) / sigmaShift_volga;
286 ext::static_pointer_cast<SimpleQuote>(atmVolQuote.currentLink())
287 ->setValue(atmVolQuote->value() - sigmaShift_volga -
288 sigmaShift_vega); // setback
289
290 // BS Delta
291 // base delta
292 ext::static_pointer_cast<SimpleQuote>(x0Quote.currentLink())
293 ->setValue(x0Quote->value() + spotShift_delta); // shift forth
294 doubleBarrierOption.recalculate();
295 Real priceBS_delta1 = doubleBarrierOption.NPV();
296
297 ext::static_pointer_cast<SimpleQuote>(x0Quote.currentLink())
298 ->setValue(x0Quote->value() - 2 * spotShift_delta); // shift back
299 doubleBarrierOption.recalculate();
300 Real priceBS_delta2 = doubleBarrierOption.NPV();
301
302 ext::static_pointer_cast<SimpleQuote>(x0Quote.currentLink())
303 ->setValue(x0Quote->value() + spotShift_delta); // set back
304 Real deltaBar1 = (priceBS_delta1 - priceBS_delta2) / (2.0 * spotShift_delta);
305
306 // shifted vanna
307 ext::static_pointer_cast<SimpleQuote>(atmVolQuote.currentLink())
308 ->setValue(atmVolQuote->value() + sigmaShift_vanna); // shift sigma
309 // shifted delta
310 ext::static_pointer_cast<SimpleQuote>(x0Quote.currentLink())
311 ->setValue(x0Quote->value() + spotShift_delta); // shift forth
312 doubleBarrierOption.recalculate();
313 priceBS_delta1 = doubleBarrierOption.NPV();
314
315 ext::static_pointer_cast<SimpleQuote>(x0Quote.currentLink())
316 ->setValue(x0Quote->value() - 2 * spotShift_delta); // shift back
317 doubleBarrierOption.recalculate();
318 priceBS_delta2 = doubleBarrierOption.NPV();
319
320 ext::static_pointer_cast<SimpleQuote>(x0Quote.currentLink())
321 ->setValue(x0Quote->value() + spotShift_delta); // set back
322 Real deltaBar2 = (priceBS_delta1 - priceBS_delta2) / (2.0 * spotShift_delta);
323
324 Real vannaBarBS = (deltaBar2 - deltaBar1) / sigmaShift_vanna;
325
326 ext::static_pointer_cast<SimpleQuote>(atmVolQuote.currentLink())
327 ->setValue(atmVolQuote->value() - sigmaShift_vanna); // set back
328
329 // Matrix
330 Matrix A(3, 3, 0.0);
331
332 // analytical
333 A[0][0] = vegaAtm_Analytical;
334 A[0][1] = vega25Call_Analytical;
335 A[0][2] = vega25Put_Analytical;
336 A[1][0] = vannaAtm_Analytical;
337 A[1][1] = vanna25Call_Analytical;
338 A[1][2] = vanna25Put_Analytical;
339 A[2][0] = volgaAtm_Analytical;
340 A[2][1] = volga25Call_Analytical;
341 A[2][2] = volga25Put_Analytical;
342
343 Array b(3, 0.0);
344 b[0] = vegaBarBS;
345 b[1] = vannaBarBS;
346 b[2] = volgaBarBS;
347 Array q = inverse(A) * b;
348
351 Real theta_tilt_minus = ((domesticTS_->zeroRate(T_, Continuous).rate() -
352 foreignTS_->zeroRate(T_, Continuous).rate()) /
353 atmVol_->value() -
354 atmVol_->value() / 2.0) *
355 std::sqrt(T_);
356 Real h =
357 1.0 / atmVol_->value() * std::log(H / x0Quote->value()) / std::sqrt(T_);
358 Real l =
359 1.0 / atmVol_->value() * std::log(L / x0Quote->value()) / std::sqrt(T_);
361
362 Real doubleNoTouch = 0.0;
363 for (int j = -series_; j < series_; j++) {
364 Real e_minus = 2 * j * (h - l) - theta_tilt_minus;
365 doubleNoTouch +=
366 std::exp(-2.0 * j * theta_tilt_minus * (h - l)) *
367 (cnd(h + e_minus) - cnd(l + e_minus)) -
368 std::exp(-2.0 * j * theta_tilt_minus * (h - l) +
369 2.0 * theta_tilt_minus * h) *
370 (cnd(h - 2.0 * h + e_minus) - cnd(l - 2.0 * h + e_minus));
371 }
372
373 Real p_survival = doubleNoTouch;
374
375 Real lambda = p_survival;
376 Real adjust = q[0] * (priceAtmCallMkt - priceAtmCallBS) +
377 q[1] * (price25CallMkt - price25CallBS) +
378 q[2] * (price25PutMkt - price25PutBS);
379 Real outPrice = priceBS + lambda * adjust; //
380 Real inPrice;
381
382 // adapt Vanilla delta
383 if (adaptVanDelta_) {
384 outPrice += lambda * (bsPriceWithSmile_ - vanillaOption);
385 // capfloored by (0, vanilla)
386 outPrice = std::max(0.0, std::min(bsPriceWithSmile_, outPrice));
387 inPrice = bsPriceWithSmile_ - outPrice;
388 } else {
389 // capfloored by (0, vanilla)
390 outPrice = std::max(0.0, std::min(vanillaOption, outPrice));
391 inPrice = vanillaOption - outPrice;
392 }
393
395 results_.value = outPrice;
396 else
397 results_.value = inPrice;
398 results_.additionalResults["VanillaPrice"] = vanillaOption;
399 results_.additionalResults["BarrierInPrice"] = inPrice;
400 results_.additionalResults["BarrierOutPrice"] = outPrice;
401 results_.additionalResults["lambda"] = lambda;
402 }
403 }
404
405
406 private:
410 const Time T_;
414 const bool adaptVanDelta_;
416 const int series_;
417 };
418
419
420}
421
422#endif
Actual/365 (Fixed) day count convention.
1-D array used in linear algebra.
Definition: array.hpp:52
Black delta calculator class.
Real atmStrike(DeltaVolQuote::AtmType atmT) const
Real strikeFromDelta(Real delta) const
Cumulative normal distribution function.
Double Barrier option on a single asset.
void enableExtrapolation(bool b=true)
enable extrapolation in subsequent calls
template base class for option pricing engines
Shared handle to an observable.
Definition: handle.hpp:41
const ext::shared_ptr< T > & currentLink() const
dereferencing
Definition: handle.hpp:148
std::map< std::string, ext::any > additionalResults
Definition: instrument.hpp:123
Real NPV() const
returns the net present value of the instrument.
Definition: instrument.hpp:167
void setPricingEngine(const ext::shared_ptr< PricingEngine > &)
set the pricing engine to be used.
Definition: instrument.cpp:35
base class for 1-D interpolations.
Matrix used in linear algebra.
Definition: matrix.hpp:41
Normal distribution function.
Calendar for reproducing theoretical calculations.
std::pair< iterator, bool > registerWith(const ext::shared_ptr< Observable > &)
Definition: observable.hpp:228
static Settings & instance()
access to the unique instance
Definition: singleton.hpp:104
Vanna Volga double-barrier option engine.
VannaVolgaDoubleBarrierEngine(Handle< DeltaVolQuote > atmVol, Handle< DeltaVolQuote > vol25Put, Handle< DeltaVolQuote > vol25Call, Handle< Quote > spotFX, Handle< YieldTermStructure > domesticTS, Handle< YieldTermStructure > foreignTS, const bool adaptVanDelta=false, const Real bsPriceWithSmile=0.0, int series=5)
VannaVolga-interpolation factory and traits
Interpolation interpolate(const I1 &xBegin, const I1 &xEnd, const I2 &yBegin) const
Real Time
continuous quantity with 1-year units
Definition: types.hpp:62
QL_REAL Real
real number
Definition: types.hpp:50
Definition: any.hpp:35
Real blackFormula(Option::Type optionType, Real strike, Real forward, Real stdDev, Real discount, Real displacement)
Matrix inverse(const Matrix &m)
Definition: matrix.cpp:44
STL namespace.