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/exercise.hpp>
21#include <ql/pricingengines/barrier/analyticdoublebarrierengine.hpp>
22#include <ql/pricingengines/blackcalculator.hpp>
23#include <utility>
24
25namespace QuantLib {
26
27 AnalyticDoubleBarrierEngine::AnalyticDoubleBarrierEngine(
28 ext::shared_ptr<GeneralizedBlackScholesProcess> process, int series)
29 : process_(std::move(process)), series_(series) {
30 registerWith(h: process_);
31 }
32
33 void AnalyticDoubleBarrierEngine::calculate() const {
34
35 QL_REQUIRE(arguments_.exercise->type() == Exercise::European,
36 "this engine handles only european options");
37
38 ext::shared_ptr<PlainVanillaPayoff> payoff =
39 ext::dynamic_pointer_cast<PlainVanillaPayoff>(r: arguments_.payoff);
40 QL_REQUIRE(payoff, "non-plain payoff given");
41
42 Real strike = payoff->strike();
43 QL_REQUIRE(strike>0.0,
44 "strike must be positive");
45
46 Real spot = underlying();
47 QL_REQUIRE(spot > 0.0, "negative or null underlying given");
48 QL_REQUIRE(!triggered(spot), "barrier(s) already touched");
49
50 DoubleBarrier::Type barrierType = arguments_.barrierType;
51
52 if (triggered(underlying: spot)) {
53 if (barrierType == DoubleBarrier::KnockIn)
54 results_.value = vanillaEquivalent(); // knocked in
55 else
56 results_.value = 0.0; // knocked out
57 } else {
58 switch (payoff->optionType()) {
59 case Option::Call:
60 switch (barrierType) {
61 case DoubleBarrier::KnockIn:
62 results_.value = callKI();
63 break;
64 case DoubleBarrier::KnockOut:
65 results_.value = callKO();
66 break;
67 case DoubleBarrier::KIKO:
68 case DoubleBarrier::KOKI:
69 QL_FAIL("unsupported double-barrier type: "
70 << barrierType);
71 default:
72 QL_FAIL("unknown double-barrier type: "
73 << barrierType);
74 }
75 break;
76 case Option::Put:
77 switch (barrierType) {
78 case DoubleBarrier::KnockIn:
79 results_.value = putKI();
80 break;
81 case DoubleBarrier::KnockOut:
82 results_.value = putKO();
83 break;
84 case DoubleBarrier::KIKO:
85 case DoubleBarrier::KOKI:
86 QL_FAIL("unsupported double-barrier type: "
87 << barrierType);
88 default:
89 QL_FAIL("unknown double-barrier type: "
90 << barrierType);
91 }
92 break;
93 default:
94 QL_FAIL("unknown type");
95 }
96 }
97 }
98
99
100 Real AnalyticDoubleBarrierEngine::underlying() const {
101 return process_->x0();
102 }
103
104 Real AnalyticDoubleBarrierEngine::strike() const {
105 ext::shared_ptr<PlainVanillaPayoff> payoff =
106 ext::dynamic_pointer_cast<PlainVanillaPayoff>(r: arguments_.payoff);
107 QL_REQUIRE(payoff, "non-plain payoff given");
108 return payoff->strike();
109 }
110
111 Time AnalyticDoubleBarrierEngine::residualTime() const {
112 return process_->time(arguments_.exercise->lastDate());
113 }
114
115 Volatility AnalyticDoubleBarrierEngine::volatility() const {
116 return process_->blackVolatility()->blackVol(t: residualTime(), strike: strike());
117 }
118
119 Real AnalyticDoubleBarrierEngine::volatilitySquared() const {
120 return volatility() * volatility();
121 }
122
123 Real AnalyticDoubleBarrierEngine::stdDeviation() const {
124 return volatility() * std::sqrt(x: residualTime());
125 }
126
127 Real AnalyticDoubleBarrierEngine::barrierLo() const {
128 return arguments_.barrier_lo;
129 }
130
131 Real AnalyticDoubleBarrierEngine::barrierHi() const {
132 return arguments_.barrier_hi;
133 }
134
135 Rate AnalyticDoubleBarrierEngine::riskFreeRate() const {
136 return process_->riskFreeRate()->zeroRate(t: residualTime(), comp: Continuous,
137 freq: NoFrequency);
138 }
139
140 DiscountFactor AnalyticDoubleBarrierEngine::riskFreeDiscount() const {
141 return process_->riskFreeRate()->discount(t: residualTime());
142 }
143
144 Rate AnalyticDoubleBarrierEngine::dividendYield() const {
145 return process_->dividendYield()->zeroRate(t: residualTime(),
146 comp: Continuous, freq: NoFrequency);
147 }
148
149 DiscountFactor AnalyticDoubleBarrierEngine::dividendDiscount() const {
150 return process_->dividendYield()->discount(t: residualTime());
151 }
152
153 Rate AnalyticDoubleBarrierEngine::costOfCarry() const {
154 return riskFreeRate() - dividendYield();
155 }
156
157 Real AnalyticDoubleBarrierEngine::vanillaEquivalent() const {
158 // Call KI equates to vanilla - callKO
159 ext::shared_ptr<StrikedTypePayoff> payoff =
160 ext::dynamic_pointer_cast<StrikedTypePayoff>(r: arguments_.payoff);
161 Real forwardPrice = underlying() * dividendDiscount() / riskFreeDiscount();
162 BlackCalculator black(payoff, forwardPrice, stdDeviation(), riskFreeDiscount());
163 Real vanilla = black.value();
164 if (vanilla < 0.0)
165 vanilla = 0.0;
166 return vanilla;
167 }
168
169 Real AnalyticDoubleBarrierEngine::callKO() const {
170 // N.B. for flat barriers mu3=mu1 and mu2=0
171 Real mu1 = 2 * costOfCarry() / volatilitySquared() + 1;
172 Real bsigma = (costOfCarry() + volatilitySquared() / 2.0) * residualTime() / stdDeviation();
173
174 Real acc1 = 0;
175 Real acc2 = 0;
176 for (int n = -series_ ; n <= series_ ; ++n) {
177 Real L2n = std::pow(x: barrierLo(), y: 2 * n);
178 Real U2n = std::pow(x: barrierHi(), y: 2 * n);
179 Real d1 = std::log( x: underlying()* U2n / (strike() * L2n) ) / stdDeviation() + bsigma;
180 Real d2 = std::log( x: underlying()* U2n / (barrierHi() * L2n) ) / stdDeviation() + bsigma;
181 Real d3 = std::log( x: std::pow(x: barrierLo(), y: 2 * n + 2) / (strike() * underlying() * U2n) ) / stdDeviation() + bsigma;
182 Real d4 = std::log( x: std::pow(x: barrierLo(), y: 2 * n + 2) / (barrierHi() * underlying() * U2n) ) / stdDeviation() + bsigma;
183
184 acc1 += std::pow( x: std::pow(x: barrierHi(), y: n) / std::pow(x: barrierLo(), y: n), y: mu1 ) *
185 (f_(d1) - f_(d2)) -
186 std::pow( x: std::pow(x: barrierLo(), y: n+1) / (std::pow(x: barrierHi(), y: n) * underlying()), y: mu1 ) *
187 (f_(d3) - f_(d4));
188
189 acc2 += std::pow( x: std::pow(x: barrierHi(), y: n) / std::pow(x: barrierLo(), y: n), y: mu1-2) *
190 (f_(d1 - stdDeviation()) - f_(d2 - stdDeviation())) -
191 std::pow( x: std::pow(x: barrierLo(), y: n+1) / (std::pow(x: barrierHi(), y: n) * underlying()), y: mu1-2 ) *
192 (f_(d3-stdDeviation()) - f_(d4-stdDeviation()));
193 }
194
195 Real rend = std::exp(x: -dividendYield() * residualTime());
196 Real kov = underlying() * rend * acc1 - strike() * riskFreeDiscount() * acc2;
197 return std::max(a: 0.0, b: kov);
198 }
199
200 Real AnalyticDoubleBarrierEngine::callKI() const {
201 // Call KI equates to vanilla - callKO
202 return std::max(a: 0.0, b: vanillaEquivalent() - callKO());
203 }
204
205 Real AnalyticDoubleBarrierEngine::putKO() const {
206 Real mu1 = 2 * costOfCarry() / volatilitySquared() + 1;
207 Real bsigma = (costOfCarry() + volatilitySquared() / 2.0) * residualTime() / stdDeviation();
208
209 Real acc1 = 0;
210 Real acc2 = 0;
211 for (int n = -series_ ; n <= series_ ; ++n) {
212 Real L2n = std::pow(x: barrierLo(), y: 2 * n);
213 Real U2n = std::pow(x: barrierHi(), y: 2 * n);
214 Real y1 = std::log( x: underlying()* U2n / (std::pow(x: barrierLo(), y: 2 * n + 1)) ) / stdDeviation() + bsigma;
215 Real y2 = std::log( x: underlying()* U2n / (strike() * L2n) ) / stdDeviation() + bsigma;
216 Real y3 = std::log( x: std::pow(x: barrierLo(), y: 2 * n + 2) / (barrierLo() * underlying() * U2n) ) / stdDeviation() + bsigma;
217 Real y4 = std::log( x: std::pow(x: barrierLo(), y: 2 * n + 2) / (strike() * underlying() * U2n) ) / stdDeviation() + bsigma;
218
219 acc1 += std::pow( x: std::pow(x: barrierHi(), y: n) / std::pow(x: barrierLo(), y: n), y: mu1-2) *
220 (f_(y1 - stdDeviation()) - f_(y2 - stdDeviation())) -
221 std::pow( x: std::pow(x: barrierLo(), y: n+1) / (std::pow(x: barrierHi(), y: n) * underlying()), y: mu1-2 ) *
222 (f_(y3-stdDeviation()) - f_(y4-stdDeviation()));
223
224 acc2 += std::pow( x: std::pow(x: barrierHi(), y: n) / std::pow(x: barrierLo(), y: n), y: mu1 ) *
225 (f_(y1) - f_(y2)) -
226 std::pow( x: std::pow(x: barrierLo(), y: n+1) / (std::pow(x: barrierHi(), y: n) * underlying()), y: mu1 ) *
227 (f_(y3) - f_(y4));
228
229 }
230
231 Real rend = std::exp(x: -dividendYield() * residualTime());
232 Real kov = strike() * riskFreeDiscount() * acc1 - underlying() * rend * acc2;
233 return std::max(a: 0.0, b: kov);
234 }
235
236 Real AnalyticDoubleBarrierEngine::putKI() const {
237 // Put KI equates to vanilla - putKO
238 return std::max(a: 0.0, b: vanillaEquivalent() - putKO());
239 }
240
241
242}
243
244

source code of quantlib/ql/pricingengines/barrier/analyticdoublebarrierengine.cpp