| 1 | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| 2 | |
| 3 | /* |
| 4 | Copyright (C) 2014 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 "partialtimebarrieroption.hpp" |
| 21 | #include "utilities.hpp" |
| 22 | #include <ql/experimental/exoticoptions/partialtimebarrieroption.hpp> |
| 23 | #include <ql/experimental/exoticoptions/analyticpartialtimebarrieroptionengine.hpp> |
| 24 | #include <ql/quotes/simplequote.hpp> |
| 25 | #include <ql/utilities/dataformatters.hpp> |
| 26 | #include <ql/time/calendars/target.hpp> |
| 27 | #include <ql/time/daycounters/actual360.hpp> |
| 28 | |
| 29 | using namespace QuantLib; |
| 30 | using namespace boost::unit_test_framework; |
| 31 | |
| 32 | namespace partial_time_barrier_option_test { |
| 33 | |
| 34 | struct TestCase { |
| 35 | Real underlying; |
| 36 | Real strike; |
| 37 | Integer days; |
| 38 | Real result; |
| 39 | }; |
| 40 | |
| 41 | } |
| 42 | |
| 43 | void PartialTimeBarrierOptionTest::testAnalyticEngine() { |
| 44 | BOOST_TEST_MESSAGE( |
| 45 | "Testing analytic engine for partial-time barrier option..." ); |
| 46 | |
| 47 | using namespace partial_time_barrier_option_test; |
| 48 | |
| 49 | Date today = Settings::instance().evaluationDate(); |
| 50 | |
| 51 | Option::Type type = Option::Call; |
| 52 | DayCounter dc = Actual360(); |
| 53 | Date maturity = today + 360; |
| 54 | ext::shared_ptr<Exercise> exercise = |
| 55 | ext::make_shared<EuropeanExercise>(args&: maturity); |
| 56 | Real barrier = 100.0; |
| 57 | Real rebate = 0.0; |
| 58 | |
| 59 | ext::shared_ptr<SimpleQuote> spot = ext::make_shared<SimpleQuote>(); |
| 60 | ext::shared_ptr<SimpleQuote> qRate = ext::make_shared<SimpleQuote>(args: 0.0); |
| 61 | ext::shared_ptr<SimpleQuote> rRate = ext::make_shared<SimpleQuote>(args: 0.1); |
| 62 | ext::shared_ptr<SimpleQuote> vol = ext::make_shared<SimpleQuote>(args: 0.25); |
| 63 | |
| 64 | Handle<Quote> underlying(spot); |
| 65 | Handle<YieldTermStructure> dividendTS(flatRate(today, forward: qRate, dc)); |
| 66 | Handle<YieldTermStructure> riskFreeTS(flatRate(today, forward: rRate, dc)); |
| 67 | Handle<BlackVolTermStructure> blackVolTS(flatVol(today, volatility: vol, dc)); |
| 68 | |
| 69 | const ext::shared_ptr<BlackScholesMertonProcess> process = |
| 70 | ext::make_shared<BlackScholesMertonProcess>(args&: underlying, |
| 71 | args&: dividendTS, |
| 72 | args&: riskFreeTS, |
| 73 | args&: blackVolTS); |
| 74 | ext::shared_ptr<PricingEngine> engine = |
| 75 | ext::make_shared<AnalyticPartialTimeBarrierOptionEngine>(args: process); |
| 76 | |
| 77 | TestCase cases[] = { |
| 78 | { .underlying: 95.0, .strike: 90.0, .days: 1, .result: 0.0393 }, |
| 79 | { .underlying: 95.0, .strike: 110.0, .days: 1, .result: 0.0000 }, |
| 80 | { .underlying: 105.0, .strike: 90.0, .days: 1, .result: 9.8751 }, |
| 81 | { .underlying: 105.0, .strike: 110.0, .days: 1, .result: 6.2303 }, |
| 82 | |
| 83 | { .underlying: 95.0, .strike: 90.0, .days: 90, .result: 6.2747 }, |
| 84 | { .underlying: 95.0, .strike: 110.0, .days: 90, .result: 3.7352 }, |
| 85 | { .underlying: 105.0, .strike: 90.0, .days: 90, .result: 15.6324 }, |
| 86 | { .underlying: 105.0, .strike: 110.0, .days: 90, .result: 9.6812 }, |
| 87 | |
| 88 | { .underlying: 95.0, .strike: 90.0, .days: 180, .result: 10.3345 }, |
| 89 | { .underlying: 95.0, .strike: 110.0, .days: 180, .result: 5.8712 }, |
| 90 | { .underlying: 105.0, .strike: 90.0, .days: 180, .result: 19.2896 }, |
| 91 | { .underlying: 105.0, .strike: 110.0, .days: 180, .result: 11.6055 }, |
| 92 | |
| 93 | { .underlying: 95.0, .strike: 90.0, .days: 270, .result: 13.4342 }, |
| 94 | { .underlying: 95.0, .strike: 110.0, .days: 270, .result: 7.1270 }, |
| 95 | { .underlying: 105.0, .strike: 90.0, .days: 270, .result: 22.0753 }, |
| 96 | { .underlying: 105.0, .strike: 110.0, .days: 270, .result: 12.7342 }, |
| 97 | |
| 98 | { .underlying: 95.0, .strike: 90.0, .days: 359, .result: 16.8576 }, |
| 99 | { .underlying: 95.0, .strike: 110.0, .days: 359, .result: 7.5763 }, |
| 100 | { .underlying: 105.0, .strike: 90.0, .days: 359, .result: 25.1488 }, |
| 101 | { .underlying: 105.0, .strike: 110.0, .days: 359, .result: 13.1376 } |
| 102 | }; |
| 103 | |
| 104 | for (auto& i : cases) { |
| 105 | Date coverEventDate = today + i.days; |
| 106 | ext::shared_ptr<StrikedTypePayoff> payoff = |
| 107 | ext::make_shared<PlainVanillaPayoff>(args&: type, args&: i.strike); |
| 108 | PartialTimeBarrierOption option(PartialBarrier::DownOut, |
| 109 | PartialBarrier::EndB1, |
| 110 | barrier, rebate, |
| 111 | coverEventDate, |
| 112 | payoff, exercise); |
| 113 | option.setPricingEngine(engine); |
| 114 | |
| 115 | spot->setValue(i.underlying); |
| 116 | Real calculated = option.NPV(); |
| 117 | Real expected = i.result; |
| 118 | Real error = std::fabs(x: calculated-expected); |
| 119 | Real tolerance = 1e-4; |
| 120 | if (error > tolerance) |
| 121 | BOOST_ERROR("Failed to reproduce partial-time barrier option value" |
| 122 | << "\n expected: " << expected |
| 123 | << "\n calculated: " << calculated |
| 124 | << "\n error: " << error); |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | |
| 129 | test_suite* PartialTimeBarrierOptionTest::suite() { |
| 130 | auto* suite = BOOST_TEST_SUITE("Partial-time barrier option tests" ); |
| 131 | |
| 132 | suite->add(QUANTLIB_TEST_CASE( |
| 133 | &PartialTimeBarrierOptionTest::testAnalyticEngine)); |
| 134 | |
| 135 | return suite; |
| 136 | } |
| 137 | |