/src/quantlib/ql/termstructures/volatility/abcdcalibration.cpp
Line | Count | Source |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2006, 2015 Ferdinando Ametrano |
5 | | Copyright (C) 2006 Cristina Duminuco |
6 | | Copyright (C) 2005, 2006 Klaus Spanderen |
7 | | Copyright (C) 2007 Giorgio Facchinetti |
8 | | Copyright (C) 2015 Paolo Mazzocchi |
9 | | |
10 | | This file is part of QuantLib, a free-software/open-source library |
11 | | for financial quantitative analysts and developers - http://quantlib.org/ |
12 | | |
13 | | QuantLib is free software: you can redistribute it and/or modify it |
14 | | under the terms of the QuantLib license. You should have received a |
15 | | copy of the license along with this program; if not, please email |
16 | | <quantlib-dev@lists.sf.net>. The license is also available online at |
17 | | <https://www.quantlib.org/license.shtml>. |
18 | | |
19 | | This program is distributed in the hope that it will be useful, but WITHOUT |
20 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
21 | | FOR A PARTICULAR PURPOSE. See the license for more details. |
22 | | */ |
23 | | |
24 | | #include <ql/math/distributions/normaldistribution.hpp> |
25 | | #include <ql/math/interpolations/abcdinterpolation.hpp> |
26 | | #include <ql/math/optimization/constraint.hpp> |
27 | | #include <ql/math/optimization/levenbergmarquardt.hpp> |
28 | | #include <ql/math/optimization/method.hpp> |
29 | | #include <ql/pricingengines/blackformula.hpp> |
30 | | #include <ql/termstructures/volatility/abcd.hpp> |
31 | | #include <ql/termstructures/volatility/abcdcalibration.hpp> |
32 | | #include <utility> |
33 | | |
34 | | namespace QuantLib { |
35 | | |
36 | | // to constrained <- from unconstrained |
37 | 0 | Array AbcdCalibration::AbcdParametersTransformation::direct(const Array& x) const { |
38 | 0 | y_[1] = x[1]; |
39 | 0 | y_[2] = std::exp(x[2]); |
40 | 0 | y_[3] = std::exp(x[3]); |
41 | 0 | y_[0] = std::exp(x[0]) - y_[3]; |
42 | 0 | return y_; |
43 | 0 | } |
44 | | |
45 | | // to unconstrained <- from constrained |
46 | 0 | Array AbcdCalibration::AbcdParametersTransformation::inverse(const Array& x) const { |
47 | 0 | y_[1] = x[1]; |
48 | 0 | y_[2] = std::log(x[2]); |
49 | 0 | y_[3] = std::log(x[3]); |
50 | 0 | y_[0] = std::log(x[0] + x[3]); |
51 | 0 | return y_; |
52 | 0 | } |
53 | | |
54 | | // to constrained <- from unconstrained |
55 | | |
56 | | AbcdCalibration::AbcdCalibration(const std::vector<Real>& t, |
57 | | const std::vector<Real>& blackVols, |
58 | | Real a, |
59 | | Real b, |
60 | | Real c, |
61 | | Real d, |
62 | | bool aIsFixed, |
63 | | bool bIsFixed, |
64 | | bool cIsFixed, |
65 | | bool dIsFixed, |
66 | | bool vegaWeighted, |
67 | | ext::shared_ptr<EndCriteria> endCriteria, |
68 | | ext::shared_ptr<OptimizationMethod> optMethod) |
69 | 0 | : aIsFixed_(aIsFixed), bIsFixed_(bIsFixed), cIsFixed_(cIsFixed), dIsFixed_(dIsFixed), a_(a), |
70 | 0 | b_(b), c_(c), d_(d), abcdEndCriteria_(EndCriteria::None), |
71 | 0 | endCriteria_(std::move(endCriteria)), optMethod_(std::move(optMethod)), |
72 | 0 | weights_(blackVols.size(), 1.0 / blackVols.size()), vegaWeighted_(vegaWeighted), times_(t), |
73 | 0 | blackVols_(blackVols) { |
74 | |
|
75 | 0 | AbcdMathFunction::validate(a, b, c, d); |
76 | |
|
77 | 0 | QL_REQUIRE(blackVols.size()==t.size(), |
78 | 0 | "mismatch between number of times (" << t.size() << |
79 | 0 | ") and blackVols (" << blackVols.size() << ")"); |
80 | | |
81 | | // if no optimization method or endCriteria is provided, we provide one |
82 | 0 | if (!optMethod_) { |
83 | 0 | Real epsfcn = 1.0e-8; |
84 | 0 | Real xtol = 1.0e-8; |
85 | 0 | Real gtol = 1.0e-8; |
86 | 0 | bool useCostFunctionsJacobian = false; |
87 | 0 | optMethod_ = ext::shared_ptr<OptimizationMethod>(new |
88 | 0 | LevenbergMarquardt(epsfcn, xtol, gtol, useCostFunctionsJacobian)); |
89 | 0 | } |
90 | 0 | if (!endCriteria_) { |
91 | 0 | Size maxIterations = 10000; |
92 | 0 | Size maxStationaryStateIterations = 1000; |
93 | 0 | Real rootEpsilon = 1.0e-8; |
94 | 0 | Real functionEpsilon = 0.3e-4; // Why 0.3e-4 ? |
95 | 0 | Real gradientNormEpsilon = 0.3e-4; // Why 0.3e-4 ? |
96 | 0 | endCriteria_ = ext::make_shared<EndCriteria>(maxIterations, maxStationaryStateIterations, |
97 | 0 | rootEpsilon, functionEpsilon, gradientNormEpsilon); |
98 | 0 | } |
99 | 0 | } |
100 | | |
101 | 0 | void AbcdCalibration::compute() { |
102 | 0 | if (vegaWeighted_) { |
103 | 0 | Real weightsSum = 0.0; |
104 | 0 | for (Size i=0; i<times_.size() ; i++) { |
105 | 0 | Real stdDev = std::sqrt(blackVols_[i]* blackVols_[i]* times_[i]); |
106 | | // when strike==forward, the blackFormulaStdDevDerivative becomes |
107 | 0 | weights_[i] = CumulativeNormalDistribution().derivative(.5*stdDev); |
108 | 0 | weightsSum += weights_[i]; |
109 | 0 | } |
110 | | // weight normalization |
111 | 0 | for (Size i=0; i<times_.size() ; i++) { |
112 | 0 | weights_[i] /= weightsSum; |
113 | 0 | } |
114 | 0 | } |
115 | | |
116 | | // there is nothing to optimize |
117 | 0 | if (aIsFixed_ && bIsFixed_ && cIsFixed_ && dIsFixed_) { |
118 | 0 | abcdEndCriteria_ = EndCriteria::None; |
119 | | //error_ = interpolationError(); |
120 | | //maxError_ = interpolationMaxError(); |
121 | 0 | return; |
122 | 0 | } else { |
123 | |
|
124 | 0 | AbcdError costFunction(this); |
125 | 0 | transformation_ = ext::shared_ptr<ParametersTransformation>(new |
126 | 0 | AbcdParametersTransformation); |
127 | |
|
128 | 0 | Array guess(4); |
129 | 0 | guess[0] = a_; |
130 | 0 | guess[1] = b_; |
131 | 0 | guess[2] = c_; |
132 | 0 | guess[3] = d_; |
133 | |
|
134 | 0 | std::vector<bool> parameterAreFixed(4); |
135 | 0 | parameterAreFixed[0] = aIsFixed_; |
136 | 0 | parameterAreFixed[1] = bIsFixed_; |
137 | 0 | parameterAreFixed[2] = cIsFixed_; |
138 | 0 | parameterAreFixed[3] = dIsFixed_; |
139 | |
|
140 | 0 | Array inversedTransformatedGuess(transformation_->inverse(guess)); |
141 | |
|
142 | 0 | ProjectedCostFunction projectedAbcdCostFunction(costFunction, |
143 | 0 | inversedTransformatedGuess, parameterAreFixed); |
144 | |
|
145 | 0 | Array projectedGuess |
146 | 0 | (projectedAbcdCostFunction.project(inversedTransformatedGuess)); |
147 | |
|
148 | 0 | NoConstraint constraint; |
149 | 0 | Problem problem(projectedAbcdCostFunction, constraint, projectedGuess); |
150 | 0 | abcdEndCriteria_ = optMethod_->minimize(problem, *endCriteria_); |
151 | 0 | Array projectedResult(problem.currentValue()); |
152 | 0 | Array transfResult(projectedAbcdCostFunction.include(projectedResult)); |
153 | |
|
154 | 0 | Array result = transformation_->direct(transfResult); |
155 | 0 | AbcdMathFunction::validate(a_, b_, c_, d_); |
156 | 0 | a_ = result[0]; |
157 | 0 | b_ = result[1]; |
158 | 0 | c_ = result[2]; |
159 | 0 | d_ = result[3]; |
160 | |
|
161 | 0 | } |
162 | 0 | } |
163 | | |
164 | 0 | Real AbcdCalibration::value(Real x) const { |
165 | 0 | return abcdBlackVolatility(x,a_,b_,c_,d_); |
166 | 0 | } |
167 | | |
168 | | std::vector<Real> AbcdCalibration::k(const std::vector<Real>& t, |
169 | 0 | const std::vector<Real>& blackVols) const { |
170 | 0 | QL_REQUIRE(blackVols.size()==t.size(), |
171 | 0 | "mismatch between number of times (" << t.size() << |
172 | 0 | ") and blackVols (" << blackVols.size() << ")"); |
173 | 0 | std::vector<Real> k(t.size()); |
174 | 0 | for (Size i=0; i<t.size() ; i++) { |
175 | 0 | k[i]=blackVols[i]/value(t[i]); |
176 | 0 | } |
177 | 0 | return k; |
178 | 0 | } |
179 | | |
180 | 0 | Real AbcdCalibration::error() const { |
181 | 0 | Size n = times_.size(); |
182 | 0 | Real error, squaredError = 0.0; |
183 | 0 | for (Size i=0; i<times_.size() ; i++) { |
184 | 0 | error = (value(times_[i]) - blackVols_[i]); |
185 | 0 | squaredError += error * error * weights_[i]; |
186 | 0 | } |
187 | 0 | return std::sqrt(n*squaredError/(n-1)); |
188 | 0 | } |
189 | | |
190 | 0 | Real AbcdCalibration::maxError() const { |
191 | 0 | Real error, maxError = QL_MIN_REAL; |
192 | 0 | for (Size i=0; i<times_.size() ; i++) { |
193 | 0 | error = std::fabs(value(times_[i]) - blackVols_[i]); |
194 | 0 | maxError = std::max(maxError, error); |
195 | 0 | } |
196 | 0 | return maxError; |
197 | 0 | } |
198 | | |
199 | | // calculate weighted differences |
200 | 0 | Array AbcdCalibration::errors() const { |
201 | 0 | Array results(times_.size()); |
202 | 0 | for (Size i=0; i<times_.size() ; i++) { |
203 | 0 | results[i] = (value(times_[i]) - blackVols_[i])* std::sqrt(weights_[i]); |
204 | 0 | } |
205 | 0 | return results; |
206 | 0 | } |
207 | | |
208 | 0 | EndCriteria::Type AbcdCalibration::endCriteria() const{ |
209 | 0 | return abcdEndCriteria_; |
210 | 0 | } |
211 | | |
212 | | } |