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 
DigitalCoupon(const ext::shared_ptr<FloatingRateCoupon> & underlying,Rate callStrike,Position::Type callPosition,bool isCallATMIncluded,Rate callDigitalPayoff,Rate putStrike,Position::Type putPosition,bool isPutATMIncluded,Rate putDigitalPayoff,const ext::shared_ptr<DigitalReplication> & replication,const bool nakedOption)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                   const ext::shared_ptr<DigitalReplication>& replication,
38                   const bool nakedOption)
39     : FloatingRateCoupon(underlying->date(),
40                          underlying->nominal(),
41                          underlying->accrualStartDate(),
42                          underlying->accrualEndDate(),
43                          underlying->fixingDays(),
44                          underlying->index(),
45                          underlying->gearing(),
46                          underlying->spread(),
47                          underlying->referencePeriodStart(),
48                          underlying->referencePeriodEnd(),
49                          underlying->dayCounter(),
50                          underlying->isInArrears()),
51       underlying_(underlying), callCsi_(0.), putCsi_(0.),
52       isCallATMIncluded_(isCallATMIncluded), isPutATMIncluded_(isPutATMIncluded),
53       isCallCashOrNothing_(false), isPutCashOrNothing_(false),
54       callLeftEps_(replication->gap()/2.), callRightEps_(replication->gap()/2.),
55       putLeftEps_(replication->gap()/2.), putRightEps_(replication->gap()/2.),
56       hasPutStrike_(false), hasCallStrike_(false),
57       replicationType_(replication->replicationType()), nakedOption_(nakedOption) {
58 
59         QL_REQUIRE(replication->gap()>0.0, "Non positive epsilon not allowed");
60 
61         if (putStrike == Null<Rate>()) {
62             QL_REQUIRE(putDigitalPayoff == Null<Rate>(),
63             "Put Cash rate non allowed if put strike is null");
64         }
65         if (callStrike == Null<Rate>()) {
66             QL_REQUIRE(callDigitalPayoff == Null<Rate>(),
67             "Call Cash rate non allowed if call strike is null");
68         }
69         if (callStrike != Null<Rate>()) {
70             hasCallStrike_ = true;
71             callStrike_ = callStrike;
72             switch (callPosition) {
73                 case Position::Long :
74                     callCsi_ = 1.0;
75                     break;
76                 case Position::Short :
77                     callCsi_ = -1.0;
78                     break;
79                 default:
80                     QL_FAIL("unsupported position type");
81             }
82             if (callDigitalPayoff != Null<Rate>()){
83                 callDigitalPayoff_ = callDigitalPayoff;
84                 isCallCashOrNothing_ = true;
85             }
86         }
87         if (putStrike != Null<Rate>()){
88             hasPutStrike_ = true;
89             putStrike_ = putStrike;
90             switch (putPosition) {
91                 case Position::Long :
92                     putCsi_ = 1.0;
93                     break;
94                 case Position::Short :
95                     putCsi_ = -1.0;
96                     break;
97                 default:
98                     QL_FAIL("unsupported position type");
99             }
100             if (putDigitalPayoff != Null<Rate>()){
101                 putDigitalPayoff_ = putDigitalPayoff;
102                 isPutCashOrNothing_ = true;
103             }
104         }
105 
106         switch (replicationType_) {
107           case Replication::Central :
108             // do nothing
109             break;
110           case Replication::Sub :
111             if (hasCallStrike_) {
112                 switch (callPosition) {
113                     case Position::Long :
114                         callLeftEps_ = 0.;
115                         callRightEps_ = replication->gap();
116                         break;
117                     case Position::Short :
118                         callLeftEps_ = replication->gap();
119                         callRightEps_ = 0.;
120                         break;
121                     default:
122                         QL_FAIL("unsupported position type");
123                 }
124             }
125             if (hasPutStrike_) {
126                 switch (putPosition) {
127                     case Position::Long :
128                         putLeftEps_ = replication->gap();
129                         putRightEps_ = 0.;
130                         break;
131                     case Position::Short :
132                         putLeftEps_ = 0.;
133                         putRightEps_ = replication->gap();
134                         break;
135                     default:
136                         QL_FAIL("unsupported position type");
137                 }
138             }
139             break;
140           case Replication::Super :
141             if (hasCallStrike_) {
142                 switch (callPosition) {
143                     case Position::Long :
144                         callLeftEps_ = replication->gap();
145                         callRightEps_ = 0.;
146                         break;
147                     case Position::Short :
148                         callLeftEps_ = 0.;
149                         callRightEps_ = replication->gap();
150                         break;
151                     default:
152                         QL_FAIL("unsupported position type");
153                 }
154             }
155             if (hasPutStrike_) {
156                 switch (putPosition) {
157                     case Position::Long :
158                         putLeftEps_ = 0.;
159                         putRightEps_ = replication->gap();
160                         break;
161                     case Position::Short :
162                         putLeftEps_ = replication->gap();
163                         putRightEps_ = 0.;
164                         break;
165                     default:
166                         QL_FAIL("unsupported position type");
167                 }
168             }
169             break;
170           default:
171             QL_FAIL("unsupported replication type");
172         }
173 
174         registerWith(underlying);
175     }
176 
177 
callOptionRate() const178     Rate DigitalCoupon::callOptionRate() const {
179 
180         Rate callOptionRate = Rate(0.);
181         if(hasCallStrike_) {
182             // Step function
183             callOptionRate = isCallCashOrNothing_ ? callDigitalPayoff_ : callStrike_;
184             CappedFlooredCoupon next(underlying_, callStrike_ + callRightEps_);
185             CappedFlooredCoupon previous(underlying_, callStrike_ - callLeftEps_);
186             callOptionRate *= (next.rate() - previous.rate())
187                             / (callLeftEps_ + callRightEps_);
188             if (!isCallCashOrNothing_) {
189                 // Call
190                 CappedFlooredCoupon atStrike(underlying_, callStrike_);
191                 Rate call = underlying_->rate() - atStrike.rate();
192                 // Sum up
193                 callOptionRate += call;
194             }
195         }
196         return callOptionRate;
197     }
198 
putOptionRate() const199     Rate DigitalCoupon::putOptionRate() const {
200 
201         Rate putOptionRate = Rate(0.);
202         if(hasPutStrike_) {
203             // Step function
204             putOptionRate = isPutCashOrNothing_ ? putDigitalPayoff_ : putStrike_;
205             CappedFlooredCoupon next(underlying_, Null<Rate>(), putStrike_ + putRightEps_);
206             CappedFlooredCoupon previous(underlying_, Null<Rate>(), putStrike_ - putLeftEps_);
207             putOptionRate *= (next.rate() - previous.rate())
208                            / (putLeftEps_ + putRightEps_);
209             if (!isPutCashOrNothing_) {
210                 // Put
211                 CappedFlooredCoupon atStrike(underlying_, Null<Rate>(), putStrike_);
212                 Rate put = - underlying_->rate() + atStrike.rate();
213                 // Sum up
214                 putOptionRate -= put;
215             }
216         }
217         return putOptionRate;
218     }
219 
rate() const220     Rate DigitalCoupon::rate() const {
221 
222         QL_REQUIRE(underlying_->pricer(), "pricer not set");
223 
224         Date fixingDate = underlying_->fixingDate();
225         Date today = Settings::instance().evaluationDate();
226         bool enforceTodaysHistoricFixings =
227             Settings::instance().enforcesTodaysHistoricFixings();
228         Rate underlyingRate = nakedOption_ ? 0.0 : underlying_->rate();
229         if (fixingDate < today ||
230             ((fixingDate == today) && enforceTodaysHistoricFixings)) {
231             // must have been fixed
232             return underlyingRate + callCsi_ * callPayoff() + putCsi_  * putPayoff();
233         }
234         if (fixingDate == today) {
235             // might have been fixed
236             Rate pastFixing =
237                 IndexManager::instance().getHistory((underlying_->index())->name())[fixingDate];
238             if (pastFixing != Null<Real>()) {
239                 return underlyingRate + callCsi_ * callPayoff() + putCsi_  * putPayoff();
240             } else
241                 return underlyingRate + callCsi_ * callOptionRate() + putCsi_ * putOptionRate();
242         }
243         return underlyingRate + callCsi_ * callOptionRate() + putCsi_ * putOptionRate();
244     }
245 
convexityAdjustment() const246     Rate DigitalCoupon::convexityAdjustment() const {
247         return underlying_->convexityAdjustment();
248     }
249 
callStrike() const250     Rate DigitalCoupon::callStrike() const {
251         if (hasCall())
252             return callStrike_;
253         else
254             return Null<Rate>();
255    }
256 
putStrike() const257     Rate DigitalCoupon::putStrike() const {
258         if (hasPut())
259             return putStrike_;
260         else
261             return Null<Rate>();
262     }
263 
callDigitalPayoff() const264     Rate DigitalCoupon::callDigitalPayoff() const {
265         if (isCallCashOrNothing_)
266             return callDigitalPayoff_;
267         else
268             return Null<Rate>();
269     }
270 
putDigitalPayoff() const271     Rate DigitalCoupon::putDigitalPayoff() const {
272         if (isPutCashOrNothing_)
273             return putDigitalPayoff_;
274         else
275             return Null<Rate>();
276     }
277 
update()278     void DigitalCoupon::update() {
279         notifyObservers();
280     }
281 
accept(AcyclicVisitor & v)282     void DigitalCoupon::accept(AcyclicVisitor& v) {
283         typedef FloatingRateCoupon super;
284         Visitor<DigitalCoupon>* v1 =
285             dynamic_cast<Visitor<DigitalCoupon>*>(&v);
286         if (v1 != 0)
287             v1->visit(*this);
288         else
289             super::accept(v);
290     }
291 
callPayoff() const292     Rate DigitalCoupon::callPayoff() const {
293         // to use only if index has fixed
294         Rate payoff(0.);
295         if(hasCallStrike_) {
296             Rate underlyingRate = underlying_->rate();
297             if ( (underlyingRate - callStrike_) > 1.e-16 ) {
298                 payoff = isCallCashOrNothing_ ? callDigitalPayoff_ : underlyingRate;
299             } else {
300                 if (isCallATMIncluded_) {
301                     if ( std::abs(callStrike_ - underlyingRate) <= 1.e-16 )
302                         payoff = isCallCashOrNothing_ ? callDigitalPayoff_ : underlyingRate;
303                 }
304             }
305         }
306         return payoff;
307     }
308 
putPayoff() const309     Rate DigitalCoupon::putPayoff() const {
310         // to use only if index has fixed
311         Rate payoff(0.);
312         if(hasPutStrike_) {
313             Rate underlyingRate = underlying_->rate();
314             if ( (putStrike_ - underlyingRate) > 1.e-16 ) {
315                 payoff = isPutCashOrNothing_ ? putDigitalPayoff_ : underlyingRate;
316             } else {
317                 // putStrike_ <= underlyingRate
318                 if (isPutATMIncluded_) {
319                     if ( std::abs(putStrike_ - underlyingRate) <= 1.e-16 )
320                         payoff = isPutCashOrNothing_ ? putDigitalPayoff_ : underlyingRate;
321                 }
322             }
323         }
324         return payoff;
325     }
326 
327 }
328 
329