Coverage Report

Created: 2025-08-11 06:28

/src/quantlib/ql/pricingengines/barrier/analytictwoassetbarrierengine.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) 2012 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/analytictwoassetbarrierengine.hpp>
22
#include <ql/math/distributions/bivariatenormaldistribution.hpp>
23
#include <ql/math/distributions/normaldistribution.hpp>
24
#include <utility>
25
26
namespace QuantLib {
27
28
    AnalyticTwoAssetBarrierEngine::AnalyticTwoAssetBarrierEngine(
29
        ext::shared_ptr<GeneralizedBlackScholesProcess> process1,
30
        ext::shared_ptr<GeneralizedBlackScholesProcess> process2,
31
        Handle<Quote> rho)
32
0
    : process1_(std::move(process1)), process2_(std::move(process2)), rho_(std::move(rho)) {
33
0
        registerWith(process1_);
34
0
        registerWith(process2_);
35
0
        registerWith(rho_);
36
0
    }
37
38
0
    void AnalyticTwoAssetBarrierEngine::calculate() const {
39
0
        ext::shared_ptr<PlainVanillaPayoff> payoff =
40
0
            ext::dynamic_pointer_cast<PlainVanillaPayoff>(arguments_.payoff);
41
0
        QL_REQUIRE(payoff, "non-plain payoff given");
42
0
        QL_REQUIRE(payoff->strike()>0.0,"strike must be positive");
43
44
0
        Real spot2 = process2_->x0();
45
        // option is triggered by S2
46
0
        QL_REQUIRE(spot2 > 0.0, "negative or null underlying given");
47
0
        QL_REQUIRE(!triggered(spot2), "barrier touched");
48
49
0
        Barrier::Type barrierType = arguments_.barrierType;
50
51
0
        switch (payoff->optionType()) {
52
0
          case Option::Call:
53
0
            switch (barrierType) {
54
0
              case Barrier::DownOut:
55
0
                results_.value = A(1,-1) +B(1,-1) ;
56
0
                break;
57
0
              case Barrier::UpOut:
58
0
                results_.value = A(1,1) + B(1,1) ;
59
0
                break;
60
0
              case Barrier::DownIn:
61
0
                results_.value = call()-(A(1,-1) +B(1,-1) );
62
0
                break;
63
0
              case Barrier::UpIn:
64
0
                results_.value = call()-(A(1,1) +B(1,1));
65
0
                break;
66
0
            }
67
0
            break;
68
0
          case Option::Put:
69
0
            switch (barrierType) {
70
0
              case Barrier::DownOut:
71
0
                results_.value = A(-1,-1)+B(-1,-1) ;
72
0
                break;
73
0
              case Barrier::UpOut:
74
0
                results_.value = A(-1,1)+B(-1,1) ;
75
0
                break;
76
0
              case Barrier::DownIn:
77
0
                results_.value = put()-(A(-1,-1) +B(-1,-1) );
78
0
                break;
79
0
              case Barrier::UpIn:
80
0
                results_.value = put()-(A(-1,1) +B(-1,1) );
81
0
                break;
82
0
            }
83
0
            break;
84
0
          default:
85
0
            QL_FAIL("unknown type");
86
0
        }
87
0
    }
88
89
0
    Real AnalyticTwoAssetBarrierEngine::underlying1() const {
90
0
        return process1_->x0();
91
0
    }
92
93
0
    Real AnalyticTwoAssetBarrierEngine::underlying2() const {
94
0
        return process2_->x0();
95
0
    }
96
97
0
    Real AnalyticTwoAssetBarrierEngine::strike() const {
98
0
        ext::shared_ptr<PlainVanillaPayoff> payoff =
99
0
            ext::dynamic_pointer_cast<PlainVanillaPayoff>(arguments_.payoff);
100
0
        QL_REQUIRE(payoff, "non-plain payoff given");
101
0
        return payoff->strike();
102
0
    }
103
104
0
    Time AnalyticTwoAssetBarrierEngine::residualTime() const {
105
0
        return process1_->time(arguments_.exercise->lastDate());
106
0
    }
107
108
0
    Volatility AnalyticTwoAssetBarrierEngine::volatility1() const {
109
0
        return process1_->blackVolatility()->blackVol(residualTime(), strike());
110
0
    }
111
112
0
    Volatility AnalyticTwoAssetBarrierEngine::volatility2() const {
113
0
        return process2_->blackVolatility()->blackVol(residualTime(), strike());
114
0
    }
115
116
0
    Real AnalyticTwoAssetBarrierEngine::barrier() const {
117
0
        return arguments_.barrier;
118
0
    }
119
120
0
    Real AnalyticTwoAssetBarrierEngine::rho() const {
121
0
        return rho_->value();
122
0
    }
123
124
0
    Rate AnalyticTwoAssetBarrierEngine::riskFreeRate() const {
125
0
        return process1_->riskFreeRate()->zeroRate(residualTime(),
126
0
                                                   Continuous, NoFrequency);
127
0
    }
128
129
130
0
    Rate AnalyticTwoAssetBarrierEngine::dividendYield1() const {
131
0
        return process1_->dividendYield()->zeroRate(residualTime(),
132
0
                                                    Continuous, NoFrequency);
133
0
    }
134
135
0
    Rate AnalyticTwoAssetBarrierEngine::dividendYield2() const {
136
0
        return process2_->dividendYield()->zeroRate(residualTime(),
137
0
                                                    Continuous, NoFrequency);
138
0
    }
139
140
0
    Rate AnalyticTwoAssetBarrierEngine::costOfCarry1() const {
141
0
        return riskFreeRate() - dividendYield1();
142
0
    }
143
144
0
    Rate AnalyticTwoAssetBarrierEngine::costOfCarry2() const {
145
0
        return riskFreeRate() - dividendYield2();
146
0
    }
147
148
0
    Real AnalyticTwoAssetBarrierEngine::d1() const {
149
0
        return (std::log(underlying1()/strike())+(mu(costOfCarry1(),volatility1())+volatility1()*volatility1())*residualTime())/
150
0
            (volatility1()*std::sqrt(residualTime()));
151
0
    }
152
153
0
    Real AnalyticTwoAssetBarrierEngine::d2() const {
154
0
        return d1() - volatility1()*std::sqrt(residualTime());
155
0
    }
156
157
0
    Real AnalyticTwoAssetBarrierEngine::d3() const {
158
0
        return d1()+ (2*rho()*std::log(barrier()/underlying2()))/(volatility2()*std::sqrt(residualTime()));
159
0
    }
160
161
0
    Real AnalyticTwoAssetBarrierEngine::d4() const {
162
0
        return d2()+ (2*rho()*std::log(barrier()/underlying2()))/(volatility2()*std::sqrt(residualTime()));
163
0
    }
164
165
0
    Real AnalyticTwoAssetBarrierEngine::e1() const {
166
0
        return (std::log(barrier()/underlying2())-(mu(costOfCarry2(),volatility2())+rho()*volatility1()*volatility2())*residualTime())/
167
0
        (volatility2()*std::sqrt(residualTime()));
168
0
    }
169
170
0
    Real AnalyticTwoAssetBarrierEngine::e2() const {
171
0
         return e1()+rho()*volatility1()*std::sqrt(residualTime());
172
0
    }
173
174
0
    Real AnalyticTwoAssetBarrierEngine::e3() const {
175
0
            return e1()-(2*std::log(barrier()/underlying2()))/(volatility2()*std::sqrt(residualTime()));
176
0
    }
177
178
0
    Real AnalyticTwoAssetBarrierEngine::e4() const {
179
0
        return e2()-(2*std::log(barrier()/underlying2()))/(volatility2()*std::sqrt(residualTime()));
180
0
    }
181
182
0
    Real AnalyticTwoAssetBarrierEngine::mu(Real b, Real vol) const {
183
0
        return b-(vol*vol)/2;
184
0
    }
185
186
0
    Real AnalyticTwoAssetBarrierEngine::call() const {
187
0
        CumulativeNormalDistribution nd;
188
0
        return underlying1()*nd(d1())-strike()*std::exp(-riskFreeRate()*residualTime())*nd(d2());
189
0
    }
190
191
0
    Real AnalyticTwoAssetBarrierEngine::put() const {
192
0
        CumulativeNormalDistribution nd;
193
0
        return strike()*std::exp(-riskFreeRate()*residualTime())*nd(-d2())-underlying1()*nd(-d1());
194
0
    }
195
196
0
    Real AnalyticTwoAssetBarrierEngine::A(Real eta, Real phi) const {
197
0
        Real S1 = underlying1(), S2 = underlying2();
198
0
        Rate b1 = costOfCarry1(), b2 = costOfCarry2();
199
0
        Rate r = riskFreeRate();
200
0
        Time T = residualTime();
201
0
        Real H = barrier(), X = strike();
202
0
        Volatility sigma1 = volatility1(), sigma2 = volatility2();
203
0
        Real rho = rho_->value();
204
205
0
        Rate mu1 = b1 - sigma1*sigma1/2.0;
206
0
        Rate mu2 = b2 - sigma2*sigma2/2.0;
207
208
0
        Real d1 = (std::log(S1/X)+(mu1+sigma1*sigma1)*T)/
209
0
            (sigma1*std::sqrt(T));
210
0
        Real d2 = d1 - sigma1*std::sqrt(T);
211
0
        Real d3 = d1 + (2*rho*std::log(H/S2))/(sigma2*std::sqrt(T));
212
0
        Real d4 = d2 + (2*rho*std::log(H/S2))/(sigma2*std::sqrt(T));
213
214
0
        Real e1 = (std::log(H/S2)-(mu2+rho*sigma1*sigma2)*T)/
215
0
            (sigma2*std::sqrt(T));
216
0
        Real e2 = e1 + rho*sigma1*std::sqrt(T);
217
0
        Real e3 = e1 - (2*std::log(H/S2))/(sigma2*std::sqrt(T));
218
0
        Real e4 = e2 - (2*std::log(H/S2))/(sigma2*std::sqrt(T));
219
220
0
        Real w =
221
0
            eta*S1*std::exp((b1-r)*T) *
222
0
            (M(eta*d1, phi*e1,-eta*phi*rho)
223
0
             -std::exp((2*(mu2+rho*sigma1*sigma2)*std::log(H/S2))/(sigma2*sigma2))
224
0
             *M(eta*d3, phi*e3, -eta*phi*rho))
225
226
0
            - eta*X*std::exp(-r*T) *
227
0
            (M(eta*d2, phi*e2, -eta*phi*rho)
228
0
             -std::exp((2*mu2*std::log(H/S2))/(sigma2*sigma2))*
229
0
             M(eta*d4, phi*e4, -eta*phi*rho) ) ;
230
231
0
        return w;
232
0
    }
233
234
0
    Real AnalyticTwoAssetBarrierEngine::B(Real, Real) const {
235
0
        return 0.0;
236
0
    }
237
238
0
    Real AnalyticTwoAssetBarrierEngine::M(Real m_a, Real m_b, Real rho) const {
239
0
        BivariateCumulativeNormalDistributionDr78 f(rho);
240
0
        return f(m_a, m_b);
241
0
    }
242
243
}
244