1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2009 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#include <ql/methods/finitedifferences/meshers/fdmblackscholesmesher.hpp>
21#include <ql/methods/finitedifferences/meshers/fdmblackscholesmultistrikemesher.hpp>
22#include <ql/methods/finitedifferences/meshers/fdmhestonvariancemesher.hpp>
23#include <ql/methods/finitedifferences/meshers/fdmmeshercomposite.hpp>
24#include <ql/methods/finitedifferences/meshers/fdmsimpleprocess1dmesher.hpp>
25#include <ql/methods/finitedifferences/meshers/uniform1dmesher.hpp>
26#include <ql/methods/finitedifferences/operators/fdmlinearoplayout.hpp>
27#include <ql/methods/finitedifferences/stepconditions/fdmstepconditioncomposite.hpp>
28#include <ql/methods/finitedifferences/utilities/fdminnervaluecalculator.hpp>
29#include <ql/pricingengines/vanilla/analytichestonengine.hpp>
30#include <ql/pricingengines/vanilla/fdhestonhullwhitevanillaengine.hpp>
31#include <ql/pricingengines/vanilla/fdhestonvanillaengine.hpp>
32#include <utility>
33
34namespace QuantLib {
35
36 QL_DEPRECATED_DISABLE_WARNING
37
38 FdHestonHullWhiteVanillaEngine::FdHestonHullWhiteVanillaEngine(
39 const ext::shared_ptr<HestonModel>& hestonModel,
40 ext::shared_ptr<HullWhiteProcess> hwProcess,
41 Real corrEquityShortRate,
42 Size tGrid,
43 Size xGrid,
44 Size vGrid,
45 Size rGrid,
46 Size dampingSteps,
47 bool controlVariate,
48 const FdmSchemeDesc& schemeDesc)
49 : GenericModelEngine<HestonModel,
50 DividendVanillaOption::arguments,
51 DividendVanillaOption::results>(hestonModel),
52 hwProcess_(std::move(hwProcess)), explicitDividends_(false),
53 corrEquityShortRate_(corrEquityShortRate), tGrid_(tGrid),
54 xGrid_(xGrid), vGrid_(vGrid), rGrid_(rGrid), dampingSteps_(dampingSteps),
55 schemeDesc_(schemeDesc), controlVariate_(controlVariate) {}
56
57 FdHestonHullWhiteVanillaEngine::FdHestonHullWhiteVanillaEngine(
58 const ext::shared_ptr<HestonModel>& hestonModel,
59 ext::shared_ptr<HullWhiteProcess> hwProcess,
60 DividendSchedule dividends,
61 Real corrEquityShortRate,
62 Size tGrid,
63 Size xGrid,
64 Size vGrid,
65 Size rGrid,
66 Size dampingSteps,
67 bool controlVariate,
68 const FdmSchemeDesc& schemeDesc)
69 : GenericModelEngine<HestonModel,
70 DividendVanillaOption::arguments,
71 DividendVanillaOption::results>(hestonModel),
72 hwProcess_(std::move(hwProcess)), dividends_(std::move(dividends)), explicitDividends_(true),
73 corrEquityShortRate_(corrEquityShortRate), tGrid_(tGrid),
74 xGrid_(xGrid), vGrid_(vGrid), rGrid_(rGrid), dampingSteps_(dampingSteps),
75 schemeDesc_(schemeDesc), controlVariate_(controlVariate) {}
76
77 QL_DEPRECATED_ENABLE_WARNING
78
79 void FdHestonHullWhiteVanillaEngine::calculate() const {
80
81 // dividends will eventually be moved out of arguments, but for now we need the switch
82 QL_DEPRECATED_DISABLE_WARNING
83 const DividendSchedule& passedDividends = explicitDividends_ ? dividends_ : arguments_.cashFlow;
84 QL_DEPRECATED_ENABLE_WARNING
85
86 // 1. cache lookup for precalculated results
87 for (auto& cachedArgs2result : cachedArgs2results_) {
88 if (cachedArgs2result.first.exercise->type() == arguments_.exercise->type() &&
89 cachedArgs2result.first.exercise->dates() == arguments_.exercise->dates()) {
90 ext::shared_ptr<PlainVanillaPayoff> p1 =
91 ext::dynamic_pointer_cast<PlainVanillaPayoff>(
92 r: arguments_.payoff);
93 ext::shared_ptr<PlainVanillaPayoff> p2 =
94 ext::dynamic_pointer_cast<PlainVanillaPayoff>(r: cachedArgs2result.first.payoff);
95
96 if ((p1 != nullptr) && p1->strike() == p2->strike() &&
97 p1->optionType() == p2->optionType()) {
98 QL_REQUIRE(passedDividends.empty(),
99 "multiple strikes engine does not work with discrete dividends");
100 results_ = cachedArgs2result.second;
101 return;
102 }
103 }
104 }
105
106 // 2. Mesher
107 const ext::shared_ptr<HestonProcess> hestonProcess=model_->process();
108 const Time maturity=hestonProcess->time(arguments_.exercise->lastDate());
109
110 // 2.1 The variance mesher
111 const Size tGridMin = 5;
112 const ext::shared_ptr<FdmHestonVarianceMesher> varianceMesher(
113 new FdmHestonVarianceMesher(vGrid_, hestonProcess,
114 maturity,std::max(a: tGridMin,b: tGrid_/50)));
115
116 // 2.2 The equity mesher
117 const ext::shared_ptr<StrikedTypePayoff> payoff =
118 ext::dynamic_pointer_cast<StrikedTypePayoff>(r: arguments_.payoff);
119 QL_REQUIRE(payoff, "wrong payoff type given");
120
121 ext::shared_ptr<Fdm1dMesher> equityMesher;
122 if (strikes_.empty()) {
123 equityMesher = ext::shared_ptr<Fdm1dMesher>(
124 new FdmBlackScholesMesher(
125 xGrid_,
126 FdmBlackScholesMesher::processHelper(
127 s0: hestonProcess->s0(), rTS: hestonProcess->dividendYield(),
128 qTS: hestonProcess->riskFreeRate(),
129 vol: varianceMesher->volaEstimate()),
130 maturity, payoff->strike(),
131 Null<Real>(), Null<Real>(), 0.0001, 1.5,
132 std::pair<Real, Real>(payoff->strike(), 0.1),
133 passedDividends));
134 }
135 else {
136 QL_REQUIRE(passedDividends.empty(),
137 "multiple strikes engine does not work with discrete dividends");
138 equityMesher = ext::shared_ptr<Fdm1dMesher>(
139 new FdmBlackScholesMultiStrikeMesher(
140 xGrid_,
141 FdmBlackScholesMesher::processHelper(
142 s0: hestonProcess->s0(), rTS: hestonProcess->dividendYield(),
143 qTS: hestonProcess->riskFreeRate(),
144 vol: varianceMesher->volaEstimate()),
145 maturity, strikes_, 0.0001, 1.5,
146 std::pair<Real, Real>(payoff->strike(), 0.075)));
147 }
148
149 //2.3 The short rate mesher
150 const ext::shared_ptr<OrnsteinUhlenbeckProcess> ouProcess(
151 new OrnsteinUhlenbeckProcess(hwProcess_->a(),hwProcess_->sigma()));
152 const ext::shared_ptr<Fdm1dMesher> shortRateMesher(
153 new FdmSimpleProcess1dMesher(rGrid_, ouProcess, maturity));
154
155 const ext::shared_ptr<FdmMesher> mesher(
156 new FdmMesherComposite(equityMesher, varianceMesher,
157 shortRateMesher));
158
159 // 3. Calculator
160 const ext::shared_ptr<FdmInnerValueCalculator> calculator(
161 new FdmLogInnerValue(arguments_.payoff, mesher, 0));
162
163 // 4. Step conditions
164 const ext::shared_ptr<FdmStepConditionComposite> conditions =
165 FdmStepConditionComposite::vanillaComposite(
166 schedule: passedDividends, exercise: arguments_.exercise,
167 mesher, calculator,
168 refDate: hestonProcess->riskFreeRate()->referenceDate(),
169 dayCounter: hestonProcess->riskFreeRate()->dayCounter());
170
171 // 5. Boundary conditions
172 const FdmBoundaryConditionSet boundaries;
173
174 // 6. Solver
175 const FdmSolverDesc solverDesc = { .mesher: mesher, .bcSet: boundaries, .condition: conditions,
176 .calculator: calculator, .maturity: maturity,
177 .timeSteps: tGrid_, .dampingSteps: dampingSteps_ };
178
179 const ext::shared_ptr<FdmHestonHullWhiteSolver> solver(
180 new FdmHestonHullWhiteSolver(Handle<HestonProcess>(hestonProcess),
181 Handle<HullWhiteProcess>(hwProcess_),
182 corrEquityShortRate_,
183 solverDesc, schemeDesc_));
184
185 const Real spot = hestonProcess->s0()->value();
186 const Real v0 = hestonProcess->v0();
187 results_.value = solver->valueAt(s: spot, v: v0, r: 0);
188 results_.delta = solver->deltaAt(s: spot, v: v0, r: 0, eps: spot*0.01);
189 results_.gamma = solver->gammaAt(s: spot, v: v0, r: 0, eps: spot*0.01);
190 results_.theta = solver->thetaAt(s: spot, v: v0, r: 0);
191
192 cachedArgs2results_.resize(new_size: strikes_.size());
193 for (Size i=0; i < strikes_.size(); ++i) {
194 cachedArgs2results_[i].first.exercise = arguments_.exercise;
195 cachedArgs2results_[i].first.payoff =
196 ext::make_shared<PlainVanillaPayoff>(
197 args: payoff->optionType(), args: strikes_[i]);
198 const Real d = payoff->strike()/strikes_[i];
199
200 QL_DEPRECATED_DISABLE_WARNING
201 DividendVanillaOption::results&
202 results = cachedArgs2results_[i].second;
203 QL_DEPRECATED_ENABLE_WARNING
204 results.value = solver->valueAt(s: spot*d, v: v0, r: 0)/d;
205 results.delta = solver->deltaAt(s: spot*d, v: v0, r: 0, eps: spot*d*0.01);
206 results.gamma = solver->gammaAt(s: spot*d, v: v0, r: 0, eps: spot*d*0.01)*d;
207 results.theta = solver->thetaAt(s: spot*d, v: v0, r: 0)/d;
208 }
209
210 if (controlVariate_) {
211 ext::shared_ptr<PricingEngine> analyticEngine(
212 new AnalyticHestonEngine(*model_, 164));
213 ext::shared_ptr<Exercise> exercise(
214 new EuropeanExercise(arguments_.exercise->lastDate()));
215
216 VanillaOption option(payoff, exercise);
217 option.setPricingEngine(analyticEngine);
218 Real analyticNPV = option.NPV();
219
220 ext::shared_ptr<FdHestonVanillaEngine> fdEngine(
221 new FdHestonVanillaEngine(*model_, tGrid_, xGrid_,
222 vGrid_, dampingSteps_,
223 schemeDesc_));
224 fdEngine->enableMultipleStrikesCaching(strikes: strikes_);
225 option.setPricingEngine(fdEngine);
226
227 Real fdNPV = option.NPV();
228 results_.value += analyticNPV - fdNPV;
229 for (Size i=0; i < strikes_.size(); ++i) {
230 VanillaOption controlVariateOption(
231 ext::shared_ptr<StrikedTypePayoff>(
232 new PlainVanillaPayoff(payoff->optionType(),
233 strikes_[i])), exercise);
234 controlVariateOption.setPricingEngine(analyticEngine);
235 analyticNPV = controlVariateOption.NPV();
236
237 controlVariateOption.setPricingEngine(fdEngine);
238 fdNPV = controlVariateOption.NPV();
239 cachedArgs2results_[i].second.value += analyticNPV - fdNPV;
240 }
241 }
242 }
243
244 void FdHestonHullWhiteVanillaEngine::update() {
245 cachedArgs2results_.clear();
246 QL_DEPRECATED_DISABLE_WARNING
247 GenericModelEngine<HestonModel,
248 DividendVanillaOption::arguments,
249 DividendVanillaOption::results>::update();
250 QL_DEPRECATED_ENABLE_WARNING
251 }
252
253 void FdHestonHullWhiteVanillaEngine::enableMultipleStrikesCaching(
254 const std::vector<Real>& strikes) {
255 strikes_ = strikes;
256 update();
257 }
258
259}
260

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