| 1 | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| 2 | |
| 3 | /* |
| 4 | Copyright (C) 2010 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 "chooseroption.hpp" |
| 21 | #include "utilities.hpp" |
| 22 | #include <ql/time/daycounters/actual360.hpp> |
| 23 | #include <ql/instruments/simplechooseroption.hpp> |
| 24 | #include <ql/instruments/complexchooseroption.hpp> |
| 25 | #include <ql/pricingengines/exotic/analyticsimplechooserengine.hpp> |
| 26 | #include <ql/pricingengines/exotic/analyticcomplexchooserengine.hpp> |
| 27 | #include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp> |
| 28 | #include <ql/utilities/dataformatters.hpp> |
| 29 | |
| 30 | using namespace QuantLib; |
| 31 | using namespace boost::unit_test_framework; |
| 32 | |
| 33 | #undef REPORT_FAILURE |
| 34 | #define REPORT_FAILURE(greekName, choosingDate, \ |
| 35 | exercise, s, q, r, today, v, \ |
| 36 | expected, calculated, tolerance) \ |
| 37 | BOOST_ERROR( \ |
| 38 | exerciseTypeToString(exercise) \ |
| 39 | << " Chooser option with " \ |
| 40 | << " spot value: " << s << "\n" \ |
| 41 | << " dividend yield: " << io::rate(q) << "\n" \ |
| 42 | << " risk-free rate: " << io::rate(r) << "\n" \ |
| 43 | << " reference date: " << today << "\n" \ |
| 44 | << " maturity: " << exercise->lastDate() << "\n" \ |
| 45 | << " volatility: " << io::volatility(v) << "\n\n" \ |
| 46 | << " expected " << greekName << ": " << expected << "\n" \ |
| 47 | << " calculated " << greekName << ": " << calculated << "\n"\ |
| 48 | << " error: " << std::fabs(expected-calculated) \ |
| 49 | << "\n" \ |
| 50 | << " tolerance: " << tolerance); |
| 51 | |
| 52 | |
| 53 | void ChooserOptionTest::testAnalyticSimpleChooserEngine(){ |
| 54 | |
| 55 | BOOST_TEST_MESSAGE("Testing analytic simple chooser option..." ); |
| 56 | |
| 57 | /* The data below are from |
| 58 | "Complete Guide to Option Pricing Formulas", Espen Gaarder Haug |
| 59 | pages 39-40 |
| 60 | */ |
| 61 | DayCounter dc = Actual360(); |
| 62 | Date today = Settings::instance().evaluationDate(); |
| 63 | |
| 64 | ext::shared_ptr<SimpleQuote> spot = ext::make_shared<SimpleQuote>(args: 50.0); |
| 65 | ext::shared_ptr<SimpleQuote> qRate = ext::make_shared<SimpleQuote>(args: 0.0); |
| 66 | ext::shared_ptr<YieldTermStructure> qTS = flatRate(today, forward: qRate, dc); |
| 67 | ext::shared_ptr<SimpleQuote> rRate = ext::make_shared<SimpleQuote>(args: 0.08); |
| 68 | ext::shared_ptr<YieldTermStructure> rTS = flatRate(today, forward: rRate, dc); |
| 69 | ext::shared_ptr<SimpleQuote> vol = ext::make_shared<SimpleQuote>(args: 0.25); |
| 70 | ext::shared_ptr<BlackVolTermStructure> volTS = flatVol(today, volatility: vol, dc); |
| 71 | |
| 72 | ext::shared_ptr<BlackScholesMertonProcess> stochProcess = |
| 73 | ext::make_shared<BlackScholesMertonProcess>( |
| 74 | args: Handle<Quote>(spot), |
| 75 | args: Handle<YieldTermStructure>(qTS), |
| 76 | args: Handle<YieldTermStructure>(rTS), |
| 77 | args: Handle<BlackVolTermStructure>(volTS)); |
| 78 | |
| 79 | ext::shared_ptr<PricingEngine> engine = |
| 80 | ext::make_shared<AnalyticSimpleChooserEngine>(args&: stochProcess); |
| 81 | |
| 82 | Real strike = 50.0; |
| 83 | |
| 84 | Date exerciseDate = today + 180; |
| 85 | ext::shared_ptr<Exercise> exercise = |
| 86 | ext::make_shared<EuropeanExercise>(args&: exerciseDate); |
| 87 | |
| 88 | Date choosingDate = today + 90; |
| 89 | SimpleChooserOption option(choosingDate,strike,exercise); |
| 90 | option.setPricingEngine(engine); |
| 91 | |
| 92 | Real calculated = option.NPV(); |
| 93 | Real expected = 6.1071; |
| 94 | Real tolerance = 3e-5; |
| 95 | if (std::fabs(x: calculated-expected) > tolerance) { |
| 96 | REPORT_FAILURE("value" , choosingDate, |
| 97 | exercise, spot->value(), |
| 98 | qRate->value(), rRate->value(), today, |
| 99 | vol->value(), expected, calculated, tolerance); |
| 100 | } |
| 101 | |
| 102 | } |
| 103 | |
| 104 | |
| 105 | void ChooserOptionTest::testAnalyticComplexChooserEngine(){ |
| 106 | BOOST_TEST_MESSAGE("Testing analytic complex chooser option..." ); |
| 107 | |
| 108 | /* The example below is from |
| 109 | "Complete Guide to Option Pricing Formulas", Espen Gaarder Haug |
| 110 | */ |
| 111 | DayCounter dc = Actual360(); |
| 112 | Date today = Date::todaysDate(); |
| 113 | |
| 114 | ext::shared_ptr<SimpleQuote> spot = ext::make_shared<SimpleQuote>(args: 50.0); |
| 115 | ext::shared_ptr<SimpleQuote> qRate = ext::make_shared<SimpleQuote>(args: 0.05); |
| 116 | ext::shared_ptr<YieldTermStructure> qTS = flatRate(today, forward: qRate, dc); |
| 117 | ext::shared_ptr<SimpleQuote> rRate = ext::make_shared<SimpleQuote>(args: 0.10); |
| 118 | ext::shared_ptr<YieldTermStructure> rTS = flatRate(today, forward: rRate, dc); |
| 119 | ext::shared_ptr<SimpleQuote> vol = ext::make_shared<SimpleQuote>(args: 0.35); |
| 120 | ext::shared_ptr<BlackVolTermStructure> volTS = flatVol(today, volatility: vol, dc); |
| 121 | |
| 122 | ext::shared_ptr<BlackScholesMertonProcess> stochProcess = |
| 123 | ext::make_shared<BlackScholesMertonProcess>( |
| 124 | args: Handle<Quote>(spot), |
| 125 | args: Handle<YieldTermStructure>(qTS), |
| 126 | args: Handle<YieldTermStructure>(rTS), |
| 127 | args: Handle<BlackVolTermStructure>(volTS)); |
| 128 | |
| 129 | ext::shared_ptr<PricingEngine> engine = |
| 130 | ext::make_shared<AnalyticComplexChooserEngine>(args&: stochProcess); |
| 131 | |
| 132 | Real callStrike = 55.0; |
| 133 | Real putStrike = 48.0; |
| 134 | |
| 135 | Date choosingDate = today + 90; |
| 136 | Date callExerciseDate = choosingDate + 180; |
| 137 | Date putExerciseDate = choosingDate + 210; |
| 138 | ext::shared_ptr<Exercise> callExercise = |
| 139 | ext::make_shared<EuropeanExercise>(args&: callExerciseDate); |
| 140 | ext::shared_ptr<Exercise> putExercise = |
| 141 | ext::make_shared<EuropeanExercise>(args&: putExerciseDate); |
| 142 | |
| 143 | ComplexChooserOption option(choosingDate,callStrike,putStrike, |
| 144 | callExercise,putExercise); |
| 145 | option.setPricingEngine(engine); |
| 146 | |
| 147 | Real calculated = option.NPV(); |
| 148 | Real expected = 6.0508; |
| 149 | Real error = std::fabs(x: calculated-expected); |
| 150 | Real tolerance = 1e-4; |
| 151 | if (error > tolerance) { |
| 152 | BOOST_ERROR("Failed to reproduce complex chooser option value" |
| 153 | << "\n expected: " << expected |
| 154 | << "\n calculated: " << calculated |
| 155 | << "\n error: " << error); |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | test_suite* ChooserOptionTest::suite() { |
| 160 | auto* suite = BOOST_TEST_SUITE("Chooser option tests" ); |
| 161 | |
| 162 | suite->add(QUANTLIB_TEST_CASE( |
| 163 | &ChooserOptionTest::testAnalyticSimpleChooserEngine)); |
| 164 | suite->add(QUANTLIB_TEST_CASE( |
| 165 | &ChooserOptionTest::testAnalyticComplexChooserEngine)); |
| 166 | |
| 167 | return suite; |
| 168 | } |
| 169 | |