1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2019 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 <http://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 fdsabrvanillaengine.hpp */
21
22#include <ql/exercise.hpp>
23#include <ql/math/distributions/normaldistribution.hpp>
24#include <ql/methods/finitedifferences/meshers/concentrating1dmesher.hpp>
25#include <ql/methods/finitedifferences/meshers/fdmcev1dmesher.hpp>
26#include <ql/methods/finitedifferences/meshers/fdmmeshercomposite.hpp>
27#include <ql/methods/finitedifferences/operators/fdmsabrop.hpp>
28#include <ql/methods/finitedifferences/solvers/fdm2dimsolver.hpp>
29#include <ql/methods/finitedifferences/stepconditions/fdmstepconditioncomposite.hpp>
30#include <ql/methods/finitedifferences/utilities/cevrndcalculator.hpp>
31#include <ql/methods/finitedifferences/utilities/fdmdiscountdirichletboundary.hpp>
32#include <ql/methods/finitedifferences/utilities/fdminnervaluecalculator.hpp>
33#include <ql/pricingengines/vanilla/fdsabrvanillaengine.hpp>
34#include <ql/termstructures/volatility/sabr.hpp>
35#include <ql/termstructures/yieldtermstructure.hpp>
36#include <utility>
37
38namespace QuantLib {
39
40 FdSabrVanillaEngine::FdSabrVanillaEngine(Real f0,
41 Real alpha,
42 Real beta,
43 Real nu,
44 Real rho,
45 Handle<YieldTermStructure> rTS,
46 Size tGrid,
47 Size fGrid,
48 Size xGrid,
49 Size dampingSteps,
50 Real scalingFactor,
51 Real eps,
52 const FdmSchemeDesc& schemeDesc)
53 : f0_(f0), alpha_(alpha), beta_(beta), nu_(nu), rho_(rho), rTS_(std::move(rTS)), tGrid_(tGrid),
54 fGrid_(fGrid), xGrid_(xGrid), dampingSteps_(dampingSteps), scalingFactor_(scalingFactor),
55 eps_(eps), schemeDesc_(schemeDesc) {
56
57 validateSabrParameters(alpha, beta: 0.5, nu, rho);
58
59 QL_REQUIRE(beta<1.0, "beta must be smaller than 1.0: "
60 << beta << " not allowed");
61
62 registerWith(h: rTS_);
63 }
64
65 void FdSabrVanillaEngine::calculate() const {
66 // 1. Mesher
67 const ext::shared_ptr<StrikedTypePayoff> payoff =
68 ext::dynamic_pointer_cast<StrikedTypePayoff>(r: arguments_.payoff);
69 QL_REQUIRE(payoff, "non-striked payoff given");
70
71 const DayCounter dc = rTS_->dayCounter();
72
73 const Date referenceDate = rTS_->referenceDate();
74 const Date maturityDate = arguments_.exercise->lastDate();
75 const Time maturityTime = dc.yearFraction(d1: referenceDate, d2: maturityDate);
76
77 const Real upperAlpha = alpha_*
78 std::exp(x: nu_*std::sqrt(x: maturityTime)*InverseCumulativeNormal()(0.75));
79
80 const ext::shared_ptr<Fdm1dMesher> cevMesher =
81 ext::make_shared<FdmCEV1dMesher>(
82 args: fGrid_, args: f0_, args: upperAlpha, args: beta_,
83 args: maturityTime, args: eps_, args: scalingFactor_,
84 args: std::make_pair(x: payoff->strike(), y: 0.025));
85
86 const Real normInvEps = InverseCumulativeNormal()(1-eps_);
87 const Real logDrift = -0.5*nu_*nu_*maturityTime;
88 const Real volRange =
89 nu_*std::sqrt(x: maturityTime)*normInvEps*scalingFactor_;
90
91 const Real xMin = std::log(x: alpha_) + logDrift - volRange;
92 const Real xMax = std::log(x: alpha_) + logDrift + volRange;
93
94 const ext::shared_ptr<Fdm1dMesher> xMesher =
95 ext::make_shared<Concentrating1dMesher>(
96 args: xMin, args: xMax, args: xGrid_, args: std::make_pair(x: std::log(x: alpha_), y: 0.1));
97
98 const ext::shared_ptr<FdmMesher> mesher =
99 ext::make_shared<FdmMesherComposite>(args: cevMesher, args: xMesher);
100
101 // 2. Calculator
102 const ext::shared_ptr<FdmInnerValueCalculator> calculator =
103 ext::make_shared<FdmCellAveragingInnerValue>(args: payoff, args: mesher, args: 0);
104
105 // 3. Step conditions
106 const ext::shared_ptr<FdmStepConditionComposite> conditions =
107 FdmStepConditionComposite::vanillaComposite(
108 schedule: DividendSchedule(), exercise: arguments_.exercise,
109 mesher, calculator, refDate: referenceDate, dayCounter: dc);
110
111 // 4. Boundary conditions
112 FdmBoundaryConditionSet boundaries;
113
114 const Real lowerBound = cevMesher->locations().front();
115 const Real upperBound = cevMesher->locations().back();
116
117 boundaries.push_back(
118 x: ext::make_shared<FdmDiscountDirichletBoundary>(
119 args: mesher, args: rTS_.currentLink(),
120 args: maturityTime, args: (*payoff)(upperBound),
121 args: 0, args: FdmDiscountDirichletBoundary::Upper));
122
123 boundaries.push_back(
124 x: ext::make_shared<FdmDiscountDirichletBoundary>(
125 args: mesher, args: rTS_.currentLink(),
126 args: maturityTime, args: (*payoff)(lowerBound),
127 args: 0, args: FdmDiscountDirichletBoundary::Lower));
128
129 // 5. Solver
130 const FdmSolverDesc solverDesc = {
131 .mesher: mesher, .bcSet: boundaries, .condition: conditions,
132 .calculator: calculator, .maturity: maturityTime, .timeSteps: tGrid_, .dampingSteps: dampingSteps_
133 };
134
135 const ext::shared_ptr<FdmLinearOpComposite> op =
136 ext::make_shared<FdmSabrOp>(
137 args: mesher, args: rTS_.currentLink(),
138 args: f0_, args: alpha_, args: beta_, args: nu_, args: rho_);
139
140 const ext::shared_ptr<Fdm2DimSolver> solver =
141 ext::make_shared<Fdm2DimSolver>(args: solverDesc, args: schemeDesc_, args: op);
142
143 results_.value = solver->interpolateAt(x: f0_, y: std::log(x: alpha_));
144 }
145}
146
147

source code of quantlib/ql/pricingengines/vanilla/fdsabrvanillaengine.cpp