Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
fxvannavolgasmilesection.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2017 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
19#include <ql/math/distributions/normaldistribution.hpp>
21
22using namespace QuantLib;
23
24namespace QuantExt {
25
26VannaVolgaSmileSection::VannaVolgaSmileSection(Real spot, Real rd, Real rf, Time t, Volatility atmVol, Volatility rr,
27 Volatility bf, bool firstApprox, const DeltaVolQuote::AtmType& atmType,
28 const DeltaVolQuote::DeltaType& deltaType, const Real delta)
29 : FxSmileSection(spot, rd, rf, t), atmVol_(atmVol), rr_(rr), bf_(bf), firstApprox_(firstApprox) {
30
31 // Consistent Pricing of FX Options
32 // Castagna & Mercurio (2006)
33 // eq(4) + (5).
34 vol_c_ = atmVol + bf_ + 0.5 * rr_;
35 vol_p_ = atmVol + bf_ - 0.5 * rr_;
36
37 // infer strikes from delta and vol quote
38 try {
39 BlackDeltaCalculator a(Option::Type::Call, deltaType, spot, domesticDiscount(), foreignDiscount(),
40 sqrt(t) * atmVol);
41 k_atm_ = a.atmStrike(atmType);
42 } catch (const std::exception& e) {
43 QL_FAIL("VannaVolgaSmileSection: Error during calculating atm strike: "
44 << e.what() << " (t=" << t << ", atmVol=" << atmVol << ", bf=" << bf_ << ", rr=" << rr << ", vol_c="
45 << vol_c_ << ", vol_p=" << vol_p_ << ", atmType=" << atmType << ", deltaType=" << deltaType
46 << ", spot=" << spot << ", domDsc=" << domesticDiscount() << ", forDsc=" << foreignDiscount() << ")");
47 }
48
49 try {
50 BlackDeltaCalculator c(Option::Type::Call, deltaType, spot, domesticDiscount(), foreignDiscount(),
51 sqrt(t) * vol_c_);
52 k_c_ = c.strikeFromDelta(delta);
53 } catch (const std::exception& e) {
54 QL_FAIL("VannaVolgaSmileSection: Error during calculating call strike at delta "
55 << delta << ": " << e.what() << " (t=" << t << ", atmVol=" << atmVol << ", bf=" << bf_ << ", rr=" << rr
56 << ", vol_c=" << vol_c_ << ", vol_p=" << vol_p_ << ", deltaType=" << deltaType << ", spot=" << spot
57 << ", domDsc=" << domesticDiscount() << ", forDsc=" << foreignDiscount() << ")");
58 }
59
60 try {
61 BlackDeltaCalculator p(Option::Type::Put, deltaType, spot, domesticDiscount(), foreignDiscount(),
62 sqrt(t) * vol_p_);
63 k_p_ = p.strikeFromDelta(-delta);
64 } catch (const std::exception& e) {
65 QL_FAIL("VannaVolgaSmileSection: Error during calculating put strike at delta "
66 << delta << ": " << e.what() << " (t=" << t << ", atmVol=" << atmVol << ", bf=" << bf_ << ", rr=" << rr
67 << ", vol_c=" << vol_c_ << ", vol_p=" << vol_p_ << ", deltaType=" << deltaType << ", spot=" << spot
68 << ", domDsc=" << domesticDiscount() << ", forDsc=" << foreignDiscount() << ")");
69 }
70}
71
72Real VannaVolgaSmileSection::d1(Real x) const {
73 return (log(spot_ / x) + (rd_ - rf_ + 0.5 * atmVol_ * atmVol_) * t_) / (atmVol_ * sqrt(t_));
74}
75
76Real VannaVolgaSmileSection::d2(Real x) const {
77 return (log(spot_ / x) + (rd_ - rf_ - 0.5 * atmVol_ * atmVol_) * t_) / (atmVol_ * sqrt(t_));
78}
79
80Volatility VannaVolgaSmileSection::volatility(Real k) const {
81 QL_REQUIRE(k >= 0, "Non-positive strike (" << k << ")");
82
83 // eq(14). Note sigma = sigma_ATM here.
84 Real k1 = k_p_;
85 Real k2 = k_atm_;
86 Real k3 = k_c_;
87
88 // TODO: Cache the (constant) denominator
89 Real r1 = log(k2 / k) * log(k3 / k) / (log(k2 / k1) * log(k3 / k1));
90 Real r2 = log(k / k1) * log(k3 / k) / (log(k2 / k1) * log(k3 / k2));
91 Real r3 = log(k / k1) * log(k / k2) / (log(k3 / k1) * log(k3 / k2));
92
93 Real sigma1_k = r1 * vol_p_ + r2 * atmVol_ + r3 * vol_c_;
94 if (firstApprox_) {
95 return std::max(sigma1_k, Real(0.0001)); // for extreme ends: cannot return negative impl vols
96 }
97
98 Real D1 = sigma1_k - atmVol_;
99
100 // No middle term as sigma = sigma_atm
101 Real D2 = r1 * d1(k1) * d2(k1) * (vol_p_ - atmVol_) * (vol_p_ - atmVol_) +
102 r3 * d1(k3) * d2(k3) * (vol_c_ - atmVol_) * (vol_c_ - atmVol_);
103
104 Real d1d2k = d1(k) * d2(k);
105
106 Real tmp = atmVol_ * atmVol_ + d1d2k * (2 * atmVol_ * D1 + D2);
107 QL_REQUIRE(tmp >= 0, "VannaVolga attempting to take square root of negative number in second approximation. "
108 "Consider using first approximation in fxvol config.");
109
110 return atmVol_ + (-atmVol_ + sqrt(tmp)) / d1d2k;
111}
112
113} // namespace QuantExt
DiscountFactor domesticDiscount() const
DiscountFactor foreignDiscount() const
VannaVolgaSmileSection(Real spot, Real rd, Real rf, Time t, Volatility atmVol, Volatility rr, Volatility bf, bool firstApprox=false, const DeltaVolQuote::AtmType &atmType=DeltaVolQuote::AtmType::AtmDeltaNeutral, const DeltaVolQuote::DeltaType &deltaType=DeltaVolQuote::DeltaType::Spot, const Real delta=0.25)
Volatility volatility(Real strike) const override
FX smile section assuming a strike/volatility space using vanna volga method.
RandomVariable sqrt(RandomVariable x)
CompiledFormula log(CompiledFormula x)