• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lballabio / QuantLib / 14910176578

08 May 2025 03:28PM UTC coverage: 73.315% (+0.02%) from 73.3%
14910176578

Pull #2195

github

web-flow
Merge 3a61f499c into 5d972fb7b
Pull Request #2195: Added `Handle<Quote>` for spread in `OISRateHelper`

32 of 33 new or added lines in 2 files covered. (96.97%)

277 existing lines in 25 files now uncovered.

56277 of 76761 relevant lines covered (73.31%)

8687029.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

59.48
/ql/pricingengines/barrier/analyticpartialtimebarrieroptionengine.cpp
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2

3
/*
4
 Copyright (C) 2014 Master IMAFA - Polytech'Nice Sophia - Université de Nice Sophia Antipolis
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/exercise.hpp>
21
#include <ql/pricingengines/barrier/analyticpartialtimebarrieroptionengine.hpp>
22
#include <ql/math/distributions/bivariatenormaldistribution.hpp>
23
#include <ql/pricingengines/vanilla/analyticeuropeanengine.hpp>
24
#include <utility>
25

26
namespace QuantLib {
27

28
    AnalyticPartialTimeBarrierOptionEngine::AnalyticPartialTimeBarrierOptionEngine(
4✔
29
        ext::shared_ptr<GeneralizedBlackScholesProcess> process)
4✔
30
    : process_(std::move(process)) {
4✔
31
        registerWith(process_);
8✔
32
    }
4✔
33

34
    Real AnalyticPartialTimeBarrierOptionEngine::calculate(PartialTimeBarrierOption::arguments& arguments,
60✔
35
                                                          const ext::shared_ptr<PlainVanillaPayoff>& payoff,
36
                                                          const ext::shared_ptr<GeneralizedBlackScholesProcess>& process) const {
37
        Barrier::Type barrierType = arguments.barrierType;
60✔
38
        PartialBarrier::Range barrierRange = arguments.barrierRange;
60✔
39
        Rate r = process->riskFreeRate()->zeroRate(residualTime(), Continuous,
60✔
40
                                                  NoFrequency);
41
        Rate q = process->dividendYield()->zeroRate(residualTime(), Continuous,
60✔
42
                                                  NoFrequency);
43
        Real barrier = arguments.barrier;
60✔
44
        Real strike = payoff->strike();
60✔
45
        
46
        switch (barrierType) {
60✔
47
          case Barrier::DownOut:
50✔
48
            switch (barrierRange) {
50✔
49
              case PartialBarrier::Start:
×
50
                return CA(1, barrier, strike, r, q);
×
51
                break;
52
              case PartialBarrier::EndB1:
50✔
53
                return CoB1(barrier, strike, r, q);
50✔
54
                break;
55
              case PartialBarrier::EndB2:
×
56
                return CoB2(Barrier::DownOut, barrier, strike, r, q);
×
57
                break;
58
              default:
×
59
                QL_FAIL("invalid barrier range");
×
60
            }
61
            break;
62

63
          case Barrier::DownIn:
×
64
            switch (barrierRange) {
×
65
              case PartialBarrier::Start:
×
66
                return CIA(1, barrier, strike, r, q);
×
67
                break;
68
              case PartialBarrier::EndB1:
×
69
              case PartialBarrier::EndB2:
70
                QL_FAIL("Down-and-in partial-time end barrier is not implemented");
×
71
              default:
×
72
                QL_FAIL("invalid barrier range");
×
73
            }
74
            break;
75

76
          case Barrier::UpOut:
10✔
77
            switch (barrierRange) {
10✔
78
              case PartialBarrier::Start:
×
79
                return CA(-1, barrier, strike, r, q);
×
80
                break;
81
              case PartialBarrier::EndB1:
10✔
82
                return CoB1(barrier, strike, r, q);
10✔
83
                break;
84
              case PartialBarrier::EndB2:
×
85
                return CoB2(Barrier::UpOut, barrier, strike, r, q);
×
86
                break;
87
              default:
×
88
                QL_FAIL("invalid barrier range");
×
89
            }
90
            break;
91

92
            case Barrier::UpIn:
×
93
              switch (barrierRange) {
×
94
                case PartialBarrier::Start:
×
95
                  return CIA(-1, barrier, strike, r, q);
×
96
                  break;
97
                case PartialBarrier::EndB1:
×
98
                case PartialBarrier::EndB2:
99
                  QL_FAIL("Up-and-in partial-time end barrier is not implemented");
×
100
                default:
×
101
                  QL_FAIL("invalid barrier range");
×
102
              }
103
              break;
104
            default:
×
105
              QL_FAIL("unknown barrier type");
×
106
          }
107
    }
108

109
    void AnalyticPartialTimeBarrierOptionEngine::calculate() const {
60✔
110
        ext::shared_ptr<PlainVanillaPayoff> payoff =
111
            ext::dynamic_pointer_cast<PlainVanillaPayoff>(arguments_.payoff);
60✔
112
        QL_REQUIRE(payoff, "non-plain payoff given");
60✔
113
        QL_REQUIRE(payoff->strike()>0.0,
60✔
114
                   "strike must be positive");
115

116
        Real spot = process_->x0();
60✔
117
        QL_REQUIRE(spot > 0.0, "negative or null underlying given");
60✔
118

119
        auto getSymmetricBarrierType = [](Barrier::Type barrierType) -> Barrier::Type {
120
          if (barrierType == Barrier::UpIn) return Barrier::DownIn;
30✔
121
          if (barrierType == Barrier::DownIn) return Barrier::UpIn;
30✔
122
          if (barrierType == Barrier::UpOut) return Barrier::DownOut;
30✔
123
          return Barrier::UpOut;
124
        };
125

126
        auto tmp_arguments_ = arguments_;
60✔
127
        if (payoff->optionType() == Option::Put)
60✔
128
        {
129
          Real spotSq = spot * spot;
30✔
130
          Real callStrike = spotSq / payoff->strike();
30✔
131
          ext::shared_ptr<PlainVanillaPayoff> callPayoff =
132
            ext::make_shared<PlainVanillaPayoff>(Option::Call, callStrike);
30✔
133
          tmp_arguments_.barrierType = getSymmetricBarrierType(arguments_.barrierType);
30✔
134
          tmp_arguments_.barrier = spotSq / arguments_.barrier;
30✔
135
          tmp_arguments_.payoff = callPayoff;
30✔
136
          auto callProcess = ext::make_shared<GeneralizedBlackScholesProcess>(
137
              process_->stateVariable(),
138
              process_->riskFreeRate(),
139
              process_->dividendYield(),
140
              process_->blackVolatility()
141
            );
30✔
142

143
          results_.value = payoff->strike() / spot * calculate(tmp_arguments_, callPayoff, callProcess);
30✔
144
        } else
145
          results_.value = calculate(tmp_arguments_, payoff, process_);
30✔
146
    }
60✔
147

UNCOV
148
    Real AnalyticPartialTimeBarrierOptionEngine::CoB2(
×
149
                                      Barrier::Type barrierType, 
150
                                      Real barrier, Real strike, Rate r, Rate q) const {
151
        Real result = 0.0;
UNCOV
152
        Real b = r - q;
×
UNCOV
153
        Real T = residualTime();
×
154
        Real S = underlying();
×
155
        Real mu_ = mu(strike, b);
×
156
        Real g1_ = g1(barrier, strike, b);
×
157
        Real g2_ = g2(barrier, strike, b);
×
158
        Real g3_ = g3(barrier, strike, b);
×
159
        Real g4_ = g4(barrier, strike, b);
×
160
        Real e1_ = e1(barrier, strike, b);
×
161
        Real e2_ = e2(barrier, strike, b);
×
162
        Real e3_ = e3(barrier, strike, b);
×
163
        Real e4_ = e4(barrier, strike, b);
×
164
        Real rho_ = rho();
×
165
        Real HSMu = HS(S, barrier, 2 * mu_);
×
166
        Real HSMu1 = HS(S, barrier, 2 * (mu_ + 1));
×
167
        Real X1 = strike * std::exp(-r * T);
×
168

169
        if (strike < barrier){
×
UNCOV
170
            switch (barrierType) {
×
171
              case Barrier::DownOut:
×
172
                result = S * std::exp((b - r) * T);
×
173
                result *= (M(g1_, e1_, rho_) - HSMu1 * M(g3_, -e3_, -rho_));
×
174
                result -= X1 * (M(g2_, e2_, rho_)-HSMu*M(g4_, -e4_, -rho_));
×
175
                return result;
×
176

177
              case Barrier::UpOut:
×
UNCOV
178
                result = S * std::exp((b - r) * T);
×
179
                result *= (M(-g1_, -e1_, rho_) - HSMu1 * M(-g3_, e3_, -rho_));
×
180
                result -= X1 * (M(-g2_, -e2_, rho_) - HSMu * M(-g4_, e4_, -rho_));
×
181
                result -= S * std::exp((b - r) * T) * 
×
182
                          (M(-d1(strike, b), -e1_, rho_) - HSMu1 * 
×
183
                          M(e3_, -f1(barrier, strike, b),-rho_));
×
184
                result += X1 * (M(-d2(strike, b), -e2_, rho_) - HSMu * 
×
185
                          M(e4_, -f2(barrier, strike, b), -rho_));
×
186
                return result;
×
187

188
              default:
×
UNCOV
189
                QL_FAIL("invalid barrier type");
×
190
            }
191
        } else {
UNCOV
192
            QL_FAIL("case of strike>barrier is not implemented for OutEnd B2 type");
×
193
        }
194
    }
195

196
    Real AnalyticPartialTimeBarrierOptionEngine::CoB1(Real barrier, Real strike, Rate r, Rate q) const {
60✔
197
        Real result = 0.0;
198
        Rate b = r - q;
60✔
199
        Real T = residualTime();
60✔
200
        Real S = underlying();
60✔
201
        Real mu_ = mu(strike, b);
60✔
202
        Real g1_ = g1(barrier, strike, b);
60✔
203
        Real g2_ = g2(barrier, strike, b);
60✔
204
        Real g3_ = g3(barrier, strike, b);
60✔
205
        Real g4_ = g4(barrier, strike, b);
60✔
206
        Real e1_ = e1(barrier, strike, b);
60✔
207
        Real e2_ = e2(barrier, strike, b);
60✔
208
        Real e3_ = e3(barrier, strike, b);
60✔
209
        Real e4_ = e4(barrier, strike, b);
60✔
210
        Real rho_ = rho();
60✔
211
        Real HSMu = HS(S, barrier, 2 * mu_);
60✔
212
        Real HSMu1 = HS(S, barrier, 2 * (mu_ + 1));
60✔
213
        Real X1 = strike * std::exp(-r * T);
60✔
214

215
        if (strike > barrier) {
60✔
216
            result = S * std::exp((b - r) * T);
40✔
217
            result *= (M(d1(strike, b), e1_, rho_) - HSMu1 * M(f1(barrier, strike, b), -e3_, -rho_));
40✔
218
            result -= X1 * (M(d2(strike, b), e2_, rho_) - HSMu * M(f2(barrier, strike, b), -e4_, -rho_));
40✔
219
            return result;
40✔
220
        } else {
221
            Real S1 = S * std::exp((b - r) * T);
20✔
222
            result = S1;
223
            result *= (M(-g1_, -e1_, rho_) - HSMu1 * M(-g3_,e3_,-rho_));
20✔
224
            result -= X1 * (M(-g2_, -e2_, rho_) - HSMu * M(-g4_, e4_, -rho_));
20✔
225
            result -= S1 * (M(-d1(strike, b), -e1_, rho_) - HSMu1 * M(-f1(barrier, strike, b), e3_, -rho_));
20✔
226
            result += X1 * (M(-d2(strike, b), -e2_, rho_) - HSMu * M(-f2(barrier, strike, b), e4_, -rho_));
20✔
227
            result += S1 * (M(g1_, e1_, rho_) - HSMu1 * M(g3_, -e3_, -rho_));
20✔
228
            result -= X1 * (M(g2_, e2_, rho_) - HSMu * M(g4_, -e4_, -rho_));
20✔
229
            return result;
20✔
230
        }
231
    }
232

233
    // eta = -1: Up-and-In Call
234
    // eta =  1: Down-and-In Call
UNCOV
235
    Real AnalyticPartialTimeBarrierOptionEngine::CIA(Integer eta, Real barrier, Real strike, Rate r, Rate q) const {
×
236
        ext::shared_ptr<EuropeanExercise> exercise =
237
            ext::dynamic_pointer_cast<EuropeanExercise>(arguments_.exercise);
×
238

239
        ext::shared_ptr<PlainVanillaPayoff> payoff =
UNCOV
240
            ext::dynamic_pointer_cast<PlainVanillaPayoff>(arguments_.payoff);
×
241

242
        VanillaOption europeanOption(payoff, exercise);
×
243

244
        europeanOption.setPricingEngine(
×
UNCOV
245
                        ext::make_shared<AnalyticEuropeanEngine>(process_));
×
246

247
        return europeanOption.NPV() - CA(eta, barrier, strike, r, q);
×
UNCOV
248
    }
×
249

250
    Real AnalyticPartialTimeBarrierOptionEngine::CA(Integer eta, Real barrier, Real strike, Rate r, Rate q) const {
×
251
        //Partial-Time-Start- OUT  Call Option calculation
252
        Real b = r - q;
×
UNCOV
253
        Real rho_ = rho();
×
254
        Real T = residualTime();
×
255
        Real S = underlying();
×
256
        Real mu_ = mu(strike, b);
×
257
        Real e1_ = e1(barrier, strike, b);
×
258
        Real e2_ = e2(barrier, strike, b);
×
259
        Real e3_ = e3(barrier, strike, b);
×
260
        Real e4_ = e4(barrier, strike, b);
×
261
        Real HSMu = HS(S, barrier,2 * mu_);
×
262
        Real HSMu1 = HS(S, barrier, 2 * (mu_ + 1));
×
263

264
        Real result;
UNCOV
265
        result = S * std::exp((b - r) * T);
×
UNCOV
266
        result *= (M(d1(strike, b), eta * e1_, eta * rho_)-HSMu1 * 
×
267
                  M(f1(barrier, strike, b), eta * e3_, eta * rho_));
×
268
        result -= (strike * std::exp(-r * T) * 
×
269
                  (M(d2(strike, b),eta * e2_, eta * rho_) - HSMu *
×
270
                  M(f2(barrier, strike, b), eta * e4_, eta * rho_)));
×
271
        return result;
×
272
    }
273

274
    Real AnalyticPartialTimeBarrierOptionEngine::underlying() const {
1,020✔
275
        return process_->x0();
1,020✔
276
    }
277

278
    Time AnalyticPartialTimeBarrierOptionEngine::residualTime() const {
1,080✔
279
        return process_->time(arguments_.exercise->lastDate());
1,080✔
280
    }
281

282
    Time AnalyticPartialTimeBarrierOptionEngine::coverEventTime() const {
600✔
283
        return process_->time(arguments_.coverEventDate);
600✔
284
    }
285

286
    Volatility AnalyticPartialTimeBarrierOptionEngine::volatility(Time t, Real strike) const {
1,380✔
287
        return process_->blackVolatility()->blackVol(t, strike);
1,380✔
288
    }
289

290
    Real AnalyticPartialTimeBarrierOptionEngine::f1(Real barrier, Real strike, Rate b) const {
120✔
291
        Real S = underlying();
120✔
292
        Real T = residualTime();
120✔
293
        Real sigma = volatility(T, strike);
120✔
294
        return (std::log(S / strike) + 2 * std::log(barrier / S) + 
120✔
295
              (b + (std::pow(sigma, 2) / 2))*T) / (sigma*std::sqrt(T));
120✔
296
    }
297

298
    Real AnalyticPartialTimeBarrierOptionEngine::f2(Real barrier, Real strike, Rate b) const {
60✔
299
        Time T = residualTime();
60✔
300
        return f1(barrier, strike, b) - volatility(T, strike) * std::sqrt(T);
60✔
301
    }
302

303
    Real AnalyticPartialTimeBarrierOptionEngine::M(Real a, Real b, Real rho) const {
400✔
304
        BivariateCumulativeNormalDistributionDr78 CmlNormDist(rho);
400✔
305
        return CmlNormDist(a,b);
400✔
306
    }
307

308
    Real AnalyticPartialTimeBarrierOptionEngine::rho() const {
60✔
309
        return std::sqrt(coverEventTime() / residualTime());
60✔
310
    }
311

312
    Rate AnalyticPartialTimeBarrierOptionEngine::mu(Real strike, Rate b) const {
60✔
313
        Volatility vol = volatility(coverEventTime(), strike);
60✔
314
        return (b - (vol * vol) / 2) / (vol * vol);
60✔
315
    }
316

317
    Real AnalyticPartialTimeBarrierOptionEngine::d1(Real strike, Rate b) const {
120✔
318
        Time T2 = residualTime();
120✔
319
        Volatility vol = volatility(T2, strike);
120✔
320
        return (std::log(underlying() / strike) + (b + vol * vol / 2) * T2) / (std::sqrt(T2) * vol);
120✔
321
    }
322

323
    Real AnalyticPartialTimeBarrierOptionEngine::d2(Real strike, Rate b) const {
60✔
324
        Time T2 = residualTime();
60✔
325
        Volatility vol = volatility(T2, strike);
60✔
326
        return d1(strike, b) - vol * std::sqrt(T2);
60✔
327
    }
328

329
    Real AnalyticPartialTimeBarrierOptionEngine::e1(Real barrier, Real strike, Rate b) const {
240✔
330
        Time T1 = coverEventTime();
240✔
331
        Volatility vol = volatility(T1, strike);
240✔
332
        return (std::log(underlying() / barrier) + (b + vol * vol / 2) * T1) / (std::sqrt(T1) * vol);
240✔
333
    }
334

335
    Real AnalyticPartialTimeBarrierOptionEngine::e2(Real barrier, Real strike, Rate b) const {
60✔
336
        Time T1 = coverEventTime();
60✔
337
        Volatility vol = volatility(T1, strike);
60✔
338
        return e1(barrier, strike, b) - vol * std::sqrt(T1);
60✔
339
    }
340

341
    Real AnalyticPartialTimeBarrierOptionEngine::e3(Real barrier, Real strike, Rate b) const {
120✔
342
        Time T1 = coverEventTime();
120✔
343
        Real vol = volatility(T1, strike);
120✔
344
        return e1(barrier, strike, b) + (2 * std::log(barrier / underlying()) / (vol * std::sqrt(T1)));
120✔
345
    }
346

347
    Real AnalyticPartialTimeBarrierOptionEngine::e4(Real barrier, Real strike, Rate b) const {
60✔
348
        Time t = coverEventTime();
60✔
349
        return e3(barrier, strike, b) - volatility(t, strike) * std::sqrt(t);
60✔
350
    }
351

352
    Real AnalyticPartialTimeBarrierOptionEngine::g1(Real barrier, Real strike, Rate b) const {
240✔
353
        Time T2 = residualTime();
240✔
354
        Volatility vol = volatility(T2, strike);
240✔
355
        return (std::log(underlying() / barrier) + (b + vol * vol / 2) * T2) / (std::sqrt(T2) * vol);
240✔
356
    }
357

358
    Real AnalyticPartialTimeBarrierOptionEngine::g2(Real barrier, Real strike, Rate b) const {
60✔
359
        Time T2 = residualTime();
60✔
360
        Volatility vol = volatility(T2, strike);
60✔
361
        return g1(barrier, strike, b) - vol * std::sqrt(T2);
60✔
362
    }
363

364
    Real AnalyticPartialTimeBarrierOptionEngine::g3(Real barrier, Real strike, Rate b) const {
120✔
365
        Time T2 = residualTime();
120✔
366
        Real vol = volatility(T2, strike);
120✔
367
        return g1(barrier, strike, b) + (2 * std::log(barrier / underlying()) /(vol * std::sqrt(T2)));
120✔
368
    }
369

370
    Real AnalyticPartialTimeBarrierOptionEngine::g4(Real barrier, Real strike, Rate b) const {
60✔
371
        Time T2 = residualTime();
60✔
372
        Real vol = volatility(T2, strike);
60✔
373
        return g3(barrier, strike, b) - vol * std::sqrt(T2);
60✔
374
    }
375

376
    Real AnalyticPartialTimeBarrierOptionEngine::HS(Real S, Real H, Real power) const {
120✔
377
        return std::pow((H / S), power);
120✔
378
    }
379

380
}
381

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc