Coverage Report

Created: 2026-06-23 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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