/src/quantlib/ql/termstructures/volatility/equityfx/localvolsurface.cpp
Line | Count | Source |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2003 Ferdinando Ametrano |
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 | | <https://www.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 | | |
20 | | #include <ql/quotes/simplequote.hpp> |
21 | | #include <ql/termstructures/volatility/equityfx/blackvoltermstructure.hpp> |
22 | | #include <ql/termstructures/volatility/equityfx/localvolsurface.hpp> |
23 | | #include <ql/termstructures/yieldtermstructure.hpp> |
24 | | #include <utility> |
25 | | |
26 | | namespace QuantLib { |
27 | | |
28 | | |
29 | 0 | const Date& LocalVolSurface::referenceDate() const { |
30 | 0 | return blackTS_->referenceDate(); |
31 | 0 | } |
32 | | |
33 | 0 | DayCounter LocalVolSurface::dayCounter() const { |
34 | 0 | return blackTS_->dayCounter(); |
35 | 0 | } |
36 | | |
37 | 0 | Date LocalVolSurface::maxDate() const { |
38 | 0 | return blackTS_->maxDate(); |
39 | 0 | } |
40 | | |
41 | 0 | Real LocalVolSurface::minStrike() const { |
42 | 0 | return blackTS_->minStrike(); |
43 | 0 | } |
44 | | |
45 | 0 | Real LocalVolSurface::maxStrike() const { |
46 | 0 | return blackTS_->maxStrike(); |
47 | 0 | } |
48 | | |
49 | | LocalVolSurface::LocalVolSurface(const Handle<BlackVolTermStructure>& blackTS, |
50 | | Handle<YieldTermStructure> riskFreeTS, |
51 | | Handle<YieldTermStructure> dividendTS, |
52 | | Handle<Quote> underlying) |
53 | 0 | : LocalVolTermStructure(blackTS->businessDayConvention(), blackTS->dayCounter()), |
54 | 0 | blackTS_(blackTS), riskFreeTS_(std::move(riskFreeTS)), dividendTS_(std::move(dividendTS)), |
55 | 0 | underlying_(std::move(underlying)) { |
56 | 0 | registerWith(blackTS_); |
57 | 0 | registerWith(riskFreeTS_); |
58 | 0 | registerWith(dividendTS_); |
59 | 0 | registerWith(underlying_); |
60 | 0 | } Unexecuted instantiation: QuantLib::LocalVolSurface::LocalVolSurface(QuantLib::Handle<QuantLib::BlackVolTermStructure> const&, QuantLib::Handle<QuantLib::YieldTermStructure>, QuantLib::Handle<QuantLib::YieldTermStructure>, QuantLib::Handle<QuantLib::Quote>) Unexecuted instantiation: QuantLib::LocalVolSurface::LocalVolSurface(QuantLib::Handle<QuantLib::BlackVolTermStructure> const&, QuantLib::Handle<QuantLib::YieldTermStructure>, QuantLib::Handle<QuantLib::YieldTermStructure>, QuantLib::Handle<QuantLib::Quote>) |
61 | | |
62 | | LocalVolSurface::LocalVolSurface(const Handle<BlackVolTermStructure>& blackTS, |
63 | | Handle<YieldTermStructure> riskFreeTS, |
64 | | Handle<YieldTermStructure> dividendTS, |
65 | | Real underlying) |
66 | 0 | : LocalVolTermStructure(blackTS->businessDayConvention(), blackTS->dayCounter()), |
67 | 0 | blackTS_(blackTS), riskFreeTS_(std::move(riskFreeTS)), dividendTS_(std::move(dividendTS)), |
68 | 0 | underlying_(ext::make_shared<SimpleQuote>(underlying)) { |
69 | 0 | registerWith(blackTS_); |
70 | 0 | registerWith(riskFreeTS_); |
71 | 0 | registerWith(dividendTS_); |
72 | 0 | } Unexecuted instantiation: QuantLib::LocalVolSurface::LocalVolSurface(QuantLib::Handle<QuantLib::BlackVolTermStructure> const&, QuantLib::Handle<QuantLib::YieldTermStructure>, QuantLib::Handle<QuantLib::YieldTermStructure>, double) Unexecuted instantiation: QuantLib::LocalVolSurface::LocalVolSurface(QuantLib::Handle<QuantLib::BlackVolTermStructure> const&, QuantLib::Handle<QuantLib::YieldTermStructure>, QuantLib::Handle<QuantLib::YieldTermStructure>, double) |
73 | | |
74 | 0 | void LocalVolSurface::accept(AcyclicVisitor& v) { |
75 | 0 | auto* v1 = dynamic_cast<Visitor<LocalVolSurface>*>(&v); |
76 | 0 | if (v1 != nullptr) |
77 | 0 | v1->visit(*this); |
78 | 0 | else |
79 | 0 | LocalVolTermStructure::accept(v); |
80 | 0 | } |
81 | | |
82 | | Volatility LocalVolSurface::localVolImpl(Time t, Real underlyingLevel) |
83 | 0 | const { |
84 | |
|
85 | 0 | DiscountFactor dr = riskFreeTS_->discount(t, true); |
86 | 0 | DiscountFactor dq = dividendTS_->discount(t, true); |
87 | 0 | Real forwardValue = underlying_->value()*dq/dr; |
88 | | |
89 | | // strike derivatives |
90 | 0 | Real strike, y, dy, strikep, strikem; |
91 | 0 | Real w, wp, wm, dwdy, d2wdy2; |
92 | 0 | strike = underlyingLevel; |
93 | 0 | y = std::log(strike/forwardValue); |
94 | 0 | dy = ((std::fabs(y) > 0.001) ? Real(y*0.0001) : 0.000001); |
95 | 0 | strikep=strike*std::exp(dy); |
96 | 0 | strikem=strike/std::exp(dy); |
97 | 0 | w = blackTS_->blackVariance(t, strike, true); |
98 | 0 | wp = blackTS_->blackVariance(t, strikep, true); |
99 | 0 | wm = blackTS_->blackVariance(t, strikem, true); |
100 | 0 | dwdy = (wp-wm)/(2.0*dy); |
101 | 0 | d2wdy2 = (wp-2.0*w+wm)/(dy*dy); |
102 | | |
103 | | // time derivative |
104 | 0 | Real dt, wpt, wmt, dwdt; |
105 | 0 | if (t==0.0) { |
106 | 0 | dt = 0.0001; |
107 | 0 | DiscountFactor drpt = riskFreeTS_->discount(t+dt, true); |
108 | 0 | DiscountFactor dqpt = dividendTS_->discount(t+dt, true); |
109 | 0 | Real strikept = strike*dr*dqpt/(drpt*dq); |
110 | | |
111 | 0 | wpt = blackTS_->blackVariance(t+dt, strikept, true); |
112 | 0 | QL_ENSURE(wpt>=w, |
113 | 0 | "decreasing variance at strike " << strike |
114 | 0 | << " between time " << t << " and time " << t+dt); |
115 | 0 | dwdt = (wpt-w)/dt; |
116 | 0 | } else { |
117 | 0 | dt = std::min<Time>(0.0001, t/2.0); |
118 | 0 | DiscountFactor drpt = riskFreeTS_->discount(t+dt, true); |
119 | 0 | DiscountFactor drmt = riskFreeTS_->discount(t-dt, true); |
120 | 0 | DiscountFactor dqpt = dividendTS_->discount(t+dt, true); |
121 | 0 | DiscountFactor dqmt = dividendTS_->discount(t-dt, true); |
122 | | |
123 | 0 | Real strikept = strike*dr*dqpt/(drpt*dq); |
124 | 0 | Real strikemt = strike*dr*dqmt/(drmt*dq); |
125 | | |
126 | 0 | wpt = blackTS_->blackVariance(t+dt, strikept, true); |
127 | 0 | wmt = blackTS_->blackVariance(t-dt, strikemt, true); |
128 | |
|
129 | 0 | QL_ENSURE(wpt>=w, |
130 | 0 | "decreasing variance at strike " << strike |
131 | 0 | << " between time " << t << " and time " << t+dt); |
132 | 0 | QL_ENSURE(w>=wmt, |
133 | 0 | "decreasing variance at strike " << strike |
134 | 0 | << " between time " << t-dt << " and time " << t); |
135 | | |
136 | 0 | dwdt = (wpt-wmt)/(2.0*dt); |
137 | 0 | } |
138 | | |
139 | 0 | if (dwdy==0.0 && d2wdy2==0.0) { // avoid /w where w might be 0.0 |
140 | 0 | return std::sqrt(dwdt); |
141 | 0 | } else { |
142 | 0 | Real den1 = 1.0 - y/w*dwdy; |
143 | 0 | Real den2 = 0.25*(-0.25 - 1.0/w + y*y/w/w)*dwdy*dwdy; |
144 | 0 | Real den3 = 0.5*d2wdy2; |
145 | 0 | Real den = den1+den2+den3; |
146 | 0 | Real result = dwdt / den; |
147 | |
|
148 | 0 | QL_ENSURE(result>=0.0, |
149 | 0 | "negative local vol^2 at strike " << strike |
150 | 0 | << " and time " << t |
151 | 0 | << "; the black vol surface is not smooth enough"); |
152 | | |
153 | 0 | return std::sqrt(result); |
154 | 0 | } |
155 | 0 | } |
156 | | |
157 | | } |