Coverage Report

Created: 2026-08-14 07:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/pricingengines/barrier/mcbarrierengine.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2003 Neil Firth
5
 Copyright (C) 2003 Ferdinando Ametrano
6
 Copyright (C) 2003, 2004, 2005 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
#include <ql/pricingengines/barrier/mcbarrierengine.hpp>
23
#include <utility>
24
25
namespace QuantLib {
26
27
    BarrierPathPricer::BarrierPathPricer(Barrier::Type barrierType,
28
                                         Real barrier,
29
                                         Real rebate,
30
                                         Option::Type type,
31
                                         Real strike,
32
                                         std::vector<DiscountFactor> discounts,
33
                                         ext::shared_ptr<StochasticProcess1D> diffProcess,
34
                                         PseudoRandom::ursg_type sequenceGen)
35
0
    : barrierType_(barrierType), barrier_(barrier), rebate_(rebate),
36
0
      diffProcess_(std::move(diffProcess)), sequenceGen_(std::move(sequenceGen)),
37
0
      payoff_(type, strike), discounts_(std::move(discounts)) {
38
0
        QL_REQUIRE(strike>=0.0,
39
0
                   "strike less than zero not allowed");
40
0
        QL_REQUIRE(barrier>0.0,
41
0
                   "barrier less/equal zero not allowed");
42
0
    }
43
44
45
0
    Real BarrierPathPricer::operator()(const Path& path) const {
46
0
        static Size null = Null<Size>();
47
0
        Size n = path.length();
48
0
        QL_REQUIRE(n>1, "the path cannot be empty");
49
50
0
        bool isOptionActive = false;
51
0
        Size knockNode = null;
52
0
        Real asset_price = path.front();
53
0
        Real new_asset_price;
54
0
        Real x, y;
55
0
        Volatility vol;
56
0
        const TimeGrid& timeGrid = path.timeGrid();
57
0
        Time dt;
58
0
        std::vector<Real> u = sequenceGen_.nextSequence().value;
59
0
        Size i;
60
61
0
        switch (barrierType_) {
62
0
          case Barrier::DownIn:
63
0
            isOptionActive = false;
64
0
            for (i = 0; i < n-1; i++) {
65
0
                new_asset_price = path[i+1];
66
                // terminal or initial vol?
67
0
                vol = diffProcess_->diffusion(timeGrid[i],asset_price);
68
0
                dt = timeGrid.dt(i);
69
70
0
                x = std::log(new_asset_price / asset_price);
71
0
                y = 0.5*(x - std::sqrt (x*x - 2*vol*vol*dt*std::log(u[i])));
72
0
                y = asset_price * std::exp(y);
73
0
                if (y <= barrier_) {
74
0
                    isOptionActive = true;
75
0
                    if (knockNode == null)
76
0
                        knockNode = i+1;
77
0
                }
78
0
                asset_price = new_asset_price;
79
0
            }
80
0
            break;
81
0
          case Barrier::UpIn:
82
0
            isOptionActive = false;
83
0
            for (i = 0; i < n-1; i++) {
84
0
                new_asset_price = path[i+1];
85
                // terminal or initial vol?
86
0
                vol = diffProcess_->diffusion(timeGrid[i],asset_price);
87
0
                dt = timeGrid.dt(i);
88
89
0
                x = std::log(new_asset_price / asset_price);
90
0
                y = 0.5*(x + std::sqrt(x*x - 2*vol*vol*dt*std::log((1-u[i]))));
91
0
                y = asset_price * std::exp(y);
92
0
                if (y >= barrier_) {
93
0
                    isOptionActive = true;
94
0
                    if (knockNode == null)
95
0
                        knockNode = i+1;
96
0
                }
97
0
                asset_price = new_asset_price;
98
0
            }
99
0
            break;
100
0
          case Barrier::DownOut:
101
0
            isOptionActive = true;
102
0
            for (i = 0; i < n-1; i++) {
103
0
                new_asset_price = path[i+1];
104
                // terminal or initial vol?
105
0
                vol = diffProcess_->diffusion(timeGrid[i],asset_price);
106
0
                dt = timeGrid.dt(i);
107
108
0
                x = std::log(new_asset_price / asset_price);
109
0
                y = 0.5*(x - std::sqrt(x*x - 2*vol*vol*dt*std::log(u[i])));
110
0
                y = asset_price * std::exp(y);
111
0
                if (y <= barrier_) {
112
0
                    isOptionActive = false;
113
0
                    if (knockNode == null)
114
0
                        knockNode = i+1;
115
0
                }
116
0
                asset_price = new_asset_price;
117
0
            }
118
0
            break;
119
0
          case Barrier::UpOut:
120
0
            isOptionActive = true;
121
0
            for (i = 0; i < n-1; i++) {
122
0
                new_asset_price = path[i+1];
123
                // terminal or initial vol?
124
0
                vol = diffProcess_->diffusion(timeGrid[i],asset_price);
125
0
                dt = timeGrid.dt(i);
126
127
0
                x = std::log(new_asset_price / asset_price);
128
0
                y = 0.5*(x + std::sqrt(x*x - 2*vol*vol*dt*std::log((1-u[i]))));
129
0
                y = asset_price * std::exp(y);
130
0
                if (y >= barrier_) {
131
0
                    isOptionActive = false;
132
0
                    if (knockNode == null)
133
0
                        knockNode = i+1;
134
0
                }
135
0
                asset_price = new_asset_price;
136
0
            }
137
0
            break;
138
0
          default:
139
0
            QL_FAIL("unknown barrier type");
140
0
        }
141
142
0
        if (isOptionActive) {
143
0
            return payoff_(asset_price) * discounts_.back();
144
0
        } else {
145
0
            switch (barrierType_) {
146
0
              case Barrier::UpIn:
147
0
              case Barrier::DownIn:
148
0
                return rebate_*discounts_.back();
149
0
              case Barrier::UpOut:
150
0
              case Barrier::DownOut:
151
0
                return rebate_*discounts_[knockNode];
152
0
              default:
153
0
                QL_FAIL("unknown barrier type");
154
0
            }
155
0
        }
156
0
    }
157
158
159
    BiasedBarrierPathPricer::BiasedBarrierPathPricer(Barrier::Type barrierType,
160
                                                     Real barrier,
161
                                                     Real rebate,
162
                                                     Option::Type type,
163
                                                     Real strike,
164
                                                     std::vector<DiscountFactor> discounts)
165
0
    : barrierType_(barrierType), barrier_(barrier), rebate_(rebate), payoff_(type, strike),
166
0
      discounts_(std::move(discounts)) {
167
0
        QL_REQUIRE(strike>=0.0,
168
0
                   "strike less than zero not allowed");
169
0
        QL_REQUIRE(barrier>0.0,
170
0
                   "barrier less/equal zero not allowed");
171
0
    }
172
173
174
0
    Real BiasedBarrierPathPricer::operator()(const Path& path) const {
175
0
        static Size null = Null<Size>();
176
0
        Size n = path.length();
177
0
        QL_REQUIRE(n>1, "the path cannot be empty");
178
179
0
        bool isOptionActive = false;
180
0
        Size knockNode = null;
181
0
        Real asset_price = path.front();
182
0
        Size i;
183
184
0
        switch (barrierType_) {
185
0
          case Barrier::DownIn:
186
0
            isOptionActive = false;
187
0
            for (i = 1; i < n; i++) {
188
0
                asset_price = path[i];
189
0
                if (asset_price <= barrier_) {
190
0
                    isOptionActive = true;
191
0
                    if (knockNode == null)
192
0
                        knockNode = i;
193
0
                }
194
0
            }
195
0
            break;
196
0
          case Barrier::UpIn:
197
0
            isOptionActive = false;
198
0
            for (i = 1; i < n; i++) {
199
0
                asset_price = path[i];
200
0
                if (asset_price >= barrier_) {
201
0
                    isOptionActive = true;
202
0
                    if (knockNode == null)
203
0
                        knockNode = i;
204
0
                }
205
0
            }
206
0
            break;
207
0
          case Barrier::DownOut:
208
0
            isOptionActive = true;
209
0
            for (i = 1; i < n; i++) {
210
0
                asset_price = path[i];
211
0
                if (asset_price <= barrier_) {
212
0
                    isOptionActive = false;
213
0
                    if (knockNode == null)
214
0
                        knockNode = i;
215
0
                }
216
0
            }
217
0
            break;
218
0
          case Barrier::UpOut:
219
0
            isOptionActive = true;
220
0
            for (i = 1; i < n; i++) {
221
0
                asset_price = path[i];
222
0
                if (asset_price >= barrier_) {
223
0
                    isOptionActive = false;
224
0
                    if (knockNode == null)
225
0
                        knockNode = i;
226
0
                }
227
0
            }
228
0
            break;
229
0
          default:
230
0
            QL_FAIL("unknown barrier type");
231
0
        }
232
233
0
        if (isOptionActive) {
234
0
            return payoff_(asset_price) * discounts_.back();
235
0
        } else {
236
0
            switch (barrierType_) {
237
0
              case Barrier::UpIn:
238
0
              case Barrier::DownIn:
239
0
                return rebate_*discounts_.back();
240
0
              case Barrier::UpOut:
241
0
              case Barrier::DownOut:
242
0
                return rebate_*discounts_[knockNode];
243
0
              default:
244
                QL_FAIL("unknown barrier type");
245
0
            }
246
0
        }
247
0
    }
248
249
}