/src/quantlib/ql/cashflows/digitalcoupon.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) 2007 Cristina Duminuco |
5 | | Copyright (C) 2007 Giorgio Facchinetti |
6 | | |
7 | | This file is part of QuantLib, a free-software/open-source library |
8 | | for financial quantitative analysts and developers - http://quantlib.org/ |
9 | | |
10 | | QuantLib is free software: you can redistribute it and/or modify it |
11 | | under the terms of the QuantLib license. You should have received a |
12 | | copy of the license along with this program; if not, please email |
13 | | <quantlib-dev@lists.sf.net>. The license is also available online at |
14 | | <http://quantlib.org/license.shtml>. |
15 | | |
16 | | This program is distributed in the hope that it will be useful, but WITHOUT |
17 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
18 | | FOR A PARTICULAR PURPOSE. See the license for more details. |
19 | | */ |
20 | | |
21 | | #include <ql/cashflows/capflooredcoupon.hpp> |
22 | | #include <ql/cashflows/digitalcoupon.hpp> |
23 | | #include <ql/indexes/indexmanager.hpp> |
24 | | #include <ql/indexes/interestrateindex.hpp> |
25 | | |
26 | | namespace QuantLib { |
27 | | |
28 | | DigitalCoupon::DigitalCoupon(const ext::shared_ptr<FloatingRateCoupon>& underlying, |
29 | | Rate callStrike, |
30 | | Position::Type callPosition, |
31 | | bool isCallATMIncluded, |
32 | | Rate callDigitalPayoff, |
33 | | Rate putStrike, |
34 | | Position::Type putPosition, |
35 | | bool isPutATMIncluded, |
36 | | Rate putDigitalPayoff, |
37 | | ext::shared_ptr<DigitalReplication> replication, |
38 | | const bool nakedOption) |
39 | 0 | : FloatingRateCoupon(underlying->date(), |
40 | 0 | underlying->nominal(), |
41 | 0 | underlying->accrualStartDate(), |
42 | 0 | underlying->accrualEndDate(), |
43 | 0 | underlying->fixingDays(), |
44 | 0 | underlying->index(), |
45 | 0 | underlying->gearing(), |
46 | 0 | underlying->spread(), |
47 | 0 | underlying->referencePeriodStart(), |
48 | 0 | underlying->referencePeriodEnd(), |
49 | 0 | underlying->dayCounter(), |
50 | 0 | underlying->isInArrears()), |
51 | 0 | underlying_(underlying), isCallATMIncluded_(isCallATMIncluded), |
52 | 0 | isPutATMIncluded_(isPutATMIncluded), nakedOption_(nakedOption) { |
53 | |
|
54 | 0 | if (replication == nullptr) |
55 | 0 | replication = ext::make_shared<DigitalReplication>(); |
56 | | |
57 | 0 | QL_REQUIRE(replication->gap()>0.0, "Non positive epsilon not allowed"); |
58 | | |
59 | 0 | callLeftEps_ = callRightEps_ = putLeftEps_ = putRightEps_ = replication->gap() / 2; |
60 | 0 | replicationType_ = replication->replicationType(); |
61 | | |
62 | 0 | if (putStrike == Null<Rate>()) { |
63 | 0 | QL_REQUIRE(putDigitalPayoff == Null<Rate>(), |
64 | 0 | "Put Cash rate non allowed if put strike is null"); |
65 | 0 | } |
66 | 0 | if (callStrike == Null<Rate>()) { |
67 | 0 | QL_REQUIRE(callDigitalPayoff == Null<Rate>(), |
68 | 0 | "Call Cash rate non allowed if call strike is null"); |
69 | 0 | } |
70 | 0 | if (callStrike != Null<Rate>()) { |
71 | 0 | hasCallStrike_ = true; |
72 | 0 | callStrike_ = callStrike; |
73 | 0 | switch (callPosition) { |
74 | 0 | case Position::Long : |
75 | 0 | callCsi_ = 1.0; |
76 | 0 | break; |
77 | 0 | case Position::Short : |
78 | 0 | callCsi_ = -1.0; |
79 | 0 | break; |
80 | 0 | default: |
81 | 0 | QL_FAIL("unsupported position type"); |
82 | 0 | } |
83 | 0 | if (callDigitalPayoff != Null<Rate>()){ |
84 | 0 | callDigitalPayoff_ = callDigitalPayoff; |
85 | 0 | isCallCashOrNothing_ = true; |
86 | 0 | } |
87 | 0 | } |
88 | 0 | if (putStrike != Null<Rate>()){ |
89 | 0 | hasPutStrike_ = true; |
90 | 0 | putStrike_ = putStrike; |
91 | 0 | switch (putPosition) { |
92 | 0 | case Position::Long : |
93 | 0 | putCsi_ = 1.0; |
94 | 0 | break; |
95 | 0 | case Position::Short : |
96 | 0 | putCsi_ = -1.0; |
97 | 0 | break; |
98 | 0 | default: |
99 | 0 | QL_FAIL("unsupported position type"); |
100 | 0 | } |
101 | 0 | if (putDigitalPayoff != Null<Rate>()){ |
102 | 0 | putDigitalPayoff_ = putDigitalPayoff; |
103 | 0 | isPutCashOrNothing_ = true; |
104 | 0 | } |
105 | 0 | } |
106 | | |
107 | 0 | switch (replicationType_) { |
108 | 0 | case Replication::Central : |
109 | | // do nothing |
110 | 0 | break; |
111 | 0 | case Replication::Sub : |
112 | 0 | if (hasCallStrike_) { |
113 | 0 | switch (callPosition) { |
114 | 0 | case Position::Long : |
115 | 0 | callLeftEps_ = 0.; |
116 | 0 | callRightEps_ = replication->gap(); |
117 | 0 | break; |
118 | 0 | case Position::Short : |
119 | 0 | callLeftEps_ = replication->gap(); |
120 | 0 | callRightEps_ = 0.; |
121 | 0 | break; |
122 | 0 | default: |
123 | 0 | QL_FAIL("unsupported position type"); |
124 | 0 | } |
125 | 0 | } |
126 | 0 | if (hasPutStrike_) { |
127 | 0 | switch (putPosition) { |
128 | 0 | case Position::Long : |
129 | 0 | putLeftEps_ = replication->gap(); |
130 | 0 | putRightEps_ = 0.; |
131 | 0 | break; |
132 | 0 | case Position::Short : |
133 | 0 | putLeftEps_ = 0.; |
134 | 0 | putRightEps_ = replication->gap(); |
135 | 0 | break; |
136 | 0 | default: |
137 | 0 | QL_FAIL("unsupported position type"); |
138 | 0 | } |
139 | 0 | } |
140 | 0 | break; |
141 | 0 | case Replication::Super : |
142 | 0 | if (hasCallStrike_) { |
143 | 0 | switch (callPosition) { |
144 | 0 | case Position::Long : |
145 | 0 | callLeftEps_ = replication->gap(); |
146 | 0 | callRightEps_ = 0.; |
147 | 0 | break; |
148 | 0 | case Position::Short : |
149 | 0 | callLeftEps_ = 0.; |
150 | 0 | callRightEps_ = replication->gap(); |
151 | 0 | break; |
152 | 0 | default: |
153 | 0 | QL_FAIL("unsupported position type"); |
154 | 0 | } |
155 | 0 | } |
156 | 0 | if (hasPutStrike_) { |
157 | 0 | switch (putPosition) { |
158 | 0 | case Position::Long : |
159 | 0 | putLeftEps_ = 0.; |
160 | 0 | putRightEps_ = replication->gap(); |
161 | 0 | break; |
162 | 0 | case Position::Short : |
163 | 0 | putLeftEps_ = replication->gap(); |
164 | 0 | putRightEps_ = 0.; |
165 | 0 | break; |
166 | 0 | default: |
167 | 0 | QL_FAIL("unsupported position type"); |
168 | 0 | } |
169 | 0 | } |
170 | 0 | break; |
171 | 0 | default: |
172 | 0 | QL_FAIL("unsupported replication type"); |
173 | 0 | } |
174 | | |
175 | 0 | registerWith(underlying); |
176 | 0 | } Unexecuted instantiation: QuantLib::DigitalCoupon::DigitalCoupon(boost::shared_ptr<QuantLib::FloatingRateCoupon> const&, double, QuantLib::Position::Type, bool, double, double, QuantLib::Position::Type, bool, double, boost::shared_ptr<QuantLib::DigitalReplication>, bool) Unexecuted instantiation: QuantLib::DigitalCoupon::DigitalCoupon(boost::shared_ptr<QuantLib::FloatingRateCoupon> const&, double, QuantLib::Position::Type, bool, double, double, QuantLib::Position::Type, bool, double, boost::shared_ptr<QuantLib::DigitalReplication>, bool) |
177 | | |
178 | | |
179 | 0 | Rate DigitalCoupon::callOptionRate() const { |
180 | |
|
181 | 0 | Rate callOptionRate = Rate(0.); |
182 | 0 | if(hasCallStrike_) { |
183 | | // Step function |
184 | 0 | callOptionRate = isCallCashOrNothing_ ? callDigitalPayoff_ : callStrike_; |
185 | 0 | CappedFlooredCoupon next(underlying_, callStrike_ + callRightEps_); |
186 | 0 | CappedFlooredCoupon previous(underlying_, callStrike_ - callLeftEps_); |
187 | 0 | callOptionRate *= (next.rate() - previous.rate()) |
188 | 0 | / (callLeftEps_ + callRightEps_); |
189 | 0 | if (!isCallCashOrNothing_) { |
190 | | // Call |
191 | 0 | CappedFlooredCoupon atStrike(underlying_, callStrike_); |
192 | 0 | Rate call = underlying_->rate() - atStrike.rate(); |
193 | | // Sum up |
194 | 0 | callOptionRate += call; |
195 | 0 | } |
196 | 0 | } |
197 | 0 | return callOptionRate; |
198 | 0 | } |
199 | | |
200 | 0 | Rate DigitalCoupon::putOptionRate() const { |
201 | |
|
202 | 0 | Rate putOptionRate = Rate(0.); |
203 | 0 | if(hasPutStrike_) { |
204 | | // Step function |
205 | 0 | putOptionRate = isPutCashOrNothing_ ? putDigitalPayoff_ : putStrike_; |
206 | 0 | CappedFlooredCoupon next(underlying_, Null<Rate>(), putStrike_ + putRightEps_); |
207 | 0 | CappedFlooredCoupon previous(underlying_, Null<Rate>(), putStrike_ - putLeftEps_); |
208 | 0 | putOptionRate *= (next.rate() - previous.rate()) |
209 | 0 | / (putLeftEps_ + putRightEps_); |
210 | 0 | if (!isPutCashOrNothing_) { |
211 | | // Put |
212 | 0 | CappedFlooredCoupon atStrike(underlying_, Null<Rate>(), putStrike_); |
213 | 0 | Rate put = - underlying_->rate() + atStrike.rate(); |
214 | | // Sum up |
215 | 0 | putOptionRate -= put; |
216 | 0 | } |
217 | 0 | } |
218 | 0 | return putOptionRate; |
219 | 0 | } |
220 | | |
221 | 0 | void DigitalCoupon::deepUpdate() { |
222 | 0 | update(); |
223 | 0 | underlying_->deepUpdate(); |
224 | 0 | } |
225 | | |
226 | 0 | void DigitalCoupon::performCalculations() const { |
227 | |
|
228 | 0 | QL_REQUIRE(underlying_->pricer(), "pricer not set"); |
229 | | |
230 | 0 | Date fixingDate = underlying_->fixingDate(); |
231 | 0 | Date today = Settings::instance().evaluationDate(); |
232 | 0 | bool enforceTodaysHistoricFixings = |
233 | 0 | Settings::instance().enforcesTodaysHistoricFixings(); |
234 | 0 | Rate underlyingRate = nakedOption_ ? 0.0 : underlying_->rate(); |
235 | 0 | if (fixingDate < today || |
236 | 0 | ((fixingDate == today) && enforceTodaysHistoricFixings)) { |
237 | | // must have been fixed |
238 | 0 | rate_ = underlyingRate + callCsi_ * callPayoff() + putCsi_ * putPayoff(); |
239 | 0 | } else if (fixingDate == today) { |
240 | | // might have been fixed |
241 | 0 | if (underlying_->index()->hasHistoricalFixing(fixingDate)) { |
242 | 0 | rate_ = underlyingRate + callCsi_ * callPayoff() + putCsi_ * putPayoff(); |
243 | 0 | } else { |
244 | 0 | rate_ = underlyingRate + callCsi_ * callOptionRate() + putCsi_ * putOptionRate(); |
245 | 0 | } |
246 | 0 | } else { |
247 | 0 | rate_ = underlyingRate + callCsi_ * callOptionRate() + putCsi_ * putOptionRate(); |
248 | 0 | } |
249 | 0 | } |
250 | | |
251 | 0 | Rate DigitalCoupon::rate() const { |
252 | 0 | calculate(); |
253 | 0 | return rate_; |
254 | 0 | } |
255 | | |
256 | 0 | Rate DigitalCoupon::convexityAdjustment() const { |
257 | 0 | return underlying_->convexityAdjustment(); |
258 | 0 | } |
259 | | |
260 | 0 | Rate DigitalCoupon::callStrike() const { |
261 | 0 | if (hasCall()) |
262 | 0 | return callStrike_; |
263 | 0 | else |
264 | 0 | return Null<Rate>(); |
265 | 0 | } |
266 | | |
267 | 0 | Rate DigitalCoupon::putStrike() const { |
268 | 0 | if (hasPut()) |
269 | 0 | return putStrike_; |
270 | 0 | else |
271 | 0 | return Null<Rate>(); |
272 | 0 | } |
273 | | |
274 | 0 | Rate DigitalCoupon::callDigitalPayoff() const { |
275 | 0 | if (isCallCashOrNothing_) |
276 | 0 | return callDigitalPayoff_; |
277 | 0 | else |
278 | 0 | return Null<Rate>(); |
279 | 0 | } |
280 | | |
281 | 0 | Rate DigitalCoupon::putDigitalPayoff() const { |
282 | 0 | if (isPutCashOrNothing_) |
283 | 0 | return putDigitalPayoff_; |
284 | 0 | else |
285 | 0 | return Null<Rate>(); |
286 | 0 | } |
287 | | |
288 | 0 | void DigitalCoupon::accept(AcyclicVisitor& v) { |
289 | 0 | typedef FloatingRateCoupon super; |
290 | 0 | auto* v1 = dynamic_cast<Visitor<DigitalCoupon>*>(&v); |
291 | 0 | if (v1 != nullptr) |
292 | 0 | v1->visit(*this); |
293 | 0 | else |
294 | 0 | super::accept(v); |
295 | 0 | } |
296 | | |
297 | 0 | Rate DigitalCoupon::callPayoff() const { |
298 | | // to use only if index has fixed |
299 | 0 | Rate payoff(0.); |
300 | 0 | if(hasCallStrike_) { |
301 | 0 | Rate underlyingRate = underlying_->rate(); |
302 | 0 | if ( (underlyingRate - callStrike_) > 1.e-16 ) { |
303 | 0 | payoff = isCallCashOrNothing_ ? callDigitalPayoff_ : underlyingRate; |
304 | 0 | } else { |
305 | 0 | if (isCallATMIncluded_) { |
306 | 0 | if ( std::abs(callStrike_ - underlyingRate) <= 1.e-16 ) |
307 | 0 | payoff = isCallCashOrNothing_ ? callDigitalPayoff_ : underlyingRate; |
308 | 0 | } |
309 | 0 | } |
310 | 0 | } |
311 | 0 | return payoff; |
312 | 0 | } |
313 | | |
314 | 0 | Rate DigitalCoupon::putPayoff() const { |
315 | | // to use only if index has fixed |
316 | 0 | Rate payoff(0.); |
317 | 0 | if(hasPutStrike_) { |
318 | 0 | Rate underlyingRate = underlying_->rate(); |
319 | 0 | if ( (putStrike_ - underlyingRate) > 1.e-16 ) { |
320 | 0 | payoff = isPutCashOrNothing_ ? putDigitalPayoff_ : underlyingRate; |
321 | 0 | } else { |
322 | | // putStrike_ <= underlyingRate |
323 | 0 | if (isPutATMIncluded_) { |
324 | 0 | if ( std::abs(putStrike_ - underlyingRate) <= 1.e-16 ) |
325 | 0 | payoff = isPutCashOrNothing_ ? putDigitalPayoff_ : underlyingRate; |
326 | 0 | } |
327 | 0 | } |
328 | 0 | } |
329 | 0 | return payoff; |
330 | 0 | } |
331 | | |
332 | | } |
333 | | |