/src/quantlib/ql/experimental/models/squarerootclvmodel.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2016 Klaus Spanderen |
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 | | /*! \file squarerootclvmodel.cpp |
21 | | \brief CLV model with a square root kernel process |
22 | | */ |
23 | | |
24 | | #include <ql/processes/blackscholesprocess.hpp> |
25 | | #include <ql/processes/squarerootprocess.hpp> |
26 | | #include <ql/math/integrals/gaussianquadratures.hpp> |
27 | | |
28 | | #include <ql/experimental/models/squarerootclvmodel.hpp> |
29 | | #include <ql/methods/finitedifferences/utilities/gbsmrndcalculator.hpp> |
30 | | |
31 | | #include <boost/math/distributions/non_central_chi_squared.hpp> |
32 | | |
33 | | #include <utility> |
34 | | |
35 | | namespace QuantLib { |
36 | | SquareRootCLVModel::SquareRootCLVModel( |
37 | | const ext::shared_ptr<GeneralizedBlackScholesProcess>& bsProcess, |
38 | | ext::shared_ptr<SquareRootProcess> sqrtProcess, |
39 | | std::vector<Date> maturityDates, |
40 | | Size lagrangeOrder, |
41 | | Real pMax, |
42 | | Real pMin) |
43 | 0 | : pMax_(pMax), pMin_(pMin), bsProcess_(bsProcess), sqrtProcess_(std::move(sqrtProcess)), |
44 | 0 | maturityDates_(std::move(maturityDates)), lagrangeOrder_(lagrangeOrder), |
45 | 0 | rndCalculator_(ext::make_shared<GBSMRNDCalculator>(bsProcess)) {} Unexecuted instantiation: QuantLib::SquareRootCLVModel::SquareRootCLVModel(boost::shared_ptr<QuantLib::GeneralizedBlackScholesProcess> const&, boost::shared_ptr<QuantLib::SquareRootProcess>, std::__1::vector<QuantLib::Date, std::__1::allocator<QuantLib::Date> >, unsigned long, double, double) Unexecuted instantiation: QuantLib::SquareRootCLVModel::SquareRootCLVModel(boost::shared_ptr<QuantLib::GeneralizedBlackScholesProcess> const&, boost::shared_ptr<QuantLib::SquareRootProcess>, std::__1::vector<QuantLib::Date, std::__1::allocator<QuantLib::Date> >, unsigned long, double, double) |
46 | | |
47 | 0 | Real SquareRootCLVModel::cdf(const Date& d, Real k) const { |
48 | 0 | return rndCalculator_->cdf(k, bsProcess_->time(d)); |
49 | 0 | } |
50 | | |
51 | | |
52 | 0 | Real SquareRootCLVModel::invCDF(const Date& d, Real q) const { |
53 | 0 | return rndCalculator_->invcdf(q, bsProcess_->time(d)); |
54 | 0 | } |
55 | | |
56 | | std::pair<Real, Real> SquareRootCLVModel::nonCentralChiSquaredParams( |
57 | 0 | const Date& d) const { |
58 | |
|
59 | 0 | const Time t = bsProcess_->time(d); |
60 | |
|
61 | 0 | const Real kappa = sqrtProcess_->a(); |
62 | 0 | const Real theta = sqrtProcess_->b(); |
63 | 0 | const Real sigma = sqrtProcess_->sigma(); |
64 | |
|
65 | 0 | const Real df = 4*theta*kappa/(sigma*sigma); |
66 | 0 | const Real ncp = 4*kappa*std::exp(-kappa*t) |
67 | 0 | / (sigma*sigma*(1-std::exp(-kappa*t)))*sqrtProcess_->x0(); |
68 | |
|
69 | 0 | return std::make_pair(df, ncp); |
70 | 0 | } |
71 | | |
72 | | |
73 | 0 | Array SquareRootCLVModel::collocationPointsX(const Date& d) const { |
74 | |
|
75 | 0 | const std::pair<Real, Real> p = nonCentralChiSquaredParams(d); |
76 | |
|
77 | 0 | Array x = GaussianQuadrature(lagrangeOrder_, |
78 | 0 | GaussNonCentralChiSquaredPolynomial(p.first, p.second)) |
79 | 0 | .x(); |
80 | |
|
81 | 0 | std::sort(x.begin(), x.end()); |
82 | |
|
83 | 0 | const boost::math::non_central_chi_squared_distribution<Real> |
84 | 0 | dist(p.first, p.second); |
85 | |
|
86 | 0 | const Real xMin = std::max(x.front(), |
87 | 0 | (pMin_ == Null<Real>()) |
88 | 0 | ? 0.0 : boost::math::quantile(dist, pMin_)); |
89 | |
|
90 | 0 | const Real xMax = std::min(x.back(), |
91 | 0 | (pMax_ == Null<Real>()) |
92 | 0 | ? QL_MAX_REAL : boost::math::quantile(dist, pMax_)); |
93 | |
|
94 | 0 | const Real b = xMin - x.front(); |
95 | 0 | const Real a = (xMax - xMin)/(x.back() - x.front()); |
96 | |
|
97 | 0 | for (Real& i : x) { |
98 | 0 | i = a * i + b; |
99 | 0 | } |
100 | |
|
101 | 0 | return x; |
102 | 0 | } |
103 | | |
104 | 0 | Array SquareRootCLVModel::collocationPointsY(const Date& d) const { |
105 | |
|
106 | 0 | const Array x = collocationPointsX(d); |
107 | 0 | const std::pair<Real, Real> params = nonCentralChiSquaredParams(d); |
108 | 0 | const boost::math::non_central_chi_squared_distribution<Real> |
109 | 0 | dist(params.first, params.second); |
110 | |
|
111 | 0 | Array s(x.size()); |
112 | 0 | for (Size i=0, n=s.size(); i < n; ++i) { |
113 | 0 | const Real q = boost::math::cdf(dist, x[i]); |
114 | |
|
115 | 0 | s[i] = invCDF(d, q); |
116 | 0 | } |
117 | |
|
118 | 0 | return s; |
119 | 0 | } |
120 | | |
121 | 0 | std::function<Real(Time, Real)> SquareRootCLVModel::g() const { |
122 | 0 | calculate(); |
123 | 0 | return g_; |
124 | 0 | } |
125 | | |
126 | 0 | void SquareRootCLVModel::performCalculations() const { |
127 | 0 | g_ = std::function<Real(Time, Real)>(MappingFunction(*this)); |
128 | 0 | } |
129 | | |
130 | | SquareRootCLVModel::MappingFunction::MappingFunction( |
131 | | const SquareRootCLVModel& model) |
132 | 0 | : s_(ext::make_shared<Matrix>( |
133 | 0 | model.maturityDates_.size(), model.lagrangeOrder_)), |
134 | 0 | x_(ext::make_shared<Matrix>( |
135 | 0 | model.maturityDates_.size(), model.lagrangeOrder_)) { |
136 | |
|
137 | 0 | std::vector<Date> maturityDates = model.maturityDates_; |
138 | 0 | std::sort(maturityDates.begin(), maturityDates.end()); |
139 | |
|
140 | 0 | const ext::shared_ptr<GeneralizedBlackScholesProcess>& |
141 | 0 | bsProcess = model.bsProcess_; |
142 | |
|
143 | 0 | for (Size i=0, n = maturityDates.size(); i < n; ++i) { |
144 | 0 | const Date maturityDate = maturityDates[i]; |
145 | |
|
146 | 0 | const Array x = model.collocationPointsX(maturityDate); |
147 | 0 | const Array y = model.collocationPointsY(maturityDate); |
148 | |
|
149 | 0 | std::copy(x.begin(), x.end(), x_->row_begin(i)); |
150 | 0 | std::copy(y.begin(), y.end(), s_->row_begin(i)); |
151 | |
|
152 | 0 | const Time maturity = bsProcess->time(maturityDate); |
153 | |
|
154 | 0 | interpl.insert( |
155 | 0 | std::make_pair(maturity, |
156 | 0 | ext::make_shared<LagrangeInterpolation>( |
157 | 0 | x_->row_begin(i), x_->row_end(i), |
158 | 0 | s_->row_begin(i)))); |
159 | 0 | } |
160 | 0 | } |
161 | | |
162 | 0 | Real SquareRootCLVModel::MappingFunction::operator()(Time t,Real x) const { |
163 | 0 | const auto ge = interpl.lower_bound(t); |
164 | |
|
165 | 0 | if (close_enough(ge->first, t)) { |
166 | 0 | return (*ge->second)(x, true); |
167 | 0 | } |
168 | | |
169 | 0 | QL_REQUIRE(ge != interpl.end() && ge != interpl.begin(), |
170 | 0 | "extrapolation to large or small t is not allowed"); |
171 | | |
172 | 0 | const Time t1 = ge->first; |
173 | 0 | const Real y1 = (*ge->second)(x, true); |
174 | |
|
175 | 0 | interpl_type::const_iterator lt = ge; |
176 | 0 | std::advance(lt, -1); |
177 | |
|
178 | 0 | const Time t0 = lt->first; |
179 | 0 | const Real y0 = (*lt->second)(x, true); |
180 | |
|
181 | 0 | return y0 + (y1 - y0)/(t1 - t0)*(t - t0); |
182 | 0 | } |
183 | | } |