/src/quantlib/ql/models/equity/hestonslvfdmmodel.cpp
Line | Count | Source |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2015 Johannes Göttker-Schnetmann |
5 | | Copyright (C) 2015 Klaus Spanderen |
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 | | |
22 | | #include <ql/models/equity/hestonslvfdmmodel.hpp> |
23 | | #include <ql/math/distributions/normaldistribution.hpp> |
24 | | #include <ql/math/integrals/discreteintegrals.hpp> |
25 | | #include <ql/math/interpolations/bilinearinterpolation.hpp> |
26 | | #include <ql/methods/finitedifferences/meshers/concentrating1dmesher.hpp> |
27 | | #include <ql/methods/finitedifferences/meshers/fdmmeshercomposite.hpp> |
28 | | #include <ql/methods/finitedifferences/meshers/predefined1dmesher.hpp> |
29 | | #include <ql/methods/finitedifferences/operators/fdmlinearoplayout.hpp> |
30 | | #include <ql/methods/finitedifferences/operators/fdmhestonfwdop.hpp> |
31 | | #include <ql/methods/finitedifferences/schemes/craigsneydscheme.hpp> |
32 | | #include <ql/methods/finitedifferences/schemes/douglasscheme.hpp> |
33 | | #include <ql/methods/finitedifferences/schemes/expliciteulerscheme.hpp> |
34 | | #include <ql/methods/finitedifferences/schemes/hundsdorferscheme.hpp> |
35 | | #include <ql/methods/finitedifferences/schemes/impliciteulerscheme.hpp> |
36 | | #include <ql/methods/finitedifferences/schemes/modifiedcraigsneydscheme.hpp> |
37 | | #include <ql/methods/finitedifferences/solvers/fdmbackwardsolver.hpp> |
38 | | #include <ql/methods/finitedifferences/utilities/fdmmesherintegral.hpp> |
39 | | #include <ql/methods/finitedifferences/utilities/localvolrndcalculator.hpp> |
40 | | #include <ql/methods/finitedifferences/utilities/squarerootprocessrndcalculator.hpp> |
41 | | #include <ql/models/equity/hestonmodel.hpp> |
42 | | #include <ql/quotes/simplequote.hpp> |
43 | | #include <ql/termstructures/volatility/equityfx/fixedlocalvolsurface.hpp> |
44 | | #include <ql/termstructures/volatility/equityfx/localvoltermstructure.hpp> |
45 | | #include <ql/timegrid.hpp> |
46 | | #include <functional> |
47 | | #include <memory> |
48 | | #include <utility> |
49 | | |
50 | | namespace QuantLib { |
51 | | |
52 | | namespace { |
53 | | ext::shared_ptr<Fdm1dMesher> varianceMesher( |
54 | | const SquareRootProcessRNDCalculator& rnd, |
55 | | Time t0, Time t1, Size vGrid, |
56 | 0 | Real v0, const HestonSLVFokkerPlanckFdmParams& params) { |
57 | |
|
58 | 0 | std::vector<std::tuple<Real, Real, bool> > cPoints; |
59 | |
|
60 | 0 | const Real v0Density = params.v0Density; |
61 | 0 | const Real upperBoundDensity = params.vUpperBoundDensity; |
62 | 0 | const Real lowerBoundDensity = params.vLowerBoundDensity; |
63 | |
|
64 | 0 | Real lowerBound = Null<Real>(), upperBound = -Null<Real>(); |
65 | |
|
66 | 0 | for (Size i=0; i <= 10; ++i) { |
67 | 0 | const Time t = t0 + i/10.0*(t1-t0); |
68 | 0 | lowerBound = std::min( |
69 | 0 | lowerBound, rnd.invcdf(params.vLowerEps, t)); |
70 | 0 | upperBound = std::max( |
71 | 0 | upperBound, rnd.invcdf(1.0-params.vUpperEps, t)); |
72 | 0 | } |
73 | |
|
74 | 0 | lowerBound = std::max(lowerBound, params.vMin); |
75 | 0 | switch (params.trafoType) { |
76 | 0 | case FdmSquareRootFwdOp::Log: |
77 | 0 | { |
78 | 0 | lowerBound = std::log(lowerBound); |
79 | 0 | upperBound = std::log(upperBound); |
80 | |
|
81 | 0 | const Real v0Center = std::log(v0); |
82 | |
|
83 | 0 | cPoints = { |
84 | 0 | std::make_tuple(lowerBound, lowerBoundDensity, false), |
85 | 0 | std::make_tuple(v0Center, v0Density, true), |
86 | 0 | std::make_tuple(upperBound, upperBoundDensity, false) |
87 | 0 | }; |
88 | |
|
89 | 0 | return ext::make_shared<Concentrating1dMesher>( |
90 | 0 | lowerBound, upperBound, vGrid, cPoints, 1e-8); |
91 | 0 | } |
92 | 0 | case FdmSquareRootFwdOp::Plain: |
93 | 0 | { |
94 | 0 | const Real v0Center = v0; |
95 | |
|
96 | 0 | cPoints = { |
97 | 0 | std::make_tuple(lowerBound, lowerBoundDensity, false), |
98 | 0 | std::make_tuple(v0Center, v0Density, true), |
99 | 0 | std::make_tuple(upperBound, upperBoundDensity, false) |
100 | 0 | }; |
101 | |
|
102 | 0 | return ext::make_shared<Concentrating1dMesher>( |
103 | 0 | lowerBound, upperBound, vGrid, cPoints, 1e-8); |
104 | 0 | } |
105 | 0 | case FdmSquareRootFwdOp::Power: |
106 | 0 | { |
107 | 0 | const Real v0Center = v0; |
108 | |
|
109 | 0 | cPoints = { |
110 | 0 | std::make_tuple(lowerBound, lowerBoundDensity, false), |
111 | 0 | std::make_tuple(v0Center, v0Density, true), |
112 | 0 | std::make_tuple(upperBound, upperBoundDensity, false) |
113 | 0 | }; |
114 | |
|
115 | 0 | return ext::make_shared<Concentrating1dMesher>( |
116 | 0 | lowerBound, upperBound, vGrid, cPoints, 1e-8); |
117 | 0 | } |
118 | 0 | default: |
119 | 0 | QL_FAIL("transformation type is not implemented"); |
120 | 0 | } |
121 | 0 | } |
122 | | |
123 | | Real integratePDF(const Array& p, |
124 | | const ext::shared_ptr<FdmMesherComposite>& mesher, |
125 | | FdmSquareRootFwdOp::TransformationType trafoType, |
126 | 0 | Real alpha) { |
127 | |
|
128 | 0 | if (trafoType != FdmSquareRootFwdOp::Power) { |
129 | 0 | return FdmMesherIntegral( |
130 | 0 | mesher, DiscreteSimpsonIntegral()).integrate(p); |
131 | 0 | } |
132 | 0 | else { |
133 | 0 | Array tp(p.size()); |
134 | 0 | for (const auto& iter : *mesher->layout()) { |
135 | 0 | const Size idx = iter.index(); |
136 | 0 | const Real nu = mesher->location(iter, 1); |
137 | |
|
138 | 0 | tp[idx] = p[idx]*std::pow(nu, alpha-1); |
139 | 0 | } |
140 | |
|
141 | 0 | return FdmMesherIntegral( |
142 | 0 | mesher, DiscreteSimpsonIntegral()).integrate(tp); |
143 | 0 | } |
144 | 0 | } |
145 | | |
146 | | |
147 | | Array rescalePDF( |
148 | | const Array& p, |
149 | | const ext::shared_ptr<FdmMesherComposite>& mesher, |
150 | 0 | FdmSquareRootFwdOp::TransformationType trafoType, Real alpha) { |
151 | |
|
152 | 0 | return p/integratePDF(p, mesher, trafoType, alpha); |
153 | 0 | } |
154 | | |
155 | | |
156 | | template <class Interpolator> |
157 | | Array reshapePDF( |
158 | | const Array& p, |
159 | | const ext::shared_ptr<FdmMesherComposite>& oldMesher, |
160 | | const ext::shared_ptr<FdmMesherComposite>& newMesher, |
161 | 0 | const Interpolator& interp = Interpolator()) { |
162 | |
|
163 | 0 | QL_REQUIRE( oldMesher->layout()->size() == newMesher->layout()->size() |
164 | 0 | && oldMesher->layout()->size() == p.size(), |
165 | 0 | "inconsistent mesher or vector size given"); |
166 | | |
167 | 0 | Matrix m(oldMesher->layout()->dim()[1], oldMesher->layout()->dim()[0]); |
168 | 0 | for (Size i=0; i < m.rows(); ++i) { |
169 | 0 | std::copy(p.begin() + i*m.columns(), |
170 | 0 | p.begin() + (i+1)*m.columns(), m.row_begin(i)); |
171 | 0 | } |
172 | 0 | const Interpolation2D interpol = interp.interpolate( |
173 | 0 | oldMesher->getFdm1dMeshers()[0]->locations().begin(), |
174 | 0 | oldMesher->getFdm1dMeshers()[0]->locations().end(), |
175 | 0 | oldMesher->getFdm1dMeshers()[1]->locations().begin(), |
176 | 0 | oldMesher->getFdm1dMeshers()[1]->locations().end(), m); |
177 | |
|
178 | 0 | Array pNew(p.size()); |
179 | 0 | for (const auto& iter : *newMesher->layout()) { |
180 | 0 | const Real x = newMesher->location(iter, 0); |
181 | 0 | const Real v = newMesher->location(iter, 1); |
182 | |
|
183 | 0 | if ( x > interpol.xMax() || x < interpol.xMin() |
184 | 0 | || v > interpol.yMax() || v < interpol.yMin() ) { |
185 | 0 | pNew[iter.index()] = 0; |
186 | 0 | } |
187 | 0 | else { |
188 | 0 | pNew[iter.index()] = interpol(x, v); |
189 | 0 | } |
190 | 0 | } |
191 | |
|
192 | 0 | return pNew; |
193 | 0 | } |
194 | | |
195 | | class FdmScheme { |
196 | | public: |
197 | 0 | virtual ~FdmScheme() = default; |
198 | | virtual void step(Array& a, Time t) = 0; |
199 | | virtual void setStep(Time dt) = 0; |
200 | | }; |
201 | | |
202 | | template <class T> |
203 | | class FdmSchemeWrapper : public FdmScheme { |
204 | | public: |
205 | | explicit FdmSchemeWrapper(T* scheme) |
206 | 0 | : scheme_(scheme) { }Unexecuted instantiation: hestonslvfdmmodel.cpp:QuantLib::(anonymous namespace)::FdmSchemeWrapper<QuantLib::HundsdorferScheme>::FdmSchemeWrapper(QuantLib::HundsdorferScheme*) Unexecuted instantiation: hestonslvfdmmodel.cpp:QuantLib::(anonymous namespace)::FdmSchemeWrapper<QuantLib::DouglasScheme>::FdmSchemeWrapper(QuantLib::DouglasScheme*) Unexecuted instantiation: hestonslvfdmmodel.cpp:QuantLib::(anonymous namespace)::FdmSchemeWrapper<QuantLib::CraigSneydScheme>::FdmSchemeWrapper(QuantLib::CraigSneydScheme*) Unexecuted instantiation: hestonslvfdmmodel.cpp:QuantLib::(anonymous namespace)::FdmSchemeWrapper<QuantLib::ModifiedCraigSneydScheme>::FdmSchemeWrapper(QuantLib::ModifiedCraigSneydScheme*) Unexecuted instantiation: hestonslvfdmmodel.cpp:QuantLib::(anonymous namespace)::FdmSchemeWrapper<QuantLib::ImplicitEulerScheme>::FdmSchemeWrapper(QuantLib::ImplicitEulerScheme*) Unexecuted instantiation: hestonslvfdmmodel.cpp:QuantLib::(anonymous namespace)::FdmSchemeWrapper<QuantLib::ExplicitEulerScheme>::FdmSchemeWrapper(QuantLib::ExplicitEulerScheme*) |
207 | | |
208 | 0 | void step(Array& a, Time t) override { scheme_->step(a, t); }Unexecuted instantiation: hestonslvfdmmodel.cpp:QuantLib::(anonymous namespace)::FdmSchemeWrapper<QuantLib::HundsdorferScheme>::step(QuantLib::Array&, double) Unexecuted instantiation: hestonslvfdmmodel.cpp:QuantLib::(anonymous namespace)::FdmSchemeWrapper<QuantLib::DouglasScheme>::step(QuantLib::Array&, double) Unexecuted instantiation: hestonslvfdmmodel.cpp:QuantLib::(anonymous namespace)::FdmSchemeWrapper<QuantLib::CraigSneydScheme>::step(QuantLib::Array&, double) Unexecuted instantiation: hestonslvfdmmodel.cpp:QuantLib::(anonymous namespace)::FdmSchemeWrapper<QuantLib::ModifiedCraigSneydScheme>::step(QuantLib::Array&, double) Unexecuted instantiation: hestonslvfdmmodel.cpp:QuantLib::(anonymous namespace)::FdmSchemeWrapper<QuantLib::ImplicitEulerScheme>::step(QuantLib::Array&, double) Unexecuted instantiation: hestonslvfdmmodel.cpp:QuantLib::(anonymous namespace)::FdmSchemeWrapper<QuantLib::ExplicitEulerScheme>::step(QuantLib::Array&, double) |
209 | 0 | void setStep(Time dt) override { scheme_->setStep(dt); }Unexecuted instantiation: hestonslvfdmmodel.cpp:QuantLib::(anonymous namespace)::FdmSchemeWrapper<QuantLib::HundsdorferScheme>::setStep(double) Unexecuted instantiation: hestonslvfdmmodel.cpp:QuantLib::(anonymous namespace)::FdmSchemeWrapper<QuantLib::DouglasScheme>::setStep(double) Unexecuted instantiation: hestonslvfdmmodel.cpp:QuantLib::(anonymous namespace)::FdmSchemeWrapper<QuantLib::CraigSneydScheme>::setStep(double) Unexecuted instantiation: hestonslvfdmmodel.cpp:QuantLib::(anonymous namespace)::FdmSchemeWrapper<QuantLib::ModifiedCraigSneydScheme>::setStep(double) Unexecuted instantiation: hestonslvfdmmodel.cpp:QuantLib::(anonymous namespace)::FdmSchemeWrapper<QuantLib::ImplicitEulerScheme>::setStep(double) Unexecuted instantiation: hestonslvfdmmodel.cpp:QuantLib::(anonymous namespace)::FdmSchemeWrapper<QuantLib::ExplicitEulerScheme>::setStep(double) |
210 | | |
211 | | private: |
212 | | const std::unique_ptr<T> scheme_; |
213 | | }; |
214 | | |
215 | | ext::shared_ptr<FdmScheme> fdmSchemeFactory( |
216 | | const FdmSchemeDesc desc, |
217 | 0 | const ext::shared_ptr<FdmLinearOpComposite>& op) { |
218 | |
|
219 | 0 | switch (desc.type) { |
220 | 0 | case FdmSchemeDesc::HundsdorferType: |
221 | 0 | return ext::shared_ptr<FdmScheme>( |
222 | 0 | new FdmSchemeWrapper<HundsdorferScheme>( |
223 | 0 | new HundsdorferScheme(desc.theta, desc.mu, op))); |
224 | 0 | case FdmSchemeDesc::DouglasType: |
225 | 0 | return ext::shared_ptr<FdmScheme>( |
226 | 0 | new FdmSchemeWrapper<DouglasScheme>( |
227 | 0 | new DouglasScheme(desc.theta, op))); |
228 | 0 | case FdmSchemeDesc::CraigSneydType: |
229 | 0 | return ext::shared_ptr<FdmScheme>( |
230 | 0 | new FdmSchemeWrapper<CraigSneydScheme>( |
231 | 0 | new CraigSneydScheme(desc.theta, desc.mu, op))); |
232 | 0 | case FdmSchemeDesc::ModifiedCraigSneydType: |
233 | 0 | return ext::shared_ptr<FdmScheme>( |
234 | 0 | new FdmSchemeWrapper<ModifiedCraigSneydScheme>( |
235 | 0 | new ModifiedCraigSneydScheme( |
236 | 0 | desc.theta, desc.mu, op))); |
237 | 0 | case FdmSchemeDesc::ImplicitEulerType: |
238 | 0 | return ext::shared_ptr<FdmScheme>( |
239 | 0 | new FdmSchemeWrapper<ImplicitEulerScheme>( |
240 | 0 | new ImplicitEulerScheme(op))); |
241 | 0 | case FdmSchemeDesc::ExplicitEulerType: |
242 | 0 | return ext::shared_ptr<FdmScheme>( |
243 | 0 | new FdmSchemeWrapper<ExplicitEulerScheme>( |
244 | 0 | new ExplicitEulerScheme(op))); |
245 | 0 | default: |
246 | 0 | QL_FAIL("Unknown scheme type"); |
247 | 0 | } |
248 | 0 | } |
249 | | } |
250 | | |
251 | | HestonSLVFDMModel::HestonSLVFDMModel(Handle<LocalVolTermStructure> localVol, |
252 | | Handle<HestonModel> hestonModel, |
253 | | const Date& endDate, |
254 | | HestonSLVFokkerPlanckFdmParams params, |
255 | | const bool logging, |
256 | | std::vector<Date> mandatoryDates, |
257 | | const Real mixingFactor) |
258 | 0 | : localVol_(std::move(localVol)), hestonModel_(std::move(hestonModel)), endDate_(endDate), |
259 | 0 | params_(params), mandatoryDates_(std::move(mandatoryDates)), |
260 | 0 | mixingFactor_(mixingFactor), logging_(logging) { |
261 | |
|
262 | 0 | registerWith(localVol_); |
263 | 0 | registerWith(hestonModel_); |
264 | 0 | } Unexecuted instantiation: QuantLib::HestonSLVFDMModel::HestonSLVFDMModel(QuantLib::Handle<QuantLib::LocalVolTermStructure>, QuantLib::Handle<QuantLib::HestonModel>, QuantLib::Date const&, QuantLib::HestonSLVFokkerPlanckFdmParams, bool, std::__1::vector<QuantLib::Date, std::__1::allocator<QuantLib::Date> >, double) Unexecuted instantiation: QuantLib::HestonSLVFDMModel::HestonSLVFDMModel(QuantLib::Handle<QuantLib::LocalVolTermStructure>, QuantLib::Handle<QuantLib::HestonModel>, QuantLib::Date const&, QuantLib::HestonSLVFokkerPlanckFdmParams, bool, std::__1::vector<QuantLib::Date, std::__1::allocator<QuantLib::Date> >, double) |
265 | | |
266 | 0 | ext::shared_ptr<HestonProcess> HestonSLVFDMModel::hestonProcess() const { |
267 | 0 | return hestonModel_->process(); |
268 | 0 | } |
269 | | |
270 | 0 | ext::shared_ptr<LocalVolTermStructure> HestonSLVFDMModel::localVol() const { |
271 | 0 | return localVol_.currentLink(); |
272 | 0 | } |
273 | | |
274 | | ext::shared_ptr<LocalVolTermStructure> |
275 | 0 | HestonSLVFDMModel::leverageFunction() const { |
276 | 0 | calculate(); |
277 | |
|
278 | 0 | return leverageFunction_; |
279 | 0 | } |
280 | | |
281 | 0 | void HestonSLVFDMModel::performCalculations() const { |
282 | 0 | logEntries_.clear(); |
283 | |
|
284 | 0 | const ext::shared_ptr<HestonProcess> hestonProcess |
285 | 0 | = hestonModel_->process(); |
286 | 0 | const ext::shared_ptr<Quote> spot |
287 | 0 | = hestonProcess->s0().currentLink(); |
288 | 0 | const ext::shared_ptr<YieldTermStructure> rTS |
289 | 0 | = hestonProcess->riskFreeRate().currentLink(); |
290 | 0 | const ext::shared_ptr<YieldTermStructure> qTS |
291 | 0 | = hestonProcess->dividendYield().currentLink(); |
292 | |
|
293 | 0 | const Real v0 = hestonProcess->v0(); |
294 | 0 | const Real kappa = hestonProcess->kappa(); |
295 | 0 | const Real theta = hestonProcess->theta(); |
296 | 0 | const Real sigma = hestonProcess->sigma(); |
297 | 0 | const Real mixedSigma = mixingFactor_ * sigma; |
298 | 0 | const Real alpha = 2*kappa*theta/(mixedSigma*mixedSigma); |
299 | |
|
300 | 0 | const Size xGrid = params_.xGrid; |
301 | 0 | const Size vGrid = params_.vGrid; |
302 | |
|
303 | 0 | const DayCounter dc = rTS->dayCounter(); |
304 | 0 | const Date referenceDate = rTS->referenceDate(); |
305 | |
|
306 | 0 | const Time T = dc.yearFraction(referenceDate, endDate_); |
307 | |
|
308 | 0 | QL_REQUIRE(referenceDate < endDate_, |
309 | 0 | "reference date must be smaller than final calibration date"); |
310 | | |
311 | 0 | QL_REQUIRE(localVol_->maxTime() >= T, |
312 | 0 | "final calibration maturity exceeds local volatility surface"); |
313 | | |
314 | | // set-up exponential time step scheme |
315 | 0 | const Time maxDt = 1.0/params_.tMaxStepsPerYear; |
316 | 0 | const Time minDt = 1.0/params_.tMinStepsPerYear; |
317 | |
|
318 | 0 | Time tIdx=0.0; |
319 | 0 | std::vector<Time> times(1, tIdx); |
320 | 0 | times.reserve(Size(T*params_.tMinStepsPerYear)); |
321 | 0 | while (tIdx < T) { |
322 | 0 | const Real decayFactor = std::exp(-params_.tStepNumberDecay*tIdx); |
323 | 0 | const Time dt = maxDt*decayFactor + minDt*(1.0-decayFactor); |
324 | |
|
325 | 0 | times.push_back(std::min(T, tIdx+=dt)); |
326 | 0 | } |
327 | |
|
328 | 0 | for (auto mandatoryDate : mandatoryDates_) { |
329 | 0 | times.push_back(dc.yearFraction(referenceDate, mandatoryDate)); |
330 | 0 | } |
331 | |
|
332 | 0 | const ext::shared_ptr<TimeGrid> timeGrid( |
333 | 0 | new TimeGrid(times.begin(), times.end())); |
334 | | |
335 | | // build 1d meshers |
336 | 0 | const LocalVolRNDCalculator localVolRND( |
337 | 0 | spot, rTS, qTS, localVol_.currentLink(), |
338 | 0 | timeGrid, xGrid, |
339 | 0 | params_.x0Density, |
340 | 0 | params_.localVolEpsProb, |
341 | 0 | params_.maxIntegrationIterations); |
342 | |
|
343 | 0 | const std::vector<Size> rescaleSteps |
344 | 0 | = localVolRND.rescaleTimeSteps(); |
345 | |
|
346 | 0 | const SquareRootProcessRNDCalculator squareRootRnd( |
347 | 0 | v0, kappa, theta, mixedSigma); |
348 | |
|
349 | 0 | const FdmSquareRootFwdOp::TransformationType trafoType |
350 | 0 | = params_.trafoType; |
351 | |
|
352 | 0 | std::vector<ext::shared_ptr<Fdm1dMesher> > xMesher, vMesher; |
353 | 0 | xMesher.reserve(timeGrid->size()); |
354 | 0 | vMesher.reserve(timeGrid->size()); |
355 | |
|
356 | 0 | xMesher.push_back(localVolRND.mesher(0.0)); |
357 | 0 | vMesher.push_back(ext::make_shared<Predefined1dMesher>( |
358 | 0 | std::vector<Real>(vGrid, v0))); |
359 | |
|
360 | 0 | Size rescaleIdx = 0; |
361 | 0 | for (Size i=1; i < timeGrid->size(); ++i) { |
362 | 0 | xMesher.push_back(localVolRND.mesher(timeGrid->at(i))); |
363 | |
|
364 | 0 | if ((rescaleIdx < rescaleSteps.size()) |
365 | 0 | && (i == rescaleSteps[rescaleIdx])) { |
366 | 0 | ++rescaleIdx; |
367 | 0 | vMesher.push_back(varianceMesher(squareRootRnd, |
368 | 0 | timeGrid->at(rescaleSteps[rescaleIdx-1]), |
369 | 0 | (rescaleIdx < rescaleSteps.size()) |
370 | 0 | ? timeGrid->at(rescaleSteps[rescaleIdx]) |
371 | 0 | : timeGrid->back(), |
372 | 0 | vGrid, v0, params_)); |
373 | 0 | } |
374 | 0 | else |
375 | 0 | vMesher.push_back(vMesher.back()); |
376 | 0 | } |
377 | | |
378 | | // start probability distribution |
379 | 0 | ext::shared_ptr<FdmMesherComposite> mesher |
380 | 0 | = ext::make_shared<FdmMesherComposite>( |
381 | 0 | xMesher.at(1), vMesher.at(1)); |
382 | |
|
383 | 0 | const Volatility lv0 |
384 | 0 | = localVol_->localVol(0.0, spot->value())/std::sqrt(v0); |
385 | |
|
386 | 0 | ext::shared_ptr<Matrix> L(new Matrix(xGrid, timeGrid->size())); |
387 | |
|
388 | 0 | const Real l0 = lv0; |
389 | 0 | std::fill(L->column_begin(0),L->column_end(0), l0); |
390 | 0 | std::fill(L->column_begin(1),L->column_end(1), l0); |
391 | | |
392 | | // create strikes from meshers |
393 | 0 | std::vector<ext::shared_ptr<std::vector<Real> > > vStrikes( |
394 | 0 | timeGrid->size()); |
395 | |
|
396 | 0 | for (Size i=0; i < timeGrid->size(); ++i) { |
397 | 0 | vStrikes[i] = ext::make_shared<std::vector<Real> >(xGrid); |
398 | 0 | if (xMesher[i]->locations().front() |
399 | 0 | == xMesher[i]->locations().back()) { |
400 | 0 | std::fill(vStrikes[i]->begin(), vStrikes[i]->end(), |
401 | 0 | std::exp(xMesher[i]->locations().front())); |
402 | 0 | } |
403 | 0 | else { |
404 | 0 | std::transform(xMesher[i]->locations().begin(), |
405 | 0 | xMesher[i]->locations().end(), |
406 | 0 | vStrikes[i]->begin(), |
407 | 0 | [](Real x) -> Real { return std::exp(x); }); |
408 | 0 | } |
409 | 0 | } |
410 | |
|
411 | 0 | const ext::shared_ptr<FixedLocalVolSurface> leverageFct( |
412 | 0 | new FixedLocalVolSurface(referenceDate, times, vStrikes, L, dc)); |
413 | |
|
414 | 0 | ext::shared_ptr<FdmLinearOpComposite> hestonFwdOp( |
415 | 0 | new FdmHestonFwdOp(mesher, hestonProcess, trafoType, leverageFct, mixingFactor_)); |
416 | |
|
417 | 0 | Array p = FdmHestonGreensFct(mesher, hestonProcess, trafoType, lv0) |
418 | 0 | .get(timeGrid->at(1), params_.greensAlgorithm); |
419 | |
|
420 | 0 | if (logging_) { |
421 | 0 | const LogEntry entry = { timeGrid->at(1), |
422 | 0 | ext::make_shared<Array>(p), mesher }; |
423 | 0 | logEntries_.push_back(entry); |
424 | 0 | } |
425 | |
|
426 | 0 | for (Size i=2; i < times.size(); ++i) { |
427 | 0 | const Time t = timeGrid->at(i); |
428 | 0 | const Time dt = t - timeGrid->at(i-1); |
429 | |
|
430 | 0 | if ( mesher->getFdm1dMeshers()[0] != xMesher[i] |
431 | 0 | || mesher->getFdm1dMeshers()[1] != vMesher[i]) { |
432 | 0 | const ext::shared_ptr<FdmMesherComposite> newMesher( |
433 | 0 | new FdmMesherComposite(xMesher[i], vMesher[i])); |
434 | |
|
435 | 0 | p = reshapePDF<Bilinear>(p, mesher, newMesher); |
436 | 0 | mesher = newMesher; |
437 | |
|
438 | 0 | p = rescalePDF(p, mesher, trafoType, alpha); |
439 | |
|
440 | 0 | hestonFwdOp = ext::shared_ptr<FdmLinearOpComposite>( |
441 | 0 | new FdmHestonFwdOp(mesher, hestonProcess, |
442 | 0 | trafoType, leverageFct, mixingFactor_)); |
443 | 0 | } |
444 | |
|
445 | 0 | Array pn = p; |
446 | 0 | const Array x(Exp( |
447 | 0 | Array(mesher->getFdm1dMeshers()[0]->locations().begin(), |
448 | 0 | mesher->getFdm1dMeshers()[0]->locations().end()))); |
449 | 0 | const Array v( |
450 | 0 | mesher->getFdm1dMeshers()[1]->locations().begin(), |
451 | 0 | mesher->getFdm1dMeshers()[1]->locations().end()); |
452 | | |
453 | | // predictor corrector steps |
454 | 0 | for (Size r=0; r < params_.predictionCorretionSteps; ++r) { |
455 | 0 | const FdmSchemeDesc fdmSchemeDesc |
456 | 0 | = (i < params_.nRannacherTimeSteps + 2) |
457 | 0 | ? FdmSchemeDesc::ImplicitEuler() |
458 | 0 | : params_.schemeDesc; |
459 | |
|
460 | 0 | const ext::shared_ptr<FdmScheme> fdmScheme( |
461 | 0 | fdmSchemeFactory(fdmSchemeDesc, hestonFwdOp)); |
462 | |
|
463 | 0 | for (Size j=0; j < x.size(); ++j) { |
464 | 0 | Array pSlice(vGrid); |
465 | 0 | for (Size k=0; k < vGrid; ++k) |
466 | 0 | pSlice[k] = pn[j + k*xGrid]; |
467 | |
|
468 | 0 | const Real pInt = (trafoType == FdmSquareRootFwdOp::Power) |
469 | 0 | ? DiscreteSimpsonIntegral()(v, Pow(v, alpha-1)*pSlice) |
470 | 0 | : DiscreteSimpsonIntegral()(v, pSlice); |
471 | |
|
472 | 0 | const Real vpInt = (trafoType == FdmSquareRootFwdOp::Log) |
473 | 0 | ? DiscreteSimpsonIntegral()(v, Exp(v)*pSlice) |
474 | 0 | : (trafoType == FdmSquareRootFwdOp::Power) |
475 | 0 | ? DiscreteSimpsonIntegral()(v, Pow(v, alpha)*pSlice) |
476 | 0 | : DiscreteSimpsonIntegral()(v, v*pSlice); |
477 | |
|
478 | 0 | const Real scale = pInt/vpInt; |
479 | 0 | const Volatility localVol = localVol_->localVol(t, x[j]); |
480 | |
|
481 | 0 | const Real l = (scale >= 0.0) |
482 | 0 | ? localVol*std::sqrt(scale) : Real(1.0); |
483 | |
|
484 | 0 | (*L)[j][i] = std::min(50.0, std::max(0.001, l)); |
485 | |
|
486 | 0 | leverageFct->setInterpolation(Linear()); |
487 | 0 | } |
488 | |
|
489 | 0 | const Real sLowerBound = std::max(x.front(), |
490 | 0 | std::exp(localVolRND.invcdf( |
491 | 0 | params_.leverageFctPropEps, t))); |
492 | 0 | const Real sUpperBound = std::min(x.back(), |
493 | 0 | std::exp(localVolRND.invcdf( |
494 | 0 | 1.0-params_.leverageFctPropEps, t))); |
495 | |
|
496 | 0 | const Real lowerL = leverageFct->localVol(t, sLowerBound); |
497 | 0 | const Real upperL = leverageFct->localVol(t, sUpperBound); |
498 | |
|
499 | 0 | for (Size j=0; j < x.size(); ++j) { |
500 | 0 | if (x[j] < sLowerBound) |
501 | 0 | std::fill(L->row_begin(j)+i, |
502 | 0 | std::min(L->row_begin(j)+i+1, L->row_end(j)), |
503 | 0 | lowerL); |
504 | 0 | else if (x[j] > sUpperBound) |
505 | 0 | std::fill(L->row_begin(j)+i, |
506 | 0 | std::min(L->row_begin(j)+i+1, L->row_end(j)), |
507 | 0 | upperL); |
508 | 0 | else if ((*L)[j][i] == Null<Real>()) |
509 | 0 | QL_FAIL("internal error"); |
510 | 0 | } |
511 | 0 | leverageFct->setInterpolation(Linear()); |
512 | |
|
513 | 0 | pn = p; |
514 | |
|
515 | 0 | fdmScheme->setStep(dt); |
516 | 0 | fdmScheme->step(pn, t); |
517 | 0 | } |
518 | 0 | p = pn; |
519 | 0 | p = rescalePDF(p, mesher, trafoType, alpha); |
520 | |
|
521 | 0 | if (logging_) { |
522 | 0 | const LogEntry entry |
523 | 0 | = { t, ext::make_shared<Array>(p), mesher }; |
524 | 0 | logEntries_.push_back(entry); |
525 | 0 | } |
526 | 0 | } |
527 | | |
528 | 0 | leverageFunction_ = leverageFct; |
529 | 0 | } |
530 | | |
531 | | const std::list<HestonSLVFDMModel::LogEntry>& HestonSLVFDMModel::logEntries() |
532 | 0 | const { |
533 | 0 | performCalculations(); |
534 | 0 | return logEntries_; |
535 | 0 | } |
536 | | } |
537 | | |