Coverage Report

Created: 2026-06-23 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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