21#include <ql/math/comparison.hpp>
26 const Real rho,
const Real forward,
27 const Real expiryTime,
const Real displacement,
28 const Size zSteps,
const Size tSteps,
30 : alpha_(alpha), beta_(beta), nu_(nu), rho_(rho), forward_(forward), expiryTime_(expiryTime),
31 displacement_(displacement), zSteps_(zSteps), tSteps_(tSteps), nStdDev_(nStdDev) {
33 QL_REQUIRE(
alpha > 0.0,
"KienitzLawsonSwayneSabrPdeDensity: alpha (" <<
alpha <<
") must be positive");
34 QL_REQUIRE(
beta >= 0.0 &&
beta < 1.0,
"KienitzLawsonSwayneSabrPdeDensity: beta (" <<
beta <<
") must be in [0,1)");
35 QL_REQUIRE(
nu > 0.0,
"KienitzLawsonSwayneSabrPdeDensity: nu (" <<
nu <<
") must be positive");
36 QL_REQUIRE(
rho > -1.0 &&
rho < 1.0,
"KienitzLawsonSwayneSabrPdeDensity: rho (" <<
rho <<
") must be in (-1,1)");
38 "KienitzLawsonSwayneSabrPdeDensity: expiryTime (" <<
expiryTime <<
") must be positve");
39 QL_REQUIRE(
zSteps > 1,
"KienitzLawsonSwayneSabrPdeDensity: zSteps (" <<
zSteps <<
") must be >1");
40 QL_REQUIRE(
tSteps > 0,
"KienitzLawsonSwayneSabrPdeDensity: tSteps (" <<
tSteps <<
") must be positive");
41 QL_REQUIRE(
nStdDev > 0.0,
"KienitzLawsonSwayneSabrPdeDensity: nStdDev (" <<
nStdDev <<
") must be positive");
47 Real betam = 1.0 -
beta_;
52 Real betam = 1.0 -
beta_;
55 par = std::max(par, 0.0);
73 const Size n,
bool firstLastRZero) {
75 if (QuantLib::close_enough(b[0], 0.0)) {
76 std::fill(u.begin(), u.end(), 0.0);
80 u[0] = firstLastRZero ? 0.0 : (R[0] / bet);
81 for (Size j = 1; j < n; ++j) {
82 gam[j] = c[j - 1] / bet;
83 bet = b[j] - a[j] * gam[j];
84 if (QuantLib::close_enough(bet, 0.0)) {
85 QL_FAIL(
"KienitzLawsonSwayneSabrPdeDensity: tridag failed");
87 if (j < n - 1 || !firstLastRZero)
88 u[j] = (R[j] - a[j] * u[j - 1]) / bet;
90 u[j] = (-a[j] * u[j - 1]) / bet;
92 for (Size j = n - 1; j >= 1; --j) {
93 u[j - 1] -= gam[j] * u[j];
98 const Real dt,
const Array& PP_in,
const Real PL_in,
99 const Real PR_in,
const Size n, Array& PP_out, Real& PL_out,
102 Array bb(n), aa(n), cc(n);
104 Real frac = dt / (2.0 *
hh_);
106 for (Size i = 1; i < n - 1; ++i) {
107 aa[i] = -frac * cm[i - 1] * Em[i - 1] / (fm[i] - fm[i - 1]);
108 bb[i] = 1.0 + frac * (cm[i] * Em[i] * (1.0 / (fm[i + 1] - fm[i]) + 1.0 / (fm[i] - fm[i - 1])));
109 cc[i] = -frac * cm[i + 1] * Em[i + 1] / (fm[i + 1] - fm[i]);
112 bb[0] = cm[0] / (fm[1] - fm[0]) * Em[0];
113 bb[n - 1] = cm[n - 1] / (fm[n - 1] - fm[n - 2]) * Em[n - 1];
115 aa[n - 1] = cm[n - 2] / (fm[n - 1] - fm[n - 2]) * Em[n - 2];
116 cc[0] = cm[1] / (fm[1] - fm[0]) * Em[1];
119 tridag(aa, bb, cc, PP_in, PP_out, n,
true);
121 PL_out = PL_in + dt * cm[1] / (fm[1] - fm[0]) * Em[1] * PP_out[1];
122 PR_out = PR_in + dt * cm[n - 2] / (fm[n - 1] - fm[n - 2]) * Em[n - 2] * PP_out[n - 2];
126 const Real dt,
const Size Nj) {
128 Array Emdt1_vec(Nj), Emdt2_vec(Nj), PP1(Nj), PP2(Nj), Em(Nj, 1.0), EmO(Nj);
129 constexpr Real b = 1.0 - M_SQRT_2;
131 Real dt2 = dt * (1.0 - 2.0 * b);
132 for (Size i = 1; i < Nj - 1; ++i) {
133 Emdt1_vec[i] = std::exp(
rho_ *
nu_ *
alpha_ * Gamma_Vec[i] * dt1);
134 Emdt2_vec[i] = std::exp(
rho_ *
nu_ *
alpha_ * Gamma_Vec[i] * dt2);
136 Emdt1_vec[0] = Emdt1_vec[1];
137 Emdt1_vec[Nj - 1] = Emdt1_vec[Nj - 2];
138 Emdt2_vec[0] = Emdt2_vec[1];
139 Emdt2_vec[Nj - 1] = Emdt2_vec[Nj - 2];
144 Real PL1, PR1, PL2, PR2;
145 for (Size it = 0; it <
tSteps_; ++it) {
146 for (Size j = 1; j < Nj - 1; ++j) {
147 Em[j] *= Emdt1_vec[j];
149 solveTimeStep_LS(fm, Ccm, Em, dt1,
SABR_Prob_Vec_,
pL_,
pR_, Nj, PP1, PL1, PR1);
150 for (Size j = 1; j < Nj - 1; ++j) {
151 Em[j] *= Emdt1_vec[j];
153 solveTimeStep_LS(fm, Ccm, Em, dt1, PP1, PL1, PR1, Nj, PP2, PL2, PR2);
154 for (Size j = 1; j < Nj - 1; ++j) {
156 Em[j] *= Emdt2_vec[j];
160 pL_ = (M_SQRT2 + 1.0) * PL2 - M_SQRT2 * PL1;
161 pR_ = (M_SQRT2 + 1.0) * PR2 - M_SQRT2 * PR1;
168 Real betam = 1.0 -
beta_;
177 Real h0 = (
zMax_ -
zMin_) /
static_cast<Real
>(j);
178 Size j0 =
static_cast<int>(-
zMin_ / h0 + 0.5);
179 hh_ = -
zMin_ / (
static_cast<Real
>(j0) - 0.5);
183 for (Size i = 0; i <
zSteps_; ++i) {
184 zm[i] =
static_cast<Real
>(i) *
hh_ +
zMin_ - 0.5 *
hh_;
192 Real fmax =
fy(ymax);
193 Real fmin =
fy(ymin);
195 fm[0] = 2 * fmin - fm[1];
198 for (Size i = 1; i <
zSteps_ - 1; ++i) {
234 Real zl =
zMin() +
static_cast<Real
>(idx - 1) *
hh();
235 Real zr =
zMin() +
static_cast<Real
>(idx) *
hh();
238 Real
alpha = (cr - p) / (cr - cl);
243 std::vector<Real> result;
244 for (
auto const& strike :
strikes) {
245 Real zstrike =
zy(
yf(strike));
246 if (zstrike <=
zMin())
247 result.push_back(
forward() - strike);
248 else if (zstrike >=
zMax())
249 result.push_back(0.0);
252 Real p = (fmax - strike) *
pR();
260 for (; k >= 1; --k) {
261 Real zm1 =
zMin() + (
static_cast<Real
>(k) - 0.5) *
hh();
271 Real zmK =
zMin() +
static_cast<Real
>(k) *
hh();
272 Real zmKm1 =
zMin() +
static_cast<Real
>(k - 1) *
hh();
273 Real ftK =
fy(
yz(zmK));
274 Real ftKm1 =
fy(
yz(zmKm1));
275 Real diff = ftK - ftKm1;
276 Real b = (2.0 * ft1 - ftKm1 - ftK) / diff;
277 Real subgridadjust = 0.5 *
hh() *
sabrProb()[k] * (ftK - strike) * (ftK - strike) / diff *
278 (1.0 + b * (ftK + 2.0 * strike - 3.0 * ftKm1) / diff);
287 std::vector<Real> result;
288 for (
auto const& strike :
strikes) {
289 Real zstrike =
zy(
yf(strike));
290 if (zstrike <=
zMin())
291 result.push_back(0.0);
292 else if (zstrike >=
zMax())
293 result.push_back(strike -
forward());
296 Real p = (strike - fmin) *
pL();
300 for (; k <=
zSteps() - 2; ++k) {
301 Real zm1 =
zMin() + (
static_cast<Real
>(k) - 0.5) *
hh();
311 Real zmK =
zMin() +
static_cast<Real
>(k) *
hh();
312 Real zmKm1 =
zMin() +
static_cast<Real
>(k - 1) *
hh();
313 Real ftK =
fy(
yz(zmK));
314 Real ftKm1 =
fy(
yz(zmKm1));
315 Real diff = ftK - ftKm1;
316 Real b = (2.0 * ft1 - ftKm1 - ftK) / diff;
317 Real subgridadjust = 0.5 *
hh() *
sabrProb()[k] * (strike - ftKm1) * (strike - ftKm1) / diff *
318 (1.0 - b * (3.0 * ftK - 2.0 * strike - ftKm1) / diff);
Real zy(const Real y) const
std::vector< Real > callPrices(const std::vector< Real > &strikes) const
void solveTimeStep_LS(const Array &fm, const Array &cm, const Array &Em, const Real dt, const Array &PP_in, const Real PL_in, const Real PR_in, const Size n, Array &PP_out, Real &PL_out, Real &PR_out)
Real yz(const Real z) const
Real fy(const Real y) const
Real yf(const Real f) const
const Array & sabrProb() const
void PDE_method(const Array &fm, const Array &Ccm, const Array &Gamma_Vec, const Real dt, const Size Nj)
Real inverseCumulative(const Real p) const
std::vector< Real > putPrices(const std::vector< Real > &strikes) const
KienitzLawsonSwayneSabrPdeDensity(const Real alpha, const Real beta, const Real nu, const Real rho, const Real forward, const Real expiryTime, const Real displacement, const Size zSteps, const Size tSteps, const Real nStdDev)
void tridag(const Array &a, const Array &b, const Array &c, const Array &R, Array &u, const Size n, const bool firstLastRZero)
Adaption of VBA code by Jörg Kienitz, 2017, to create a SABR density with PDE methods.