1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 This file is part of QuantLib, a free-software/open-source library
5 for financial quantitative analysts and developers - http://quantlib.org/
6 QuantLib is free software: you can redistribute it and/or modify it
7 under the terms of the QuantLib license. You should have received a
8 copy of the license along with this program; if not, please email
9 <quantlib-dev@lists.sf.net>. The license is also available online at
10 <http://quantlib.org/license.shtml>.
11 This program is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 FOR A PARTICULAR PURPOSE. See the license for more details.
14*/
15
16/*! \file mcforwardeuropeanhestonengine.hpp
17 \brief Monte Carlo engine for forward-starting strike-reset European options using Heston-like process
18*/
19
20#ifndef quantlib_mc_forward_european_heston_engine_hpp
21#define quantlib_mc_forward_european_heston_engine_hpp
22
23#include <ql/models/equity/hestonmodel.hpp>
24#include <ql/pricingengines/forward/mcforwardvanillaengine.hpp>
25#include <ql/pricingengines/vanilla/analytichestonengine.hpp>
26#include <ql/processes/hestonprocess.hpp>
27#include <utility>
28
29namespace QuantLib {
30
31 /*! References:
32
33 Control Variate trade-off considerations discussed in pull request:
34 https://github.com/lballabio/QuantLib/pull/948
35
36 \ingroup forwardengines
37
38 \test
39 - Heston MC prices for a flat Heston process are
40 compared to analytical BS prices with the same
41 volatility for a range of moneynesses
42 - Heston MC prices for a forward-starting option
43 resetting at t=0 are compared to semi-analytical
44 Heston prices for a range of moneynesses
45 */
46 template <class RNG = PseudoRandom,
47 class S = Statistics, class P = HestonProcess>
48 class MCForwardEuropeanHestonEngine
49 : public MCForwardVanillaEngine<MultiVariate,RNG,S> {
50 public:
51 typedef
52 typename MCForwardVanillaEngine<MultiVariate,RNG,S>::path_generator_type
53 path_generator_type;
54 typedef
55 typename MCForwardVanillaEngine<MultiVariate,RNG,S>::path_pricer_type
56 path_pricer_type;
57 typedef typename MCForwardVanillaEngine<MultiVariate,RNG,S>::stats_type
58 stats_type;
59 // constructor
60 MCForwardEuropeanHestonEngine(
61 const ext::shared_ptr<P>& process,
62 Size timeSteps,
63 Size timeStepsPerYear,
64 bool antitheticVariate,
65 Size requiredSamples,
66 Real requiredTolerance,
67 Size maxSamples,
68 BigNatural seed,
69 bool controlVariate = false);
70 protected:
71 ext::shared_ptr<path_pricer_type> pathPricer() const override;
72
73 // Use the vanilla option running from t=0 to t=expiryTime with an analytic Heston pricer
74 // as a control variate. Works well if resetTime small.
75 ext::shared_ptr<path_pricer_type> controlPathPricer() const override;
76 ext::shared_ptr<PricingEngine> controlPricingEngine() const override {
77 ext::shared_ptr<P> process = ext::dynamic_pointer_cast<P>(this->process_);
78 QL_REQUIRE(process, "Heston-like process required");
79
80 ext::shared_ptr<HestonModel> hestonModel(new HestonModel(process));
81 return ext::shared_ptr<PricingEngine>(new
82 AnalyticHestonEngine(hestonModel));
83 }
84 };
85
86
87 template <class RNG = PseudoRandom,
88 class S = Statistics, class P = HestonProcess>
89 class MakeMCForwardEuropeanHestonEngine {
90 public:
91 explicit MakeMCForwardEuropeanHestonEngine(ext::shared_ptr<P> process);
92 // named parameters
93 MakeMCForwardEuropeanHestonEngine& withSteps(Size steps);
94 MakeMCForwardEuropeanHestonEngine& withStepsPerYear(Size steps);
95 MakeMCForwardEuropeanHestonEngine& withSamples(Size samples);
96 MakeMCForwardEuropeanHestonEngine& withAbsoluteTolerance(Real tolerance);
97 MakeMCForwardEuropeanHestonEngine& withMaxSamples(Size samples);
98 MakeMCForwardEuropeanHestonEngine& withSeed(BigNatural seed);
99 MakeMCForwardEuropeanHestonEngine& withAntitheticVariate(bool b = true);
100 MakeMCForwardEuropeanHestonEngine& withControlVariate(bool b = false);
101 // conversion to pricing engine
102 operator ext::shared_ptr<PricingEngine>() const;
103 private:
104 ext::shared_ptr<P> process_;
105 bool antithetic_ = false, controlVariate_ = false;
106 Size steps_, stepsPerYear_, samples_, maxSamples_;
107 Real tolerance_;
108 BigNatural seed_ = 0;
109 };
110
111
112 class ForwardEuropeanHestonPathPricer : public PathPricer<MultiPath> {
113 public:
114 ForwardEuropeanHestonPathPricer(Option::Type type,
115 Real moneyness,
116 Size resetIndex,
117 DiscountFactor discount);
118 Real operator()(const MultiPath& multiPath) const override;
119
120 private:
121 Option::Type type_;
122 Real moneyness_;
123 Size resetIndex_;
124 DiscountFactor discount_;
125 };
126
127
128 // inline definitions
129
130 template <class RNG, class S, class P>
131 inline MCForwardEuropeanHestonEngine<RNG,S,P>::MCForwardEuropeanHestonEngine(
132 const ext::shared_ptr<P>& process,
133 Size timeSteps,
134 Size timeStepsPerYear,
135 bool antitheticVariate,
136 Size requiredSamples,
137 Real requiredTolerance,
138 Size maxSamples,
139 BigNatural seed,
140 bool controlVariate)
141 : MCForwardVanillaEngine<MultiVariate,RNG,S>(process,
142 timeSteps,
143 timeStepsPerYear,
144 false,
145 antitheticVariate,
146 requiredSamples,
147 requiredTolerance,
148 maxSamples,
149 seed,
150 controlVariate) {}
151
152
153 template <class RNG, class S, class P>
154 inline ext::shared_ptr<typename MCForwardEuropeanHestonEngine<RNG,S,P>::path_pricer_type>
155 MCForwardEuropeanHestonEngine<RNG,S,P>::pathPricer() const {
156
157 TimeGrid timeGrid = this->timeGrid();
158
159 Time resetTime = this->process_->time(this->arguments_.resetDate);
160 Size resetIndex = timeGrid.closestIndex(t: resetTime);
161
162 ext::shared_ptr<PlainVanillaPayoff> payoff =
163 ext::dynamic_pointer_cast<PlainVanillaPayoff>(
164 this->arguments_.payoff);
165 QL_REQUIRE(payoff, "non-plain payoff given");
166
167 ext::shared_ptr<EuropeanExercise> exercise =
168 ext::dynamic_pointer_cast<EuropeanExercise>(
169 this->arguments_.exercise);
170 QL_REQUIRE(exercise, "wrong exercise given");
171
172 ext::shared_ptr<P> process =
173 ext::dynamic_pointer_cast<P>(this->process_);
174 QL_REQUIRE(process, "Heston like process required");
175
176 return ext::shared_ptr<typename
177 MCForwardEuropeanHestonEngine<RNG,S,P>::path_pricer_type>(
178 new ForwardEuropeanHestonPathPricer(
179 payoff->optionType(),
180 this->arguments_.moneyness,
181 resetIndex,
182 process->riskFreeRate()->discount(
183 timeGrid.back())));
184 }
185
186 template <class RNG, class S, class P>
187 inline ext::shared_ptr<typename MCForwardEuropeanHestonEngine<RNG,S,P>::path_pricer_type>
188 MCForwardEuropeanHestonEngine<RNG,S,P>::controlPathPricer() const {
189
190 // Control variate prices a vanilla option on the path, and compares to analytical Heston
191 // vanilla price. First entry in TimeGrid is 0, so use the existing path pricer reset at 0
192 Size resetIndex = 0;
193 TimeGrid timeGrid = this->timeGrid();
194
195 ext::shared_ptr<PlainVanillaPayoff> payoff =
196 ext::dynamic_pointer_cast<PlainVanillaPayoff>(
197 this->arguments_.payoff);
198 QL_REQUIRE(payoff, "non-plain payoff given");
199
200 ext::shared_ptr<EuropeanExercise> exercise =
201 ext::dynamic_pointer_cast<EuropeanExercise>(
202 this->arguments_.exercise);
203 QL_REQUIRE(exercise, "wrong exercise given");
204
205 ext::shared_ptr<P> process =
206 ext::dynamic_pointer_cast<P>(this->process_);
207 QL_REQUIRE(process, "Heston like process required");
208
209 return ext::shared_ptr<typename
210 MCForwardEuropeanHestonEngine<RNG,S,P>::path_pricer_type>(
211 new ForwardEuropeanHestonPathPricer(
212 payoff->optionType(),
213 this->arguments_.moneyness,
214 resetIndex,
215 process->riskFreeRate()->discount(
216 timeGrid.back())));
217 }
218
219 template <class RNG, class S, class P>
220 inline MakeMCForwardEuropeanHestonEngine<RNG, S, P>::MakeMCForwardEuropeanHestonEngine(
221 ext::shared_ptr<P> process)
222 : process_(std::move(process)), steps_(Null<Size>()), stepsPerYear_(Null<Size>()),
223 samples_(Null<Size>()), maxSamples_(Null<Size>()), tolerance_(Null<Real>()) {}
224
225 template <class RNG, class S, class P>
226 inline MakeMCForwardEuropeanHestonEngine<RNG,S,P>&
227 MakeMCForwardEuropeanHestonEngine<RNG,S,P>::withSteps(Size steps) {
228 steps_ = steps;
229 return *this;
230 }
231
232 template <class RNG, class S, class P>
233 inline MakeMCForwardEuropeanHestonEngine<RNG,S,P>&
234 MakeMCForwardEuropeanHestonEngine<RNG,S,P>::withStepsPerYear(Size steps) {
235 stepsPerYear_ = steps;
236 return *this;
237 }
238
239 template <class RNG, class S, class P>
240 inline MakeMCForwardEuropeanHestonEngine<RNG,S,P>&
241 MakeMCForwardEuropeanHestonEngine<RNG,S,P>::withSamples(Size samples) {
242 QL_REQUIRE(tolerance_ == Null<Real>(),
243 "tolerance already set");
244 samples_ = samples;
245 return *this;
246 }
247
248 template <class RNG, class S, class P>
249 inline MakeMCForwardEuropeanHestonEngine<RNG,S,P>&
250 MakeMCForwardEuropeanHestonEngine<RNG,S,P>::withAbsoluteTolerance(
251 Real tolerance) {
252 QL_REQUIRE(samples_ == Null<Size>(),
253 "number of samples already set");
254 QL_REQUIRE(RNG::allowsErrorEstimate,
255 "chosen random generator policy "
256 "does not allow an error estimate");
257 tolerance_ = tolerance;
258 return *this;
259 }
260
261 template <class RNG, class S, class P>
262 inline MakeMCForwardEuropeanHestonEngine<RNG,S,P>&
263 MakeMCForwardEuropeanHestonEngine<RNG,S,P>::withMaxSamples(Size samples) {
264 maxSamples_ = samples;
265 return *this;
266 }
267
268 template <class RNG, class S, class P>
269 inline MakeMCForwardEuropeanHestonEngine<RNG,S,P>&
270 MakeMCForwardEuropeanHestonEngine<RNG,S,P>::withSeed(BigNatural seed) {
271 seed_ = seed;
272 return *this;
273 }
274
275 template <class RNG, class S, class P>
276 inline MakeMCForwardEuropeanHestonEngine<RNG,S,P>&
277 MakeMCForwardEuropeanHestonEngine<RNG,S,P>::withAntitheticVariate(bool b) {
278 antithetic_ = b;
279 return *this;
280 }
281
282 template <class RNG, class S, class P>
283 inline MakeMCForwardEuropeanHestonEngine<RNG,S,P>&
284 MakeMCForwardEuropeanHestonEngine<RNG,S,P>::withControlVariate(bool b) {
285 controlVariate_ = b;
286 return *this;
287 }
288
289 template <class RNG, class S, class P>
290 inline MakeMCForwardEuropeanHestonEngine<RNG,S,P>::operator ext::shared_ptr<PricingEngine>()
291 const {
292 QL_REQUIRE(steps_ != Null<Size>() || stepsPerYear_ != Null<Size>(),
293 "number of steps not given");
294 QL_REQUIRE(steps_ == Null<Size>() || stepsPerYear_ == Null<Size>(),
295 "number of steps overspecified - set EITHER steps OR stepsPerYear");
296 return ext::shared_ptr<PricingEngine>(new
297 MCForwardEuropeanHestonEngine<RNG,S,P>(process_,
298 steps_,
299 stepsPerYear_,
300 antithetic_,
301 samples_,
302 tolerance_,
303 maxSamples_,
304 seed_,
305 controlVariate_));
306 }
307}
308
309
310#endif
311

source code of quantlib/ql/pricingengines/forward/mcforwardeuropeanhestonengine.hpp