Coverage Report

Created: 2026-02-03 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/pricingengines/barrier/discretizedbarrieroption.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2014 Thema Consulting SA
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
 <https://www.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/pricingengines/barrier/discretizedbarrieroption.hpp>
21
#include <vector>
22
23
namespace QuantLib {
24
25
    DiscretizedBarrierOption::DiscretizedBarrierOption(
26
                                         const BarrierOption::arguments& args,
27
                                         const StochasticProcess& process,
28
                                         const TimeGrid& grid)
29
0
    : arguments_(args), vanilla_(arguments_, process, grid) {
30
0
        QL_REQUIRE(!args.exercise->dates().empty(), "specify at least one stopping date");
31
32
0
        stoppingTimes_.resize(args.exercise->dates().size());
33
0
        for (Size i=0; i<stoppingTimes_.size(); ++i) {
34
0
            stoppingTimes_[i] =
35
0
                process.time(args.exercise->date(i));
36
0
            if (!grid.empty()) {
37
                // adjust to the given grid
38
0
                stoppingTimes_[i] = grid.closestTime(stoppingTimes_[i]);
39
0
            }
40
0
        }
41
0
    }
42
43
0
    void DiscretizedBarrierOption::reset(Size size) {
44
0
        vanilla_.initialize(method(), time());
45
0
        values_ = Array(size, 0.0);
46
0
        adjustValues();
47
0
    }
48
49
0
    void DiscretizedBarrierOption::postAdjustValuesImpl() {
50
0
        if (arguments_.barrierType==Barrier::DownIn ||
51
0
                     arguments_.barrierType==Barrier::UpIn) {
52
0
            vanilla_.rollback(time());
53
0
        }
54
0
        Array grid = method()->grid(time());
55
0
        checkBarrier(values_, grid);
56
0
    }
57
58
0
    void DiscretizedBarrierOption::checkBarrier(Array &optvalues, const Array &grid) const {
59
60
0
        Time now = time();
61
0
        bool endTime = isOnTime(stoppingTimes_.back());
62
0
        bool stoppingTime = false;         
63
0
        switch (arguments_.exercise->type()) {
64
0
          case Exercise::American:
65
0
            if (now <= stoppingTimes_[1] &&
66
0
                now >= stoppingTimes_[0])
67
0
                stoppingTime = true;
68
0
            break;
69
0
          case Exercise::European:
70
0
            if (isOnTime(stoppingTimes_[0]))
71
0
                stoppingTime = true;
72
0
            break;
73
0
          case Exercise::Bermudan:
74
0
              for (Real i : stoppingTimes_) {
75
0
                  if (isOnTime(i)) {
76
0
                      stoppingTime = true;
77
0
                      break;
78
0
                  }
79
0
              }
80
0
            break;
81
0
          default:
82
0
            QL_FAIL("invalid option type");
83
0
        }
84
0
        for (Size j=0; j<optvalues.size(); j++) {
85
0
            switch (arguments_.barrierType) {
86
0
              case Barrier::DownIn:
87
0
                  if (grid[j] <= arguments_.barrier) {
88
                     // knocked in
89
0
                     if (stoppingTime) {
90
0
                        optvalues[j] = std::max(vanilla_.values()[j],
91
0
                                      (*arguments_.payoff)(grid[j]));
92
0
                     }
93
0
                     else
94
0
                         optvalues[j] = vanilla_.values()[j]; 
95
0
                  }
96
0
                  else if (endTime)
97
0
                      optvalues[j] = arguments_.rebate;
98
0
                  break;
99
0
              case Barrier::DownOut:
100
0
                  if (grid[j] <= arguments_.barrier)
101
0
                      optvalues[j] = arguments_.rebate; // knocked out
102
0
                  else if (stoppingTime) {
103
0
                      optvalues[j] = std::max(optvalues[j],
104
0
                                     (*arguments_.payoff)(grid[j]));
105
0
                  }
106
0
                  break;
107
0
              case Barrier::UpIn:
108
0
                  if (grid[j] >= arguments_.barrier) {
109
                     // knocked in
110
0
                     if (stoppingTime) {
111
0
                         optvalues[j] = std::max(vanilla_.values()[j],
112
0
                                      (*arguments_.payoff)(grid[j]));
113
0
                     }
114
0
                     else
115
0
                         optvalues[j] = vanilla_.values()[j]; 
116
0
                  }
117
0
                  else if (endTime)
118
0
                      optvalues[j] = arguments_.rebate;
119
0
                  break;
120
0
              case Barrier::UpOut:
121
0
                  if (grid[j] >= arguments_.barrier)
122
0
                     optvalues[j] = arguments_.rebate; // knocked out
123
0
                  else if (stoppingTime)
124
0
                      optvalues[j] = std::max(optvalues[j],
125
0
                                     (*arguments_.payoff)(grid[j]));
126
0
                  break;
127
0
              default:
128
0
                  QL_FAIL("invalid barrier type");
129
0
            }
130
0
        }
131
0
    }
132
133
134
135
    DiscretizedDermanKaniBarrierOption::DiscretizedDermanKaniBarrierOption(
136
                                         const BarrierOption::arguments& args,
137
                                         const StochasticProcess& process,
138
                                         const TimeGrid& grid)
139
0
    : unenhanced_(args, process, grid) {
140
0
    }
141
142
0
    void DiscretizedDermanKaniBarrierOption::reset(Size size) {
143
0
        unenhanced_.initialize(method(), time());
144
0
        values_ = Array(size, 0.0);
145
0
        adjustValues();
146
0
    }
147
148
0
    void DiscretizedDermanKaniBarrierOption::postAdjustValuesImpl() {
149
0
        unenhanced_.rollback(time());
150
151
0
        Array grid = method()->grid(time());
152
0
        adjustBarrier(values_, grid);
153
0
        unenhanced_.checkBarrier(values_, grid); // compute payoffs
154
0
    }
155
156
0
    void DiscretizedDermanKaniBarrierOption::adjustBarrier(Array &optvalues, const Array &grid) {
157
0
        Real barrier = unenhanced_.arguments().barrier;
158
0
        Real rebate = unenhanced_.arguments().rebate;
159
0
        switch (unenhanced_.arguments().barrierType) {
160
0
           case Barrier::DownIn:
161
0
              for (Size j=0; j<optvalues.size()-1; ++j) {
162
0
                  if (grid[j]<=barrier && grid[j+1] > barrier) {
163
                      // grid[j+1] above barrier, grid[j] under (in),
164
                      // interpolate optvalues[j+1]
165
0
                      Real ltob = (barrier-grid[j]);
166
0
                      Real htob = (grid[j+1]-barrier);
167
0
                      Real htol = (grid[j+1]-grid[j]);
168
0
                      Real u1 = unenhanced_.values()[j+1];
169
0
                      Real t1 = unenhanced_.vanilla()[j+1];
170
0
                      optvalues[j+1] = std::max(0.0, (ltob*t1+htob*u1)/htol);
171
0
                  }
172
0
              }
173
0
              break;
174
0
           case Barrier::DownOut:
175
0
              for (Size j=0; j<optvalues.size()-1; ++j) {
176
0
                  if (grid[j]<=barrier && grid[j+1] > barrier) {
177
                      // grid[j+1] above barrier, grid[j] under (out),
178
                      // interpolate optvalues[j+1]
179
0
                      Real a = (barrier-grid[j])*rebate;
180
0
                      Real b = (grid[j+1]-barrier)*unenhanced_.values()[j+1];
181
0
                      Real c = (grid[j+1]-grid[j]);
182
0
                      optvalues[j+1] = std::max(0.0, (a+b)/c);
183
0
                  }
184
0
              }
185
0
              break;
186
0
           case Barrier::UpIn:
187
0
              for (Size j=0; j<optvalues.size()-1; ++j) {
188
0
                  if (grid[j] < barrier && grid[j+1] >= barrier) {
189
                      // grid[j+1] above barrier (in), grid[j] under, 
190
                      // interpolate optvalues[j]
191
0
                      Real ltob = (barrier-grid[j]);
192
0
                      Real htob = (grid[j+1]-barrier);
193
0
                      Real htol = (grid[j+1]-grid[j]);
194
0
                      Real u = unenhanced_.values()[j];
195
0
                      Real t = unenhanced_.vanilla()[j];
196
0
                      optvalues[j] = std::max(0.0, (ltob*u+htob*t)/htol); // derman std
197
0
                  }
198
0
               }
199
0
              break;
200
0
           case Barrier::UpOut:
201
0
              for (Size j=0; j<optvalues.size()-1; ++j) {
202
0
                  if (grid[j] < barrier && grid[j+1] >= barrier) {
203
                      // grid[j+1] above barrier (out), grid[j] under, 
204
                      // interpolate optvalues[j]
205
0
                      Real a = (barrier-grid[j])*unenhanced_.values()[j];
206
0
                      Real b = (grid[j+1]-barrier)*rebate;
207
0
                      Real c = (grid[j+1]-grid[j]);
208
0
                      optvalues[j] = std::max(0.0, (a+b)/c);
209
0
                  }
210
0
              }
211
0
              break;
212
0
        }
213
0
    }
214
215
}
216