Coverage Report

Created: 2025-08-11 06:28

/src/quantlib/ql/experimental/barrieroption/discretizeddoublebarrieroption.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2015 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
 <http://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/experimental/barrieroption/discretizeddoublebarrieroption.hpp>
21
#include <vector>
22
23
namespace QuantLib {
24
25
    DiscretizedDoubleBarrierOption::DiscretizedDoubleBarrierOption(
26
                                         const DoubleBarrierOption::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 DiscretizedDoubleBarrierOption::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 DiscretizedDoubleBarrierOption::postAdjustValuesImpl() {
50
0
        if (arguments_.barrierType!=DoubleBarrier::KnockOut) {
51
0
            vanilla_.rollback(time());
52
0
        }
53
0
        Array grid = method()->grid(time());
54
0
        checkBarrier(values_, grid);
55
0
    }
56
57
0
    void DiscretizedDoubleBarrierOption::checkBarrier(Array &optvalues, const Array &grid) const {
58
59
0
        Time now = time();
60
0
        bool endTime = isOnTime(stoppingTimes_.back());
61
0
        bool stoppingTime = false;         
62
0
        switch (arguments_.exercise->type()) {
63
0
          case Exercise::American:
64
0
            if (now <= stoppingTimes_[1] &&
65
0
                now >= stoppingTimes_[0])
66
0
                stoppingTime = true;
67
0
            break;
68
0
          case Exercise::European:
69
0
            if (isOnTime(stoppingTimes_[0]))
70
0
                stoppingTime = true;
71
0
            break;
72
0
          case Exercise::Bermudan:
73
0
              for (Real i : stoppingTimes_) {
74
0
                  if (isOnTime(i)) {
75
0
                      stoppingTime = true;
76
0
                      break;
77
0
                  }
78
0
              }
79
0
            break;
80
0
          default:
81
0
            QL_FAIL("invalid option type");
82
0
        }
83
0
        for (Size j=0; j<optvalues.size(); j++) {
84
0
            switch (arguments_.barrierType) {
85
0
              case DoubleBarrier::KnockIn:
86
0
                  if (grid[j] <= arguments_.barrier_lo) {
87
                     // knocked in dn
88
0
                     if (stoppingTime) {
89
0
                         optvalues[j] = std::max(vanilla()[j],
90
0
                                      (*arguments_.payoff)(grid[j]));
91
0
                     }
92
0
                     else
93
0
                         optvalues[j] = vanilla()[j]; 
94
0
                  }
95
0
                  else if (grid[j] >= arguments_.barrier_hi) {
96
                     // knocked in up
97
0
                     if (stoppingTime) {
98
0
                         optvalues[j] = std::max(vanilla()[j],
99
0
                                      (*arguments_.payoff)(grid[j]));
100
0
                     }
101
0
                     else
102
0
                         optvalues[j] = vanilla()[j]; 
103
0
                  }
104
0
                  else if (endTime)
105
0
                      optvalues[j] = arguments_.rebate;
106
0
                  break;
107
0
              case DoubleBarrier::KnockOut:
108
0
                  if (grid[j] <= arguments_.barrier_lo)
109
0
                      optvalues[j] = arguments_.rebate; // knocked out lo
110
0
                  else if (grid[j] >= arguments_.barrier_hi)
111
0
                     optvalues[j] = arguments_.rebate; // knocked out hi
112
0
                  else if (stoppingTime)
113
0
                      optvalues[j] = std::max(optvalues[j],
114
0
                                     (*arguments_.payoff)(grid[j]));
115
0
                  break;
116
0
              case DoubleBarrier::KIKO:
117
                  // low barrier is KI, high is KO
118
0
                  if (grid[j] <= arguments_.barrier_lo) {
119
                     // knocked in dn
120
0
                     if (stoppingTime) {
121
0
                         optvalues[j] = std::max(vanilla()[j],
122
0
                                      (*arguments_.payoff)(grid[j]));
123
0
                     }
124
0
                     else
125
0
                         optvalues[j] = vanilla()[j];
126
0
                  }
127
0
                  else if (grid[j] >= arguments_.barrier_hi)
128
0
                     optvalues[j] = arguments_.rebate; // knocked out hi
129
0
                  else if (endTime)
130
0
                      optvalues[j] = arguments_.rebate;
131
0
                  break;
132
0
              case DoubleBarrier::KOKI:
133
                  // low barrier is KO, high is KI
134
0
                  if (grid[j] <= arguments_.barrier_lo)
135
0
                      optvalues[j] = arguments_.rebate; // knocked out lo
136
0
                  else if (grid[j] >= arguments_.barrier_hi) {
137
                     // knocked in up
138
0
                     if (stoppingTime) {
139
0
                         optvalues[j] = std::max(vanilla()[j],
140
0
                                      (*arguments_.payoff)(grid[j]));
141
0
                     }
142
0
                     else
143
0
                         optvalues[j] = vanilla()[j];
144
0
                  }
145
0
                  else if (endTime)
146
0
                      optvalues[j] = arguments_.rebate;
147
0
                  break;
148
0
              default:
149
0
                  QL_FAIL("invalid barrier type");
150
0
            }
151
0
        }
152
0
    }
153
154
155
156
    DiscretizedDermanKaniDoubleBarrierOption::DiscretizedDermanKaniDoubleBarrierOption(
157
                                         const DoubleBarrierOption::arguments& args,
158
                                         const StochasticProcess& process,
159
                                         const TimeGrid& grid)
160
0
    : unenhanced_(args, process, grid) {
161
0
    }
162
163
0
    void DiscretizedDermanKaniDoubleBarrierOption::reset(Size size) {
164
0
        unenhanced_.initialize(method(), time());
165
0
        values_ = Array(size, 0.0);
166
0
        adjustValues();
167
0
    }
168
169
0
    void DiscretizedDermanKaniDoubleBarrierOption::postAdjustValuesImpl() {
170
0
        unenhanced_.rollback(time());
171
172
0
        Array grid = method()->grid(time());
173
0
        unenhanced_.checkBarrier(values_, grid); // compute payoffs
174
0
        adjustBarrier(values_, grid);
175
0
    }
176
177
0
    void DiscretizedDermanKaniDoubleBarrierOption::adjustBarrier(Array &optvalues, const Array &grid) {
178
0
        Real barrier_lo = unenhanced_.arguments().barrier_lo;
179
0
        Real barrier_hi = unenhanced_.arguments().barrier_hi;
180
0
        Real rebate = unenhanced_.arguments().rebate;
181
0
        switch (unenhanced_.arguments().barrierType) {
182
0
           case DoubleBarrier::KnockIn:
183
0
              for (Size j=0; j<optvalues.size()-1; ++j) {
184
0
                  if (grid[j]<=barrier_lo && grid[j+1] > barrier_lo) {
185
                     // grid[j+1] above barrier_lo, grid[j] under (in),
186
                     // interpolate optvalues[j+1]
187
0
                     Real ltob = (barrier_lo-grid[j]);
188
0
                     Real htob = (grid[j+1]-barrier_lo);
189
0
                     Real htol = (grid[j+1]-grid[j]);
190
0
                     Real u1 = unenhanced_.values()[j+1];
191
0
                     Real t1 = unenhanced_.vanilla()[j+1];
192
0
                     optvalues[j+1] = std::max(0.0, (ltob*t1+htob*u1)/htol); // derman std
193
0
                  }
194
0
                  else if (grid[j] < barrier_hi && grid[j+1] >= barrier_hi) {
195
                     // grid[j+1] above barrier_hi (in), grid[j] under, 
196
                     // interpolate optvalues[j]
197
0
                     Real ltob = (barrier_hi-grid[j]);
198
0
                     Real htob = (grid[j+1]-barrier_hi);
199
0
                     Real htol = (grid[j+1]-grid[j]);
200
0
                     Real u = unenhanced_.values()[j];
201
0
                     Real t = unenhanced_.vanilla()[j];
202
0
                     optvalues[j] = std::max(0.0, (ltob*u+htob*t)/htol); // derman std
203
0
                  }
204
0
              }
205
0
              break;
206
0
           case DoubleBarrier::KnockOut:
207
0
              for (Size j=0; j<optvalues.size()-1; ++j) {
208
0
                  if (grid[j]<=barrier_lo && grid[j+1] > barrier_lo) {
209
                     // grid[j+1] above barrier_lo, grid[j] under (out),
210
                     // interpolate optvalues[j+1]
211
0
                     Real a = (barrier_lo-grid[j])*rebate;
212
0
                     Real b = (grid[j+1]-barrier_lo)*unenhanced_.values()[j+1];
213
0
                     Real c = (grid[j+1]-grid[j]);
214
0
                     optvalues[j+1] = std::max(0.0, (a+b)/c);
215
0
                  }
216
0
                  else if (grid[j] < barrier_hi && grid[j+1] >= barrier_hi) {
217
                     // grid[j+1] above barrier_hi (out), grid[j] under, 
218
                     // interpolate optvalues[j]
219
0
                     Real a = (barrier_hi-grid[j])*unenhanced_.values()[j];
220
0
                     Real b = (grid[j+1]-barrier_hi)*rebate;
221
0
                     Real c = (grid[j+1]-grid[j]);
222
0
                     optvalues[j] = std::max(0.0, (a+b)/c);
223
0
                  }
224
0
              }
225
0
              break;
226
0
           default:
227
0
              QL_FAIL("unsupported barrier type");
228
0
              break;
229
0
        }
230
0
    }
231
232
}