/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 | | |