[go: up one dir, main page]

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 "digitalcoupon.hpp"
22#include "utilities.hpp"
23#include <ql/indexes/ibor/euribor.hpp>
24#include <ql/utilities/dataformatters.hpp>
25#include <ql/cashflows/digitalcoupon.hpp>
26#include <ql/cashflows/capflooredcoupon.hpp>
27#include <ql/settings.hpp>
28#include <ql/quotes/simplequote.hpp>
29#include <ql/termstructures/volatility/optionlet/constantoptionletvol.hpp>
30#include <ql/pricingengines/blackformula.hpp>
31#include <ql/time/daycounters/actual360.hpp>
32#include <ql/pricingengines/vanilla/analyticeuropeanengine.hpp>
33#include <ql/processes/blackscholesprocess.hpp>
34#include <ql/math/distributions/normaldistribution.hpp>
35
36using namespace QuantLib;
37using namespace boost::unit_test_framework;
38
39namespace digital_coupon_test {
40
41 struct CommonVars {
42 // global data
43 Date today, settlement;
44 Real nominal;
45 Calendar calendar;
46 ext::shared_ptr<IborIndex> index;
47 Natural fixingDays;
48 RelinkableHandle<YieldTermStructure> termStructure;
49 Real optionTolerance;
50 Real blackTolerance;
51
52 // setup
53 CommonVars() {
54 fixingDays = 2;
55 nominal = 1000000.0;
56 index = ext::shared_ptr<IborIndex>(new Euribor6M(termStructure));
57 calendar = index->fixingCalendar();
58 today = calendar.adjust(Settings::instance().evaluationDate());
59 Settings::instance().evaluationDate() = today;
60 settlement = calendar.advance(today,n: fixingDays,unit: Days);
61 termStructure.linkTo(h: flatRate(today: settlement,forward: 0.05,dc: Actual365Fixed()));
62 optionTolerance = 1.e-04;
63 blackTolerance = 1e-10;
64 }
65 };
66
67}
68
69
70void DigitalCouponTest::testAssetOrNothing() {
71
72 BOOST_TEST_MESSAGE("Testing European asset-or-nothing digital coupon...");
73
74 using namespace digital_coupon_test;
75
76 /* Call Payoff = (aL+b)Heaviside(aL+b-X) = a Max[L-X'] + (b+aX')Heaviside(L-X')
77 Value Call = aF N(d1') + bN(d2')
78 Put Payoff = (aL+b)Heaviside(X-aL-b) = -a Max[X-L'] + (b+aX')Heaviside(X'-L)
79 Value Put = aF N(-d1') + bN(-d2')
80 where:
81 d1' = ln(F/X')/stdDev + 0.5*stdDev;
82 */
83
84 CommonVars vars;
85
86 Volatility vols[] = { 0.05, 0.15, 0.30 };
87 Rate strikes[] = { 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07 };
88 Real gearings[] = { 1.0, 2.8 };
89 Rate spreads[] = { 0.0, 0.005 };
90
91 Real gap = 1e-7; /* low, in order to compare digital option value
92 with black formula result */
93 ext::shared_ptr<DigitalReplication>
94 replication(new DigitalReplication(Replication::Central, gap));
95 for (Real capletVol : vols) {
96 RelinkableHandle<OptionletVolatilityStructure> vol;
97 vol.linkTo(h: ext::shared_ptr<OptionletVolatilityStructure>(new ConstantOptionletVolatility(
98 vars.today, vars.calendar, Following, capletVol, Actual360())));
99 for (Real strike : strikes) {
100 for (Size k = 9; k < 10; k++) {
101 Date startDate = vars.calendar.advance(date: vars.settlement,period: (k+1)*Years);
102 Date endDate = vars.calendar.advance(date: vars.settlement,period: (k+2)*Years);
103 Rate nullstrike = Null<Rate>();
104 Date paymentDate = endDate;
105 for (Size h=0; h<LENGTH(gearings); h++) {
106
107 Real gearing = gearings[h];
108 Rate spread = spreads[h];
109
110 ext::shared_ptr<FloatingRateCoupon> underlying(new
111 IborCoupon(paymentDate, vars.nominal,
112 startDate, endDate,
113 vars.fixingDays, vars.index,
114 gearing, spread));
115 // Floating Rate Coupon - Call Digital option
116 DigitalCoupon digitalCappedCoupon(underlying,
117 strike, Position::Short, false, nullstrike,
118 nullstrike, Position::Short, false, nullstrike,
119 replication);
120 ext::shared_ptr<IborCouponPricer> pricer(new
121 BlackIborCouponPricer(vol));
122 digitalCappedCoupon.setPricer(pricer);
123
124 // Check digital option price vs N(d1) price
125 Time accrualPeriod = underlying->accrualPeriod();
126 Real discount = vars.termStructure->discount(d: endDate);
127 Date exerciseDate = underlying->fixingDate();
128 Rate forward = underlying->rate();
129 Rate effFwd = (forward-spread)/gearing;
130 Rate effStrike = (strike-spread)/gearing;
131 Real stdDev = std::sqrt(x: vol->blackVariance(optionDate: exerciseDate, strike: effStrike));
132 CumulativeNormalDistribution phi;
133 Real d1 = std::log(x: effFwd/effStrike)/stdDev + 0.5*stdDev;
134 Real d2 = d1 - stdDev;
135 Real N_d1 = phi(d1);
136 Real N_d2 = phi(d2);
137 Real nd1Price = (gearing * effFwd * N_d1 + spread * N_d2)
138 * vars.nominal * accrualPeriod * discount;
139 Real optionPrice = digitalCappedCoupon.callOptionRate() *
140 vars.nominal * accrualPeriod * discount;
141 Real error = std::abs(x: nd1Price - optionPrice);
142 if (error>vars.optionTolerance)
143 BOOST_ERROR("\nDigital Call Option:" <<
144 "\nVolatility = " << io::rate(capletVol) <<
145 "\nStrike = " << io::rate(strike) <<
146 "\nExercise = " << k+1 << " years" <<
147 "\nOption price by replication = " << optionPrice <<
148 "\nOption price by Cox-Rubinstein formula = " << nd1Price <<
149 "\nError " << error);
150
151 // Check digital option price vs N(d1) price using Vanilla Option class
152 if (spread==0.0) {
153 ext::shared_ptr<Exercise>
154 exercise(new EuropeanExercise(exerciseDate));
155 Real discountAtFixing = vars.termStructure->discount(d: exerciseDate);
156 ext::shared_ptr<SimpleQuote>
157 fwd(new SimpleQuote(effFwd*discountAtFixing));
158 ext::shared_ptr<SimpleQuote> qRate(new SimpleQuote(0.0));
159 ext::shared_ptr<YieldTermStructure>
160 qTS = flatRate(today: vars.today, forward: qRate, dc: Actual360());
161 ext::shared_ptr<SimpleQuote> vol(new SimpleQuote(0.0));
162 ext::shared_ptr<BlackVolTermStructure>
163 volTS = flatVol(today: vars.today, volatility: capletVol, dc: Actual360());
164 ext::shared_ptr<StrikedTypePayoff>
165 callPayoff(new AssetOrNothingPayoff(Option::Call,effStrike));
166 ext::shared_ptr<BlackScholesMertonProcess> stochProcess(new
167 BlackScholesMertonProcess(Handle<Quote>(fwd),
168 Handle<YieldTermStructure>(qTS),
169 Handle<YieldTermStructure>(vars.termStructure),
170 Handle<BlackVolTermStructure>(volTS)));
171 ext::shared_ptr<PricingEngine>
172 engine(new AnalyticEuropeanEngine(stochProcess));
173 VanillaOption callOpt(callPayoff, exercise);
174 callOpt.setPricingEngine(engine);
175 Real callVO = vars.nominal * gearing
176 * accrualPeriod * callOpt.NPV()
177 * discount / discountAtFixing
178 * forward / effFwd;
179 error = std::abs(x: nd1Price - callVO);
180 if (error>vars.blackTolerance)
181 BOOST_ERROR("\nDigital Call Option:" <<
182 "\nVolatility = " << io::rate(capletVol) <<
183 "\nStrike = " << io::rate(strike) <<
184 "\nExercise = " << k+1 << " years" <<
185 "\nOption price by Black asset-ot-nothing payoff = " << callVO <<
186 "\nOption price by Cox-Rubinstein = " << nd1Price <<
187 "\nError " << error );
188 }
189
190 // Floating Rate Coupon + Put Digital option
191 DigitalCoupon digitalFlooredCoupon(underlying,
192 nullstrike, Position::Long, false, nullstrike,
193 strike, Position::Long, false, nullstrike,
194 replication);
195 digitalFlooredCoupon.setPricer(pricer);
196
197 // Check digital option price vs N(d1) price
198 N_d1 = phi(-d1);
199 N_d2 = phi(-d2);
200 nd1Price = (gearing * effFwd * N_d1 + spread * N_d2)
201 * vars.nominal * accrualPeriod * discount;
202 optionPrice = digitalFlooredCoupon.putOptionRate() *
203 vars.nominal * accrualPeriod * discount;
204 error = std::abs(x: nd1Price - optionPrice);
205 if (error>vars.optionTolerance)
206 BOOST_ERROR("\nDigital Put Option:" <<
207 "\nVolatility = " << io::rate(capletVol) <<
208 "\nStrike = " << io::rate(strike) <<
209 "\nExercise = " << k+1 << " years" <<
210 "\nOption price by replication = " << optionPrice <<
211 "\nOption price by Cox-Rubinstein = " << nd1Price <<
212 "\nError " << error );
213
214 // Check digital option price vs N(d1) price using Vanilla Option class
215 if (spread==0.0) {
216 ext::shared_ptr<Exercise>
217 exercise(new EuropeanExercise(exerciseDate));
218 Real discountAtFixing = vars.termStructure->discount(d: exerciseDate);
219 ext::shared_ptr<SimpleQuote>
220 fwd(new SimpleQuote(effFwd*discountAtFixing));
221 ext::shared_ptr<SimpleQuote> qRate(new SimpleQuote(0.0));
222 ext::shared_ptr<YieldTermStructure>
223 qTS = flatRate(today: vars.today, forward: qRate, dc: Actual360());
224 ext::shared_ptr<SimpleQuote> vol(new SimpleQuote(0.0));
225 ext::shared_ptr<BlackVolTermStructure>
226 volTS = flatVol(today: vars.today, volatility: capletVol, dc: Actual360());
227 ext::shared_ptr<BlackScholesMertonProcess> stochProcess(new
228 BlackScholesMertonProcess(Handle<Quote>(fwd),
229 Handle<YieldTermStructure>(qTS),
230 Handle<YieldTermStructure>(vars.termStructure),
231 Handle<BlackVolTermStructure>(volTS)));
232 ext::shared_ptr<StrikedTypePayoff>
233 putPayoff(new AssetOrNothingPayoff(Option::Put, effStrike));
234 ext::shared_ptr<PricingEngine> engine(new AnalyticEuropeanEngine(stochProcess));
235 VanillaOption putOpt(putPayoff, exercise);
236 putOpt.setPricingEngine(engine);
237 Real putVO = vars.nominal * gearing
238 * accrualPeriod * putOpt.NPV()
239 * discount / discountAtFixing
240 * forward / effFwd;
241 error = std::abs(x: nd1Price - putVO);
242 if (error>vars.blackTolerance)
243 BOOST_ERROR("\nDigital Put Option:" <<
244 "\nVolatility = " << io::rate(capletVol) <<
245 "\nStrike = " << io::rate(strike) <<
246 "\nExercise = " << k+1 << " years" <<
247 "\nOption price by Black asset-ot-nothing payoff = " << putVO <<
248 "\nOption price by Cox-Rubinstein = " << nd1Price <<
249 "\nError " << error );
250 }
251 }
252 }
253 }
254 }
255}
256
257void DigitalCouponTest::testAssetOrNothingDeepInTheMoney() {
258
259 BOOST_TEST_MESSAGE("Testing European deep in-the-money asset-or-nothing "
260 "digital coupon...");
261
262 using namespace digital_coupon_test;
263
264 CommonVars vars;
265
266 Real gearing = 1.0;
267 Real spread = 0.0;
268
269 Volatility capletVolatility = 0.0001;
270 RelinkableHandle<OptionletVolatilityStructure> volatility;
271 volatility.linkTo(h: ext::shared_ptr<OptionletVolatilityStructure>(new
272 ConstantOptionletVolatility(vars.today, vars.calendar, Following,
273 capletVolatility, Actual360())));
274
275 for (Size k = 0; k<10; k++) { // Loop on start and end dates
276 Date startDate = vars.calendar.advance(date: vars.settlement,period: (k+1)*Years);
277 Date endDate = vars.calendar.advance(date: vars.settlement,period: (k+2)*Years);
278 Rate nullstrike = Null<Rate>();
279 Date paymentDate = endDate;
280
281 ext::shared_ptr<FloatingRateCoupon> underlying(new
282 IborCoupon(paymentDate, vars.nominal,
283 startDate, endDate,
284 vars.fixingDays, vars.index,
285 gearing, spread));
286
287 // Floating Rate Coupon - Deep-in-the-money Call Digital option
288 Rate strike = 0.001;
289 DigitalCoupon digitalCappedCoupon(underlying,
290 strike, Position::Short, false, nullstrike,
291 nullstrike, Position::Short, false, nullstrike);
292 ext::shared_ptr<IborCouponPricer> pricer(new
293 BlackIborCouponPricer(volatility));
294 digitalCappedCoupon.setPricer(pricer);
295
296 // Check price vs its target price
297 Time accrualPeriod = underlying->accrualPeriod();
298 Real discount = vars.termStructure->discount(d: endDate);
299
300 Real targetOptionPrice = underlying->price(discountingCurve: vars.termStructure);
301 Real targetPrice = 0.0;
302 Real digitalPrice = digitalCappedCoupon.price(discountingCurve: vars.termStructure);
303 Real error = std::fabs(x: targetPrice - digitalPrice);
304 Real tolerance = 1e-08;
305 if (error>tolerance)
306 BOOST_ERROR("\nFloating Coupon - Digital Call Option:" <<
307 "\nVolatility = " << io::rate(capletVolatility) <<
308 "\nStrike = " << io::rate(strike) <<
309 "\nExercise = " << k+1 << " years" <<
310 "\nCoupon Price = " << digitalPrice <<
311 "\nTarget price = " << targetPrice <<
312 "\nError = " << error );
313
314 // Check digital option price
315 Real replicationOptionPrice = digitalCappedCoupon.callOptionRate() *
316 vars.nominal * accrualPeriod * discount;
317 error = std::abs(x: targetOptionPrice - replicationOptionPrice);
318 Real optionTolerance = 1e-08;
319 if (error>optionTolerance)
320 BOOST_ERROR("\nDigital Call Option:" <<
321 "\nVolatility = " << io::rate(capletVolatility) <<
322 "\nStrike = " << io::rate(strike) <<
323 "\nExercise = " << k+1 << " years" <<
324 "\nPrice by replication = " << replicationOptionPrice <<
325 "\nTarget price = " << targetOptionPrice <<
326 "\nError = " << error);
327
328 // Floating Rate Coupon + Deep-in-the-money Put Digital option
329 strike = 0.99;
330 DigitalCoupon digitalFlooredCoupon(underlying,
331 nullstrike, Position::Long, false, nullstrike,
332 strike, Position::Long, false, nullstrike);
333 digitalFlooredCoupon.setPricer(pricer);
334
335 // Check price vs its target price
336 targetOptionPrice = underlying->price(discountingCurve: vars.termStructure);
337 targetPrice = underlying->price(discountingCurve: vars.termStructure) + targetOptionPrice ;
338 digitalPrice = digitalFlooredCoupon.price(discountingCurve: vars.termStructure);
339 error = std::fabs(x: targetPrice - digitalPrice);
340 tolerance = 2.5e-06;
341 if (error>tolerance)
342 BOOST_ERROR("\nFloating Coupon + Digital Put Option:" <<
343 "\nVolatility = " << io::rate(capletVolatility) <<
344 "\nStrike = " << io::rate(strike) <<
345 "\nExercise = " << k+1 << " years" <<
346 "\nDigital coupon price = " << digitalPrice <<
347 "\nTarget price = " << targetPrice <<
348 "\nError " << error);
349
350 // Check digital option
351 replicationOptionPrice = digitalFlooredCoupon.putOptionRate() *
352 vars.nominal * accrualPeriod * discount;
353 error = std::abs(x: targetOptionPrice - replicationOptionPrice);
354 optionTolerance = 2.5e-06;
355 if (error>optionTolerance)
356 BOOST_ERROR("\nDigital Put Option:" <<
357 "\nVolatility = " << io::rate(capletVolatility) <<
358 "\nStrike = " << io::rate(strike) <<
359 "\nExercise = " << k+1 << " years" <<
360 "\nPrice by replication = " << replicationOptionPrice <<
361 "\nTarget price = " << targetOptionPrice <<
362 "\nError " << error);
363 }
364}
365
366void DigitalCouponTest::testAssetOrNothingDeepOutTheMoney() {
367
368 BOOST_TEST_MESSAGE("Testing European deep out-the-money asset-or-nothing "
369 "digital coupon...");
370
371 using namespace digital_coupon_test;
372
373 CommonVars vars;
374
375 Real gearing = 1.0;
376 Real spread = 0.0;
377
378 Volatility capletVolatility = 0.0001;
379 RelinkableHandle<OptionletVolatilityStructure> volatility;
380 volatility.linkTo(h: ext::shared_ptr<OptionletVolatilityStructure>(new
381 ConstantOptionletVolatility(vars.today, vars.calendar, Following,
382 capletVolatility, Actual360())));
383
384 for (Size k = 0; k<10; k++) { // loop on start and end dates
385 Date startDate = vars.calendar.advance(date: vars.settlement,period: (k+1)*Years);
386 Date endDate = vars.calendar.advance(date: vars.settlement,period: (k+2)*Years);
387 Rate nullstrike = Null<Rate>();
388 Date paymentDate = endDate;
389
390 ext::shared_ptr<FloatingRateCoupon> underlying(new
391 IborCoupon(paymentDate, vars.nominal,
392 startDate, endDate,
393 vars.fixingDays, vars.index,
394 gearing, spread));
395
396 // Floating Rate Coupon - Deep-out-of-the-money Call Digital option
397 Rate strike = 0.99;
398 DigitalCoupon digitalCappedCoupon(underlying,
399 strike, Position::Short, false, nullstrike,
400 nullstrike, Position::Long, false, nullstrike);
401 ext::shared_ptr<IborCouponPricer> pricer(new BlackIborCouponPricer(volatility));
402 digitalCappedCoupon.setPricer(pricer);
403
404 // Check price vs its target
405 Time accrualPeriod = underlying->accrualPeriod();
406 Real discount = vars.termStructure->discount(d: endDate);
407
408 Real targetPrice = underlying->price(discountingCurve: vars.termStructure);
409 Real digitalPrice = digitalCappedCoupon.price(discountingCurve: vars.termStructure);
410 Real error = std::fabs(x: targetPrice - digitalPrice);
411 Real tolerance = 1e-10;
412 if (error>tolerance)
413 BOOST_ERROR("\nFloating Coupon - Digital Call Option :" <<
414 "\nVolatility = " << io::rate(capletVolatility) <<
415 "\nStrike = " << io::rate(strike) <<
416 "\nExercise = " << k+1 << " years" <<
417 "\nCoupon price = " << digitalPrice <<
418 "\nTarget price = " << targetPrice <<
419 "\nError = " << error );
420
421 // Check digital option price
422 Real targetOptionPrice = 0.;
423 Real replicationOptionPrice = digitalCappedCoupon.callOptionRate() *
424 vars.nominal * accrualPeriod * discount;
425 error = std::abs(x: targetOptionPrice - replicationOptionPrice);
426 Real optionTolerance = 1e-08;
427 if (error>optionTolerance)
428 BOOST_ERROR("\nDigital Call Option:" <<
429 "\nVolatility = " << io::rate(capletVolatility) <<
430 "\nStrike = " << io::rate(strike) <<
431 "\nExercise = " << k+1 << " years" <<
432 "\nPrice by replication = " << replicationOptionPrice <<
433 "\nTarget price = " << targetOptionPrice <<
434 "\nError = " << error );
435
436 // Floating Rate Coupon - Deep-out-of-the-money Put Digital option
437 strike = 0.01;
438 DigitalCoupon digitalFlooredCoupon(underlying,
439 nullstrike, Position::Long, false, nullstrike,
440 strike, Position::Long, false, nullstrike);
441 digitalFlooredCoupon.setPricer(pricer);
442
443 // Check price vs its target
444 targetPrice = underlying->price(discountingCurve: vars.termStructure);
445 digitalPrice = digitalFlooredCoupon.price(discountingCurve: vars.termStructure);
446 tolerance = 1e-08;
447 error = std::fabs(x: targetPrice - digitalPrice);
448 if (error>tolerance)
449 BOOST_ERROR("\nFloating Coupon + Digital Put Coupon:" <<
450 "\nVolatility = " << io::rate(capletVolatility) <<
451 "\nStrike = " << io::rate(strike) <<
452 "\nExercise = " << k+1 << " years" <<
453 "\nCoupon price = " << digitalPrice <<
454 "\nTarget price = " << targetPrice <<
455 "\nError = " << error );
456
457 // Check digital option
458 targetOptionPrice = 0.0;
459 replicationOptionPrice = digitalFlooredCoupon.putOptionRate() *
460 vars.nominal * accrualPeriod * discount;
461 error = std::abs(x: targetOptionPrice - replicationOptionPrice);
462 if (error>optionTolerance)
463 BOOST_ERROR("\nDigital Put Coupon:" <<
464 "\nVolatility = " << io::rate(capletVolatility) <<
465 "\nStrike = " << io::rate(strike) <<
466 "\nExercise = " << k+1 << " years" <<
467 "\nPrice by replication = " << replicationOptionPrice <<
468 "\nTarget price = " << targetOptionPrice <<
469 "\nError = " << error );
470 }
471}
472
473void DigitalCouponTest::testCashOrNothing() {
474
475 BOOST_TEST_MESSAGE("Testing European cash-or-nothing digital coupon...");
476
477 /* Call Payoff = R Heaviside(aL+b-X)
478 Value Call = R N(d2')
479 Put Payoff = R Heaviside(X-aL-b)
480 Value Put = R N(-d2')
481 where:
482 d2' = ln(F/X')/stdDev - 0.5*stdDev;
483 */
484
485 using namespace digital_coupon_test;
486
487 CommonVars vars;
488
489 Volatility vols[] = { 0.05, 0.15, 0.30 };
490 Rate strikes[] = { 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07 };
491
492 Real gearing = 3.0;
493 Real spread = -0.0002;
494
495 Real gap = 1e-08; /* very low, in order to compare digital option value
496 with black formula result */
497 ext::shared_ptr<DigitalReplication> replication(new
498 DigitalReplication(Replication::Central, gap));
499
500 for (Real capletVol : vols) {
501 RelinkableHandle<OptionletVolatilityStructure> vol;
502 vol.linkTo(h: ext::shared_ptr<OptionletVolatilityStructure>(new ConstantOptionletVolatility(
503 vars.today, vars.calendar, Following, capletVol, Actual360())));
504 for (Real strike : strikes) {
505 for (Size k = 0; k < 10; k++) {
506 Date startDate = vars.calendar.advance(date: vars.settlement,period: (k+1)*Years);
507 Date endDate = vars.calendar.advance(date: vars.settlement,period: (k+2)*Years);
508 Rate nullstrike = Null<Rate>();
509 Rate cashRate = 0.01;
510
511 Date paymentDate = endDate;
512 ext::shared_ptr<FloatingRateCoupon> underlying(new
513 IborCoupon(paymentDate, vars.nominal,
514 startDate, endDate,
515 vars.fixingDays, vars.index,
516 gearing, spread));
517 // Floating Rate Coupon - Call Digital option
518 DigitalCoupon digitalCappedCoupon(underlying,
519 strike, Position::Short, false, cashRate,
520 nullstrike, Position::Short, false, nullstrike,
521 replication);
522 ext::shared_ptr<IborCouponPricer> pricer(new BlackIborCouponPricer(vol));
523 digitalCappedCoupon.setPricer(pricer);
524
525 // Check digital option price vs N(d2) price
526 Date exerciseDate = underlying->fixingDate();
527 Rate forward = underlying->rate();
528 Rate effFwd = (forward-spread)/gearing;
529 Rate effStrike = (strike-spread)/gearing;
530 Time accrualPeriod = underlying->accrualPeriod();
531 Real discount = vars.termStructure->discount(d: endDate);
532 Real stdDev = std::sqrt(x: vol->blackVariance(optionDate: exerciseDate, strike: effStrike));
533 Real ITM = blackFormulaCashItmProbability(optionType: Option::Call, strike: effStrike,
534 forward: effFwd, stdDev);
535 Real nd2Price = ITM * vars.nominal * accrualPeriod * discount * cashRate;
536 Real optionPrice = digitalCappedCoupon.callOptionRate() *
537 vars.nominal * accrualPeriod * discount;
538 Real error = std::abs(x: nd2Price - optionPrice);
539 if (error>vars.optionTolerance)
540 BOOST_ERROR("\nDigital Call Option:" <<
541 "\nVolatility = " << io::rate(capletVol) <<
542 "\nStrike = " << io::rate(strike) <<
543 "\nExercise = " << k+1 << " years" <<
544 "\nPrice by replication = " << optionPrice <<
545 "\nPrice by Reiner-Rubinstein = " << nd2Price <<
546 "\nError = " << error );
547
548 // Check digital option price vs N(d2) price using Vanilla Option class
549 ext::shared_ptr<Exercise> exercise(new EuropeanExercise(exerciseDate));
550 Real discountAtFixing = vars.termStructure->discount(d: exerciseDate);
551 ext::shared_ptr<SimpleQuote> fwd(new SimpleQuote(effFwd*discountAtFixing));
552 ext::shared_ptr<SimpleQuote> qRate(new SimpleQuote(0.0));
553 ext::shared_ptr<YieldTermStructure> qTS = flatRate(today: vars.today, forward: qRate, dc: Actual360());
554 ext::shared_ptr<SimpleQuote> vol(new SimpleQuote(0.0));
555 ext::shared_ptr<BlackVolTermStructure> volTS = flatVol(today: vars.today, volatility: capletVol,
556 dc: Actual360());
557 ext::shared_ptr<StrikedTypePayoff> callPayoff(new CashOrNothingPayoff(
558 Option::Call, effStrike, cashRate));
559 ext::shared_ptr<BlackScholesMertonProcess> stochProcess(new
560 BlackScholesMertonProcess(Handle<Quote>(fwd),
561 Handle<YieldTermStructure>(qTS),
562 Handle<YieldTermStructure>(vars.termStructure),
563 Handle<BlackVolTermStructure>(volTS)));
564 ext::shared_ptr<PricingEngine> engine(new AnalyticEuropeanEngine(stochProcess));
565 VanillaOption callOpt(callPayoff, exercise);
566 callOpt.setPricingEngine(engine);
567 Real callVO = vars.nominal * accrualPeriod * callOpt.NPV()
568 * discount / discountAtFixing;
569 error = std::abs(x: nd2Price - callVO);
570 if (error>vars.blackTolerance)
571 BOOST_ERROR("\nDigital Call Option:" <<
572 "\nVolatility = " << io::rate(capletVol) <<
573 "\nStrike = " << io::rate(strike) <<
574 "\nExercise = " << k+1 << " years" <<
575 "\nOption price by Black asset-ot-nothing payoff = " << callVO <<
576 "\nOption price by Reiner-Rubinstein = " << nd2Price <<
577 "\nError " << error );
578
579 // Floating Rate Coupon + Put Digital option
580 DigitalCoupon digitalFlooredCoupon(underlying,
581 nullstrike, Position::Long, false, nullstrike,
582 strike, Position::Long, false, cashRate,
583 replication);
584 digitalFlooredCoupon.setPricer(pricer);
585
586
587 // Check digital option price vs N(d2) price
588 ITM = blackFormulaCashItmProbability(optionType: Option::Put,
589 strike: effStrike,
590 forward: effFwd,
591 stdDev);
592 nd2Price = ITM * vars.nominal * accrualPeriod * discount * cashRate;
593 optionPrice = digitalFlooredCoupon.putOptionRate() *
594 vars.nominal * accrualPeriod * discount;
595 error = std::abs(x: nd2Price - optionPrice);
596 if (error>vars.optionTolerance)
597 BOOST_ERROR("\nPut Digital Option:" <<
598 "\nVolatility = " << io::rate(capletVol) <<
599 "\nStrike = " << io::rate(strike) <<
600 "\nExercise = " << k+1 << " years" <<
601 "\nPrice by replication = " << optionPrice <<
602 "\nPrice by Reiner-Rubinstein = " << nd2Price <<
603 "\nError = " << error );
604
605 // Check digital option price vs N(d2) price using Vanilla Option class
606 ext::shared_ptr<StrikedTypePayoff> putPayoff(new
607 CashOrNothingPayoff(Option::Put, effStrike, cashRate));
608 VanillaOption putOpt(putPayoff, exercise);
609 putOpt.setPricingEngine(engine);
610 Real putVO = vars.nominal * accrualPeriod * putOpt.NPV()
611 * discount / discountAtFixing;
612 error = std::abs(x: nd2Price - putVO);
613 if (error>vars.blackTolerance)
614 BOOST_ERROR("\nDigital Put Option:" <<
615 "\nVolatility = " << io::rate(capletVol) <<
616 "\nStrike = " << io::rate(strike) <<
617 "\nExercise = " << k+1 << " years" <<
618 "\nOption price by Black asset-ot-nothing payoff = " << putVO <<
619 "\nOption price by Reiner-Rubinstein = " << nd2Price <<
620 "\nError " << error );
621 }
622 }
623 }
624}
625
626void DigitalCouponTest::testCashOrNothingDeepInTheMoney() {
627
628 BOOST_TEST_MESSAGE("Testing European deep in-the-money cash-or-nothing "
629 "digital coupon...");
630
631 using namespace digital_coupon_test;
632
633 CommonVars vars;
634
635 Real gearing = 1.0;
636 Real spread = 0.0;
637
638 Volatility capletVolatility = 0.0001;
639 RelinkableHandle<OptionletVolatilityStructure> volatility;
640 volatility.linkTo(h: ext::shared_ptr<OptionletVolatilityStructure>(new
641 ConstantOptionletVolatility(vars.today, vars.calendar, Following,
642 capletVolatility, Actual360())));
643
644 for (Size k = 0; k<10; k++) { // Loop on start and end dates
645 Date startDate = vars.calendar.advance(date: vars.settlement,period: (k+1)*Years);
646 Date endDate = vars.calendar.advance(date: vars.settlement,period: (k+2)*Years);
647 Rate nullstrike = Null<Rate>();
648 Rate cashRate = 0.01;
649 Date paymentDate = endDate;
650
651 ext::shared_ptr<FloatingRateCoupon> underlying(new
652 IborCoupon(paymentDate, vars.nominal,
653 startDate, endDate,
654 vars.fixingDays, vars.index,
655 gearing, spread));
656 // Floating Rate Coupon - Deep-in-the-money Call Digital option
657 Rate strike = 0.001;
658 DigitalCoupon digitalCappedCoupon(underlying,
659 strike, Position::Short, false, cashRate,
660 nullstrike, Position::Short, false, nullstrike);
661 ext::shared_ptr<IborCouponPricer> pricer(new
662 BlackIborCouponPricer(volatility));
663 digitalCappedCoupon.setPricer(pricer);
664
665 // Check price vs its target
666 Time accrualPeriod = underlying->accrualPeriod();
667 Real discount = vars.termStructure->discount(d: endDate);
668
669 Real targetOptionPrice = cashRate * vars.nominal * accrualPeriod * discount;
670 Real targetPrice = underlying->price(discountingCurve: vars.termStructure) - targetOptionPrice;
671 Real digitalPrice = digitalCappedCoupon.price(discountingCurve: vars.termStructure);
672
673 Real error = std::fabs(x: targetPrice - digitalPrice);
674 Real tolerance = 1e-07;
675 if (error>tolerance)
676 BOOST_ERROR("\nFloating Coupon - Digital Call Coupon:" <<
677 "\nVolatility = " << io::rate(capletVolatility) <<
678 "\nStrike = " << io::rate(strike) <<
679 "\nExercise = " << k+1 << " years" <<
680 "\nCoupon price = " << digitalPrice <<
681 "\nTarget price = " << targetPrice <<
682 "\nError " << error );
683
684 // Check digital option price
685 Real replicationOptionPrice = digitalCappedCoupon.callOptionRate() *
686 vars.nominal * accrualPeriod * discount;
687 error = std::abs(x: targetOptionPrice - replicationOptionPrice);
688 Real optionTolerance = 1e-07;
689 if (error>optionTolerance)
690 BOOST_ERROR("\nDigital Call Option:" <<
691 "\nVolatility = " << io::rate(capletVolatility) <<
692 "\nStrike = " << io::rate(strike) <<
693 "\nExercise = " << k+1 << " years" <<
694 "\nPrice by replication = " << replicationOptionPrice <<
695 "\nTarget price = " << targetOptionPrice <<
696 "\nError = " << error);
697
698 // Floating Rate Coupon + Deep-in-the-money Put Digital option
699 strike = 0.99;
700 DigitalCoupon digitalFlooredCoupon(underlying,
701 nullstrike, Position::Long, false, nullstrike,
702 strike, Position::Long, false, cashRate);
703 digitalFlooredCoupon.setPricer(pricer);
704
705 // Check price vs its target
706 targetPrice = underlying->price(discountingCurve: vars.termStructure) + targetOptionPrice;
707 digitalPrice = digitalFlooredCoupon.price(discountingCurve: vars.termStructure);
708 error = std::fabs(x: targetPrice - digitalPrice);
709 if (error>tolerance)
710 BOOST_ERROR("\nFloating Coupon + Digital Put Option:" <<
711 "\nVolatility = " << io::rate(capletVolatility) <<
712 "\nStrike = " << io::rate(strike) <<
713 "\nExercise = " << k+1 << " years" <<
714 "\nCoupon price = " << digitalPrice <<
715 "\nTarget price = " << targetPrice <<
716 "\nError = " << error );
717
718 // Check digital option
719 replicationOptionPrice = digitalFlooredCoupon.putOptionRate() *
720 vars.nominal * accrualPeriod * discount;
721 error = std::abs(x: targetOptionPrice - replicationOptionPrice);
722 if (error>optionTolerance)
723 BOOST_ERROR("\nDigital Put Coupon:" <<
724 "\nVolatility = " << io::rate(capletVolatility) <<
725 "\nStrike = " << io::rate(strike) <<
726 "\nExercise = " << k+1 << " years" <<
727 "\nPrice by replication = " << replicationOptionPrice <<
728 "\nTarget price = " << targetOptionPrice <<
729 "\nError = " << error );
730 }
731}
732
733void DigitalCouponTest::testCashOrNothingDeepOutTheMoney() {
734
735 BOOST_TEST_MESSAGE("Testing European deep out-the-money cash-or-nothing "
736 "digital coupon...");
737
738 using namespace digital_coupon_test;
739
740 CommonVars vars;
741
742 Real gearing = 1.0;
743 Real spread = 0.0;
744
745 Volatility capletVolatility = 0.0001;
746 RelinkableHandle<OptionletVolatilityStructure> volatility;
747 volatility.linkTo(h: ext::shared_ptr<OptionletVolatilityStructure>(new
748 ConstantOptionletVolatility(vars.today, vars.calendar, Following,
749 capletVolatility, Actual360())));
750
751 for (Size k = 0; k<10; k++) { // loop on start and end dates
752 Date startDate = vars.calendar.advance(date: vars.settlement,period: (k+1)*Years);
753 Date endDate = vars.calendar.advance(date: vars.settlement,period: (k+2)*Years);
754 Rate nullstrike = Null<Rate>();
755 Rate cashRate = 0.01;
756 Date paymentDate = endDate;
757
758 ext::shared_ptr<FloatingRateCoupon> underlying(new
759 IborCoupon(paymentDate, vars.nominal,
760 startDate, endDate,
761 vars.fixingDays, vars.index,
762 gearing, spread));
763 // Deep out-of-the-money Capped Digital Coupon
764 Rate strike = 0.99;
765 DigitalCoupon digitalCappedCoupon(underlying,
766 strike, Position::Short, false, cashRate,
767 nullstrike, Position::Short, false, nullstrike);
768
769 ext::shared_ptr<IborCouponPricer> pricer(new BlackIborCouponPricer(volatility));
770 digitalCappedCoupon.setPricer(pricer);
771
772 // Check price vs its target
773 Time accrualPeriod = underlying->accrualPeriod();
774 Real discount = vars.termStructure->discount(d: endDate);
775
776 Real targetPrice = underlying->price(discountingCurve: vars.termStructure);
777 Real digitalPrice = digitalCappedCoupon.price(discountingCurve: vars.termStructure);
778 Real error = std::fabs(x: targetPrice - digitalPrice);
779 Real tolerance = 1e-10;
780 if (error>tolerance)
781 BOOST_ERROR("\nFloating Coupon + Digital Call Option:" <<
782 "\nVolatility = " << io::rate(capletVolatility) <<
783 "\nStrike = " << io::rate(strike) <<
784 "\nExercise = " << k+1 << " years" <<
785 "\nCoupon price = " << digitalPrice <<
786 "\nTarget price = " << targetPrice <<
787 "\nError = " << error );
788
789 // Check digital option price
790 Real targetOptionPrice = 0.;
791 Real replicationOptionPrice = digitalCappedCoupon.callOptionRate() *
792 vars.nominal * accrualPeriod * discount;
793 error = std::abs(x: targetOptionPrice - replicationOptionPrice);
794 Real optionTolerance = 1e-10;
795 if (error>optionTolerance)
796 BOOST_ERROR("\nDigital Call Option:" <<
797 "\nVolatility = " << io::rate(capletVolatility) <<
798 "\nStrike = " << io::rate(strike) <<
799 "\nExercise = " << k+1 << " years" <<
800 "\nPrice by replication = " << replicationOptionPrice <<
801 "\nTarget price = " << targetOptionPrice <<
802 "\nError = " << error );
803
804 // Deep out-of-the-money Floored Digital Coupon
805 strike = 0.01;
806 DigitalCoupon digitalFlooredCoupon(underlying,
807 nullstrike, Position::Long, false, nullstrike,
808 strike, Position::Long, false, cashRate);
809 digitalFlooredCoupon.setPricer(pricer);
810
811 // Check price vs its target
812 targetPrice = underlying->price(discountingCurve: vars.termStructure);
813 digitalPrice = digitalFlooredCoupon.price(discountingCurve: vars.termStructure);
814 tolerance = 1e-09;
815 error = std::fabs(x: targetPrice - digitalPrice);
816 if (error>tolerance)
817 BOOST_ERROR("\nDigital Floored Coupon:" <<
818 "\nVolatility = " << io::rate(capletVolatility) <<
819 "\nStrike = " << io::rate(strike) <<
820 "\nExercise = " << k+1 << " years" <<
821 "\nCoupon price = " << digitalPrice <<
822 "\nTarget price = " << targetPrice <<
823 "\nError = " << error );
824
825 // Check digital option
826 targetOptionPrice = 0.0;
827 replicationOptionPrice = digitalFlooredCoupon.putOptionRate() *
828 vars.nominal * accrualPeriod * discount;
829 error = std::abs(x: targetOptionPrice - replicationOptionPrice);
830 if (error>optionTolerance)
831 BOOST_ERROR("\nDigital Put Option:" <<
832 "\nVolatility = " << io::rate(capletVolatility) <<
833 "\nStrike = " << io::rate(strike) <<
834 "\nExercise = " << k+1 << " years" <<
835 "\nPrice by replication " << replicationOptionPrice <<
836 "\nTarget price " << targetOptionPrice <<
837 "\nError " << error );
838 }
839}
840
841
842void DigitalCouponTest::testCallPutParity() {
843
844 BOOST_TEST_MESSAGE("Testing call/put parity for European digital coupon...");
845
846 using namespace digital_coupon_test;
847
848 CommonVars vars;
849
850 Volatility vols[] = { 0.05, 0.15, 0.30 };
851 Rate strikes[] = { 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07 };
852
853 Real gearing = 1.0;
854 Real spread = 0.0;
855
856 for (Real capletVolatility : vols) {
857 RelinkableHandle<OptionletVolatilityStructure> volatility;
858 volatility.linkTo(
859 h: ext::shared_ptr<OptionletVolatilityStructure>(new ConstantOptionletVolatility(
860 vars.today, vars.calendar, Following, capletVolatility, Actual360())));
861 for (Real strike : strikes) {
862 for (Size k = 0; k < 10; k++) {
863 Date startDate = vars.calendar.advance(date: vars.settlement,period: (k+1)*Years);
864 Date endDate = vars.calendar.advance(date: vars.settlement,period: (k+2)*Years);
865 Rate nullstrike = Null<Rate>();
866
867 Date paymentDate = endDate;
868
869 ext::shared_ptr<FloatingRateCoupon> underlying(new
870 IborCoupon(paymentDate, vars.nominal,
871 startDate, endDate,
872 vars.fixingDays, vars.index,
873 gearing, spread));
874 // Cash-or-Nothing
875 Rate cashRate = 0.01;
876 // Floating Rate Coupon + Call Digital option
877 DigitalCoupon cash_digitalCallCoupon(underlying,
878 strike, Position::Long, false, cashRate,
879 nullstrike, Position::Long, false, nullstrike);
880 ext::shared_ptr<IborCouponPricer> pricer(new
881 BlackIborCouponPricer(volatility));
882 cash_digitalCallCoupon.setPricer(pricer);
883 // Floating Rate Coupon - Put Digital option
884 DigitalCoupon cash_digitalPutCoupon(underlying,
885 nullstrike, Position::Long, false, nullstrike,
886 strike, Position::Short, false, cashRate);
887
888 cash_digitalPutCoupon.setPricer(pricer);
889 Real digitalPrice = cash_digitalCallCoupon.price(discountingCurve: vars.termStructure) -
890 cash_digitalPutCoupon.price(discountingCurve: vars.termStructure);
891 // Target price
892 Time accrualPeriod = underlying->accrualPeriod();
893 Real discount = vars.termStructure->discount(d: endDate);
894 Real targetPrice = vars.nominal * accrualPeriod * discount * cashRate;
895
896 Real error = std::fabs(x: targetPrice - digitalPrice);
897 Real tolerance = 1.e-08;
898 if (error>tolerance)
899 BOOST_ERROR("\nCash-or-nothing:" <<
900 "\nVolatility = " << io::rate(capletVolatility) <<
901 "\nStrike = " << io::rate(strike) <<
902 "\nExercise = " << k+1 << " years" <<
903 "\nPrice = " << digitalPrice <<
904 "\nTarget Price = " << targetPrice <<
905 "\nError = " << error );
906
907 // Asset-or-Nothing
908 // Floating Rate Coupon + Call Digital option
909 DigitalCoupon asset_digitalCallCoupon(underlying,
910 strike, Position::Long, false, nullstrike,
911 nullstrike, Position::Long, false, nullstrike);
912 asset_digitalCallCoupon.setPricer(pricer);
913 // Floating Rate Coupon - Put Digital option
914 DigitalCoupon asset_digitalPutCoupon(underlying,
915 nullstrike, Position::Long, false, nullstrike,
916 strike, Position::Short, false, nullstrike);
917 asset_digitalPutCoupon.setPricer(pricer);
918 digitalPrice = asset_digitalCallCoupon.price(discountingCurve: vars.termStructure) -
919 asset_digitalPutCoupon.price(discountingCurve: vars.termStructure);
920 // Target price
921 targetPrice = vars.nominal * accrualPeriod * discount * underlying->rate();
922 error = std::fabs(x: targetPrice - digitalPrice);
923 tolerance = 1.e-07;
924 if (error>tolerance)
925 BOOST_ERROR("\nAsset-or-nothing:" <<
926 "\nVolatility = " << io::rate(capletVolatility) <<
927 "\nStrike = " << io::rate(strike) <<
928 "\nExercise = " << k+1 << " years" <<
929 "\nPrice = " << digitalPrice <<
930 "\nTarget Price = " << targetPrice <<
931 "\nError = " << error );
932 }
933 }
934 }
935}
936
937void DigitalCouponTest::testReplicationType() {
938
939 BOOST_TEST_MESSAGE("Testing replication type for European digital coupon...");
940
941 using namespace digital_coupon_test;
942
943 CommonVars vars;
944
945 Volatility vols[] = { 0.05, 0.15, 0.30 };
946 Rate strikes[] = { 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07 };
947
948 Real gearing = 1.0;
949 Real spread = 0.0;
950
951 Real gap = 1e-04;
952 ext::shared_ptr<DigitalReplication> subReplication(new
953 DigitalReplication(Replication::Sub, gap));
954 ext::shared_ptr<DigitalReplication> centralReplication(new
955 DigitalReplication(Replication::Central, gap));
956 ext::shared_ptr<DigitalReplication> superReplication(new
957 DigitalReplication(Replication::Super, gap));
958
959 for (Real capletVolatility : vols) {
960 RelinkableHandle<OptionletVolatilityStructure> volatility;
961 volatility.linkTo(h: ext::shared_ptr<OptionletVolatilityStructure>(new
962 ConstantOptionletVolatility(vars.today, vars.calendar, Following,
963 capletVolatility, Actual360())));
964 for (Real strike : strikes) {
965 for (Size k = 0; k < 10; k++) {
966 Date startDate = vars.calendar.advance(date: vars.settlement,period: (k+1)*Years);
967 Date endDate = vars.calendar.advance(date: vars.settlement,period: (k+2)*Years);
968 Rate nullstrike = Null<Rate>();
969
970 Date paymentDate = endDate;
971
972 ext::shared_ptr<FloatingRateCoupon> underlying(new
973 IborCoupon(paymentDate, vars.nominal,
974 startDate, endDate,
975 vars.fixingDays, vars.index,
976 gearing, spread));
977 // Cash-or-Nothing
978 Rate cashRate = 0.005;
979 // Floating Rate Coupon + Call Digital option
980 DigitalCoupon sub_cash_longDigitalCallCoupon(underlying,
981 strike, Position::Long, false, cashRate,
982 nullstrike, Position::Long, false, nullstrike,
983 subReplication);
984 DigitalCoupon central_cash_longDigitalCallCoupon(underlying,
985 strike, Position::Long, false, cashRate,
986 nullstrike, Position::Long, false, nullstrike,
987 centralReplication);
988 DigitalCoupon over_cash_longDigitalCallCoupon(underlying,
989 strike, Position::Long, false, cashRate,
990 nullstrike, Position::Long, false, nullstrike,
991 superReplication);
992 ext::shared_ptr<IborCouponPricer> pricer(new
993 BlackIborCouponPricer(volatility));
994 sub_cash_longDigitalCallCoupon.setPricer(pricer);
995 central_cash_longDigitalCallCoupon.setPricer(pricer);
996 over_cash_longDigitalCallCoupon.setPricer(pricer);
997 Real sub_digitalPrice = sub_cash_longDigitalCallCoupon.price(discountingCurve: vars.termStructure);
998 Real central_digitalPrice = central_cash_longDigitalCallCoupon.price(discountingCurve: vars.termStructure);
999 Real over_digitalPrice = over_cash_longDigitalCallCoupon.price(discountingCurve: vars.termStructure);
1000 Real tolerance = 1.e-09;
1001 if ( ( (sub_digitalPrice > central_digitalPrice) &&
1002 std::abs(x: central_digitalPrice - sub_digitalPrice)>tolerance ) ||
1003 ( (central_digitalPrice>over_digitalPrice) &&
1004 std::abs(x: central_digitalPrice - over_digitalPrice)>tolerance ) ) {
1005 BOOST_ERROR("\nCash-or-nothing: Floating Rate Coupon + Call Digital option" <<
1006 "\nVolatility = " << io::rate(capletVolatility) <<
1007 "\nStrike = " << io::rate(strike) <<
1008 "\nExercise = " << k+1 << " years" <<
1009 std::setprecision(20) <<
1010 "\nSub-Replication Price = " << sub_digitalPrice <<
1011 "\nCentral-Replication Price = " << central_digitalPrice <<
1012 "\nOver-Replication Price = " << over_digitalPrice);
1013 }
1014
1015 // Floating Rate Coupon - Call Digital option
1016 DigitalCoupon sub_cash_shortDigitalCallCoupon(underlying,
1017 strike, Position::Short, false, cashRate,
1018 nullstrike, Position::Long, false, nullstrike,
1019 subReplication);
1020 DigitalCoupon central_cash_shortDigitalCallCoupon(underlying,
1021 strike, Position::Short, false, cashRate,
1022 nullstrike, Position::Long, false, nullstrike,
1023 centralReplication);
1024 DigitalCoupon over_cash_shortDigitalCallCoupon(underlying,
1025 strike, Position::Short, false, cashRate,
1026 nullstrike, Position::Long, false, nullstrike,
1027 superReplication);
1028 sub_cash_shortDigitalCallCoupon.setPricer(pricer);
1029 central_cash_shortDigitalCallCoupon.setPricer(pricer);
1030 over_cash_shortDigitalCallCoupon.setPricer(pricer);
1031 sub_digitalPrice = sub_cash_shortDigitalCallCoupon.price(discountingCurve: vars.termStructure);
1032 central_digitalPrice = central_cash_shortDigitalCallCoupon.price(discountingCurve: vars.termStructure);
1033 over_digitalPrice = over_cash_shortDigitalCallCoupon.price(discountingCurve: vars.termStructure);
1034 if ( ( (sub_digitalPrice > central_digitalPrice) &&
1035 std::abs(x: central_digitalPrice - sub_digitalPrice)>tolerance ) ||
1036 ( (central_digitalPrice>over_digitalPrice) &&
1037 std::abs(x: central_digitalPrice - over_digitalPrice)>tolerance ) )
1038 BOOST_ERROR("\nCash-or-nothing: Floating Rate Coupon - Call Digital option" <<
1039 "\nVolatility = " << io::rate(capletVolatility) <<
1040 "\nStrike = " << io::rate(strike) <<
1041 "\nExercise = " << k+1 << " years" <<
1042 std::setprecision(20) <<
1043 "\nSub-Replication Price = " << sub_digitalPrice <<
1044 "\nCentral-Replication Price = " << central_digitalPrice <<
1045 "\nOver-Replication Price = " << over_digitalPrice);
1046 // Floating Rate Coupon + Put Digital option
1047 DigitalCoupon sub_cash_longDigitalPutCoupon(underlying,
1048 nullstrike, Position::Long, false, nullstrike,
1049 strike, Position::Long, false, cashRate,
1050 subReplication);
1051 DigitalCoupon central_cash_longDigitalPutCoupon(underlying,
1052 nullstrike, Position::Long, false, nullstrike,
1053 strike, Position::Long, false, cashRate,
1054 centralReplication);
1055 DigitalCoupon over_cash_longDigitalPutCoupon(underlying,
1056 nullstrike, Position::Long, false, nullstrike,
1057 strike, Position::Long, false, cashRate,
1058 superReplication);
1059 sub_cash_longDigitalPutCoupon.setPricer(pricer);
1060 central_cash_longDigitalPutCoupon.setPricer(pricer);
1061 over_cash_longDigitalPutCoupon.setPricer(pricer);
1062 sub_digitalPrice = sub_cash_longDigitalPutCoupon.price(discountingCurve: vars.termStructure);
1063 central_digitalPrice = central_cash_longDigitalPutCoupon.price(discountingCurve: vars.termStructure);
1064 over_digitalPrice = over_cash_longDigitalPutCoupon.price(discountingCurve: vars.termStructure);
1065 if ( ( (sub_digitalPrice > central_digitalPrice) &&
1066 std::abs(x: central_digitalPrice - sub_digitalPrice)>tolerance ) ||
1067 ( (central_digitalPrice>over_digitalPrice) &&
1068 std::abs(x: central_digitalPrice - over_digitalPrice)>tolerance ) )
1069 BOOST_ERROR("\nCash-or-nothing: Floating Rate Coupon + Put Digital option" <<
1070 "\nVolatility = " << io::rate(capletVolatility) <<
1071 "\nStrike = " << io::rate(strike) <<
1072 "\nExercise = " << k+1 << " years" <<
1073 std::setprecision(20) <<
1074 "\nSub-Replication Price = " << sub_digitalPrice <<
1075 "\nCentral-Replication Price = " << central_digitalPrice <<
1076 "\nOver-Replication Price = " << over_digitalPrice);
1077
1078 // Floating Rate Coupon - Put Digital option
1079 DigitalCoupon sub_cash_shortDigitalPutCoupon(underlying,
1080 nullstrike, Position::Long, false, nullstrike,
1081 strike, Position::Short, false, cashRate,
1082 subReplication);
1083 DigitalCoupon central_cash_shortDigitalPutCoupon(underlying,
1084 nullstrike, Position::Long, false, nullstrike,
1085 strike, Position::Short, false, cashRate,
1086 centralReplication);
1087 DigitalCoupon over_cash_shortDigitalPutCoupon(underlying,
1088 nullstrike, Position::Long, false, nullstrike,
1089 strike, Position::Short, false, cashRate,
1090 superReplication);
1091 sub_cash_shortDigitalPutCoupon.setPricer(pricer);
1092 central_cash_shortDigitalPutCoupon.setPricer(pricer);
1093 over_cash_shortDigitalPutCoupon.setPricer(pricer);
1094 sub_digitalPrice = sub_cash_shortDigitalPutCoupon.price(discountingCurve: vars.termStructure);
1095 central_digitalPrice = central_cash_shortDigitalPutCoupon.price(discountingCurve: vars.termStructure);
1096 over_digitalPrice = over_cash_shortDigitalPutCoupon.price(discountingCurve: vars.termStructure);
1097 if ( ( (sub_digitalPrice > central_digitalPrice) &&
1098 std::abs(x: central_digitalPrice - sub_digitalPrice)>tolerance ) ||
1099 ( (central_digitalPrice>over_digitalPrice) &&
1100 std::abs(x: central_digitalPrice - over_digitalPrice)>tolerance ) )
1101 BOOST_ERROR("\nCash-or-nothing: Floating Rate Coupon + Call Digital option" <<
1102 "\nVolatility = " << io::rate(capletVolatility) <<
1103 "\nStrike = " << io::rate(strike) <<
1104 "\nExercise = " << k+1 << " years" <<
1105 std::setprecision(20) <<
1106 "\nSub-Replication Price = " << sub_digitalPrice <<
1107 "\nCentral-Replication Price = " << central_digitalPrice <<
1108 "\nOver-Replication Price = " << over_digitalPrice);
1109 }
1110 }
1111 }
1112}
1113
1114
1115test_suite* DigitalCouponTest::suite() {
1116 auto* suite = BOOST_TEST_SUITE("Digital coupon tests");
1117 suite->add(QUANTLIB_TEST_CASE(&DigitalCouponTest::testAssetOrNothing));
1118 suite->add(QUANTLIB_TEST_CASE(&DigitalCouponTest::testAssetOrNothingDeepInTheMoney));
1119 suite->add(QUANTLIB_TEST_CASE(&DigitalCouponTest::testAssetOrNothingDeepOutTheMoney));
1120 suite->add(QUANTLIB_TEST_CASE(&DigitalCouponTest::testCashOrNothing));
1121 suite->add(QUANTLIB_TEST_CASE(&DigitalCouponTest::testCashOrNothingDeepInTheMoney));
1122 suite->add(QUANTLIB_TEST_CASE(&DigitalCouponTest::testCashOrNothingDeepOutTheMoney));
1123 suite->add(QUANTLIB_TEST_CASE(&DigitalCouponTest::testCallPutParity));
1124 suite->add(QUANTLIB_TEST_CASE(&DigitalCouponTest::testReplicationType));
1125 return suite;
1126}
1127

source code of quantlib/test-suite/digitalcoupon.cpp