Coverage Report

Created: 2026-03-31 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/experimental/barrieroption/suowangdoublebarrierengine.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2013 Yue Tian
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/exercise.hpp>
21
#include <ql/experimental/barrieroption/suowangdoublebarrierengine.hpp>
22
#include <ql/instruments/europeanoption.hpp>
23
#include <ql/pricingengines/vanilla/analyticeuropeanengine.hpp>
24
#include <utility>
25
26
namespace QuantLib {
27
28
    SuoWangDoubleBarrierEngine::SuoWangDoubleBarrierEngine(
29
        ext::shared_ptr<GeneralizedBlackScholesProcess> process, int series)
30
0
    : process_(std::move(process)), series_(series) {
31
0
        registerWith(process_);
32
0
    }
33
34
0
    void SuoWangDoubleBarrierEngine::calculate() const {
35
36
0
        ext::shared_ptr<PlainVanillaPayoff> payoff =
37
0
            ext::dynamic_pointer_cast<PlainVanillaPayoff>(arguments_.payoff);
38
0
        QL_REQUIRE(payoff, "non-plain payoff given");
39
0
        QL_REQUIRE(payoff->strike()>0.0,
40
0
                   "strike must be positive");
41
42
0
        Real K = payoff->strike();
43
0
        Real S = process_->x0();
44
0
        QL_REQUIRE(S > 0.0, "negative or null underlying given");
45
0
        QL_REQUIRE(!triggered(S), "barrier touched");
46
47
0
        DoubleBarrier::Type barrierType = arguments_.barrierType;
48
0
        QL_REQUIRE(barrierType == DoubleBarrier::KnockOut || 
49
0
                   barrierType == DoubleBarrier::KnockIn,
50
0
                   "only KnockIn and KnockOut options supported");
51
52
0
        Real L = arguments_.barrier_lo;
53
0
        Real H = arguments_.barrier_hi;
54
0
        Real K_up = std::min(H, K);
55
0
        Real K_down = std::max(L, K);
56
0
        Time T = residualTime();
57
0
        Real rd = riskFreeRate();
58
0
        Real dd = riskFreeDiscount();
59
0
        Real rf = dividendYield();
60
0
        Real df = dividendDiscount();
61
0
        Real vol = volatility();
62
0
        Real mu = rd - rf - vol*vol/2.0;
63
0
        Real sgn = mu > 0 ? 1.0 :(mu < 0 ? -1.0: 0.0);
64
        //rebate
65
0
        Real R_L = arguments_.rebate;
66
0
        Real R_H = arguments_.rebate;
67
68
        //european option
69
0
        EuropeanOption europeanOption(payoff, arguments_.exercise);
70
0
        ext::shared_ptr<PricingEngine> analyticEuropeanEngine =
71
0
            ext::make_shared<AnalyticEuropeanEngine>(process_);
72
0
        europeanOption.setPricingEngine(analyticEuropeanEngine);
73
0
        Real european = europeanOption.NPV();
74
75
0
        Real barrierOut = 0;
76
0
        Real rebateIn = 0;
77
0
        for(int n = -series_; n < series_; n++){
78
0
            Real d1 = D(S/H*std::pow(L/H, 2.0*n), vol*vol+mu, vol, T);
79
0
            Real d2 = d1 - vol*std::sqrt(T);
80
0
            Real g1 = D(H/S*std::pow(L/H, 2.0*n - 1.0), vol*vol+mu, vol, T);
81
0
            Real g2 = g1 - vol*std::sqrt(T);
82
0
            Real h1 = D(S/H*std::pow(L/H, 2.0*n - 1.0), vol*vol+mu, vol, T);
83
0
            Real h2 = h1 - vol*std::sqrt(T);
84
0
            Real k1 = D(L/S*std::pow(L/H, 2.0*n - 1.0), vol*vol+mu, vol, T);
85
0
            Real k2 = k1 - vol*std::sqrt(T);
86
0
            Real d1_down = D(S/K_down*std::pow(L/H, 2.0*n), vol*vol+mu, vol, T);
87
0
            Real d2_down = d1_down - vol*std::sqrt(T);
88
0
            Real d1_up = D(S/K_up*std::pow(L/H, 2.0*n), vol*vol+mu, vol, T);
89
0
            Real d2_up = d1_up - vol*std::sqrt(T);
90
0
            Real k1_down = D((H*H)/(K_down*S)*std::pow(L/H, 2.0*n), vol*vol+mu, vol, T);
91
0
            Real k2_down = k1_down - vol*std::sqrt(T);
92
0
            Real k1_up = D((H*H)/(K_up*S)*std::pow(L/H, 2.0*n), vol*vol+mu, vol, T);
93
0
            Real k2_up = k1_up - vol*std::sqrt(T);
94
95
0
            if( payoff->optionType() == Option::Call) {
96
0
                barrierOut += std::pow(L/H, 2.0 * n * mu/(vol*vol))*
97
0
                            (df*S*std::pow(L/H, 2.0*n)*(f_(d1_down)-f_(d1))
98
0
                            -dd*K*(f_(d2_down)-f_(d2))
99
0
                            -df*std::pow(L/H, 2.0*n)*H*H/S*std::pow(H/S, 2.0*mu/(vol*vol))*(f_(k1_down)-f_(k1))
100
0
                            +dd*K*std::pow(H/S,2.0*mu/(vol*vol))*(f_(k2_down)-f_(k2)));
101
0
            }
102
0
            else if(payoff->optionType() == Option::Put){
103
0
                barrierOut += std::pow(L/H, 2.0 * n * mu/(vol*vol))*
104
0
                            (dd*K*(f_(h2)-f_(d2_up))
105
0
                            -df*S*std::pow(L/H, 2.0*n)*(f_(h1)-f_(d1_up))
106
0
                            -dd*K*std::pow(H/S,2.0*mu/(vol*vol))*(f_(g2)-f_(k2_up))
107
0
                            +df*std::pow(L/H, 2.0*n)*H*H/S*std::pow(H/S, 2.0*mu/(vol*vol))*(f_(g1)-f_(k1_up)));
108
0
            }
109
0
            else {
110
0
                QL_FAIL("option type not recognized");
111
0
            }
112
113
0
            Real v1 = D(H/S*std::pow(H/L, 2.0*n), -mu, vol, T);
114
0
            Real v2 = D(H/S*std::pow(H/L, 2.0*n), mu, vol, T);
115
0
            Real v3 = D(S/L*std::pow(H/L, 2.0*n), -mu, vol, T);
116
0
            Real v4 = D(S/L*std::pow(H/L, 2.0*n), mu, vol, T);
117
0
            rebateIn +=  dd * R_H * sgn * (std::pow(L/H, 2.0*n*mu/(vol*vol)) * f_(sgn * v1) - std::pow(H/S, 2.0*mu/(vol*vol)) * f_(-sgn * v2))
118
0
                       + dd * R_L * sgn * (std::pow(L/S, 2.0*mu/(vol*vol)) * f_(-sgn * v3) - std::pow(H/L, 2.0*n*mu/(vol*vol)) * f_(sgn * v4));
119
0
        }
120
121
        //rebate paid at maturity
122
0
        if(barrierType == DoubleBarrier::KnockOut)
123
0
            results_.value = barrierOut ;
124
0
        else
125
0
            results_.value = european - barrierOut;
126
0
        results_.additionalResults["vanilla"] = european;
127
0
        results_.additionalResults["barrierOut"] = barrierOut;
128
0
        results_.additionalResults["barrierIn"] = Real(european - barrierOut);
129
0
        results_.additionalResults["rebateIn"] = rebateIn;
130
0
    }
131
132
133
0
    Real SuoWangDoubleBarrierEngine::strike() const {
134
0
        ext::shared_ptr<PlainVanillaPayoff> payoff =
135
0
            ext::dynamic_pointer_cast<PlainVanillaPayoff>(arguments_.payoff);
136
0
        QL_REQUIRE(payoff, "non-plain payoff given");
137
0
        return payoff->strike();
138
0
    }
139
140
0
    Time SuoWangDoubleBarrierEngine::residualTime() const {
141
0
        return process_->time(arguments_.exercise->lastDate());
142
0
    }
143
144
0
    Volatility SuoWangDoubleBarrierEngine::volatility() const {
145
0
        return process_->blackVolatility()->blackVol(residualTime(), strike());
146
0
    }
147
148
0
    Rate SuoWangDoubleBarrierEngine::riskFreeRate() const {
149
0
        return process_->riskFreeRate()->zeroRate(residualTime(), Continuous,
150
0
                                                  NoFrequency);
151
0
    }
152
153
0
    DiscountFactor SuoWangDoubleBarrierEngine::riskFreeDiscount() const {
154
0
        return process_->riskFreeRate()->discount(residualTime());
155
0
    }
156
157
0
    Rate SuoWangDoubleBarrierEngine::dividendYield() const {
158
0
        return process_->dividendYield()->zeroRate(residualTime(),
159
0
                                                   Continuous, NoFrequency);
160
0
    }
161
162
0
    DiscountFactor SuoWangDoubleBarrierEngine::dividendDiscount() const {
163
0
        return process_->dividendYield()->discount(residualTime());
164
0
    }
165
166
0
    Real SuoWangDoubleBarrierEngine::D(Real X, Real lambda, Real sigma, Real T) const {
167
0
        return (std::log(X) + lambda * T)/(sigma * std::sqrt(T));
168
0
    }
169
170
}
171