/src/quantlib/ql/pricingengines/mcsimulation.hpp
Line | Count | Source |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2003 Ferdinando Ametrano |
5 | | Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl |
6 | | Copyright (C) 2007 StatPro Italia srl |
7 | | |
8 | | This file is part of QuantLib, a free-software/open-source library |
9 | | for financial quantitative analysts and developers - http://quantlib.org/ |
10 | | |
11 | | QuantLib is free software: you can redistribute it and/or modify it |
12 | | under the terms of the QuantLib license. You should have received a |
13 | | copy of the license along with this program; if not, please email |
14 | | <quantlib-dev@lists.sf.net>. The license is also available online at |
15 | | <https://www.quantlib.org/license.shtml>. |
16 | | |
17 | | This program is distributed in the hope that it will be useful, but WITHOUT |
18 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
19 | | FOR A PARTICULAR PURPOSE. See the license for more details. |
20 | | */ |
21 | | |
22 | | /*! \file mcsimulation.hpp |
23 | | \brief framework for Monte Carlo engines |
24 | | */ |
25 | | |
26 | | #ifndef quantlib_montecarlo_engine_hpp |
27 | | #define quantlib_montecarlo_engine_hpp |
28 | | |
29 | | #include <ql/methods/montecarlo/montecarlomodel.hpp> |
30 | | |
31 | | namespace QuantLib { |
32 | | |
33 | | //! base class for Monte Carlo engines |
34 | | /*! Eventually this class might offer greeks methods. Deriving a |
35 | | class from McSimulation gives an easy way to write a Monte |
36 | | Carlo engine. |
37 | | |
38 | | See McVanillaEngine as an example. |
39 | | */ |
40 | | |
41 | | template <template <class> class MC, class RNG, class S = Statistics> |
42 | | class McSimulation { |
43 | | public: |
44 | | typedef typename MonteCarloModel<MC,RNG,S>::path_generator_type |
45 | | path_generator_type; |
46 | | typedef typename MonteCarloModel<MC,RNG,S>::path_pricer_type |
47 | | path_pricer_type; |
48 | | typedef typename MonteCarloModel<MC,RNG,S>::stats_type |
49 | | stats_type; |
50 | | typedef typename MonteCarloModel<MC,RNG,S>::result_type result_type; |
51 | | |
52 | 709 | virtual ~McSimulation() = default; |
53 | | //! add samples until the required absolute tolerance is reached |
54 | | result_type value(Real tolerance, |
55 | | Size maxSamples = QL_MAX_INTEGER, |
56 | | Size minSamples = 1023) const; |
57 | | //! simulate a fixed number of samples |
58 | | result_type valueWithSamples(Size samples) const; |
59 | | //! error estimated using the samples simulated so far |
60 | | result_type errorEstimate() const; |
61 | | //! access to the sample accumulator for richer statistics |
62 | | const stats_type& sampleAccumulator() const; |
63 | | //! basic calculate method provided to inherited pricing engines |
64 | | void calculate(Real requiredTolerance, |
65 | | Size requiredSamples, |
66 | | Size maxSamples) const; |
67 | | protected: |
68 | | McSimulation(bool antitheticVariate, |
69 | | bool controlVariate) |
70 | 709 | : antitheticVariate_(antitheticVariate), |
71 | 709 | controlVariate_(controlVariate) {} |
72 | | virtual ext::shared_ptr<path_pricer_type> pathPricer() const = 0; |
73 | | virtual ext::shared_ptr<path_generator_type> pathGenerator() |
74 | | const = 0; |
75 | | virtual TimeGrid timeGrid() const = 0; |
76 | 0 | virtual ext::shared_ptr<path_pricer_type> controlPathPricer() const { |
77 | 0 | return ext::shared_ptr<path_pricer_type>(); |
78 | 0 | } |
79 | | virtual ext::shared_ptr<path_generator_type> |
80 | 0 | controlPathGenerator() const { |
81 | 0 | return ext::shared_ptr<path_generator_type>(); |
82 | 0 | } |
83 | 0 | virtual ext::shared_ptr<PricingEngine> controlPricingEngine() const { |
84 | 0 | return ext::shared_ptr<PricingEngine>(); |
85 | 0 | } |
86 | 0 | virtual result_type controlVariateValue() const { |
87 | 0 | return Null<result_type>(); |
88 | 0 | } |
89 | | template <class Sequence> |
90 | | static Real maxError(const Sequence& sequence) { |
91 | | return *std::max_element(sequence.begin(), sequence.end()); |
92 | | } |
93 | 0 | static Real maxError(Real error) { |
94 | 0 | return error; |
95 | 0 | } |
96 | | |
97 | | mutable ext::shared_ptr<MonteCarloModel<MC,RNG,S> > mcModel_; |
98 | | bool antitheticVariate_, controlVariate_; |
99 | | }; |
100 | | |
101 | | |
102 | | // inline definitions |
103 | | template <template <class> class MC, class RNG, class S> |
104 | | inline typename McSimulation<MC,RNG,S>::result_type |
105 | | McSimulation<MC,RNG,S>::value(Real tolerance, |
106 | | Size maxSamples, |
107 | 0 | Size minSamples) const { |
108 | 0 | Size sampleNumber = |
109 | 0 | mcModel_->sampleAccumulator().samples(); |
110 | 0 | if (sampleNumber<minSamples) { |
111 | 0 | mcModel_->addSamples(minSamples-sampleNumber); |
112 | 0 | sampleNumber = mcModel_->sampleAccumulator().samples(); |
113 | 0 | } |
114 | |
|
115 | 0 | Size nextBatch; |
116 | 0 | Real order; |
117 | 0 | result_type error(mcModel_->sampleAccumulator().errorEstimate()); |
118 | 0 | while (maxError(error) > tolerance) { |
119 | 0 | QL_REQUIRE(sampleNumber<maxSamples, |
120 | 0 | "max number of samples (" << maxSamples |
121 | 0 | << ") reached, while error (" << error |
122 | 0 | << ") is still above tolerance (" << tolerance << ")"); |
123 | | |
124 | | // conservative estimate of how many samples are needed |
125 | 0 | order = maxError(Real(error*error))/tolerance/tolerance; |
126 | 0 | nextBatch = |
127 | 0 | Size(std::max<Real>(static_cast<Real>(sampleNumber)*order*0.8 - static_cast<Real>(sampleNumber), |
128 | 0 | static_cast<Real>(minSamples))); |
129 | | |
130 | | // do not exceed maxSamples |
131 | 0 | nextBatch = std::min(nextBatch, maxSamples-sampleNumber); |
132 | 0 | sampleNumber += nextBatch; |
133 | 0 | mcModel_->addSamples(nextBatch); |
134 | 0 | error = result_type(mcModel_->sampleAccumulator().errorEstimate()); |
135 | 0 | } |
136 | | |
137 | 0 | return result_type(mcModel_->sampleAccumulator().mean()); |
138 | 0 | } |
139 | | |
140 | | |
141 | | template <template <class> class MC, class RNG, class S> |
142 | | inline typename McSimulation<MC,RNG,S>::result_type |
143 | 709 | McSimulation<MC,RNG,S>::valueWithSamples(Size samples) const { |
144 | | |
145 | 709 | Size sampleNumber = mcModel_->sampleAccumulator().samples(); |
146 | | |
147 | 709 | QL_REQUIRE(samples>=sampleNumber, |
148 | 709 | "number of already simulated samples (" << sampleNumber |
149 | 709 | << ") greater than requested samples (" << samples << ")"); |
150 | | |
151 | 709 | mcModel_->addSamples(samples-sampleNumber); |
152 | | |
153 | 709 | return result_type(mcModel_->sampleAccumulator().mean()); |
154 | 709 | } |
155 | | |
156 | | |
157 | | template <template <class> class MC, class RNG, class S> |
158 | | inline void McSimulation<MC,RNG,S>::calculate(Real requiredTolerance, |
159 | | Size requiredSamples, |
160 | 709 | Size maxSamples) const { |
161 | | |
162 | 709 | QL_REQUIRE(requiredTolerance != Null<Real>() || |
163 | 709 | requiredSamples != Null<Size>(), |
164 | 709 | "neither tolerance nor number of samples set"); |
165 | | |
166 | | //! Initialize the one-factor Monte Carlo |
167 | 709 | if (this->controlVariate_) { |
168 | |
|
169 | 0 | result_type controlVariateValue = this->controlVariateValue(); |
170 | 0 | QL_REQUIRE(controlVariateValue != Null<result_type>(), |
171 | 0 | "engine does not provide " |
172 | 0 | "control-variation price"); |
173 | | |
174 | 0 | ext::shared_ptr<path_pricer_type> controlPP = |
175 | 0 | this->controlPathPricer(); |
176 | 0 | QL_REQUIRE(controlPP, |
177 | 0 | "engine does not provide " |
178 | 0 | "control-variation path pricer"); |
179 | | |
180 | 0 | ext::shared_ptr<path_generator_type> controlPG = |
181 | 0 | this->controlPathGenerator(); |
182 | |
|
183 | 0 | this->mcModel_ = |
184 | 0 | ext::shared_ptr<MonteCarloModel<MC,RNG,S> >( |
185 | 0 | new MonteCarloModel<MC,RNG,S>( |
186 | 0 | pathGenerator(), this->pathPricer(), stats_type(), |
187 | 0 | this->antitheticVariate_, controlPP, |
188 | 0 | controlVariateValue, controlPG)); |
189 | 709 | } else { |
190 | 709 | this->mcModel_ = |
191 | 709 | ext::shared_ptr<MonteCarloModel<MC,RNG,S> >( |
192 | 709 | new MonteCarloModel<MC,RNG,S>( |
193 | 709 | pathGenerator(), this->pathPricer(), S(), |
194 | 709 | this->antitheticVariate_)); |
195 | 709 | } |
196 | | |
197 | 709 | if (requiredTolerance != Null<Real>()) { |
198 | 0 | if (maxSamples != Null<Size>()) |
199 | 0 | this->value(requiredTolerance, maxSamples); |
200 | 0 | else |
201 | 0 | this->value(requiredTolerance); |
202 | 709 | } else { |
203 | 709 | this->valueWithSamples(requiredSamples); |
204 | 709 | } |
205 | | |
206 | 709 | } |
207 | | |
208 | | template <template <class> class MC, class RNG, class S> |
209 | | inline typename McSimulation<MC,RNG,S>::result_type |
210 | | McSimulation<MC,RNG,S>::errorEstimate() const { |
211 | | return mcModel_->sampleAccumulator().errorEstimate(); |
212 | | } |
213 | | |
214 | | template <template <class> class MC, class RNG, class S> |
215 | | inline const typename McSimulation<MC,RNG,S>::stats_type& |
216 | | McSimulation<MC,RNG,S>::sampleAccumulator() const { |
217 | | return mcModel_->sampleAccumulator(); |
218 | | } |
219 | | |
220 | | } |
221 | | |
222 | | |
223 | | #endif |