Coverage Report

Created: 2026-06-08 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/models/equity/hestonslvmcmodel.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
/*! \file hestonslvmcmodel.cpp
22
*/
23
24
#include <ql/math/functional.hpp>
25
#include <ql/termstructures/volatility/equityfx/fixedlocalvolsurface.hpp>
26
#include <ql/models/equity/hestonslvmcmodel.hpp>
27
#include <ql/processes/hestonslvprocess.hpp>
28
29
#pragma push_macro("BOOST_DISABLE_ASSERTS")
30
#ifndef BOOST_DISABLE_ASSERTS
31
#define BOOST_DISABLE_ASSERTS
32
#endif
33
#include <boost/multi_array.hpp>
34
#pragma pop_macro("BOOST_DISABLE_ASSERTS")
35
36
#include <utility>
37
38
namespace QuantLib {
39
    HestonSLVMCModel::HestonSLVMCModel(
40
        Handle<LocalVolTermStructure> localVol,
41
        Handle<HestonModel> hestonModel,
42
        ext::shared_ptr<BrownianGeneratorFactory> brownianGeneratorFactory,
43
        const Date& endDate,
44
        Size timeStepsPerYear,
45
        Size nBins,
46
        Size calibrationPaths,
47
        const std::vector<Date>& mandatoryDates,
48
        const Real mixingFactor)
49
0
    : localVol_(std::move(localVol)), hestonModel_(std::move(hestonModel)),
50
0
      brownianGeneratorFactory_(std::move(brownianGeneratorFactory)), endDate_(endDate),
51
0
      nBins_(nBins), calibrationPaths_(calibrationPaths), mixingFactor_(mixingFactor) {
52
53
0
        registerWith(localVol_);
54
0
        registerWith(hestonModel_);
55
56
0
        const DayCounter dc = hestonModel_->process()->riskFreeRate()->dayCounter();
57
0
        const Date refDate = hestonModel_->process()->riskFreeRate()->referenceDate();
58
59
0
        std::vector<Time> gridTimes;
60
0
        gridTimes.reserve(mandatoryDates.size()+1);
61
0
        for (auto mandatoryDate : mandatoryDates) {
62
0
            gridTimes.push_back(dc.yearFraction(refDate, mandatoryDate));
63
0
        }
64
0
        gridTimes.push_back(dc.yearFraction(refDate, endDate));
65
66
0
        timeGrid_ = ext::make_shared<TimeGrid>(gridTimes.begin(), gridTimes.end(),
67
0
                std::max(Size(2), Size(gridTimes.back()*timeStepsPerYear)));
68
0
    }
Unexecuted instantiation: QuantLib::HestonSLVMCModel::HestonSLVMCModel(QuantLib::Handle<QuantLib::LocalVolTermStructure>, QuantLib::Handle<QuantLib::HestonModel>, boost::shared_ptr<QuantLib::BrownianGeneratorFactory>, QuantLib::Date const&, unsigned long, unsigned long, unsigned long, std::__1::vector<QuantLib::Date, std::__1::allocator<QuantLib::Date> > const&, double)
Unexecuted instantiation: QuantLib::HestonSLVMCModel::HestonSLVMCModel(QuantLib::Handle<QuantLib::LocalVolTermStructure>, QuantLib::Handle<QuantLib::HestonModel>, boost::shared_ptr<QuantLib::BrownianGeneratorFactory>, QuantLib::Date const&, unsigned long, unsigned long, unsigned long, std::__1::vector<QuantLib::Date, std::__1::allocator<QuantLib::Date> > const&, double)
69
70
0
    ext::shared_ptr<HestonProcess> HestonSLVMCModel::hestonProcess() const {
71
0
        return hestonModel_->process();
72
0
    }
73
74
0
    ext::shared_ptr<LocalVolTermStructure> HestonSLVMCModel::localVol() const {
75
0
        return localVol_.currentLink();
76
0
    }
77
78
    ext::shared_ptr<LocalVolTermStructure>
79
0
    HestonSLVMCModel::leverageFunction() const {
80
0
        calculate();
81
82
0
        return leverageFunction_;
83
0
    }
84
85
0
    void HestonSLVMCModel::performCalculations() const {
86
0
        const ext::shared_ptr<HestonProcess> hestonProcess
87
0
            = hestonModel_->process();
88
0
        const ext::shared_ptr<Quote> spot
89
0
            = hestonProcess->s0().currentLink();
90
91
0
        const Real v0            = hestonProcess->v0();
92
0
        const DayCounter dc      = hestonProcess->riskFreeRate()->dayCounter();
93
0
        const Date referenceDate = hestonProcess->riskFreeRate()->referenceDate();
94
95
0
        const Volatility lv0
96
0
            = localVol_->localVol(0.0, spot->value())/std::sqrt(v0);
97
98
0
        const ext::shared_ptr<Matrix> L(new Matrix(nBins_, timeGrid_->size()));
99
100
0
        std::vector<ext::shared_ptr<std::vector<Real> > >
101
0
            vStrikes(timeGrid_->size());
102
0
        for (Size i=0; i < timeGrid_->size(); ++i) {
103
0
            const Integer u = nBins_/2;
104
0
            const Real dx = spot->value()*std::sqrt(QL_EPSILON);
105
106
0
            vStrikes[i] = ext::make_shared<std::vector<Real> >(nBins_);
107
108
0
            for (Integer j=0; j < Integer(nBins_); ++j)
109
0
                vStrikes[i]->at(j) = spot->value() + (j - u)*dx;
110
0
        }
111
112
0
        std::fill(L->column_begin(0),L->column_end(0), lv0);
113
114
0
        leverageFunction_ = ext::make_shared<FixedLocalVolSurface>(
115
0
            referenceDate,
116
0
            std::vector<Time>(timeGrid_->begin(), timeGrid_->end()),
117
0
            vStrikes, L, dc);
118
119
0
        const ext::shared_ptr<HestonSLVProcess> slvProcess
120
0
            = ext::make_shared<HestonSLVProcess>(hestonProcess, leverageFunction_, mixingFactor_);
121
122
0
        std::vector<std::pair<Real, Real> > pairs(
123
0
                calibrationPaths_, std::make_pair(spot->value(), v0));
124
125
0
        const Size k = calibrationPaths_ / nBins_;
126
0
        const Size m = calibrationPaths_ % nBins_;
127
128
0
        const Size timeSteps = timeGrid_->size()-1;
129
130
0
        QL_DEPRECATED_DISABLE_WARNING
131
0
        typedef boost::multi_array<Real, 3> path_type;
132
0
        path_type paths(boost::extents[calibrationPaths_][timeSteps][2]);
133
0
        QL_DEPRECATED_ENABLE_WARNING
134
135
0
        const ext::shared_ptr<BrownianGenerator> brownianGenerator =
136
0
            brownianGeneratorFactory_->create(2, timeSteps);
137
138
0
        for (Size i=0; i < calibrationPaths_; ++i) {
139
0
            brownianGenerator->nextPath();
140
0
            std::vector<Real> tmp(2);
141
0
            for (Size j=0; j < timeSteps; ++j) {
142
0
                brownianGenerator->nextStep(tmp);
143
0
                paths[i][j][0] = tmp[0];
144
0
                paths[i][j][1] = tmp[1];
145
0
            }
146
0
        }
147
148
0
        for (Size n=1; n < timeGrid_->size(); ++n) {
149
0
            const Time t = timeGrid_->at(n-1);
150
0
            const Time dt = timeGrid_->dt(n-1);
151
152
0
            Array x0(2), dw(2);
153
154
0
            for (Size i=0; i < calibrationPaths_; ++i) {
155
0
                x0[0] = pairs[i].first;
156
0
                x0[1] = pairs[i].second;
157
158
0
                dw[0] = paths[i][n-1][0];
159
0
                dw[1] = paths[i][n-1][1];
160
161
0
                x0 = slvProcess->evolve(t, x0, dt, dw);
162
163
0
                pairs[i].first = x0[0];
164
0
                pairs[i].second = x0[1];
165
0
            }
166
167
0
            std::sort(pairs.begin(), pairs.end());
168
169
0
            Size s = 0U, e = 0U;
170
0
            for (Size i=0; i < nBins_; ++i) {
171
0
                const Size inc = k + static_cast<unsigned long>(i < m);
172
0
                e = s + inc;
173
174
0
                Real sum=0.0;
175
0
                for (Size j=s; j < e; ++j) {
176
0
                    sum+=pairs[j].second;
177
0
                }
178
0
                sum/=inc;
179
180
0
                vStrikes[n]->at(i) = 0.5*(pairs[e-1].first + pairs[s].first);
181
0
                (*L)[i][n] = std::sqrt(squared(localVol_->localVol(t, vStrikes[n]->at(i), true))/sum);
182
183
0
                s = e;
184
0
            }
185
186
0
            leverageFunction_->setInterpolation<Linear>();
187
0
        }
188
0
    }
189
}