/src/quantlib/ql/termstructures/volatility/smilesection.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2006 Mario Pucci |
5 | | Copyright (C) 2013, 2015 Peter Caspers |
6 | | |
7 | | This file is part of QuantLib, a free-software/open-source library |
8 | | for financial quantitative analysts and developers - http://quantlib.org/ |
9 | | |
10 | | QuantLib is free software: you can redistribute it and/or modify it |
11 | | under the terms of the QuantLib license. You should have received a |
12 | | copy of the license along with this program; if not, please email |
13 | | <quantlib-dev@lists.sf.net>. The license is also available online at |
14 | | <https://www.quantlib.org/license.shtml>. |
15 | | |
16 | | This program is distributed in the hope that it will be useful, but WITHOUT |
17 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
18 | | FOR A PARTICULAR PURPOSE. See the license for more details. |
19 | | */ |
20 | | |
21 | | #include <ql/math/comparison.hpp> |
22 | | #include <ql/pricingengines/blackformula.hpp> |
23 | | #include <ql/settings.hpp> |
24 | | #include <ql/termstructures/volatility/smilesection.hpp> |
25 | | #include <utility> |
26 | | |
27 | | using std::sqrt; |
28 | | |
29 | | namespace QuantLib { |
30 | | |
31 | 0 | void SmileSection::update() { |
32 | 0 | if (isFloating_) { |
33 | 0 | referenceDate_ = Settings::instance().evaluationDate(); |
34 | 0 | initializeExerciseTime(); |
35 | 0 | } |
36 | 0 | } |
37 | | |
38 | 0 | void SmileSection::initializeExerciseTime() const { |
39 | 0 | QL_REQUIRE(exerciseDate_>=referenceDate_, |
40 | 0 | "expiry date (" << exerciseDate_ << |
41 | 0 | ") must be greater than reference date (" << |
42 | 0 | referenceDate_ << ")"); |
43 | 0 | exerciseTime_ = dc_.yearFraction(referenceDate_, exerciseDate_); |
44 | 0 | } |
45 | | |
46 | | SmileSection::SmileSection(const Date& d, |
47 | | DayCounter dc, |
48 | | const Date& referenceDate, |
49 | | const VolatilityType type, |
50 | | const Rate shift) |
51 | 0 | : exerciseDate_(d), dc_(std::move(dc)), volatilityType_(type), shift_(shift) { |
52 | 0 | isFloating_ = referenceDate==Date(); |
53 | 0 | if (isFloating_) { |
54 | 0 | registerWith(Settings::instance().evaluationDate()); |
55 | 0 | referenceDate_ = Settings::instance().evaluationDate(); |
56 | 0 | } else |
57 | 0 | referenceDate_ = referenceDate; |
58 | 0 | SmileSection::initializeExerciseTime(); |
59 | 0 | } |
60 | | |
61 | | SmileSection::SmileSection(Time exerciseTime, |
62 | | DayCounter dc, |
63 | | const VolatilityType type, |
64 | | const Rate shift) |
65 | 0 | : isFloating_(false), dc_(std::move(dc)), exerciseTime_(exerciseTime), volatilityType_(type), |
66 | 0 | shift_(shift) { |
67 | 0 | QL_REQUIRE(exerciseTime_>=0.0, |
68 | 0 | "expiry time must be positive: " << |
69 | 0 | exerciseTime_ << " not allowed"); |
70 | 0 | } |
71 | | |
72 | | Real SmileSection::optionPrice(Rate strike, |
73 | | Option::Type type, |
74 | 0 | Real discount) const { |
75 | 0 | Real atm = atmLevel(); |
76 | 0 | QL_REQUIRE(atm != Null<Real>(), |
77 | 0 | "smile section must provide atm level to compute option price"); |
78 | | // if lognormal or shifted lognormal, |
79 | | // for strike at -shift, return option price even if outside |
80 | | // minstrike, maxstrike interval |
81 | 0 | if (volatilityType() == ShiftedLognormal) |
82 | 0 | return blackFormula(type,strike,atm, std::fabs(strike+shift()) < QL_EPSILON ? |
83 | 0 | 0.2 : Real(sqrt(variance(strike))),discount,shift()); |
84 | 0 | else |
85 | 0 | return bachelierBlackFormula(type,strike,atm,sqrt(variance(strike)),discount); |
86 | 0 | } |
87 | | |
88 | | Real SmileSection::digitalOptionPrice(Rate strike, |
89 | | Option::Type type, |
90 | | Real discount, |
91 | 0 | Real gap) const { |
92 | 0 | Real m = volatilityType() == ShiftedLognormal ? Real(-shift()) : -QL_MAX_REAL; |
93 | 0 | Real kl = std::max(strike-gap/2.0,m); |
94 | 0 | Real kr = kl+gap; |
95 | 0 | return (type==Option::Call ? 1.0 : -1.0) * |
96 | 0 | (optionPrice(kl,type,discount)-optionPrice(kr,type,discount)) / gap; |
97 | 0 | } |
98 | | |
99 | 0 | Real SmileSection::density(Rate strike, Real discount, Real gap) const { |
100 | 0 | Real m = volatilityType() == ShiftedLognormal ? Real(-shift()) : -QL_MAX_REAL; |
101 | 0 | Real kl = std::max(strike-gap/2.0,m); |
102 | 0 | Real kr = kl+gap; |
103 | 0 | return (digitalOptionPrice(kl,Option::Call,discount,gap) - |
104 | 0 | digitalOptionPrice(kr,Option::Call,discount,gap)) / gap; |
105 | 0 | } |
106 | | |
107 | 0 | Real SmileSection::vega(Rate strike, Real discount) const { |
108 | 0 | Real atm = atmLevel(); |
109 | 0 | QL_REQUIRE(atm != Null<Real>(), |
110 | 0 | "smile section must provide atm level to compute option vega"); |
111 | 0 | if (volatilityType() == ShiftedLognormal) |
112 | 0 | return blackFormulaVolDerivative(strike,atmLevel(), |
113 | 0 | sqrt(variance(strike)), |
114 | 0 | exerciseTime(),discount,shift())*0.01; |
115 | 0 | else |
116 | 0 | QL_FAIL("vega for normal smilesection not yet implemented"); |
117 | 0 | } |
118 | | |
119 | | Real SmileSection::volatility(Rate strike, VolatilityType volatilityType, |
120 | 0 | Real shift) const { |
121 | 0 | if(volatilityType == volatilityType_ && close(shift,this->shift())) |
122 | 0 | return volatility(strike); |
123 | 0 | Real atm = atmLevel(); |
124 | 0 | QL_REQUIRE(atm != Null<Real>(), |
125 | 0 | "smile section must provide atm level to compute converted volatilties"); |
126 | 0 | Option::Type type = strike >= atm ? Option::Call : Option::Put; |
127 | 0 | Real premium = optionPrice(strike,type); |
128 | 0 | Real premiumAtm = optionPrice(atm,type); |
129 | 0 | if (volatilityType == ShiftedLognormal) { |
130 | 0 | try { |
131 | 0 | return blackFormulaImpliedStdDev(type, strike, atm, premium, |
132 | 0 | 1.0, shift) / |
133 | 0 | std::sqrt(exerciseTime()); |
134 | 0 | } catch(...) { |
135 | 0 | return blackFormulaImpliedStdDevChambers( |
136 | 0 | type, strike, atm, premium, premiumAtm, 1.0, shift) / |
137 | 0 | std::sqrt(exerciseTime()); |
138 | 0 | } |
139 | 0 | } else { |
140 | 0 | return bachelierBlackFormulaImpliedVol(type, strike, atm, |
141 | 0 | exerciseTime(), premium); |
142 | 0 | } |
143 | 0 | } |
144 | | } |