diff --git a/CMakeLists.txt b/CMakeLists.txt index 6bd07e7f256..05418566772 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,7 @@ set(QL_INSTALL_CMAKEDIR "lib/cmake/${PACKAGE_NAME}" CACHE STRING option(QL_BUILD_BENCHMARK "Build benchmark" ON) option(QL_BUILD_EXAMPLES "Build examples" ON) option(QL_BUILD_TEST_SUITE "Build test suite" ON) +option(QL_BUILD_FUZZ_TEST_SUITE "Build fuzz test suite" OFF) option(QL_ENABLE_OPENMP "Detect and use OpenMP" OFF) option(QL_ENABLE_PARALLEL_UNIT_TEST_RUNNER "Enable the parallel unit test runner" OFF) option(QL_ENABLE_SESSIONS "Singletons return different instances for different sessions" OFF) @@ -269,6 +270,10 @@ if (QL_BUILD_TEST_SUITE OR QL_BUILD_BENCHMARK) add_subdirectory(test-suite) endif() +if ('${CMAKE_CXX_COMPILER_ID}' MATCHES 'Clang' AND QL_BUILD_FUZZ_TEST_SUITE) + add_subdirectory(fuzz-test-suite) +endif() + # CPack support (make package, make package_source) set(CPACK_PACKAGE_VERSION_MAJOR ${QUANTLIB_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${QUANTLIB_VERSION_MINOR}) diff --git a/CMakePresets.json b/CMakePresets.json index 189ba7abbeb..632c4ad97b6 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -174,51 +174,49 @@ { "name": "linux-clang-ninja-debug", "inherits": [ - "linux-clang-debug", - "ninja" + "ninja", + "linux-clang-debug" ] }, { "name": "linux-clang-ninja-release", "inherits": [ - "linux-clang-release", - "ninja" + "ninja", + "linux-clang-release" ] }, { "name": "linux-clang-ninja-relwithdebinfo", "inherits": [ - "linux-clang-relwithdebinfo", - "ninja" + "ninja", + "linux-clang-relwithdebinfo" ] }, { "name": "linux-gcc-ninja-debug", "inherits": [ - "linux-gcc-debug", - "ninja" + "ninja", + "linux-gcc-debug" ] }, { "name": "linux-gcc-ninja-release", "inherits": [ - "linux-gcc-release", - "ninja" + "ninja", + "linux-gcc-release" ] }, { "name": "linux-gcc-ninja-relwithdebinfo", "inherits": [ - "linux-gcc-relwithdebinfo", - "ninja" + "ninja", + "linux-gcc-relwithdebinfo" ] }, { "name": "linux-gcc-debug-with-clang-tidy", "inherits": [ - "linux-gcc-base", - "make", - "_debug" + "linux-gcc-debug" ], "cacheVariables": { "QL_CLANG_TIDY_OPTIONS": "-warnings-as-errors=*", @@ -276,9 +274,7 @@ { "name": "linux-ci-build-with-clang-tidy", "inherits": [ - "linux-gcc-base", - "make", - "_debug" + "linux-gcc-debug" ], "cacheVariables": { "BOOST_ROOT": "/usr", @@ -292,9 +288,7 @@ { "name": "linux-ci-build-with-nonstandard-options", "inherits": [ - "linux-gcc-base", - "ninja", - "_release" + "linux-gcc-ninja-release" ], "cacheVariables": { "BOOST_ROOT": "/usr", @@ -319,9 +313,7 @@ { "name": "windows-ci-build-with-nonstandard-options", "inherits": [ - "windows-msvc-base", - "ninja", - "_release" + "windows-msvc-release" ], "cacheVariables": { "CMAKE_CXX_STANDARD": "17", diff --git a/QuantLib.vcxproj b/QuantLib.vcxproj index cdbdcdd64d9..81efc70968c 100644 --- a/QuantLib.vcxproj +++ b/QuantLib.vcxproj @@ -1109,6 +1109,7 @@ + @@ -2311,6 +2312,7 @@ + diff --git a/QuantLib.vcxproj.filters b/QuantLib.vcxproj.filters index 9ad95815029..2a38c4cbf63 100644 --- a/QuantLib.vcxproj.filters +++ b/QuantLib.vcxproj.filters @@ -1242,6 +1242,9 @@ math\randomnumbers + + math\randomnumbers + math\randomnumbers @@ -4967,6 +4970,9 @@ math\matrixutilities + + math\randomnumbers + math\randomnumbers diff --git a/fuzz-test-suite/CMakeLists.txt b/fuzz-test-suite/CMakeLists.txt new file mode 100644 index 00000000000..aaddcbfefd4 --- /dev/null +++ b/fuzz-test-suite/CMakeLists.txt @@ -0,0 +1,21 @@ +# Determine the flags for fuzzing. Use OSS-Fuzz's configuration if available, otherwise fall back to defaults. +if(DEFINED ENV{LIB_FUZZING_ENGINE}) + set(FUZZING_ENGINE $ENV{LIB_FUZZING_ENGINE}) + set(FUZZING_COMPILE_FLAGS "") + set(FUZZING_LINK_FLAGS "${FUZZING_ENGINE}") +else() + set(FUZZING_COMPILE_FLAGS "-fsanitize=fuzzer") + set(FUZZING_LINK_FLAGS "-fsanitize=fuzzer") +endif() + +# Define the fuzz target +add_executable(DateParserFuzzer dateparserfuzzer.cpp) + +# Apply the determined flags +set_target_properties(DateParserFuzzer PROPERTIES + COMPILE_FLAGS "${FUZZING_COMPILE_FLAGS}" + LINK_FLAGS "${FUZZING_LINK_FLAGS}" +) + +# Link QuantLib and any other necessary libraries +target_link_libraries(DateParserFuzzer ql_library ${QL_THREAD_LIBRARIES}) diff --git a/fuzz-test-suite/dateparserfuzzer.cpp b/fuzz-test-suite/dateparserfuzzer.cpp new file mode 100644 index 00000000000..7c6f6423fdd --- /dev/null +++ b/fuzz-test-suite/dateparserfuzzer.cpp @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include +#include +#include + +#ifndef __clang__ +#pragma message("Fuzzer headers are available from clang, other compilers have not been tested.") +#endif +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp(data, size); + constexpr int kMaxString = 100; + try { + (void)QuantLib::DateParser::parseFormatted(fdp.ConsumeRandomLengthString(kMaxString), "%Y-%m-%d"); + (void)QuantLib::DateParser::parseISO(fdp.ConsumeRandomLengthString(kMaxString)); + } catch (const std::exception& e) { + // Handle or ignore exceptions + } + return 0; +} diff --git a/ql/CMakeLists.txt b/ql/CMakeLists.txt index 21a14c304f5..9e578e0d265 100644 --- a/ql/CMakeLists.txt +++ b/ql/CMakeLists.txt @@ -414,6 +414,7 @@ set(QL_SOURCES math/polynomialmathfunction.cpp math/primenumbers.cpp math/quadratic.cpp + math/randomnumbers/burley2020sobolrsg.cpp math/randomnumbers/faurersg.cpp math/randomnumbers/haltonrsg.cpp math/randomnumbers/knuthuniformrng.cpp @@ -1518,6 +1519,7 @@ set(QL_HEADERS math/primenumbers.hpp math/quadratic.hpp math/randomnumbers/boxmullergaussianrng.hpp + math/randomnumbers/burley2020sobolrsg.hpp math/randomnumbers/centrallimitgaussianrng.hpp math/randomnumbers/faurersg.hpp math/randomnumbers/haltonrsg.hpp diff --git a/ql/math/randomnumbers/Makefile.am b/ql/math/randomnumbers/Makefile.am index 21faedb2ad0..ac088630f85 100644 --- a/ql/math/randomnumbers/Makefile.am +++ b/ql/math/randomnumbers/Makefile.am @@ -5,6 +5,7 @@ this_includedir=${includedir}/${subdir} this_include_HEADERS = \ all.hpp \ boxmullergaussianrng.hpp \ + burley2020sobolrsg.hpp \ centrallimitgaussianrng.hpp \ faurersg.hpp \ haltonrsg.hpp \ @@ -27,8 +28,9 @@ this_include_HEADERS = \ xoshiro256starstaruniformrng.hpp cpp_files = \ - faurersg.cpp \ - haltonrsg.cpp \ + faurersg.cpp \ + haltonrsg.cpp \ + burley2020sobolrsg.cpp \ knuthuniformrng.cpp \ latticersg.cpp \ latticerules.cpp \ diff --git a/ql/math/randomnumbers/all.hpp b/ql/math/randomnumbers/all.hpp index adcaafb4a07..6b1d5fc35a4 100644 --- a/ql/math/randomnumbers/all.hpp +++ b/ql/math/randomnumbers/all.hpp @@ -2,6 +2,7 @@ /* Add the files to be included into Makefile.am instead. */ #include +#include #include #include #include diff --git a/ql/math/randomnumbers/burley2020sobolrsg.cpp b/ql/math/randomnumbers/burley2020sobolrsg.cpp new file mode 100644 index 00000000000..ec441ee21b7 --- /dev/null +++ b/ql/math/randomnumbers/burley2020sobolrsg.cpp @@ -0,0 +1,153 @@ +/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + Copyright (C) 2023 Peter Caspers + + This file is part of QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + + QuantLib is free software: you can redistribute it and/or modify it + under the terms of the QuantLib license. You should have received a + copy of the license along with this program; if not, please email + . The license is also available online at + . + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the license for more details. +*/ + +#include +#include +#include + +namespace QuantLib { + + Burley2020SobolRsg::Burley2020SobolRsg(Size dimensionality, + unsigned long seed, + SobolRsg::DirectionIntegers directionIntegers, + unsigned long scrambleSeed) + : dimensionality_(dimensionality), seed_(seed), directionIntegers_(directionIntegers), + integerSequence_(dimensionality), sequence_(std::vector(dimensionality), 1.0) { + reset(); + group4Seeds_.resize((dimensionality_ - 1) / 4 + 1); + MersenneTwisterUniformRng mt(scrambleSeed); + for (auto& s : group4Seeds_) { + s = static_cast(mt.nextInt32()); + } + } + + void Burley2020SobolRsg::reset() const { + sobolRsg_ = ext::make_shared(dimensionality_, seed_, directionIntegers_, false); + nextSequenceCounter_ = 0; + } + + const std::vector& Burley2020SobolRsg::skipTo(std::uint32_t n) const { + reset(); + for (Size k = 0; k < n + 1; ++k) { + nextInt32Sequence(); + } + return integerSequence_; + } + + namespace { + + // for reverseBits() see http://graphics.stanford.edu/~seander/bithacks.html#BitReverseTable + + static const std::uint8_t bitReverseTable[] = { + 0u, 128u, 64u, 192u, 32u, 160u, 96u, 224u, 16u, 144u, 80u, 208u, 48u, 176u, + 112u, 240u, 8u, 136u, 72u, 200u, 40u, 168u, 104u, 232u, 24u, 152u, 88u, 216u, + 56u, 184u, 120u, 248u, 4u, 132u, 68u, 196u, 36u, 164u, 100u, 228u, 20u, 148u, + 84u, 212u, 52u, 180u, 116u, 244u, 12u, 140u, 76u, 204u, 44u, 172u, 108u, 236u, + 28u, 156u, 92u, 220u, 60u, 188u, 124u, 252u, 2u, 130u, 66u, 194u, 34u, 162u, + 98u, 226u, 18u, 146u, 82u, 210u, 50u, 178u, 114u, 242u, 10u, 138u, 74u, 202u, + 42u, 170u, 106u, 234u, 26u, 154u, 90u, 218u, 58u, 186u, 122u, 250u, 6u, 134u, + 70u, 198u, 38u, 166u, 102u, 230u, 22u, 150u, 86u, 214u, 54u, 182u, 118u, 246u, + 14u, 142u, 78u, 206u, 46u, 174u, 110u, 238u, 30u, 158u, 94u, 222u, 62u, 190u, + 126u, 254u, 1u, 129u, 65u, 193u, 33u, 161u, 97u, 225u, 17u, 145u, 81u, 209u, + 49u, 177u, 113u, 241u, 9u, 137u, 73u, 201u, 41u, 169u, 105u, 233u, 25u, 153u, + 89u, 217u, 57u, 185u, 121u, 249u, 5u, 133u, 69u, 197u, 37u, 165u, 101u, 229u, + 21u, 149u, 85u, 213u, 53u, 181u, 117u, 245u, 13u, 141u, 77u, 205u, 45u, 173u, + 109u, 237u, 29u, 157u, 93u, 221u, 61u, 189u, 125u, 253u, 3u, 131u, 67u, 195u, + 35u, 163u, 99u, 227u, 19u, 147u, 83u, 211u, 51u, 179u, 115u, 243u, 11u, 139u, + 75u, 203u, 43u, 171u, 107u, 235u, 27u, 155u, 91u, 219u, 59u, 187u, 123u, 251u, + 7u, 135u, 71u, 199u, 39u, 167u, 103u, 231u, 23u, 151u, 87u, 215u, 55u, 183u, + 119u, 247u, 15u, 143u, 79u, 207u, 47u, 175u, 111u, 239u, 31u, 159u, 95u, 223u, + 63u, 191u, 127u, 255u}; + + inline std::uint32_t reverseBits(std::uint32_t x) { + return (bitReverseTable[x & 0xff] << 24) | (bitReverseTable[(x >> 8) & 0xff] << 16) | + (bitReverseTable[(x >> 16) & 0xff] << 8) | (bitReverseTable[(x >> 24) & 0xff]); + } + + inline std::uint32_t laine_karras_permutation(std::uint32_t x, std::uint32_t seed) { + x += seed; + x ^= x * 0x6c50b47cu; + x ^= x * 0xb82f1e52u; + x ^= x * 0xc7afe638u; + x ^= x * 0x8d22f6e6u; + return x; + } + + inline std::uint32_t nested_uniform_scramble(std::uint32_t x, std::uint32_t seed) { + x = reverseBits(x); + x = laine_karras_permutation(x, seed); + x = reverseBits(x); + return x; + } + + // the results depend a lot on the details of the hash_combine() function that is used + // we use hash_combine() calling hash(), hash_mix() as implemented here: + // https://github.com/boostorg/container_hash/blob/boost-1.83.0/include/boost/container_hash/hash.hpp#L560 + // https://github.com/boostorg/container_hash/blob/boost-1.83.0/include/boost/container_hash/hash.hpp#L115 + // https://github.com/boostorg/container_hash/blob/boost-1.83.0/include/boost/container_hash/detail/hash_mix.hpp#L67 + + inline std::uint64_t local_hash_mix(std::uint64_t x) { + const std::uint64_t m = 0xe9846af9b1a615d; + x ^= x >> 32; + x *= m; + x ^= x >> 32; + x *= m; + x ^= x >> 28; + return x; + } + + inline std::uint64_t local_hash(const std::uint64_t v) { + std::uint64_t seed = 0; + seed = (v >> 32) + local_hash_mix(seed); + seed = (v & 0xFFFFFFFF) + local_hash_mix(seed); + return seed; + } + + inline std::uint64_t local_hash_combine(std::uint64_t x, const uint64_t v) { + return local_hash_mix(x + 0x9e3779b9 + local_hash(v)); + } + } + + const std::vector& Burley2020SobolRsg::nextInt32Sequence() const { + auto n = nested_uniform_scramble(nextSequenceCounter_, group4Seeds_[0]); + const auto& seq = sobolRsg_->skipTo(n); + std::copy(seq.begin(), seq.end(), integerSequence_.begin()); + Size i = 0, group = 0; + do { + std::uint64_t seed = group4Seeds_[group++]; + for (Size g = 0; g < 4 && i < dimensionality_; ++g, ++i) { + seed = local_hash_combine(seed, g); + integerSequence_[i] = + nested_uniform_scramble(integerSequence_[i], static_cast(seed)); + } + } while (i < dimensionality_); + QL_REQUIRE(++nextSequenceCounter_ != 0, + "Burley2020SobolRsg::nextIn32Sequence(): period exceeded"); + return integerSequence_; + } + + const SobolRsg::sample_type& Burley2020SobolRsg::nextSequence() const { + const std::vector& v = nextInt32Sequence(); + // normalize to get a double in (0,1) + for (Size k = 0; k < dimensionality_; ++k) { + sequence_.value[k] = static_cast(v[k]) / 4294967296.0; + } + return sequence_; + } +} diff --git a/ql/math/randomnumbers/burley2020sobolrsg.hpp b/ql/math/randomnumbers/burley2020sobolrsg.hpp new file mode 100644 index 00000000000..445fc3a7679 --- /dev/null +++ b/ql/math/randomnumbers/burley2020sobolrsg.hpp @@ -0,0 +1,64 @@ +/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + Copyright (C) 2023 Peter Caspers + + This file is part of QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + + QuantLib is free software: you can redistribute it and/or modify it + under the terms of the QuantLib license. You should have received a + copy of the license along with this program; if not, please email + . The license is also available online at + . + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the license for more details. +*/ + +/*! \file burley2020sobolrsg.hpp + \brief scrambled Sobol sequence following Burley, 2020 +*/ + +#ifndef quantlib_burley2020_scrambled_sobolrsg_hpp +#define quantlib_burley2020_scrambled_sobolrsg_hpp + +#include +#include + +namespace QuantLib { + + //! Scrambled sobol sequence according to Burley, 2020 + /*! Reference: Brent Burley: Practical Hash-based Owen Scrambling, + Journal of Computer Graphics Techniques, Vol. 9, No. 4, 2020 */ + class Burley2020SobolRsg { + public: + typedef Sample> sample_type; + explicit Burley2020SobolRsg( + Size dimensionality, + unsigned long seed = 42, + SobolRsg::DirectionIntegers directionIntegers = SobolRsg::Jaeckel, + unsigned long scrambleSeed = 43); + const std::vector& skipTo(std::uint32_t n) const; + const std::vector& nextInt32Sequence() const; + const SobolRsg::sample_type& nextSequence() const; + const sample_type& lastSequence() const { return sequence_; } + Size dimension() const { return dimensionality_; } + + private: + void reset() const; + Size dimensionality_; + unsigned long seed_; + SobolRsg::DirectionIntegers directionIntegers_; + mutable ext::shared_ptr sobolRsg_; + mutable std::vector integerSequence_; + mutable sample_type sequence_; + mutable std::uint32_t nextSequenceCounter_; + mutable std::vector group4Seeds_; + }; + +} + + +#endif diff --git a/ql/math/randomnumbers/sobolbrownianbridgersg.cpp b/ql/math/randomnumbers/sobolbrownianbridgersg.cpp index 19198fc3687..8845d16f15d 100644 --- a/ql/math/randomnumbers/sobolbrownianbridgersg.cpp +++ b/ql/math/randomnumbers/sobolbrownianbridgersg.cpp @@ -25,27 +25,31 @@ #include namespace QuantLib { - SobolBrownianBridgeRsg::SobolBrownianBridgeRsg( - Size factors, Size steps, - SobolBrownianGenerator::Ordering ordering, - unsigned long seed, - SobolRsg::DirectionIntegers directionIntegers) - : factors_(factors), steps_(steps), dim_(factors*steps), - seq_(sample_type::value_type(factors*steps), 1.0), - gen_(factors, steps, ordering, seed, directionIntegers) { + + namespace { + void setNextSequence(SobolBrownianGeneratorBase& gen, std::vector& seq) { + gen.nextPath(); + std::vector output(gen.numberOfFactors()); + for (Size i = 0; i < gen.numberOfSteps(); ++i) { + gen.nextStep(output); + std::copy(output.begin(), output.end(), seq.begin() + i * gen.numberOfFactors()); + } + } } + SobolBrownianBridgeRsg::SobolBrownianBridgeRsg(Size factors, + Size steps, + SobolBrownianGenerator::Ordering ordering, + unsigned long seed, + SobolRsg::DirectionIntegers directionIntegers) + : seq_(sample_type::value_type(factors * steps), 1.0), + gen_(factors, steps, ordering, seed, directionIntegers) {} + const SobolBrownianBridgeRsg::sample_type& SobolBrownianBridgeRsg::nextSequence() const { - gen_.nextPath(); - std::vector output(factors_); - for (Size i=0; i < steps_; ++i) { - gen_.nextStep(output); - std::copy(output.begin(), output.end(), - seq_.value.begin()+i*factors_); - } - + setNextSequence(gen_, seq_.value); return seq_; + } const SobolBrownianBridgeRsg::sample_type& @@ -54,6 +58,32 @@ namespace QuantLib { } Size SobolBrownianBridgeRsg::dimension() const { - return dim_; + return gen_.numberOfFactors() * gen_.numberOfSteps(); + } + + Burley2020SobolBrownianBridgeRsg::Burley2020SobolBrownianBridgeRsg( + Size factors, + Size steps, + SobolBrownianGenerator::Ordering ordering, + unsigned long seed, + SobolRsg::DirectionIntegers directionIntegers, + unsigned long scrambleSeed) + : seq_(sample_type::value_type(factors * steps), 1.0), + gen_(factors, steps, ordering, seed, directionIntegers, scrambleSeed) {} + + const Burley2020SobolBrownianBridgeRsg::sample_type& + Burley2020SobolBrownianBridgeRsg::nextSequence() const { + setNextSequence(gen_, seq_.value); + return seq_; + } + + const Burley2020SobolBrownianBridgeRsg::sample_type& + Burley2020SobolBrownianBridgeRsg::lastSequence() const { + return seq_; } + + Size Burley2020SobolBrownianBridgeRsg::dimension() const { + return gen_.numberOfFactors() * gen_.numberOfSteps(); + } + } diff --git a/ql/math/randomnumbers/sobolbrownianbridgersg.hpp b/ql/math/randomnumbers/sobolbrownianbridgersg.hpp index ee699be9690..e877fe34516 100644 --- a/ql/math/randomnumbers/sobolbrownianbridgersg.hpp +++ b/ql/math/randomnumbers/sobolbrownianbridgersg.hpp @@ -45,10 +45,30 @@ namespace QuantLib { Size dimension() const; private: - const Size factors_, steps_, dim_; mutable sample_type seq_; mutable SobolBrownianGenerator gen_; }; + + class Burley2020SobolBrownianBridgeRsg { + public: + typedef Sample > sample_type; + + Burley2020SobolBrownianBridgeRsg( + Size factors, + Size steps, + SobolBrownianGenerator::Ordering ordering = SobolBrownianGenerator::Diagonal, + unsigned long seed = 42, + SobolRsg::DirectionIntegers directionIntegers = SobolRsg::JoeKuoD7, + unsigned long scrambleSeed = 43); + + const sample_type& nextSequence() const; + const sample_type& lastSequence() const; + Size dimension() const; + + private: + mutable sample_type seq_; + mutable Burley2020SobolBrownianGenerator gen_; + }; } #endif diff --git a/ql/math/randomnumbers/sobolrsg.cpp b/ql/math/randomnumbers/sobolrsg.cpp index 30264c10f7d..1e0810e843c 100644 --- a/ql/math/randomnumbers/sobolrsg.cpp +++ b/ql/math/randomnumbers/sobolrsg.cpp @@ -78479,10 +78479,14 @@ namespace QuantLib { const double SobolRsg::normalizationFactor_ = 0.5/(1UL<<(SobolRsg::bits_-1)); - SobolRsg::SobolRsg(Size dimensionality, unsigned long seed, DirectionIntegers directionIntegers) + SobolRsg::SobolRsg(Size dimensionality, + unsigned long seed, + DirectionIntegers directionIntegers, + bool useGrayCode) : dimensionality_(dimensionality), sequence_(std::vector(dimensionality), 1.0), integerSequence_(dimensionality, 0), - directionIntegers_(dimensionality, std::vector(bits_)) { + directionIntegers_(dimensionality, std::vector(bits_)), + useGrayCode_(useGrayCode) { QL_REQUIRE(dimensionality>0, "dimensionality must be greater than 0"); @@ -78765,33 +78769,60 @@ namespace QuantLib { */ // initialize the Sobol integer/double vectors - // first draw - for (k=0; k& SobolRsg::skipTo(std::uint_least32_t skip) const { + std::uint_least32_t N = skip + 1; - // Convert to Gray code - std::uint_least32_t G = N ^ (N>>1); - for (Size k=0; k> index & 1) != 0U) - integerSequence_[k] ^= directionIntegers_[k][index]; + if (useGrayCode_) { + unsigned int ops = (unsigned int)(std::log((double)N) / M_LN2) + 1; + + // Convert to Gray code + std::uint_least32_t G = N ^ (N >> 1); + for (Size k = 0; k < dimensionality_; k++) { + integerSequence_[k] = 0; + for (Size index = 0; index < ops; index++) { + if ((G >> index & 1) != 0U) + integerSequence_[k] ^= directionIntegers_[k][index]; + } + } + } else { + std::fill(integerSequence_.begin(), integerSequence_.end(), 0u); + std::uint_least32_t mask = 1; + for (Size index = 0; index < bits_; index++) { + if ((N & mask) != 0U) { + for (Size k = 0; k < dimensionality_; k++) { + integerSequence_[k] ^= directionIntegers_[k][index]; + } + } + mask = mask << 1; } } sequenceCounter_ = skip; + return integerSequence_; } - - const std::vector& SobolRsg::nextInt32Sequence() const { + + if (!useGrayCode_) { + skipTo(sequenceCounter_); + if (firstDraw_) { + firstDraw_ = false; + } else { + ++sequenceCounter_; + QL_REQUIRE(sequenceCounter_ != 0, "period exceeded"); + } + return integerSequence_; + } + if (firstDraw_) { // it was precomputed in the constructor firstDraw_ = false; diff --git a/ql/math/randomnumbers/sobolrsg.hpp b/ql/math/randomnumbers/sobolrsg.hpp index d94581f390b..ee52a5084fa 100644 --- a/ql/math/randomnumbers/sobolrsg.hpp +++ b/ql/math/randomnumbers/sobolrsg.hpp @@ -114,12 +114,21 @@ namespace QuantLib { Unit, Jaeckel, SobolLevitan, SobolLevitanLemieux, JoeKuoD5, JoeKuoD6, JoeKuoD7, Kuo, Kuo2, Kuo3 }; - /*! \pre dimensionality must be <= PPMT_MAX_DIM */ + /*! The so called generating integer is chosen to be \f$\gamma(n) = n\f$ if useGrayCode is set to false and + \f$\gamma(n) = G(n)\f$ where \f$G(n)\f$ is the Gray code of \f$n\f$ otherwise. The Sobol integers are then + constructed using formula 8.20 resp. 8.23, see "Monte Carlo Methods in Finance" by Peter Jäckel. The default + is to use the Gray code since this allows a faster sequence generation. The Burley2020SobolRsg relies on an + underlying SobolRsg not using the Gray code on the other hand due to its specific way of constructing the + integer sequence. + + \pre dimensionality must be <= PPMT_MAX_DIM + */ explicit SobolRsg(Size dimensionality, unsigned long seed = 0, - DirectionIntegers directionIntegers = Jaeckel); + DirectionIntegers directionIntegers = Jaeckel, + bool useGrayCode = true); /*! skip to the n-th sample in the low-discrepancy sequence */ - void skipTo(std::uint_least32_t n); + const std::vector& skipTo(std::uint_least32_t n) const; const std::vector& nextInt32Sequence() const; const SobolRsg::sample_type& nextSequence() const { @@ -140,6 +149,7 @@ namespace QuantLib { mutable sample_type sequence_; mutable std::vector integerSequence_; std::vector> directionIntegers_; + bool useGrayCode_; }; } diff --git a/ql/models/marketmodels/browniangenerators/sobolbrowniangenerator.cpp b/ql/models/marketmodels/browniangenerators/sobolbrowniangenerator.cpp index 20d7f6a2411..038fa5af280 100644 --- a/ql/models/marketmodels/browniangenerators/sobolbrowniangenerator.cpp +++ b/ql/models/marketmodels/browniangenerators/sobolbrowniangenerator.cpp @@ -107,13 +107,10 @@ namespace QuantLib { } - SobolBrownianGenerator::SobolBrownianGenerator(Size factors, + SobolBrownianGeneratorBase::SobolBrownianGeneratorBase(Size factors, Size steps, - Ordering ordering, - unsigned long seed, - SobolRsg::DirectionIntegers integers) + Ordering ordering) : factors_(factors), steps_(steps), ordering_(ordering), - generator_(SobolRsg(factors * steps, seed, integers), InverseCumulativeNormal()), bridge_(steps), orderedIndices_(factors, std::vector(steps)), bridgedVariates_(factors, std::vector(steps)) { @@ -133,12 +130,8 @@ namespace QuantLib { } - Real SobolBrownianGenerator::nextPath() { - typedef InverseCumulativeRsg::sample_type - sample_type; - - const sample_type& sample = generator_.nextSequence(); + Real SobolBrownianGeneratorBase::nextPath() { + const auto& sample = nextSequence(); // Brownian-bridge the variates according to the ordered indices for (Size i=0; i >& - SobolBrownianGenerator::orderedIndices() const { + SobolBrownianGeneratorBase::orderedIndices() const { return orderedIndices_; } - std::vector > SobolBrownianGenerator::transform( + std::vector > SobolBrownianGeneratorBase::transform( const std::vector >& variates) { QL_REQUIRE( (variates.size() == factors_*steps_), @@ -190,7 +183,7 @@ namespace QuantLib { return retVal; } - Real SobolBrownianGenerator::nextStep(std::vector& output) { + Real SobolBrownianGeneratorBase::nextStep(std::vector& output) { #if defined(QL_EXTRA_SAFETY_CHECKS) QL_REQUIRE(output.size() == factors_, "size mismatch"); QL_REQUIRE(lastStep_ + Burley2020SobolBrownianGeneratorFactory::create(Size factors, Size steps) const { + return ext::shared_ptr(new Burley2020SobolBrownianGenerator( + factors, steps, ordering_, seed_, integers_, scrambleSeed_)); + } } diff --git a/ql/models/marketmodels/browniangenerators/sobolbrowniangenerator.hpp b/ql/models/marketmodels/browniangenerators/sobolbrowniangenerator.hpp index f769bf2605d..eacb9abbae3 100644 --- a/ql/models/marketmodels/browniangenerators/sobolbrowniangenerator.hpp +++ b/ql/models/marketmodels/browniangenerators/sobolbrowniangenerator.hpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -34,7 +35,7 @@ namespace QuantLib { /*! Incremental Brownian generator using a Sobol generator, inverse-cumulative Gaussian method, and Brownian bridging. */ - class SobolBrownianGenerator : public BrownianGenerator { + class SobolBrownianGeneratorBase : public BrownianGenerator { public: enum Ordering { Factors, /*!< The variates with the best quality will be @@ -46,13 +47,10 @@ namespace QuantLib { most important factors and the largest steps. */ }; - SobolBrownianGenerator( + SobolBrownianGeneratorBase( Size factors, Size steps, - Ordering ordering, - unsigned long seed = 0, - SobolRsg::DirectionIntegers directionIntegers - = SobolRsg::Jaeckel); + Ordering ordering); Real nextPath() override; Real nextStep(std::vector&) override; @@ -65,10 +63,12 @@ namespace QuantLib { std::vector > transform( const std::vector >& variates); + protected: + virtual const SobolRsg::sample_type& nextSequence() = 0; + private: Size factors_, steps_; Ordering ordering_; - InverseCumulativeRsg generator_; BrownianBridge bridge_; // work variables Size lastStep_ = 0; @@ -76,19 +76,62 @@ namespace QuantLib { std::vector > bridgedVariates_; }; + class SobolBrownianGenerator : public SobolBrownianGeneratorBase { + public: + SobolBrownianGenerator(Size factors, + Size steps, + Ordering ordering, + unsigned long seed = 0, + SobolRsg::DirectionIntegers directionIntegers = SobolRsg::Jaeckel); + + private: + const SobolRsg::sample_type& nextSequence() override; + InverseCumulativeRsg generator_; + }; + class SobolBrownianGeneratorFactory : public BrownianGeneratorFactory { public: - SobolBrownianGeneratorFactory( - SobolBrownianGenerator::Ordering ordering, - unsigned long seed = 0, - SobolRsg::DirectionIntegers directionIntegers - = SobolRsg::Jaeckel); + explicit SobolBrownianGeneratorFactory( + SobolBrownianGenerator::Ordering ordering, + unsigned long seed = 0, + SobolRsg::DirectionIntegers directionIntegers = SobolRsg::Jaeckel); + ext::shared_ptr create(Size factors, Size steps) const override; + + private: + SobolBrownianGenerator::Ordering ordering_; + unsigned long seed_; + SobolRsg::DirectionIntegers integers_; + }; + + class Burley2020SobolBrownianGenerator : public SobolBrownianGeneratorBase { + public: + Burley2020SobolBrownianGenerator( + Size factors, + Size steps, + Ordering ordering, + unsigned long seed = 42, + SobolRsg::DirectionIntegers directionIntegers = SobolRsg::Jaeckel, + unsigned long scrambleSeed = 43); + + private: + const Burley2020SobolRsg::sample_type& nextSequence() override; + InverseCumulativeRsg generator_; + }; + + class Burley2020SobolBrownianGeneratorFactory : public BrownianGeneratorFactory { + public: + explicit Burley2020SobolBrownianGeneratorFactory( + SobolBrownianGenerator::Ordering ordering, + unsigned long seed = 42, + SobolRsg::DirectionIntegers directionIntegers = SobolRsg::Jaeckel, + unsigned long scrambleSeed = 43); ext::shared_ptr create(Size factors, Size steps) const override; private: SobolBrownianGenerator::Ordering ordering_; unsigned long seed_; SobolRsg::DirectionIntegers integers_; + unsigned long scrambleSeed_; }; } diff --git a/test-suite/CMakeLists.txt b/test-suite/CMakeLists.txt index 68052a261c8..3d9a1050d39 100644 --- a/test-suite/CMakeLists.txt +++ b/test-suite/CMakeLists.txt @@ -126,6 +126,7 @@ set(QL_TEST_SOURCES period.cpp piecewiseyieldcurve.cpp piecewisezerospreadedtermstructure.cpp + quantlibglobalfixture.cpp quantlibtestsuite.cpp quantooption.cpp quotes.cpp @@ -140,6 +141,7 @@ set(QL_TEST_SOURCES shortratemodels.cpp sofrfutures.cpp solvers.cpp + speedlevel.cpp spreadoption.cpp squarerootclvmodel.cpp stats.cpp @@ -172,103 +174,6 @@ set(QL_TEST_SOURCES ) set(QL_TEST_HEADERS - americanoption.hpp - amortizingbond.hpp - andreasenhugevolatilityinterpl.hpp - array.hpp - asianoptions.hpp - assetswap.hpp - autocovariances.hpp - barrieroption.hpp - basismodels.hpp - basisswapratehelpers.hpp - basketoption.hpp - batesmodel.hpp - bermudanswaption.hpp - binaryoption.hpp - blackdeltacalculator.hpp - blackformula.hpp - bondforward.hpp - bonds.hpp - brownianbridge.hpp - businessdayconventions.hpp - calendars.hpp - callablebonds.hpp - capfloor.hpp - capflooredcoupon.hpp - cashflows.hpp - catbonds.hpp - cdo.hpp - cdsoption.hpp - chooseroption.hpp - cliquetoption.hpp - cms.hpp - cms_normal.hpp - cmsspread.hpp - commodityunitofmeasure.hpp - compiledboostversion.hpp - compoundoption.hpp - convertiblebonds.hpp - covariance.hpp - creditdefaultswap.hpp - creditriskplus.hpp - crosscurrencyratehelpers.hpp - currency.hpp - curvestates.hpp - dates.hpp - daycounters.hpp - defaultprobabilitycurves.hpp - digitalcoupon.hpp - digitaloption.hpp - distributions.hpp - dividendoption.hpp - doublebarrieroption.hpp - doublebinaryoption.hpp - equityindex.hpp - equitycashflow.hpp - equitytotalreturnswap.hpp - europeanoption.hpp - everestoption.hpp - exchangerate.hpp - extendedtrees.hpp - extensibleoptions.hpp - fastfouriertransform.hpp - fdcev.hpp - fdcir.hpp - fdheston.hpp - fdmlinearop.hpp - fdsabr.hpp - fittedbonddiscountcurve.hpp - forwardoption.hpp - forwardrateagreement.hpp - functions.hpp - garch.hpp - gaussianquadratures.hpp - gjrgarchmodel.hpp - gsr.hpp - hestonmodel.hpp - hestonslvmodel.hpp - himalayaoption.hpp - hybridhestonhullwhiteprocess.hpp - indexes.hpp - inflation.hpp - inflationcapfloor.hpp - inflationcapflooredcoupon.hpp - inflationcpibond.hpp - inflationcpicapfloor.hpp - inflationcpiswap.hpp - inflationvolatility.hpp - instruments.hpp - integrals.hpp - interestrates.hpp - interpolations.hpp - jumpdiffusion.hpp - lazyobject.hpp - libormarketmodel.hpp - libormarketmodelprocess.hpp - linearleastsquaresregression.hpp - lookbackoptions.hpp - lowdiscrepancysequences.hpp margrabeoption.hpp marketmodel.hpp marketmodel_cms.hpp @@ -300,6 +205,7 @@ set(QL_TEST_HEADERS period.hpp piecewiseyieldcurve.hpp piecewisezerospreadedtermstructure.hpp + quantlibglobalfixture.hpp quantooption.hpp quotes.hpp rangeaccrual.hpp @@ -330,6 +236,7 @@ set(QL_TEST_HEADERS timegrid.hpp timeseries.hpp tqreigendecomposition.hpp + toplevelfixture.hpp tracing.hpp transformedgrid.hpp twoassetbarrieroption.hpp @@ -349,26 +256,27 @@ set(QL_TEST_HEADERS set(QL_BENCHMARK_SOURCES quantlibbenchmark.cpp - americanoption.cpp americanoption.hpp - asianoptions.cpp asianoptions.hpp - barrieroption.cpp barrieroption.hpp - basketoption.cpp basketoption.hpp - batesmodel.cpp batesmodel.hpp - convertiblebonds.cpp convertiblebonds.hpp - digitaloption.cpp digitaloption.hpp - dividendoption.cpp dividendoption.hpp - europeanoption.cpp europeanoption.hpp - fdheston.cpp fdheston.hpp - hestonmodel.cpp hestonmodel.hpp - interpolations.cpp interpolations.hpp - jumpdiffusion.cpp jumpdiffusion.hpp - lowdiscrepancysequences.cpp lowdiscrepancysequences.hpp + americanoption.cpp + asianoptions.cpp + barrieroption.cpp + basketoption.cpp + batesmodel.cpp + convertiblebonds.cpp + digitaloption.cpp + dividendoption.cpp + europeanoption.cpp + fdheston.cpp + hestonmodel.cpp + interpolations.cpp + jumpdiffusion.cpp + lowdiscrepancysequences.cpp marketmodel_cms.cpp marketmodel_cms.hpp marketmodel_smm.cpp marketmodel_smm.hpp quantooption.cpp quantooption.hpp + quantlibglobalfixture.cpp quantlibglobalfixture.hpp riskstats.cpp riskstats.hpp shortratemodels.cpp shortratemodels.hpp - + speedlevel.cpp speedlevel.hpp utilities.cpp utilities.hpp swaptionvolstructuresutilities.hpp ) @@ -408,6 +316,7 @@ endif() IF (QL_BUILD_BENCHMARK) add_executable(ql_benchmark ${QL_BENCHMARK_SOURCES}) set_target_properties(ql_benchmark PROPERTIES OUTPUT_NAME "quantlib-benchmark") + set_source_files_properties(quantlibbenchmark.cpp PROPERTIES SKIP_UNITY_BUILD_INCLUSION true) if (NOT Boost_USE_STATIC_LIBS) target_compile_definitions(ql_benchmark PRIVATE BOOST_ALL_DYN_LINK BOOST_TEST_DYN_LINK) endif() diff --git a/test-suite/Makefile.am b/test-suite/Makefile.am index df3d28b1e93..03f366045df 100644 --- a/test-suite/Makefile.am +++ b/test-suite/Makefile.am @@ -128,6 +128,7 @@ QL_TEST_SRCS = \ period.cpp \ piecewiseyieldcurve.cpp \ piecewisezerospreadedtermstructure.cpp \ + quantlibglobalfixture.cpp \ quantooption.cpp \ quotes.cpp \ rangeaccrual.cpp \ @@ -141,6 +142,7 @@ QL_TEST_SRCS = \ shortratemodels.cpp \ sofrfutures.cpp \ solvers.cpp \ + speedlevel.cpp \ spreadoption.cpp \ squarerootclvmodel.cpp \ stats.cpp \ @@ -172,104 +174,6 @@ QL_TEST_SRCS = \ zerocouponswap.cpp QL_TEST_HDRS = \ - speedlevel.hpp \ - americanoption.hpp \ - amortizingbond.hpp \ - andreasenhugevolatilityinterpl.hpp \ - array.hpp \ - asianoptions.hpp \ - assetswap.hpp \ - autocovariances.hpp \ - barrieroption.hpp \ - binaryoption.hpp \ - basismodels.hpp \ - basisswapratehelpers.hpp \ - basketoption.hpp \ - batesmodel.hpp \ - bermudanswaption.hpp \ - blackdeltacalculator.hpp \ - blackformula.hpp \ - bondforward.hpp \ - bonds.hpp \ - brownianbridge.hpp \ - businessdayconventions.hpp \ - calendars.hpp \ - callablebonds.hpp \ - capfloor.hpp \ - capflooredcoupon.hpp \ - cashflows.hpp \ - catbonds.hpp \ - cdo.hpp \ - cdsoption.hpp \ - chooseroption.hpp \ - cliquetoption.hpp \ - cms.hpp \ - cms_normal.hpp \ - cmsspread.hpp \ - commodityunitofmeasure.hpp \ - compiledboostversion.hpp \ - compoundoption.hpp \ - convertiblebonds.hpp \ - covariance.hpp \ - creditdefaultswap.hpp \ - creditriskplus.hpp \ - crosscurrencyratehelpers.hpp \ - currency.hpp \ - curvestates.hpp \ - dates.hpp \ - daycounters.hpp \ - defaultprobabilitycurves.hpp \ - digitalcoupon.hpp \ - digitaloption.hpp \ - distributions.hpp \ - dividendoption.hpp \ - doublebarrieroption.hpp \ - doublebinaryoption.hpp \ - equityindex.hpp \ - equitycashflow.hpp \ - equitytotalreturnswap.hpp \ - europeanoption.hpp \ - everestoption.hpp \ - exchangerate.hpp \ - extendedtrees.hpp \ - extensibleoptions.hpp \ - fastfouriertransform.hpp \ - fdheston.hpp \ - fdcir.hpp \ - fdmlinearop.hpp \ - fdcev.hpp \ - fdsabr.hpp \ - fittedbonddiscountcurve.hpp \ - forwardoption.hpp \ - forwardrateagreement.hpp \ - functions.hpp \ - garch.hpp \ - gaussianquadratures.hpp \ - gjrgarchmodel.hpp \ - gsr.hpp \ - hestonmodel.hpp \ - hestonslvmodel.hpp \ - himalayaoption.hpp \ - hybridhestonhullwhiteprocess.hpp \ - indexes.hpp \ - inflation.hpp \ - inflationcapfloor.hpp \ - inflationcapflooredcoupon.hpp \ - inflationcpibond.hpp \ - inflationcpicapfloor.hpp \ - inflationcpiswap.hpp \ - inflationvolatility.hpp \ - instruments.hpp \ - integrals.hpp \ - interestrates.hpp \ - interpolations.hpp \ - jumpdiffusion.hpp \ - lazyobject.hpp \ - libormarketmodel.hpp \ - libormarketmodelprocess.hpp \ - linearleastsquaresregression.hpp \ - lookbackoptions.hpp \ - lowdiscrepancysequences.hpp \ margrabeoption.hpp \ marketmodel.hpp \ marketmodel_cms.hpp \ @@ -300,6 +204,7 @@ QL_TEST_HDRS = \ period.hpp \ piecewiseyieldcurve.hpp \ piecewisezerospreadedtermstructure.hpp \ + quantlibglobalfixture.hpp \ quantooption.hpp \ quotes.hpp \ rangeaccrual.hpp \ @@ -313,6 +218,7 @@ QL_TEST_HDRS = \ shortratemodels.hpp \ sofrfutures.hpp \ solvers.hpp \ + speedlevel.hpp \ spreadoption.hpp \ squarerootclvmodel.hpp \ stats.hpp \ @@ -330,6 +236,7 @@ QL_TEST_HDRS = \ timeseries.hpp \ transformedgrid.hpp \ tqreigendecomposition.hpp \ + toplevelfixture.hpp \ tracing.hpp \ twoassetbarrieroption.hpp \ twoassetcorrelationoption.hpp \ @@ -365,32 +272,21 @@ QL_BENCHMARK_SRCS = \ lowdiscrepancysequences.cpp \ marketmodel_cms.cpp \ marketmodel_smm.cpp \ + quantlibglobalfixture.cpp \ quantooption.cpp \ riskstats.cpp \ shortratemodels.cpp \ + speedlevel.cpp \ utilities.cpp QL_BENCHMARK_HDRS = \ - americanoption.hpp \ - asianoptions.hpp \ - barrieroption.hpp \ - doublebarrieroption.hpp \ - basketoption.hpp \ - batesmodel.hpp \ - convertiblebonds.hpp \ - digitaloption.hpp \ - dividendoption.hpp \ - europeanoption.hpp \ - fdheston.hpp \ - hestonmodel.hpp \ - interpolations.hpp \ - jumpdiffusion.hpp \ - lowdiscrepancysequences.hpp \ marketmodel_cms.hpp \ marketmodel_smm.hpp \ + quantlibglobalfixture.hpp \ quantooption.hpp \ riskstats.hpp \ shortratemodels.hpp \ + speedlevel.hpp \ utilities.hpp QL_BENCHMARKS = ${QL_BENCHMARK_SRCS} ${QL_BENCHMARK_HDRS} @@ -427,13 +323,14 @@ unity_test.cpp: Makefile.am echo "#include \"test-suite/$$i\"" >> $@; \ done -nodist_quantlib_benchmark_SOURCES = unity_benchmark.cpp +nodist_quantlib_benchmark_SOURCES = unity_benchmark.cpp quantlibbenchmark.cpp +UNITY_SRC = $(filter-out quantlibbenchmark.cpp,$(QL_BENCHMARK_SRCS)) unity_benchmark.cpp: Makefile.am echo "/* This file is automatically generated; do not edit. */" > $@ echo "/* Add the files to be included into Makefile.am instead. */" >> $@ echo >> $@ - for i in $(QL_BENCHMARK_SRCS); do \ + for i in $(UNITY_SRC); do \ echo "#include \"test-suite/$$i\"" >> $@; \ done diff --git a/test-suite/americanoption.cpp b/test-suite/americanoption.cpp index 777c4ce53e0..3c2688f8403 100644 --- a/test-suite/americanoption.cpp +++ b/test-suite/americanoption.cpp @@ -19,7 +19,8 @@ FOR A PARTICULAR PURPOSE. See the license for more details. */ -#include "americanoption.hpp" +#include "speedlevel.hpp" +#include "toplevelfixture.hpp" #include "utilities.hpp" #include #include @@ -83,8 +84,11 @@ namespace { } +BOOST_FIXTURE_TEST_SUITE(QuantLibTest, TopLevelFixture) -void AmericanOptionTest::testBaroneAdesiWhaleyValues() { +BOOST_AUTO_TEST_SUITE(AmericanOptionTest) + +BOOST_AUTO_TEST_CASE(testBaroneAdesiWhaleyValues) { BOOST_TEST_MESSAGE("Testing Barone-Adesi and Whaley approximation " "for American options..."); @@ -184,8 +188,7 @@ void AmericanOptionTest::testBaroneAdesiWhaleyValues() { } } - -void AmericanOptionTest::testBjerksundStenslandValues() { +BOOST_AUTO_TEST_CASE(testBjerksundStenslandValues) { BOOST_TEST_MESSAGE("Testing Bjerksund and Stensland approximation " "for American options..."); @@ -330,8 +333,7 @@ namespace { } - -void AmericanOptionTest::testJuValues() { +BOOST_AUTO_TEST_CASE(testJuValues) { BOOST_TEST_MESSAGE("Testing Ju approximation for American options..."); @@ -381,8 +383,7 @@ void AmericanOptionTest::testJuValues() { } } - -void AmericanOptionTest::testFdValues() { +BOOST_AUTO_TEST_CASE(testFdValues) { BOOST_TEST_MESSAGE("Testing finite-difference and QR+ engine " "for American options..."); @@ -563,18 +564,17 @@ namespace { } - -void AmericanOptionTest::testFdAmericanGreeks() { +BOOST_AUTO_TEST_CASE(testFdAmericanGreeks) { BOOST_TEST_MESSAGE("Testing finite-differences American option greeks..."); testFdGreeks(); } -void AmericanOptionTest::testFdShoutGreeks() { +BOOST_AUTO_TEST_CASE(testFdShoutGreeks, *precondition(if_speed(Fast))) { BOOST_TEST_MESSAGE("Testing finite-differences shout option greeks..."); testFdGreeks(); } -void AmericanOptionTest::testFDShoutNPV() { +BOOST_AUTO_TEST_CASE(testFDShoutNPV) { BOOST_TEST_MESSAGE("Testing finite-differences shout option pricing..."); const auto dc = Actual365Fixed(); @@ -631,7 +631,7 @@ void AmericanOptionTest::testFDShoutNPV() { } } -void AmericanOptionTest::testZeroVolFDShoutNPV() { +BOOST_AUTO_TEST_CASE(testZeroVolFDShoutNPV) { BOOST_TEST_MESSAGE("Testing zero volatility shout option pricing with discrete dividends..."); const auto dc = Actual365Fixed(); @@ -710,7 +710,7 @@ void AmericanOptionTest::testZeroVolFDShoutNPV() { } } -void AmericanOptionTest::testLargeDividendShoutNPV() { +BOOST_AUTO_TEST_CASE(testLargeDividendShoutNPV) { BOOST_TEST_MESSAGE("Testing zero strike shout option pricing with discrete dividends..."); const auto dc = Actual365Fixed(); @@ -792,7 +792,7 @@ void AmericanOptionTest::testLargeDividendShoutNPV() { } } -void AmericanOptionTest::testEscrowedVsSpotAmericanOption() { +BOOST_AUTO_TEST_CASE(testEscrowedVsSpotAmericanOption) { BOOST_TEST_MESSAGE("Testing escrowed vs spot dividend model for American options..."); const auto dc = Actual360(); @@ -863,8 +863,7 @@ void AmericanOptionTest::testEscrowedVsSpotAmericanOption() { } } - -void AmericanOptionTest::testTodayIsDividendDate() { +BOOST_AUTO_TEST_CASE(testTodayIsDividendDate) { BOOST_TEST_MESSAGE("Testing escrowed vs spot dividend model on dividend dates for American options..."); const auto dc = Actual360(); @@ -982,8 +981,7 @@ void AmericanOptionTest::testTodayIsDividendDate() { } } - -void AmericanOptionTest::testCallPutParity() { +BOOST_AUTO_TEST_CASE(testCallPutParity) { BOOST_TEST_MESSAGE("Testing call/put parity for American options..."); // R.L. McDonald, M.D. Schroder: A parity result for American option @@ -1071,7 +1069,7 @@ void AmericanOptionTest::testCallPutParity() { } } -void AmericanOptionTest::testQdPlusBoundaryValues() { +BOOST_AUTO_TEST_CASE(testQdPlusBoundaryValues) { BOOST_TEST_MESSAGE("Testing QD+ boundary approximation..."); const DayCounter dc = Actual365Fixed(); @@ -1122,7 +1120,7 @@ void AmericanOptionTest::testQdPlusBoundaryValues() { } } -void AmericanOptionTest::testQdPlusBoundaryConvergence() { +BOOST_AUTO_TEST_CASE(testQdPlusBoundaryConvergence) { BOOST_TEST_MESSAGE("Testing QD+ boundary convergence..."); const DayCounter dc = Actual365Fixed(); @@ -1187,7 +1185,7 @@ void AmericanOptionTest::testQdPlusBoundaryConvergence() { } } -void AmericanOptionTest::testQdAmericanEngines() { +BOOST_AUTO_TEST_CASE(testQdAmericanEngines) { BOOST_TEST_MESSAGE("Testing QD+ American option pricing..."); const DayCounter dc = Actual365Fixed(); @@ -1432,7 +1430,7 @@ void AmericanOptionTest::testQdAmericanEngines() { }; } -void AmericanOptionTest::testQdFpIterationScheme() { +BOOST_AUTO_TEST_CASE(testQdFpIterationScheme) { BOOST_TEST_MESSAGE("Testing Legendre and tanh-sinh iteration " "scheme for QD+ fixed-point American engine..."); @@ -1459,8 +1457,7 @@ void AmericanOptionTest::testQdFpIterationScheme() { } } - -void AmericanOptionTest::testAndersenLakeHighPrecisionExample() { +BOOST_AUTO_TEST_CASE(testAndersenLakeHighPrecisionExample) { BOOST_TEST_MESSAGE("Testing Andersen, Lake and Offengenden " "high precision example..."); @@ -1556,8 +1553,7 @@ void AmericanOptionTest::testAndersenLakeHighPrecisionExample() { } } - -void AmericanOptionTest::testQdEngineStandardExample() { +BOOST_AUTO_TEST_CASE(testQdEngineStandardExample) { BOOST_TEST_MESSAGE("Testing Andersen, Lake and Offengenden " "standard example..."); @@ -1649,7 +1645,7 @@ namespace { }; } -void AmericanOptionTest::testBulkQdFpAmericanEngine() { +BOOST_AUTO_TEST_CASE(testBulkQdFpAmericanEngine) { BOOST_TEST_MESSAGE("Testing Andersen, Lake and Offengenden " "bulk examples..."); @@ -1758,7 +1754,7 @@ void AmericanOptionTest::testBulkQdFpAmericanEngine() { << "\n tol : " << tolMax); } -void AmericanOptionTest::testQdEngineWithLobattoIntegral() { +BOOST_AUTO_TEST_CASE(testQdEngineWithLobattoIntegral) { BOOST_TEST_MESSAGE("Testing Andersen, Lake and Offengenden " "with high precision Gauss-Lobatto integration..."); @@ -1825,7 +1821,7 @@ void AmericanOptionTest::testQdEngineWithLobattoIntegral() { } } -void AmericanOptionTest::testQdNegativeDividendYield() { +BOOST_AUTO_TEST_CASE(testQdNegativeDividendYield) { BOOST_TEST_MESSAGE("Testing Andersen, Lake and Offengenden " "with positive or zero interest rate and " "negative dividend yield..."); @@ -1896,7 +1892,7 @@ void AmericanOptionTest::testQdNegativeDividendYield() { } } -void AmericanOptionTest::testBjerksundStenslandEuropeanGreeks() { +BOOST_AUTO_TEST_CASE(testBjerksundStenslandEuropeanGreeks) { BOOST_TEST_MESSAGE("Testing Bjerksund-Stensland greeks when early " "exercise is not optimal..."); @@ -1973,8 +1969,7 @@ void AmericanOptionTest::testBjerksundStenslandEuropeanGreeks() { } } - -void AmericanOptionTest::testBjerksundStenslandAmericanGreeks() { +BOOST_AUTO_TEST_CASE(testBjerksundStenslandAmericanGreeks) { BOOST_TEST_MESSAGE("Testing Bjerksund-Stensland American greeks..."); const Date today = Date(5, December, 2022); @@ -2164,8 +2159,7 @@ void AmericanOptionTest::testBjerksundStenslandAmericanGreeks() { } } - -void AmericanOptionTest::testSingleBjerksundStenslandGreeks() { +BOOST_AUTO_TEST_CASE(testSingleBjerksundStenslandGreeks) { BOOST_TEST_MESSAGE("Testing a single Bjerksund-Stensland greeks set..."); const Date today = Date(20, January, 2023); @@ -2248,39 +2242,6 @@ void AmericanOptionTest::testSingleBjerksundStenslandGreeks() { BOOST_FAIL("American exercise type expected"); } +BOOST_AUTO_TEST_SUITE_END() -test_suite* AmericanOptionTest::suite(SpeedLevel speed) { - auto* suite = BOOST_TEST_SUITE("American option tests"); - - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testBaroneAdesiWhaleyValues)); - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testBjerksundStenslandValues)); - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testJuValues)); - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testFdValues)); - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testFdAmericanGreeks)); - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testFDShoutNPV)); - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testZeroVolFDShoutNPV)); - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testLargeDividendShoutNPV)); - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testEscrowedVsSpotAmericanOption)); - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testTodayIsDividendDate)); - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testCallPutParity)); - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testQdPlusBoundaryValues)); - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testQdPlusBoundaryConvergence)); - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testQdAmericanEngines)); - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testQdFpIterationScheme)); - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testAndersenLakeHighPrecisionExample)); - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testQdEngineStandardExample)); - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testBulkQdFpAmericanEngine)); - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testQdEngineWithLobattoIntegral)); - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testQdNegativeDividendYield)); - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testBjerksundStenslandEuropeanGreeks)); - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testBjerksundStenslandAmericanGreeks)); - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testSingleBjerksundStenslandGreeks)); - - - if (speed <= Fast) { - suite->add(QUANTLIB_TEST_CASE(&AmericanOptionTest::testFdShoutGreeks)); - } - - return suite; -} - +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/test-suite/americanoption.hpp b/test-suite/americanoption.hpp deleted file mode 100644 index 251eade5538..00000000000 --- a/test-suite/americanoption.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2003 Ferdinando Ametrano - Copyright (C) 2005 Joseph Wang - Copyright (C) 2005 StatPro Italia srl - - This file is part of QuantLib, a free-software/open-source library - for financial quantitative analysts and developers - http://quantlib.org/ - - QuantLib is free software: you can redistribute it and/or modify it - under the terms of the QuantLib license. You should have received a - copy of the license along with this program; if not, please email - . The license is also available online at - . - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the license for more details. -*/ - -#ifndef quantlib_test_american_option_hpp -#define quantlib_test_american_option_hpp - -#include -#include "speedlevel.hpp" - -/* remember to document new and/or updated tests in the Doxygen - comment block of the corresponding class */ - -class AmericanOptionTest { - public: - static void testBaroneAdesiWhaleyValues(); - static void testBjerksundStenslandValues(); - static void testJuValues(); - static void testFdValues(); - static void testFdAmericanGreeks(); - static void testFdShoutGreeks(); - static void testFDShoutNPV(); - static void testZeroVolFDShoutNPV(); - static void testLargeDividendShoutNPV(); - static void testEscrowedVsSpotAmericanOption(); - static void testTodayIsDividendDate(); - static void testCallPutParity(); - static void testQdPlusBoundaryValues(); - static void testQdPlusBoundaryConvergence(); - static void testQdAmericanEngines(); - static void testQdFpIterationScheme(); - static void testAndersenLakeHighPrecisionExample(); - static void testQdEngineStandardExample(); - static void testBulkQdFpAmericanEngine(); - static void testQdEngineWithLobattoIntegral(); - static void testQdNegativeDividendYield(); - static void testBjerksundStenslandEuropeanGreeks(); - static void testBjerksundStenslandAmericanGreeks(); - static void testSingleBjerksundStenslandGreeks(); - - static boost::unit_test_framework::test_suite* suite(SpeedLevel); -}; - - -#endif diff --git a/test-suite/amortizingbond.cpp b/test-suite/amortizingbond.cpp index 0cf4bc7fc5e..c5ad555cd26 100644 --- a/test-suite/amortizingbond.cpp +++ b/test-suite/amortizingbond.cpp @@ -15,7 +15,7 @@ FOR A PARTICULAR PURPOSE. See the license for more details. */ -#include "amortizingbond.hpp" +#include "toplevelfixture.hpp" #include "utilities.hpp" #include #include @@ -31,7 +31,11 @@ using namespace QuantLib; using namespace boost::unit_test_framework; -void AmortizingBondTest::testAmortizingFixedRateBond() { +BOOST_FIXTURE_TEST_SUITE(QuantLibTest, TopLevelFixture) + +BOOST_AUTO_TEST_SUITE(AmortizingBondTest) + +BOOST_AUTO_TEST_CASE(testAmortizingFixedRateBond) { BOOST_TEST_MESSAGE("Testing amortizing fixed rate bond..."); /* @@ -93,7 +97,7 @@ void AmortizingBondTest::testAmortizingFixedRateBond() { } } -void AmortizingBondTest::testBrazilianAmortizingFixedRateBond() { +BOOST_AUTO_TEST_CASE(testBrazilianAmortizingFixedRateBond) { BOOST_TEST_MESSAGE("Testing Brazilian amortizing fixed rate bond..."); /* @@ -213,7 +217,7 @@ void AmortizingBondTest::testBrazilianAmortizingFixedRateBond() { } -void AmortizingBondTest::testAmortizingFixedRateBondWithDrawDown() { +BOOST_AUTO_TEST_CASE(testAmortizingFixedRateBondWithDrawDown) { BOOST_TEST_MESSAGE("Testing amortizing fixed rate bond with draw-down..."); Date issueDate = Date(19, May, 2012); @@ -273,10 +277,6 @@ void AmortizingBondTest::testAmortizingFixedRateBondWithDrawDown() { } -test_suite* AmortizingBondTest::suite() { - auto* suite = BOOST_TEST_SUITE("Amortizing Bond tests"); - suite->add(QUANTLIB_TEST_CASE(&AmortizingBondTest::testAmortizingFixedRateBond)); - suite->add(QUANTLIB_TEST_CASE(&AmortizingBondTest::testBrazilianAmortizingFixedRateBond)); - suite->add(QUANTLIB_TEST_CASE(&AmortizingBondTest::testAmortizingFixedRateBondWithDrawDown)); - return suite; -} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test-suite/amortizingbond.hpp b/test-suite/amortizingbond.hpp deleted file mode 100644 index dd9a9597a18..00000000000 --- a/test-suite/amortizingbond.hpp +++ /dev/null @@ -1,33 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2014 Cheng Li - - This file is part of QuantLib, a free-software/open-source library - for financial quantitative analysts and developers - http://quantlib.org/ - - QuantLib is free software: you can redistribute it and/or modify it - under the terms of the QuantLib license. You should have received a - copy of the license along with this program; if not, please email - . The license is also available online at - . - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the license for more details. -*/ - -#ifndef quantlib_test_amortizing_bond_hpp -#define quantlib_test_amortizing_bond_hpp - -#include - -class AmortizingBondTest { - public: - static void testAmortizingFixedRateBond(); - static void testBrazilianAmortizingFixedRateBond(); - static void testAmortizingFixedRateBondWithDrawDown(); - static boost::unit_test_framework::test_suite* suite(); -}; - -#endif diff --git a/test-suite/andreasenhugevolatilityinterpl.cpp b/test-suite/andreasenhugevolatilityinterpl.cpp index 1f7747ca563..26973bb8710 100644 --- a/test-suite/andreasenhugevolatilityinterpl.cpp +++ b/test-suite/andreasenhugevolatilityinterpl.cpp @@ -17,7 +17,8 @@ FOR A PARTICULAR PURPOSE. See the license for more details. */ -#include "andreasenhugevolatilityinterpl.hpp" +#include "speedlevel.hpp" +#include "toplevelfixture.hpp" #include "utilities.hpp" #include #include @@ -341,10 +342,56 @@ namespace andreasen_huge_volatility_interpl_test { return { spot, rTS, qTS, calibrationSet }; } + + std::pair > sabrData() { + + const DayCounter dc = Actual365Fixed(); + const Date today = Date(4, January, 2018); + + const Real alpha = 0.15; + const Real beta = 0.8; + const Real nu = 0.5; + const Real rho = -0.48; + const Real forward = 0.03; + const Size maturityInYears = 20; + + const Date maturityDate = today + Period(maturityInYears, Years); + const Time maturity = dc.yearFraction(today, maturityDate); + + AndreasenHugeVolatilityInterpl::CalibrationSet calibrationSet; + + const Real strikes[] = { 0.02, 0.025, 0.03, 0.035, 0.04, 0.05, 0.06 }; + + for (Real strike : strikes) { + const Volatility vol = sabrVolatility(strike, forward, maturity, alpha, beta, nu, rho); + + calibrationSet.emplace_back( + ext::make_shared( + ext::make_shared( + Option::Call, strike), + ext::make_shared(maturityDate)), + ext::make_shared(vol)); + } + + const Handle rTS(flatRate(today, forward, dc)); + const Handle qTS(flatRate(today, forward, dc)); + + Handle spot(ext::make_shared(forward)); + + const CalibrationData data = { spot, rTS, qTS, calibrationSet}; + + std::vector parameter = { alpha, beta, nu, rho, forward, maturity }; + + return std::make_pair(data, parameter); + } } -void AndreasenHugeVolatilityInterplTest::testAndreasenHugePut() { +BOOST_FIXTURE_TEST_SUITE(QuantLibTest, TopLevelFixture) + +BOOST_AUTO_TEST_SUITE(AndreasenHugeVolatilityInterplTest) + +BOOST_AUTO_TEST_CASE(testAndreasenHugePut, *precondition(if_speed(Fast))) { BOOST_TEST_MESSAGE( "Testing Andreasen-Huge example with Put calibration..."); @@ -363,7 +410,7 @@ void AndreasenHugeVolatilityInterplTest::testAndreasenHugePut() { testAndreasenHugeVolatilityInterpolation(data, expected); } -void AndreasenHugeVolatilityInterplTest::testAndreasenHugeCall() { +BOOST_AUTO_TEST_CASE(testAndreasenHugeCall, *precondition(if_speed(Fast))) { BOOST_TEST_MESSAGE( "Testing Andreasen-Huge example with Call calibration..."); @@ -382,7 +429,7 @@ void AndreasenHugeVolatilityInterplTest::testAndreasenHugeCall() { testAndreasenHugeVolatilityInterpolation(data, expected); } -void AndreasenHugeVolatilityInterplTest::testAndreasenHugeCallPut() { +BOOST_AUTO_TEST_CASE(testAndreasenHugeCallPut, *precondition(if_speed(Fast))) { BOOST_TEST_MESSAGE( "Testing Andreasen-Huge example with instantaneous " @@ -402,7 +449,7 @@ void AndreasenHugeVolatilityInterplTest::testAndreasenHugeCallPut() { testAndreasenHugeVolatilityInterpolation(data, expected); } -void AndreasenHugeVolatilityInterplTest::testLinearInterpolation() { +BOOST_AUTO_TEST_CASE(testLinearInterpolation, *precondition(if_speed(Fast))) { BOOST_TEST_MESSAGE( "Testing Andreasen-Huge example with linear interpolation..."); @@ -420,7 +467,7 @@ void AndreasenHugeVolatilityInterplTest::testLinearInterpolation() { testAndreasenHugeVolatilityInterpolation(data, expected); } -void AndreasenHugeVolatilityInterplTest::testPiecewiseConstantInterpolation() { +BOOST_AUTO_TEST_CASE(testPiecewiseConstantInterpolation, *precondition(if_speed(Fast))) { BOOST_TEST_MESSAGE( "Testing Andreasen-Huge example with piecewise constant interpolation..."); @@ -438,7 +485,7 @@ void AndreasenHugeVolatilityInterplTest::testPiecewiseConstantInterpolation() { testAndreasenHugeVolatilityInterpolation(data, expected); } -void AndreasenHugeVolatilityInterplTest::testTimeDependentInterestRates() { +BOOST_AUTO_TEST_CASE(testTimeDependentInterestRates, *precondition(if_speed(Fast))) { BOOST_TEST_MESSAGE( "Testing Andreasen-Huge volatility interpolation with " @@ -524,7 +571,7 @@ void AndreasenHugeVolatilityInterplTest::testTimeDependentInterestRates() { testAndreasenHugeVolatilityInterpolation(irData, expected); } -void AndreasenHugeVolatilityInterplTest::testSingleOptionCalibration() { +BOOST_AUTO_TEST_CASE(testSingleOptionCalibration) { BOOST_TEST_MESSAGE( "Testing Andreasen-Huge volatility interpolation with " "a single option..."); @@ -583,7 +630,7 @@ void AndreasenHugeVolatilityInterplTest::testSingleOptionCalibration() { } } -void AndreasenHugeVolatilityInterplTest::testArbitrageFree() { +BOOST_AUTO_TEST_CASE(testArbitrageFree) { BOOST_TEST_MESSAGE( "Testing Andreasen-Huge volatility interpolation gives " "arbitrage free prices..."); @@ -672,7 +719,7 @@ void AndreasenHugeVolatilityInterplTest::testArbitrageFree() { } } -void AndreasenHugeVolatilityInterplTest::testBarrierOptionPricing() { +BOOST_AUTO_TEST_CASE(testBarrierOptionPricing, *precondition(if_speed(Fast))) { BOOST_TEST_MESSAGE( "Testing Barrier option pricing with Andreasen-Huge " "local volatility surface..."); @@ -776,51 +823,7 @@ void AndreasenHugeVolatilityInterplTest::testBarrierOptionPricing() { } } -namespace andreasen_huge_volatility_interpl_test { - std::pair > sabrData() { - - const DayCounter dc = Actual365Fixed(); - const Date today = Date(4, January, 2018); - - const Real alpha = 0.15; - const Real beta = 0.8; - const Real nu = 0.5; - const Real rho = -0.48; - const Real forward = 0.03; - const Size maturityInYears = 20; - - const Date maturityDate = today + Period(maturityInYears, Years); - const Time maturity = dc.yearFraction(today, maturityDate); - - AndreasenHugeVolatilityInterpl::CalibrationSet calibrationSet; - - const Real strikes[] = { 0.02, 0.025, 0.03, 0.035, 0.04, 0.05, 0.06 }; - - for (Real strike : strikes) { - const Volatility vol = sabrVolatility(strike, forward, maturity, alpha, beta, nu, rho); - - calibrationSet.emplace_back( - ext::make_shared( - ext::make_shared( - Option::Call, strike), - ext::make_shared(maturityDate)), - ext::make_shared(vol)); - } - - const Handle rTS(flatRate(today, forward, dc)); - const Handle qTS(flatRate(today, forward, dc)); - - Handle spot(ext::make_shared(forward)); - - const CalibrationData data = { spot, rTS, qTS, calibrationSet}; - - std::vector parameter = { alpha, beta, nu, rho, forward, maturity }; - - return std::make_pair(data, parameter); - } -} - -void AndreasenHugeVolatilityInterplTest::testPeterAndFabiensExample() { +BOOST_AUTO_TEST_CASE(testPeterAndFabiensExample) { BOOST_TEST_MESSAGE( "Testing Peter's and Fabien's SABR example..."); @@ -868,7 +871,7 @@ void AndreasenHugeVolatilityInterplTest::testPeterAndFabiensExample() { } } -void AndreasenHugeVolatilityInterplTest::testDifferentOptimizers() { +BOOST_AUTO_TEST_CASE(testDifferentOptimizers) { BOOST_TEST_MESSAGE( "Testing different optimizer for Andreasen-Huge " "volatility interpolation..."); @@ -899,7 +902,7 @@ void AndreasenHugeVolatilityInterplTest::testDifferentOptimizers() { } } -void AndreasenHugeVolatilityInterplTest::testMovingReferenceDate() { +BOOST_AUTO_TEST_CASE(testMovingReferenceDate) { BOOST_TEST_MESSAGE( "Testing that reference date of adapter surface moves along with " "evaluation date..."); @@ -974,7 +977,7 @@ void AndreasenHugeVolatilityInterplTest::testMovingReferenceDate() { << "\n tolerance : " << tol); } -void AndreasenHugeVolatilityInterplTest::testFlatVolCalibration() { +BOOST_AUTO_TEST_CASE(testFlatVolCalibration) { BOOST_TEST_MESSAGE( "Testing Andreasen-Huge example with flat volatility surface..."); @@ -1036,27 +1039,6 @@ void AndreasenHugeVolatilityInterplTest::testFlatVolCalibration() { testAndreasenHugeVolatilityInterpolation(flatVolData, expected); } +BOOST_AUTO_TEST_SUITE_END() -test_suite* AndreasenHugeVolatilityInterplTest::suite(SpeedLevel speed) { - auto* suite = BOOST_TEST_SUITE("Andreasen-Huge volatility interpolation tests"); - - suite->add(QUANTLIB_TEST_CASE(&AndreasenHugeVolatilityInterplTest::testSingleOptionCalibration)); - suite->add(QUANTLIB_TEST_CASE(&AndreasenHugeVolatilityInterplTest::testArbitrageFree)); - suite->add(QUANTLIB_TEST_CASE(&AndreasenHugeVolatilityInterplTest::testPeterAndFabiensExample)); - suite->add(QUANTLIB_TEST_CASE(&AndreasenHugeVolatilityInterplTest::testDifferentOptimizers)); - suite->add(QUANTLIB_TEST_CASE(&AndreasenHugeVolatilityInterplTest::testMovingReferenceDate)); - suite->add(QUANTLIB_TEST_CASE(&AndreasenHugeVolatilityInterplTest::testFlatVolCalibration)); - - if (speed <= Fast) { - suite->add(QUANTLIB_TEST_CASE(&AndreasenHugeVolatilityInterplTest::testAndreasenHugePut)); - suite->add(QUANTLIB_TEST_CASE(&AndreasenHugeVolatilityInterplTest::testAndreasenHugeCall)); - suite->add(QUANTLIB_TEST_CASE(&AndreasenHugeVolatilityInterplTest::testAndreasenHugeCallPut)); - suite->add(QUANTLIB_TEST_CASE(&AndreasenHugeVolatilityInterplTest::testLinearInterpolation)); - suite->add(QUANTLIB_TEST_CASE(&AndreasenHugeVolatilityInterplTest::testPiecewiseConstantInterpolation)); - suite->add(QUANTLIB_TEST_CASE(&AndreasenHugeVolatilityInterplTest::testTimeDependentInterestRates)); - suite->add(QUANTLIB_TEST_CASE(&AndreasenHugeVolatilityInterplTest::testBarrierOptionPricing)); - } - - return suite; -} - +BOOST_AUTO_TEST_SUITE_END() diff --git a/test-suite/andreasenhugevolatilityinterpl.hpp b/test-suite/andreasenhugevolatilityinterpl.hpp deleted file mode 100644 index 3522e75cf35..00000000000 --- a/test-suite/andreasenhugevolatilityinterpl.hpp +++ /dev/null @@ -1,49 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2017, 2018 Klaus Spanderen - - This file is part of QuantLib, a free-software/open-source library - for financial quantitative analysts and developers - http://quantlib.org/ - - QuantLib is free software: you can redistribute it and/or modify it - under the terms of the QuantLib license. You should have received a - copy of the license along with this program; if not, please email - . The license is also available online at - . - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the license for more details. -*/ - -#ifndef quantlib_test_local_volatility_hpp -#define quantlib_test_local_volatility_hpp - -#include -#include "speedlevel.hpp" - -/* remember to document new and/or updated tests in the Doxygen - comment block of the corresponding class */ - -class AndreasenHugeVolatilityInterplTest { - public: - static void testAndreasenHugePut(); - static void testAndreasenHugeCall(); - static void testAndreasenHugeCallPut(); - static void testLinearInterpolation(); - static void testPiecewiseConstantInterpolation(); - static void testTimeDependentInterestRates(); - static void testSingleOptionCalibration(); - static void testArbitrageFree(); - static void testBarrierOptionPricing(); - static void testPeterAndFabiensExample(); - static void testDifferentOptimizers(); - static void testMovingReferenceDate(); - static void testFlatVolCalibration(); - - static boost::unit_test_framework::test_suite* suite(SpeedLevel speed); -}; - - -#endif diff --git a/test-suite/array.cpp b/test-suite/array.cpp index 33cc2abe197..673a58b6790 100644 --- a/test-suite/array.cpp +++ b/test-suite/array.cpp @@ -17,7 +17,7 @@ FOR A PARTICULAR PURPOSE. See the license for more details. */ -#include "array.hpp" +#include "toplevelfixture.hpp" #include "utilities.hpp" #include #include @@ -32,7 +32,11 @@ namespace array_test { }; } -void ArrayTest::testConstruction() { +BOOST_FIXTURE_TEST_SUITE(QuantLibTest, TopLevelFixture) + +BOOST_AUTO_TEST_SUITE(ArrayTest) + +BOOST_AUTO_TEST_CASE(testConstruction) { BOOST_TEST_MESSAGE("Testing array construction..."); @@ -117,7 +121,7 @@ void ArrayTest::testConstruction() { } } -void ArrayTest::testArrayFunctions() { +BOOST_AUTO_TEST_CASE(testArrayFunctions) { BOOST_TEST_MESSAGE("Testing array functions..."); @@ -178,7 +182,7 @@ void ArrayTest::testArrayFunctions() { } } -void ArrayTest::testArrayResize() { +BOOST_AUTO_TEST_CASE(testArrayResize) { BOOST_TEST_MESSAGE("Testing array resize..."); Array a(10,1.0,1.0); @@ -213,7 +217,7 @@ void ArrayTest::testArrayResize() { QL_CHECK_CLOSE(actual[i], expected[i], 100 * QL_EPSILON); \ } \ -void ArrayTest::testArrayOperators() { +BOOST_AUTO_TEST_CASE(testArrayOperators) { BOOST_TEST_MESSAGE("Testing array operators..."); auto get_array = []() { @@ -327,12 +331,7 @@ void ArrayTest::testArrayOperators() { QL_CHECK_CLOSE_ARRAY(real_rvalue_quotient, scalar_quotient_2); } -test_suite* ArrayTest::suite() { - auto* suite = BOOST_TEST_SUITE("array tests"); - suite->add(QUANTLIB_TEST_CASE(&ArrayTest::testConstruction)); - suite->add(QUANTLIB_TEST_CASE(&ArrayTest::testArrayFunctions)); - suite->add(QUANTLIB_TEST_CASE(&ArrayTest::testArrayResize)); - suite->add(QUANTLIB_TEST_CASE(&ArrayTest::testArrayOperators)); - return suite; -} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test-suite/array.hpp b/test-suite/array.hpp deleted file mode 100644 index ea2d280d3b0..00000000000 --- a/test-suite/array.hpp +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2005 StatPro Italia srl - - This file is part of QuantLib, a free-software/open-source library - for financial quantitative analysts and developers - http://quantlib.org/ - - QuantLib is free software: you can redistribute it and/or modify it - under the terms of the QuantLib license. You should have received a - copy of the license along with this program; if not, please email - . The license is also available online at - . - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the license for more details. -*/ - -#ifndef quantlib_test_array_hpp -#define quantlib_test_array_hpp - -#include - -/* remember to document new and/or updated tests in the Doxygen - comment block of the corresponding class */ - -class ArrayTest { - public: - static void testConstruction(); - static void testArrayFunctions(); - static void testArrayResize(); - static void testArrayOperators(); - static boost::unit_test_framework::test_suite* suite(); -}; - - -#endif diff --git a/test-suite/asianoptions.cpp b/test-suite/asianoptions.cpp index 07c30b134e1..836e7b452e7 100644 --- a/test-suite/asianoptions.cpp +++ b/test-suite/asianoptions.cpp @@ -22,7 +22,8 @@ FOR A PARTICULAR PURPOSE. See the license for more details. */ -#include "asianoptions.hpp" +#include "speedlevel.hpp" +#include "toplevelfixture.hpp" #include "utilities.hpp" #include #include @@ -89,10 +90,63 @@ namespace { QL_FAIL("unknown averaging"); } + struct DiscreteAverageData { + Option::Type type; + Real underlying; + Real strike; + Rate dividendYield; + Rate riskFreeRate; + Time first; + Time length; + Size fixings; + Volatility volatility; + bool controlVariate; + Real result; + }; + + struct ContinuousAverageData { + Option::Type type; + Real spot; + Real currentAverage; + Real strike; + Rate dividendYield; + Rate riskFreeRate; + Volatility volatility; + Natural length; + Natural elapsed; + Real result; + }; + + struct DiscreteAverageDataTermStructure { + Option::Type type; + Real underlying; + Real strike; + Rate b; + Rate riskFreeRate; + Time first; // t1 + Time expiry; + Size fixings; + Volatility volatility; + std::string slope; + Real result; + }; + + struct VecerData { + Real spot; + Rate riskFreeRate; + Volatility volatility; + Real strike; + Natural length; + Real result; + Real tolerance; + }; } +BOOST_FIXTURE_TEST_SUITE(QuantLibTest, TopLevelFixture) -void AsianOptionTest::testAnalyticContinuousGeometricAveragePrice() { +BOOST_AUTO_TEST_SUITE(AsianOptionTest) + +BOOST_AUTO_TEST_CASE(testAnalyticContinuousGeometricAveragePrice) { BOOST_TEST_MESSAGE( "Testing analytic continuous geometric average-price Asians..."); @@ -172,8 +226,7 @@ void AsianOptionTest::testAnalyticContinuousGeometricAveragePrice() { } - -void AsianOptionTest::testAnalyticContinuousGeometricAveragePriceGreeks() { +BOOST_AUTO_TEST_CASE(testAnalyticContinuousGeometricAveragePriceGreeks) { BOOST_TEST_MESSAGE( "Testing analytic continuous geometric average-price Asian greeks..."); @@ -316,8 +369,7 @@ void AsianOptionTest::testAnalyticContinuousGeometricAveragePriceGreeks() { } } - -void AsianOptionTest::testAnalyticDiscreteGeometricAveragePrice() { +BOOST_AUTO_TEST_CASE(testAnalyticDiscreteGeometricAveragePrice) { BOOST_TEST_MESSAGE( "Testing analytic discrete geometric average-price Asians..."); @@ -379,7 +431,7 @@ void AsianOptionTest::testAnalyticDiscreteGeometricAveragePrice() { } } -void AsianOptionTest::testAnalyticDiscreteGeometricAverageStrike() { +BOOST_AUTO_TEST_CASE(testAnalyticDiscreteGeometricAverageStrike) { BOOST_TEST_MESSAGE( "Testing analytic discrete geometric average-strike Asians..."); @@ -438,8 +490,7 @@ void AsianOptionTest::testAnalyticDiscreteGeometricAverageStrike() { } } - -void AsianOptionTest::testMCDiscreteGeometricAveragePrice() { +BOOST_AUTO_TEST_CASE(testMCDiscreteGeometricAveragePrice) { BOOST_TEST_MESSAGE( "Testing Monte Carlo discrete geometric average-price Asians..."); @@ -508,7 +559,6 @@ void AsianOptionTest::testMCDiscreteGeometricAveragePrice() { } } - void testDiscreteGeometricAveragePriceHeston(const ext::shared_ptr& engine, const Real tol[]) { @@ -571,7 +621,7 @@ void testDiscreteGeometricAveragePriceHeston(const ext::shared_ptr tolerance) { REPORT_FAILURE("value", averageType, 1.0, 0.0, std::vector(), payoff, europeanExercise, spot->value(), @@ -581,44 +631,7 @@ void testDiscreteGeometricAveragePriceHeston(const ext::shared_ptr spot(ext::shared_ptr(new SimpleQuote(100))); - ext::shared_ptr qRate(new SimpleQuote(0.0)); - ext::shared_ptr qTS = flatRate(today, qRate, dc); - ext::shared_ptr rRate(new SimpleQuote(0.05)); - ext::shared_ptr rTS = flatRate(today, rRate, dc); - - Real v0 = 0.09; - Real kappa = 1.15; - Real theta = 0.0348; - Real sigma = 0.39; - Real rho = -0.64; - - ext::shared_ptr hestonProcess(new - HestonProcess(Handle(rTS), Handle(qTS), - spot, v0, kappa, theta, sigma, rho)); - - ext::shared_ptr engine(new - AnalyticDiscreteGeometricAveragePriceAsianHestonEngine(hestonProcess)); - - testDiscreteGeometricAveragePriceHeston(engine, tol); -} - - -void AsianOptionTest::testMCDiscreteGeometricAveragePriceHeston() { +BOOST_AUTO_TEST_CASE(testMCDiscreteGeometricAveragePriceHeston, *precondition(if_speed(Fast))) { BOOST_TEST_MESSAGE("Testing MC discrete geometric average-price Asians under Heston..."); @@ -657,179 +670,7 @@ void AsianOptionTest::testMCDiscreteGeometricAveragePriceHeston() { testDiscreteGeometricAveragePriceHeston(engine, tol); } - -void AsianOptionTest::testDiscreteGeometricAveragePriceHestonPastFixings() { - - BOOST_TEST_MESSAGE("Testing Analytic vs MC for seasoned discrete geometric Asians under Heston..."); - - // 30-day options need wider tolerance due to uncertainty around what "weekly - // fixing" dates mean over a 30-day month! - - int days[] = {30, 90, 180, 360, 720}; - Real strikes[] = {90, 100, 110}; - - Real tol[3][5][2] = {{{ - 0.04, // strike=90, days=30, k=0 - 0.04, // strike=90, days=30, k=1 - }, - { - 0.04, // strike=90, days=90, k=0 - 0.04, // strike=90, days=90, k=1 - }, - { - 0.04, // strike=90, days=180, k=0 - 0.04, // strike=90, days=180, k=1 - }, - { - 0.05, // strike=90, days=360, k=0 - 0.04, // strike=90, days=360, k=1 - }, - { - 0.04, // strike=90, days=720, k=0 - 0.04, // strike=90, days=720, k=1 - }}, - - {{ - 0.04, // strike=100, days=30, k=0 - 0.04, // strike=100, days=30, k=1 - }, - { - 0.04, // strike=100, days=90, k=0 - 0.04, // strike=100, days=90, k=1 - }, - { - 0.04, // strike=100, days=180, k=0 - 0.04, // strike=100, days=180, k=1 - }, - { - 0.06, // strike=100, days=360, k=0 - 0.06, // strike=100, days=360, k=1 - }, - { - 0.06, // strike=100, days=720, k=0 - 0.05, // strike=100, days=720, k=1 - }}, - - {{ - 0.04, // strike=110, days=30, k=0 - 0.04, // strike=110, days=30, k=1 - }, - { - 0.04, // strike=110, days=90, k=0 - 0.04, // strike=110, days=90, k=1 - }, - { - 0.04, // strike=110, days=180, k=0 - 0.04, // strike=110, days=180, k=1 - }, - { - 0.05, // strike=110, days=360, k=0 - 0.04, // strike=110, days=360, k=1 - }, - { - 0.06, // strike=110, days=720, k=0 - 0.05, // strike=110, days=720, k=1 - }}}; - - DayCounter dc = Actual365Fixed(); - Date today = Settings::instance().evaluationDate(); - - Handle spot(ext::shared_ptr(new SimpleQuote(100))); - ext::shared_ptr qRate(new SimpleQuote(0.0)); - ext::shared_ptr qTS = flatRate(today, qRate, dc); - ext::shared_ptr rRate(new SimpleQuote(0.05)); - ext::shared_ptr rTS = flatRate(today, rRate, dc); - - Real v0 = 0.09; - Real kappa = 1.15; - Real theta = 0.0348; - Real sigma = 0.39; - Real rho = -0.64; - - ext::shared_ptr hestonProcess(new - HestonProcess(Handle(rTS), Handle(qTS), - spot, v0, kappa, theta, sigma, rho)); - - ext::shared_ptr analyticEngine(new - AnalyticDiscreteGeometricAveragePriceAsianHestonEngine(hestonProcess)); - - ext::shared_ptr mcEngine = - MakeMCDiscreteGeometricAPHestonEngine(hestonProcess) - .withSamples(8191) - .withSeed(43); - - Option::Type type(Option::Call); - Average::Type averageType = Average::Geometric; - - for (Size strike_index = 0; strike_index < LENGTH(strikes); strike_index++) { - - for (Size day_index = 0; day_index < LENGTH(days); day_index++) { - - for (Size k=0; k<2; k++) { - - Size futureFixings = int(std::floor(days[day_index] / 30.0)); - std::vector fixingDates(futureFixings); - Date expiryDate = today + days[day_index] * Days; - - for (int i=futureFixings-1; i>=0; i--) { - fixingDates[i] = expiryDate - i * 30; - } - - ext::shared_ptr europeanExercise(new EuropeanExercise(expiryDate)); - ext::shared_ptr payoff(new PlainVanillaPayoff(type, strikes[strike_index])); - - Real runningAccumulator = 1.0; - Size pastFixingsCount = 0; - if (k == 0) { - runningAccumulator = 100.0; - pastFixingsCount = 1; - } else { - runningAccumulator = 95.0 * 100.0 * 105.0; - pastFixingsCount = 3; - } - - DiscreteAveragingAsianOption option(averageType, runningAccumulator, pastFixingsCount, - fixingDates, payoff, europeanExercise); - - option.setPricingEngine(analyticEngine); - Real analyticPrice = option.NPV(); - - option.setPricingEngine(mcEngine); - Real mcPrice = option.NPV(); - - auto tolerance = tol[strike_index][day_index][k]; - - if (std::fabs(analyticPrice-mcPrice) > tolerance) { - REPORT_FAILURE("value", averageType, runningAccumulator, pastFixingsCount, - std::vector(), payoff, europeanExercise, spot->value(), - qRate->value(), rRate->value(), today, - std::sqrt(v0), analyticPrice, mcPrice, tolerance); - } - } - } - } -} - -namespace { - - struct DiscreteAverageData { - Option::Type type; - Real underlying; - Real strike; - Rate dividendYield; - Rate riskFreeRate; - Time first; - Time length; - Size fixings; - Volatility volatility; - bool controlVariate; - Real result; - }; - -} - - -void AsianOptionTest::testMCDiscreteArithmeticAveragePrice() { +BOOST_AUTO_TEST_CASE(testMCDiscreteArithmeticAveragePrice, *precondition(if_speed(Fast))) { BOOST_TEST_MESSAGE( "Testing Monte Carlo discrete arithmetic average-price Asians..."); @@ -990,8 +831,7 @@ void AsianOptionTest::testMCDiscreteArithmeticAveragePrice() { } } - -void AsianOptionTest::testMCDiscreteArithmeticAveragePriceHeston() { +BOOST_AUTO_TEST_CASE(testMCDiscreteArithmeticAveragePriceHeston, *precondition(if_speed(Slow))) { BOOST_TEST_MESSAGE( "Testing Monte Carlo discrete arithmetic average-price Asians in Heston model..."); @@ -1183,8 +1023,7 @@ void AsianOptionTest::testMCDiscreteArithmeticAveragePriceHeston() { } - -void AsianOptionTest::testMCDiscreteArithmeticAverageStrike() { +BOOST_AUTO_TEST_CASE(testMCDiscreteArithmeticAverageStrike) { BOOST_TEST_MESSAGE( "Testing Monte Carlo discrete arithmetic average-strike Asians..."); @@ -1317,7 +1156,7 @@ void AsianOptionTest::testMCDiscreteArithmeticAverageStrike() { } } -void AsianOptionTest::testAnalyticDiscreteGeometricAveragePriceGreeks() { +BOOST_AUTO_TEST_CASE(testAnalyticDiscreteGeometricAveragePriceGreeks) { BOOST_TEST_MESSAGE("Testing discrete-averaging geometric Asian greeks..."); @@ -1465,8 +1304,7 @@ void AsianOptionTest::testAnalyticDiscreteGeometricAveragePriceGreeks() { } } - -void AsianOptionTest::testPastFixings() { +BOOST_AUTO_TEST_CASE(testPastFixings) { BOOST_TEST_MESSAGE("Testing use of past fixings in Asian options..."); @@ -1634,7 +1472,7 @@ void AsianOptionTest::testPastFixings() { } -void AsianOptionTest::testPastFixingsModelDependency() { +BOOST_AUTO_TEST_CASE(testPastFixingsModelDependency) { BOOST_TEST_MESSAGE( "Testing use of past fixings in Asian options where model dependency is flagged..."); @@ -1759,8 +1597,7 @@ void AsianOptionTest::testPastFixingsModelDependency() { } } - -void AsianOptionTest::testAllFixingsInThePast() { +BOOST_AUTO_TEST_CASE(testAllFixingsInThePast) { BOOST_TEST_MESSAGE( "Testing Asian options with all fixing dates in the past..."); @@ -1893,49 +1730,214 @@ void AsianOptionTest::testAllFixingsInThePast() { } } -namespace { +BOOST_AUTO_TEST_CASE(testTurnbullWakemanAsianEngine) { - struct ContinuousAverageData { - Option::Type type; - Real spot; - Real currentAverage; - Real strike; - Rate dividendYield; - Rate riskFreeRate; - Volatility volatility; - Natural length; - Natural elapsed; - Real result; - }; + BOOST_TEST_MESSAGE("Testing Turnbull-Wakeman engine for discrete-time arithmetic average-rate " + "Asians options with term structure support..."); + + // Data from Haug, "Option Pricing Formulas", Table 4-28, p.201 + // Type, underlying, strike, b, rfRate, t1, expiry, fixings, base vol, slope, expected result + DiscreteAverageDataTermStructure cases[] = { + {Option::Call, 100, 80, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "flat", 19.5152}, + {Option::Call, 100, 80, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "up", 19.5063}, + {Option::Call, 100, 80, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "down", 19.5885}, + {Option::Put, 100, 80, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "flat", 0.0090}, + {Option::Put, 100, 80, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "up", 0.0001}, + {Option::Put, 100, 80, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "down", 0.0823}, + + {Option::Call, 100, 90, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "flat", 10.1437}, + {Option::Call, 100, 90, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "up", 9.8313}, + {Option::Call, 100, 90, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "down", 10.7062}, + {Option::Put, 100, 90, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "flat", 0.3906}, + {Option::Put, 100, 90, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "up", 0.0782}, + {Option::Put, 100, 90, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "down", 0.9531}, + + {Option::Call, 100, 100, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "flat", 3.2700}, + {Option::Call, 100, 100, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "up", 2.2819}, + {Option::Call, 100, 100, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "down", 4.3370}, + {Option::Put, 100, 100, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "flat", 3.2700}, + {Option::Put, 100, 100, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "up", 2.2819}, + {Option::Put, 100, 100, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "down", 4.3370}, + + {Option::Call, 100, 110, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "flat", 0.5515}, + {Option::Call, 100, 110, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "up", 0.1314}, + {Option::Call, 100, 110, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "down", 1.2429}, + {Option::Put, 100, 110, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "flat", 10.3046}, + {Option::Put, 100, 110, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "up", 9.8845}, + {Option::Put, 100, 110, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "down", 10.9960}, + + {Option::Call, 100, 120, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "flat", 0.0479}, + {Option::Call, 100, 120, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "up", 0.0016}, + {Option::Call, 100, 120, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "down", 0.2547}, + {Option::Put, 100, 120, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "flat", 19.5541}, + {Option::Put, 100, 120, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "up", 19.5078}, + {Option::Put, 100, 120, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "down", 19.7609}}; + + DayCounter dc = Actual360(); + Date today = Settings::instance().evaluationDate(); + + for (auto& l : cases) { + Time dt = (l.expiry - l.first) / (l.fixings - 1); + std::vector fixingDates(l.fixings); + fixingDates[0] = today + timeToDays(l.first, 360); + + for (Size i = 1; i < l.fixings; i++) { + fixingDates[i] = today + timeToDays(i * dt + l.first, 360); + } + + // Set up market data + ext::shared_ptr spot(new SimpleQuote(l.underlying)); + ext::shared_ptr qTS = flatRate(today, l.b + l.riskFreeRate, dc); + ext::shared_ptr rTS = flatRate(today, l.riskFreeRate, dc); + ext::shared_ptr volTS; + Volatility volSlope = 0.005; + if (l.slope == "flat") { + volTS = flatVol(today, l.volatility, dc); + } else if (l.slope == "up") { + std::vector volatilities(l.fixings); + for (Size i = 0; i < l.fixings; ++i) { + // Loop to fill a vector of vols from 7.5 % to 20 % + volatilities[i] = l.volatility - (l.fixings - 1) * volSlope + i * volSlope; + } + volTS = + ext::make_shared(today, fixingDates, volatilities, dc, true); + } else if (l.slope == "down") { + std::vector volatilities(l.fixings); + for (Size i = 0; i < l.fixings; ++i) { + // Loop to fill a vector of vols from 32.5 % to 20 % + volatilities[i] = l.volatility + (l.fixings - 1) * volSlope - i * volSlope; + } + volTS = + ext::make_shared(today, fixingDates, volatilities, dc, false); + } else { + QL_FAIL("unexpected slope type in engine test case"); + } + + Average::Type averageType = Average::Arithmetic; + + ext::shared_ptr payoff(new PlainVanillaPayoff(l.type, l.strike)); + + Date maturity = today + timeToDays(l.expiry, 360); + + ext::shared_ptr exercise(new EuropeanExercise(maturity)); + + ext::shared_ptr stochProcess(new BlackScholesMertonProcess( + Handle(spot), Handle(qTS), Handle(rTS), + Handle(volTS))); + + // Construct engine + ext::shared_ptr engine( + new TurnbullWakemanAsianEngine(stochProcess)); + + DiscreteAveragingAsianOption option(averageType, 0, 0, fixingDates, payoff, exercise); + option.setPricingEngine(engine); + + Real calculated = option.NPV(); + Real expected = l.result; + Real tolerance = 2.5e-3; + Real error = std::fabs(expected - calculated); + if (error > tolerance) { + BOOST_ERROR( + "Failed to reproduce expected NPV:" + << "\n type: " << l.type << "\n spot: " << l.underlying + << "\n strike: " << l.strike << "\n dividend yield: " + << l.b + l.riskFreeRate << "\n risk-free rate: " << l.riskFreeRate + << "\n volatility: " << l.volatility << "\n slope: " << l.slope + << "\n reference date: " << today << "\n expiry: " << l.expiry + << "\n expected value: " << expected << "\n calculated: " << calculated + << "\n error: " << error); + } + + // Compare greeks to numerical greeks + Real dS = 0.001; + Real delta = option.delta(); + Real gamma = option.gamma(); + + ext::shared_ptr spotUp(new SimpleQuote(l.underlying+dS)); + ext::shared_ptr spotDown(new SimpleQuote(l.underlying-dS)); + + ext::shared_ptr stochProcessUp(new BlackScholesMertonProcess( + Handle(spotUp), Handle(qTS), Handle(rTS), + Handle(volTS))); + + ext::shared_ptr stochProcessDown(new BlackScholesMertonProcess( + Handle(spotDown), Handle(qTS), Handle(rTS), + Handle(volTS))); + + ext::shared_ptr engineUp( + new TurnbullWakemanAsianEngine(stochProcessUp)); + + ext::shared_ptr engineDown( + new TurnbullWakemanAsianEngine(stochProcessDown)); + + option.setPricingEngine(engineUp); + Real calculatedUp = option.NPV(); + + option.setPricingEngine(engineDown); + Real calculatedDown = option.NPV(); + Real deltaBump = (calculatedUp - calculatedDown) / (2 * dS); + Real gammaBump = (calculatedUp + calculatedDown - 2*calculated) / (dS * dS); + + tolerance = 1.0e-6; + Real deltaError = std::fabs(deltaBump - delta); + if (deltaError > tolerance) { + BOOST_ERROR( + "Analytical delta failed to match bump delta:" + << "\n type: " << l.type << "\n spot: " << l.underlying + << "\n strike: " << l.strike << "\n dividend yield: " + << l.b + l.riskFreeRate << "\n risk-free rate: " << l.riskFreeRate + << "\n volatility: " << l.volatility << "\n slope: " << l.slope + << "\n reference date: " << today << "\n expiry: " << l.expiry + << "\n analytic delta: " << delta << "\n bump delta: " << deltaBump + << "\n error: " << deltaError); + } + + Real gammaError = std::fabs(gammaBump - gamma); + if (gammaError > tolerance) { + BOOST_ERROR( + "Analytical gamma failed to match bump gamma:" + << "\n type: " << l.type << "\n spot: " << l.underlying + << "\n strike: " << l.strike << "\n dividend yield: " + << l.b + l.riskFreeRate << "\n risk-free rate: " << l.riskFreeRate + << "\n volatility: " << l.volatility << "\n slope: " << l.slope + << "\n reference date: " << today << "\n expiry: " << l.expiry + << "\n analytic gamma: " << gamma << "\n bump gamma: " << gammaBump + << "\n error: " << gammaError); + } + } } -void AsianOptionTest::testLevyEngine() { +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(AsianOptionExperimentalTest) + +BOOST_AUTO_TEST_CASE(testLevyEngine) { BOOST_TEST_MESSAGE("Testing Levy engine for Asians options..."); // data from Haug, "Option Pricing Formulas", p.99-100 ContinuousAverageData cases[] = { - { Option::Call, 6.80, 6.80, 6.90, 0.09, 0.07, 0.14, 180, 0, 0.0944 }, - { Option::Put, 6.80, 6.80, 6.90, 0.09, 0.07, 0.14, 180, 0, 0.2237 }, - { Option::Call, 100.0, 100.0, 95.0, 0.05, 0.1, 0.15, 270, 0, 7.0544 }, - { Option::Call, 100.0, 100.0, 95.0, 0.05, 0.1, 0.15, 270, 90, 5.6731 }, - { Option::Call, 100.0, 100.0, 95.0, 0.05, 0.1, 0.15, 270, 180, 5.0806 }, - { Option::Call, 100.0, 100.0, 95.0, 0.05, 0.1, 0.35, 270, 0, 10.1213 }, - { Option::Call, 100.0, 100.0, 95.0, 0.05, 0.1, 0.35, 270, 90, 6.9705 }, - { Option::Call, 100.0, 100.0, 95.0, 0.05, 0.1, 0.35, 270, 180, 5.1411 }, - { Option::Call, 100.0, 100.0, 100.0, 0.05, 0.1, 0.15, 270, 0, 3.7845 }, - { Option::Call, 100.0, 100.0, 100.0, 0.05, 0.1, 0.15, 270, 90, 1.9964 }, - { Option::Call, 100.0, 100.0, 100.0, 0.05, 0.1, 0.15, 270, 180, 0.6722 }, - { Option::Call, 100.0, 100.0, 100.0, 0.05, 0.1, 0.35, 270, 0, 7.5038 }, - { Option::Call, 100.0, 100.0, 100.0, 0.05, 0.1, 0.35, 270, 90, 4.0687 }, - { Option::Call, 100.0, 100.0, 100.0, 0.05, 0.1, 0.35, 270, 180, 1.4222 }, - { Option::Call, 100.0, 100.0, 105.0, 0.05, 0.1, 0.15, 270, 0, 1.6729 }, - { Option::Call, 100.0, 100.0, 105.0, 0.05, 0.1, 0.15, 270, 90, 0.3565 }, - { Option::Call, 100.0, 100.0, 105.0, 0.05, 0.1, 0.15, 270, 180, 0.0004 }, - { Option::Call, 100.0, 100.0, 105.0, 0.05, 0.1, 0.35, 270, 0, 5.4071 }, - { Option::Call, 100.0, 100.0, 105.0, 0.05, 0.1, 0.35, 270, 90, 2.1359 }, - { Option::Call, 100.0, 100.0, 105.0, 0.05, 0.1, 0.35, 270, 180, 0.1552 } + { Option::Call, 6.80, 6.80, 6.90, 0.09, 0.07, 0.14, 180, 0, 0.0944 }, + { Option::Put, 6.80, 6.80, 6.90, 0.09, 0.07, 0.14, 180, 0, 0.2237 }, + { Option::Call, 100.0, 100.0, 95.0, 0.05, 0.1, 0.15, 270, 0, 7.0544 }, + { Option::Call, 100.0, 100.0, 95.0, 0.05, 0.1, 0.15, 270, 90, 5.6731 }, + { Option::Call, 100.0, 100.0, 95.0, 0.05, 0.1, 0.15, 270, 180, 5.0806 }, + { Option::Call, 100.0, 100.0, 95.0, 0.05, 0.1, 0.35, 270, 0, 10.1213 }, + { Option::Call, 100.0, 100.0, 95.0, 0.05, 0.1, 0.35, 270, 90, 6.9705 }, + { Option::Call, 100.0, 100.0, 95.0, 0.05, 0.1, 0.35, 270, 180, 5.1411 }, + { Option::Call, 100.0, 100.0, 100.0, 0.05, 0.1, 0.15, 270, 0, 3.7845 }, + { Option::Call, 100.0, 100.0, 100.0, 0.05, 0.1, 0.15, 270, 90, 1.9964 }, + { Option::Call, 100.0, 100.0, 100.0, 0.05, 0.1, 0.15, 270, 180, 0.6722 }, + { Option::Call, 100.0, 100.0, 100.0, 0.05, 0.1, 0.35, 270, 0, 7.5038 }, + { Option::Call, 100.0, 100.0, 100.0, 0.05, 0.1, 0.35, 270, 90, 4.0687 }, + { Option::Call, 100.0, 100.0, 100.0, 0.05, 0.1, 0.35, 270, 180, 1.4222 }, + { Option::Call, 100.0, 100.0, 105.0, 0.05, 0.1, 0.15, 270, 0, 1.6729 }, + { Option::Call, 100.0, 100.0, 105.0, 0.05, 0.1, 0.15, 270, 90, 0.3565 }, + { Option::Call, 100.0, 100.0, 105.0, 0.05, 0.1, 0.15, 270, 180, 0.0004 }, + { Option::Call, 100.0, 100.0, 105.0, 0.05, 0.1, 0.35, 270, 0, 5.4071 }, + { Option::Call, 100.0, 100.0, 105.0, 0.05, 0.1, 0.35, 270, 90, 2.1359 }, + { Option::Call, 100.0, 100.0, 105.0, 0.05, 0.1, 0.35, 270, 180, 0.1552 } }; DayCounter dc = Actual360(); @@ -1959,14 +1961,14 @@ void AsianOptionTest::testLevyEngine() { ext::shared_ptr exercise(new EuropeanExercise(maturity)); ext::shared_ptr stochProcess(new - BlackScholesMertonProcess(Handle(spot), - Handle(qTS), - Handle(rTS), - Handle(volTS))); + BlackScholesMertonProcess(Handle(spot), + Handle(qTS), + Handle(rTS), + Handle(volTS))); ext::shared_ptr engine( new ContinuousArithmeticAsianLevyEngine( - stochProcess, Handle(average), startDate)); + stochProcess, Handle(average), startDate)); ContinuousAveragingAsianOption option(averageType, payoff, exercise); @@ -1990,21 +1992,7 @@ void AsianOptionTest::testLevyEngine() { } } -namespace { - - struct VecerData { - Real spot; - Rate riskFreeRate; - Volatility volatility; - Real strike; - Natural length; - Real result; - Real tolerance; - }; - -} - -void AsianOptionTest::testVecerEngine() { +BOOST_AUTO_TEST_CASE(testVecerEngine) { BOOST_TEST_MESSAGE("Testing Vecer engine for Asian options..."); VecerData cases[] = { @@ -2056,7 +2044,7 @@ void AsianOptionTest::testVecerEngine() { } } -void AsianOptionTest::testAnalyticContinuousGeometricAveragePriceHeston() { +BOOST_AUTO_TEST_CASE(testAnalyticContinuousGeometricAveragePriceHeston) { BOOST_TEST_MESSAGE("Testing analytic continuous geometric Asians under Heston..."); @@ -2070,11 +2058,11 @@ void AsianOptionTest::testAnalyticContinuousGeometricAveragePriceHeston() { // Prices from Table 1 (params obey Feller condition) Real prices[] = {10.6571, 6.5871, 3.4478, 1.4552, 0.4724, 16.5030, 13.7625, 11.3374, 9.2245, - 7.4122, 20.5102, 18.3060, 16.2895, 14.4531, 12.7882}; + 7.4122, 20.5102, 18.3060, 16.2895, 14.4531, 12.7882}; // Prices from Table 4 (params do not obey Feller condition) Real prices_2[] = {10.6425, 6.4362, 3.1578, 1.1936, 0.3609, 14.9955, 11.6707, 8.7767, 6.3818, - 4.5118, 18.1219, 15.2009, 12.5707, 10.2539, 8.2611}; + 4.5118, 18.1219, 15.2009, 12.5707, 10.2539, 8.2611}; // 0.2 and 3.0 match to 1e-4. Unfortunatly 1.5 corresponds to 547.5 days, 547 and 548 // bound the expected answer but are both out by ~5e-3 @@ -2098,11 +2086,11 @@ void AsianOptionTest::testAnalyticContinuousGeometricAveragePriceHeston() { Real rho = -0.64; ext::shared_ptr hestonProcess(new - HestonProcess(Handle(rTS), Handle(qTS), - spot, v0, kappa, theta, sigma, rho)); + HestonProcess(Handle(rTS), Handle(qTS), + spot, v0, kappa, theta, sigma, rho)); ext::shared_ptr engine(new - AnalyticContinuousGeometricAveragePriceAsianHestonEngine(hestonProcess)); + AnalyticContinuousGeometricAveragePriceAsianHestonEngine(hestonProcess)); for (Size i=0; i tolerance) { REPORT_FAILURE("value", averageType, 1.0, 0.0, - std::vector(), payoff, europeanExercise, spot->value(), - qRate->value(), rRate->value(), today, - std::sqrt(v0), expected, calculated, tolerance); + std::vector(), payoff, europeanExercise, spot->value(), + qRate->value(), rRate->value(), today, + std::sqrt(v0), expected, calculated, tolerance); } } @@ -2134,11 +2122,11 @@ void AsianOptionTest::testAnalyticContinuousGeometricAveragePriceHeston() { Real rho_2 = -0.3; ext::shared_ptr hestonProcess_2(new - HestonProcess(Handle(rTS), Handle(qTS), - spot, v0_2, kappa_2, theta_2, sigma_2, rho_2)); + HestonProcess(Handle(rTS), Handle(qTS), + spot, v0_2, kappa_2, theta_2, sigma_2, rho_2)); ext::shared_ptr engine_2(new - AnalyticContinuousGeometricAveragePriceAsianHestonEngine(hestonProcess_2)); + AnalyticContinuousGeometricAveragePriceAsianHestonEngine(hestonProcess_2)); for (Size i=0; i tolerance) { REPORT_FAILURE("value", averageType, 1.0, 0.0, - std::vector(), payoff, europeanExercise, spot->value(), - qRate->value(), rRate->value(), today, - std::sqrt(v0), expected, calculated, tolerance); + std::vector(), payoff, europeanExercise, spot->value(), + qRate->value(), rRate->value(), today, + std::sqrt(v0), expected, calculated, tolerance); } } @@ -2170,19 +2158,19 @@ void AsianOptionTest::testAnalyticContinuousGeometricAveragePriceHeston() { // 73, 348 and 1095 are 0.2, 1.5 and 3.0 years respectively in Actual365Fixed Time days_3[] = {30, 91, 182, 365, 730, 1095, 30, 91, 182, 365, 730, 1095, 30, - 91, 182, 365, 730, 1095}; + 91, 182, 365, 730, 1095}; Real strikes_3[] = {90, 90, 90, 90, 90, 90, 100, 100, 100, 100, 100, 100, 110, 110, 110, 110, 110, 110}; // 30-day options need wider tolerance due to the day-bracket issue discussed above Real tol_3[] = {2.0e-2, 1.0e-2, 1.0e-2, 1.0e-2, 1.0e-2, 1.0e-2, 2.0e-2, 1.0e-2, - 1.0e-2, 1.0e-2, 1.0e-2, 1.0e-2, 2.0e-2, 1.0e-2, 1.0e-2, 1.0e-2, - 1.0e-2, 1.0e-2}; + 1.0e-2, 1.0e-2, 1.0e-2, 1.0e-2, 2.0e-2, 1.0e-2, 1.0e-2, 1.0e-2, + 1.0e-2, 1.0e-2}; // Prices from Tables 1, 2 and 3 Real prices_3[] = {10.1513, 10.8175, 11.8664, 13.5931, 16.0988, 17.9475, 2.0472, - 3.5735, 5.0588, 7.1132, 9.9139, 11.9959, 0.0350, 0.4869, - 1.3376, 2.8569, 5.2804, 7.2682}; + 3.5735, 5.0588, 7.1132, 9.9139, 11.9959, 0.0350, 0.4869, + 1.3376, 2.8569, 5.2804, 7.2682}; // Note that although these parameters look similar to the first set above, theta // is a factor of 10 smaller. I guess there is a mis-transcription somewhere! @@ -2193,11 +2181,11 @@ void AsianOptionTest::testAnalyticContinuousGeometricAveragePriceHeston() { Real rho_3 = -0.64; ext::shared_ptr hestonProcess_3(new - HestonProcess(Handle(rTS), Handle(qTS), - spot, v0_3, kappa_3, theta_3, sigma_3, rho_3)); + HestonProcess(Handle(rTS), Handle(qTS), + spot, v0_3, kappa_3, theta_3, sigma_3, rho_3)); ext::shared_ptr engine_3(new - AnalyticContinuousGeometricAveragePriceAsianHestonEngine(hestonProcess_3)); + AnalyticContinuousGeometricAveragePriceAsianHestonEngine(hestonProcess_3)); for (Size i=0; i tolerance) { REPORT_FAILURE("value", averageType, 1.0, 0.0, - std::vector(), payoff, europeanExercise, spot->value(), - qRate->value(), rRate->value(), today, - std::sqrt(v0), expected, calculated, tolerance); + std::vector(), payoff, europeanExercise, spot->value(), + qRate->value(), rRate->value(), today, + std::sqrt(v0), expected, calculated, tolerance); } } } -namespace { - struct DiscreteAverageDataTermStructure { - Option::Type type; - Real underlying; - Real strike; - Rate b; - Rate riskFreeRate; - Time first; // t1 - Time expiry; - Size fixings; - Volatility volatility; - std::string slope; - Real result; - }; -} +BOOST_AUTO_TEST_CASE(testAnalyticDiscreteGeometricAveragePriceHeston) { -void AsianOptionTest::testTurnbullWakemanAsianEngine() { + BOOST_TEST_MESSAGE("Testing analytic discrete geometric average-price Asians under Heston..."); - BOOST_TEST_MESSAGE("Testing Turnbull-Wakeman engine for discrete-time arithmetic average-rate " - "Asians options with term structure support..."); + // 30-day options need wider tolerance due to uncertainty around what "weekly + // fixing" dates mean over a 30-day month! + Real tol[] = {3.0e-2, 2.0e-2, 2.0e-2, 2.0e-2, 3.0e-2, 4.0e-2, 8.0e-2, 1.0e-2, + 2.0e-2, 3.0e-2, 3.0e-2, 4.0e-2, 2.0e-2, 1.0e-2, 1.0e-2, 2.0e-2, + 3.0e-2, 4.0e-2}; - // Data from Haug, "Option Pricing Formulas", Table 4-28, p.201 - // Type, underlying, strike, b, rfRate, t1, expiry, fixings, base vol, slope, expected result - DiscreteAverageDataTermStructure cases[] = { - {Option::Call, 100, 80, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "flat", 19.5152}, - {Option::Call, 100, 80, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "up", 19.5063}, - {Option::Call, 100, 80, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "down", 19.5885}, - {Option::Put, 100, 80, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "flat", 0.0090}, - {Option::Put, 100, 80, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "up", 0.0001}, - {Option::Put, 100, 80, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "down", 0.0823}, - - {Option::Call, 100, 90, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "flat", 10.1437}, - {Option::Call, 100, 90, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "up", 9.8313}, - {Option::Call, 100, 90, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "down", 10.7062}, - {Option::Put, 100, 90, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "flat", 0.3906}, - {Option::Put, 100, 90, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "up", 0.0782}, - {Option::Put, 100, 90, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "down", 0.9531}, + DayCounter dc = Actual365Fixed(); + Date today = Settings::instance().evaluationDate(); - {Option::Call, 100, 100, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "flat", 3.2700}, - {Option::Call, 100, 100, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "up", 2.2819}, - {Option::Call, 100, 100, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "down", 4.3370}, - {Option::Put, 100, 100, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "flat", 3.2700}, - {Option::Put, 100, 100, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "up", 2.2819}, - {Option::Put, 100, 100, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "down", 4.3370}, + Handle spot(ext::shared_ptr(new SimpleQuote(100))); + ext::shared_ptr qRate(new SimpleQuote(0.0)); + ext::shared_ptr qTS = flatRate(today, qRate, dc); + ext::shared_ptr rRate(new SimpleQuote(0.05)); + ext::shared_ptr rTS = flatRate(today, rRate, dc); - {Option::Call, 100, 110, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "flat", 0.5515}, - {Option::Call, 100, 110, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "up", 0.1314}, - {Option::Call, 100, 110, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "down", 1.2429}, - {Option::Put, 100, 110, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "flat", 10.3046}, - {Option::Put, 100, 110, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "up", 9.8845}, - {Option::Put, 100, 110, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "down", 10.9960}, + Real v0 = 0.09; + Real kappa = 1.15; + Real theta = 0.0348; + Real sigma = 0.39; + Real rho = -0.64; - {Option::Call, 100, 120, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "flat", 0.0479}, - {Option::Call, 100, 120, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "up", 0.0016}, - {Option::Call, 100, 120, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "down", 0.2547}, - {Option::Put, 100, 120, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "flat", 19.5541}, - {Option::Put, 100, 120, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "up", 19.5078}, - {Option::Put, 100, 120, 0, 0.05, 1.0 / 52, 0.5, 26, 0.2, "down", 19.7609}}; + ext::shared_ptr hestonProcess(new + HestonProcess(Handle(rTS), Handle(qTS), + spot, v0, kappa, theta, sigma, rho)); - DayCounter dc = Actual360(); - Date today = Settings::instance().evaluationDate(); + ext::shared_ptr engine(new + AnalyticDiscreteGeometricAveragePriceAsianHestonEngine(hestonProcess)); - for (auto& l : cases) { - Time dt = (l.expiry - l.first) / (l.fixings - 1); - std::vector fixingDates(l.fixings); - fixingDates[0] = today + timeToDays(l.first, 360); + AsianOptionTest::testDiscreteGeometricAveragePriceHeston(engine, tol); +} - for (Size i = 1; i < l.fixings; i++) { - fixingDates[i] = today + timeToDays(i * dt + l.first, 360); - } +BOOST_AUTO_TEST_CASE(testDiscreteGeometricAveragePriceHestonPastFixings) { - // Set up market data - ext::shared_ptr spot(new SimpleQuote(l.underlying)); - ext::shared_ptr qTS = flatRate(today, l.b + l.riskFreeRate, dc); - ext::shared_ptr rTS = flatRate(today, l.riskFreeRate, dc); - ext::shared_ptr volTS; - Volatility volSlope = 0.005; - if (l.slope == "flat") { - volTS = flatVol(today, l.volatility, dc); - } else if (l.slope == "up") { - std::vector volatilities(l.fixings); - for (Size i = 0; i < l.fixings; ++i) { - // Loop to fill a vector of vols from 7.5 % to 20 % - volatilities[i] = l.volatility - (l.fixings - 1) * volSlope + i * volSlope; - } - volTS = - ext::make_shared(today, fixingDates, volatilities, dc, true); - } else if (l.slope == "down") { - std::vector volatilities(l.fixings); - for (Size i = 0; i < l.fixings; ++i) { - // Loop to fill a vector of vols from 32.5 % to 20 % - volatilities[i] = l.volatility + (l.fixings - 1) * volSlope - i * volSlope; - } - volTS = - ext::make_shared(today, fixingDates, volatilities, dc, false); - } else { - QL_FAIL("unexpected slope type in engine test case"); - } + BOOST_TEST_MESSAGE("Testing Analytic vs MC for seasoned discrete geometric Asians under Heston..."); - Average::Type averageType = Average::Arithmetic; + // 30-day options need wider tolerance due to uncertainty around what "weekly + // fixing" dates mean over a 30-day month! - ext::shared_ptr payoff(new PlainVanillaPayoff(l.type, l.strike)); + int days[] = {30, 90, 180, 360, 720}; + Real strikes[] = {90, 100, 110}; - Date maturity = today + timeToDays(l.expiry, 360); + Real tol[3][5][2] = {{{ + 0.04, // strike=90, days=30, k=0 + 0.04, // strike=90, days=30, k=1 + }, + { + 0.04, // strike=90, days=90, k=0 + 0.04, // strike=90, days=90, k=1 + }, + { + 0.04, // strike=90, days=180, k=0 + 0.04, // strike=90, days=180, k=1 + }, + { + 0.05, // strike=90, days=360, k=0 + 0.04, // strike=90, days=360, k=1 + }, + { + 0.04, // strike=90, days=720, k=0 + 0.04, // strike=90, days=720, k=1 + }}, - ext::shared_ptr exercise(new EuropeanExercise(maturity)); + {{ + 0.04, // strike=100, days=30, k=0 + 0.04, // strike=100, days=30, k=1 + }, + { + 0.04, // strike=100, days=90, k=0 + 0.04, // strike=100, days=90, k=1 + }, + { + 0.04, // strike=100, days=180, k=0 + 0.04, // strike=100, days=180, k=1 + }, + { + 0.06, // strike=100, days=360, k=0 + 0.06, // strike=100, days=360, k=1 + }, + { + 0.06, // strike=100, days=720, k=0 + 0.05, // strike=100, days=720, k=1 + }}, - ext::shared_ptr stochProcess(new BlackScholesMertonProcess( - Handle(spot), Handle(qTS), Handle(rTS), - Handle(volTS))); + {{ + 0.04, // strike=110, days=30, k=0 + 0.04, // strike=110, days=30, k=1 + }, + { + 0.04, // strike=110, days=90, k=0 + 0.04, // strike=110, days=90, k=1 + }, + { + 0.04, // strike=110, days=180, k=0 + 0.04, // strike=110, days=180, k=1 + }, + { + 0.05, // strike=110, days=360, k=0 + 0.04, // strike=110, days=360, k=1 + }, + { + 0.06, // strike=110, days=720, k=0 + 0.05, // strike=110, days=720, k=1 + }}}; - // Construct engine - ext::shared_ptr engine( - new TurnbullWakemanAsianEngine(stochProcess)); + DayCounter dc = Actual365Fixed(); + Date today = Settings::instance().evaluationDate(); - DiscreteAveragingAsianOption option(averageType, 0, 0, fixingDates, payoff, exercise); - option.setPricingEngine(engine); + Handle spot(ext::shared_ptr(new SimpleQuote(100))); + ext::shared_ptr qRate(new SimpleQuote(0.0)); + ext::shared_ptr qTS = flatRate(today, qRate, dc); + ext::shared_ptr rRate(new SimpleQuote(0.05)); + ext::shared_ptr rTS = flatRate(today, rRate, dc); - Real calculated = option.NPV(); - Real expected = l.result; - Real tolerance = 2.5e-3; - Real error = std::fabs(expected - calculated); - if (error > tolerance) { - BOOST_ERROR( - "Failed to reproduce expected NPV:" - << "\n type: " << l.type << "\n spot: " << l.underlying - << "\n strike: " << l.strike << "\n dividend yield: " - << l.b + l.riskFreeRate << "\n risk-free rate: " << l.riskFreeRate - << "\n volatility: " << l.volatility << "\n slope: " << l.slope - << "\n reference date: " << today << "\n expiry: " << l.expiry - << "\n expected value: " << expected << "\n calculated: " << calculated - << "\n error: " << error); - } + Real v0 = 0.09; + Real kappa = 1.15; + Real theta = 0.0348; + Real sigma = 0.39; + Real rho = -0.64; - // Compare greeks to numerical greeks - Real dS = 0.001; - Real delta = option.delta(); - Real gamma = option.gamma(); + ext::shared_ptr hestonProcess(new + HestonProcess(Handle(rTS), Handle(qTS), + spot, v0, kappa, theta, sigma, rho)); - ext::shared_ptr spotUp(new SimpleQuote(l.underlying+dS)); - ext::shared_ptr spotDown(new SimpleQuote(l.underlying-dS)); + ext::shared_ptr analyticEngine(new + AnalyticDiscreteGeometricAveragePriceAsianHestonEngine(hestonProcess)); - ext::shared_ptr stochProcessUp(new BlackScholesMertonProcess( - Handle(spotUp), Handle(qTS), Handle(rTS), - Handle(volTS))); + ext::shared_ptr mcEngine = + MakeMCDiscreteGeometricAPHestonEngine(hestonProcess) + .withSamples(8191) + .withSeed(43); - ext::shared_ptr stochProcessDown(new BlackScholesMertonProcess( - Handle(spotDown), Handle(qTS), Handle(rTS), - Handle(volTS))); + Option::Type type(Option::Call); + Average::Type averageType = Average::Geometric; - ext::shared_ptr engineUp( - new TurnbullWakemanAsianEngine(stochProcessUp)); + for (Size strike_index = 0; strike_index < LENGTH(strikes); strike_index++) { - ext::shared_ptr engineDown( - new TurnbullWakemanAsianEngine(stochProcessDown)); + for (Size day_index = 0; day_index < LENGTH(days); day_index++) { - option.setPricingEngine(engineUp); - Real calculatedUp = option.NPV(); + for (Size k=0; k<2; k++) { - option.setPricingEngine(engineDown); - Real calculatedDown = option.NPV(); + Size futureFixings = int(std::floor(days[day_index] / 30.0)); + std::vector fixingDates(futureFixings); + Date expiryDate = today + days[day_index] * Days; - Real deltaBump = (calculatedUp - calculatedDown) / (2 * dS); - Real gammaBump = (calculatedUp + calculatedDown - 2*calculated) / (dS * dS); + for (int i=futureFixings-1; i>=0; i--) { + fixingDates[i] = expiryDate - i * 30; + } - tolerance = 1.0e-6; - Real deltaError = std::fabs(deltaBump - delta); - if (deltaError > tolerance) { - BOOST_ERROR( - "Analytical delta failed to match bump delta:" - << "\n type: " << l.type << "\n spot: " << l.underlying - << "\n strike: " << l.strike << "\n dividend yield: " - << l.b + l.riskFreeRate << "\n risk-free rate: " << l.riskFreeRate - << "\n volatility: " << l.volatility << "\n slope: " << l.slope - << "\n reference date: " << today << "\n expiry: " << l.expiry - << "\n analytic delta: " << delta << "\n bump delta: " << deltaBump - << "\n error: " << deltaError); - } + ext::shared_ptr europeanExercise(new EuropeanExercise(expiryDate)); + ext::shared_ptr payoff(new PlainVanillaPayoff(type, strikes[strike_index])); - Real gammaError = std::fabs(gammaBump - gamma); - if (gammaError > tolerance) { - BOOST_ERROR( - "Analytical gamma failed to match bump gamma:" - << "\n type: " << l.type << "\n spot: " << l.underlying - << "\n strike: " << l.strike << "\n dividend yield: " - << l.b + l.riskFreeRate << "\n risk-free rate: " << l.riskFreeRate - << "\n volatility: " << l.volatility << "\n slope: " << l.slope - << "\n reference date: " << today << "\n expiry: " << l.expiry - << "\n analytic gamma: " << gamma << "\n bump gamma: " << gammaBump - << "\n error: " << gammaError); - } - } -} + Real runningAccumulator = 1.0; + Size pastFixingsCount = 0; + if (k == 0) { + runningAccumulator = 100.0; + pastFixingsCount = 1; + } else { + runningAccumulator = 95.0 * 100.0 * 105.0; + pastFixingsCount = 3; + } -test_suite* AsianOptionTest::suite(SpeedLevel speed) { - auto* suite = BOOST_TEST_SUITE("Asian option tests"); - - suite->add(QUANTLIB_TEST_CASE(&AsianOptionTest::testAnalyticContinuousGeometricAveragePrice)); - suite->add(QUANTLIB_TEST_CASE(&AsianOptionTest::testAnalyticContinuousGeometricAveragePriceGreeks)); - suite->add(QUANTLIB_TEST_CASE(&AsianOptionTest::testAnalyticDiscreteGeometricAveragePrice)); - suite->add(QUANTLIB_TEST_CASE(&AsianOptionTest::testAnalyticDiscreteGeometricAverageStrike)); - suite->add(QUANTLIB_TEST_CASE(&AsianOptionTest::testMCDiscreteGeometricAveragePrice)); - suite->add(QUANTLIB_TEST_CASE(&AsianOptionTest::testMCDiscreteArithmeticAverageStrike)); - suite->add(QUANTLIB_TEST_CASE(&AsianOptionTest::testAnalyticDiscreteGeometricAveragePriceGreeks)); - suite->add(QUANTLIB_TEST_CASE(&AsianOptionTest::testPastFixings)); - suite->add(QUANTLIB_TEST_CASE(&AsianOptionTest::testAllFixingsInThePast)); - suite->add(QUANTLIB_TEST_CASE(&AsianOptionTest::testTurnbullWakemanAsianEngine)); - suite->add(QUANTLIB_TEST_CASE(&AsianOptionTest::testPastFixingsModelDependency)); - - if (speed <= Fast) { - suite->add(QUANTLIB_TEST_CASE(&AsianOptionTest::testMCDiscreteArithmeticAveragePrice)); - suite->add(QUANTLIB_TEST_CASE(&AsianOptionTest::testMCDiscreteGeometricAveragePriceHeston)); - } + DiscreteAveragingAsianOption option(averageType, runningAccumulator, pastFixingsCount, + fixingDates, payoff, europeanExercise); - if (speed == Slow) { - suite->add(QUANTLIB_TEST_CASE(&AsianOptionTest::testMCDiscreteArithmeticAveragePriceHeston)); - } + option.setPricingEngine(analyticEngine); + Real analyticPrice = option.NPV(); - return suite; -} + option.setPricingEngine(mcEngine); + Real mcPrice = option.NPV(); -test_suite* AsianOptionTest::experimental(SpeedLevel speed) { - auto* suite = BOOST_TEST_SUITE("Asian option experimental tests"); - suite->add(QUANTLIB_TEST_CASE(&AsianOptionTest::testLevyEngine)); - suite->add(QUANTLIB_TEST_CASE(&AsianOptionTest::testVecerEngine)); - suite->add(QUANTLIB_TEST_CASE(&AsianOptionTest::testAnalyticContinuousGeometricAveragePriceHeston)); - suite->add(QUANTLIB_TEST_CASE(&AsianOptionTest::testAnalyticDiscreteGeometricAveragePriceHeston)); - suite->add(QUANTLIB_TEST_CASE(&AsianOptionTest::testDiscreteGeometricAveragePriceHestonPastFixings)); + auto tolerance = tol[strike_index][day_index][k]; - return suite; + if (std::fabs(analyticPrice-mcPrice) > tolerance) { + REPORT_FAILURE("value", averageType, runningAccumulator, pastFixingsCount, + std::vector(), payoff, europeanExercise, spot->value(), + qRate->value(), rRate->value(), today, + std::sqrt(v0), analyticPrice, mcPrice, tolerance); + } + } + } + } } +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/test-suite/asianoptions.hpp b/test-suite/asianoptions.hpp deleted file mode 100644 index 25337aecf98..00000000000 --- a/test-suite/asianoptions.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2003, 2004 Ferdinando Ametrano - Copyright (C) 2008, 2017 StatPro Italia srl - Copyright (C) 2009 Master IMAFA - Polytech'Nice Sophia - Université de Nice Sophia Antipolis - Copyright (C) 2021 Skandinaviska Enskilda Banken AB (publ) - - This file is part of QuantLib, a free-software/open-source library - for financial quantitative analysts and developers - http://quantlib.org/ - - QuantLib is free software: you can redistribute it and/or modify it - under the terms of the QuantLib license. You should have received a - copy of the license along with this program; if not, please email - . The license is also available online at - . - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the license for more details. -*/ - -#ifndef quantlib_test_asian_options_hpp -#define quantlib_test_asian_options_hpp - -#include -#include "speedlevel.hpp" - -/* remember to document new and/or updated tests in the Doxygen - comment block of the corresponding class */ - -class AsianOptionTest { - public: - static void testAnalyticContinuousGeometricAveragePrice(); - static void testAnalyticContinuousGeometricAveragePriceGreeks(); - static void testAnalyticContinuousGeometricAveragePriceHeston(); - static void testAnalyticDiscreteGeometricAveragePrice(); - static void testAnalyticDiscreteGeometricAveragePriceHeston(); - static void testAnalyticDiscreteGeometricAverageStrike(); - static void testDiscreteGeometricAveragePriceHestonPastFixings(); - static void testMCDiscreteGeometricAveragePrice(); - static void testMCDiscreteGeometricAveragePriceHeston(); - static void testMCDiscreteArithmeticAveragePrice(); - static void testMCDiscreteArithmeticAveragePriceHeston(); - static void testMCDiscreteArithmeticAverageStrike(); - static void testAnalyticDiscreteGeometricAveragePriceGreeks(); - static void testPastFixings(); - static void testPastFixingsModelDependency(); - static void testAllFixingsInThePast(); - static void testLevyEngine(); - static void testVecerEngine(); - static void testTurnbullWakemanAsianEngine(); - static boost::unit_test_framework::test_suite* suite(SpeedLevel); - static boost::unit_test_framework::test_suite* experimental(SpeedLevel); -}; - - -#endif diff --git a/test-suite/assetswap.cpp b/test-suite/assetswap.cpp index 613d7ff987f..a4c0a9dcb9d 100644 --- a/test-suite/assetswap.cpp +++ b/test-suite/assetswap.cpp @@ -17,7 +17,7 @@ FOR A PARTICULAR PURPOSE. See the license for more details. */ -#include "assetswap.hpp" +#include "toplevelfixture.hpp" #include "utilities.hpp" #include #include @@ -112,7 +112,12 @@ namespace asset_swap_test { } -void AssetSwapTest::testConsistency() { + +BOOST_FIXTURE_TEST_SUITE(QuantLibTest, TopLevelFixture) // fails with QL_USE_INDEXED_COUPON + +BOOST_AUTO_TEST_SUITE(AssetSwapTest) + +BOOST_AUTO_TEST_CASE(testConsistency) { BOOST_TEST_MESSAGE( "Testing consistency between fair price and fair spread..."); @@ -499,7 +504,7 @@ void AssetSwapTest::testConsistency() { } -void AssetSwapTest::testImpliedValue() { +BOOST_AUTO_TEST_CASE(testImpliedValue) { BOOST_TEST_MESSAGE("Testing implied bond value against asset-swap fair" " price with null spread..."); @@ -867,7 +872,7 @@ void AssetSwapTest::testImpliedValue() { } -void AssetSwapTest::testMarketASWSpread() { +BOOST_AUTO_TEST_CASE(testMarketASWSpread) { BOOST_TEST_MESSAGE("Testing relationship between market asset swap" " and par asset swap..."); @@ -1307,7 +1312,7 @@ void AssetSwapTest::testMarketASWSpread() { } -void AssetSwapTest::testZSpread() { +BOOST_AUTO_TEST_CASE(testZSpread) { BOOST_TEST_MESSAGE("Testing clean and dirty price with null Z-spread " "against theoretical prices..."); @@ -1629,7 +1634,7 @@ void AssetSwapTest::testZSpread() { } -void AssetSwapTest::testGenericBondImplied() { +BOOST_AUTO_TEST_CASE(testGenericBondImplied) { BOOST_TEST_MESSAGE("Testing implied generic-bond value against" " asset-swap fair price with null spread..."); @@ -2019,8 +2024,7 @@ void AssetSwapTest::testGenericBondImplied() { } } - -void AssetSwapTest::testMASWWithGenericBond() { +BOOST_AUTO_TEST_CASE(testMASWWithGenericBond) { BOOST_TEST_MESSAGE("Testing market asset swap against par asset swap " "with generic bond..."); @@ -2495,7 +2499,7 @@ void AssetSwapTest::testMASWWithGenericBond() { } -void AssetSwapTest::testZSpreadWithGenericBond() { +BOOST_AUTO_TEST_CASE(testZSpreadWithGenericBond) { BOOST_TEST_MESSAGE("Testing clean and dirty price with null Z-spread " "against theoretical prices..."); @@ -2861,8 +2865,7 @@ void AssetSwapTest::testZSpreadWithGenericBond() { } } - -void AssetSwapTest::testSpecializedBondVsGenericBond() { +BOOST_AUTO_TEST_CASE(testSpecializedBondVsGenericBond) { BOOST_TEST_MESSAGE("Testing clean and dirty prices for specialized bond" " against equivalent generic bond..."); @@ -3423,7 +3426,7 @@ void AssetSwapTest::testSpecializedBondVsGenericBond() { } -void AssetSwapTest::testSpecializedBondVsGenericBondUsingAsw() { +BOOST_AUTO_TEST_CASE(testSpecializedBondVsGenericBondUsingAsw) { BOOST_TEST_MESSAGE("Testing asset-swap prices and spreads for specialized" " bond against equivalent generic bond..."); @@ -4252,20 +4255,6 @@ void AssetSwapTest::testSpecializedBondVsGenericBondUsingAsw() { } } +BOOST_AUTO_TEST_SUITE_END() -test_suite* AssetSwapTest::suite() { - auto* suite = BOOST_TEST_SUITE("AssetSwap tests"); - suite->add(QUANTLIB_TEST_CASE(&AssetSwapTest::testConsistency)); - suite->add(QUANTLIB_TEST_CASE(&AssetSwapTest::testImpliedValue)); - suite->add(QUANTLIB_TEST_CASE(&AssetSwapTest::testMarketASWSpread)); - suite->add(QUANTLIB_TEST_CASE(&AssetSwapTest::testZSpread)); - suite->add(QUANTLIB_TEST_CASE(&AssetSwapTest::testGenericBondImplied)); - suite->add(QUANTLIB_TEST_CASE(&AssetSwapTest::testMASWWithGenericBond)); - suite->add(QUANTLIB_TEST_CASE(&AssetSwapTest::testZSpreadWithGenericBond)); - suite->add(QUANTLIB_TEST_CASE( - &AssetSwapTest::testSpecializedBondVsGenericBond)); - suite->add(QUANTLIB_TEST_CASE( - &AssetSwapTest::testSpecializedBondVsGenericBondUsingAsw)); - - return suite; -} +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/test-suite/assetswap.hpp b/test-suite/assetswap.hpp deleted file mode 100644 index cb4b27f3df1..00000000000 --- a/test-suite/assetswap.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2007 Chiara Fornarola - - This file is part of QuantLib, a free-software/open-source library - for financial quantitative analysts and developers - http://quantlib.org/ - - QuantLib is free software: you can redistribute it and/or modify it - under the terms of the QuantLib license. You should have received a - copy of the license along with this program; if not, please email - . The license is also available online at - . - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the license for more details. -*/ - -#ifndef quantlib_test_assetswap_hpp -#define quantlib_test_assetswap_hpp - -#include - -/* remember to document new and/or updated tests in the Doxygen - comment block of the corresponding class */ - -class AssetSwapTest { - public: - static void testConsistency(); - static void testImpliedValue(); - static void testMarketASWSpread(); - static void testZSpread(); - static void testGenericBondImplied(); - static void testMASWWithGenericBond(); - static void testZSpreadWithGenericBond(); - static void testSpecializedBondVsGenericBond(); - static void testSpecializedBondVsGenericBondUsingAsw(); - static boost::unit_test_framework::test_suite* suite(); -}; - - -#endif diff --git a/test-suite/autocovariances.cpp b/test-suite/autocovariances.cpp index c14d7b51e7a..e7e5d63e76b 100644 --- a/test-suite/autocovariances.cpp +++ b/test-suite/autocovariances.cpp @@ -17,14 +17,18 @@ FOR A PARTICULAR PURPOSE. See the license for more details. */ -#include "autocovariances.hpp" +#include "toplevelfixture.hpp" #include "utilities.hpp" #include using namespace QuantLib; using namespace boost::unit_test_framework; -void AutocovariancesTest::testConvolutions() { +BOOST_FIXTURE_TEST_SUITE(QuantLibTest, TopLevelFixture) + +BOOST_AUTO_TEST_SUITE(AutocovariancesTest) + +BOOST_AUTO_TEST_CASE(testConvolutions) { BOOST_TEST_MESSAGE("Testing convolutions..."); Array x(10, 1, 1); Array conv(6); @@ -38,7 +42,7 @@ void AutocovariancesTest::testConvolutions() { << " expected: " << Array(expected, expected+6)); } -void AutocovariancesTest::testAutoCovariances() { +BOOST_AUTO_TEST_CASE(testAutoCovariances) { BOOST_TEST_MESSAGE("Testing auto-covariances..."); Array x(10, 1, 1); Array acovf(6); @@ -57,7 +61,7 @@ void AutocovariancesTest::testAutoCovariances() { << " expected: " << Array(expected, expected+6)); } -void AutocovariancesTest::testAutoCorrelations() { +BOOST_AUTO_TEST_CASE(testAutoCorrelations) { BOOST_TEST_MESSAGE("Testing auto-correlations..."); Array x(10, 1, 1); Array acorf(6); @@ -83,11 +87,6 @@ void AutocovariancesTest::testAutoCorrelations() { << " expected: " << Array(10, -4.5, 1)); } -test_suite* AutocovariancesTest::suite() { - auto* suite = BOOST_TEST_SUITE("auto-covariance tests"); - suite->add(QUANTLIB_TEST_CASE(&AutocovariancesTest::testConvolutions)); - suite->add(QUANTLIB_TEST_CASE(&AutocovariancesTest::testAutoCovariances)); - suite->add(QUANTLIB_TEST_CASE(&AutocovariancesTest::testAutoCorrelations)); - return suite; -} +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/test-suite/autocovariances.hpp b/test-suite/autocovariances.hpp deleted file mode 100644 index a8c839d8532..00000000000 --- a/test-suite/autocovariances.hpp +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2010 Liquidnet Holdings, Inc. - - This file is part of QuantLib, a free-software/open-source library - for financial quantitative analysts and developers - http://quantlib.org/ - - QuantLib is free software: you can redistribute it and/or modify it - under the terms of the QuantLib license. You should have received a - copy of the license along with this program; if not, please email - . The license is also available online at - . - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the license for more details. -*/ - -#ifndef quantlib_test_autocovariances_hpp -#define quantlib_test_autocovariances_hpp - -#include - -/* remember to document new and/or updated tests in the Doxygen - comment block of the corresponding class */ - -class AutocovariancesTest { - public: - static void testConvolutions(); - static void testAutoCovariances(); - static void testAutoCorrelations(); - static boost::unit_test_framework::test_suite* suite(); -}; - - -#endif diff --git a/test-suite/barrieroption.cpp b/test-suite/barrieroption.cpp index c616fa52247..f73885a281f 100644 --- a/test-suite/barrieroption.cpp +++ b/test-suite/barrieroption.cpp @@ -21,7 +21,7 @@ FOR A PARTICULAR PURPOSE. See the license for more details. */ -#include "barrieroption.hpp" +#include "toplevelfixture.hpp" #include "utilities.hpp" #include #include @@ -173,7 +173,11 @@ namespace barrier_option_test { BOOST_FAIL("exception expected"); \ } \ -void BarrierOptionTest::testParity() { +BOOST_FIXTURE_TEST_SUITE(QuantLibTest, TopLevelFixture) + +BOOST_AUTO_TEST_SUITE(BarrierOptionTest) + +BOOST_AUTO_TEST_CASE(testParity) { BOOST_TEST_MESSAGE("Testing that knock-in plus knock-out barrier options " "replicate a European option..."); @@ -247,8 +251,7 @@ void BarrierOptionTest::testParity() { << "\n error: " << error); } } - -void BarrierOptionTest::testHaugValues() { +BOOST_AUTO_TEST_CASE(testHaugValues) { BOOST_TEST_MESSAGE("Testing barrier options against Haug's values..."); @@ -515,7 +518,7 @@ void BarrierOptionTest::testHaugValues() { } } -void BarrierOptionTest::testBabsiriValues() { +BOOST_AUTO_TEST_CASE(testBabsiriValues) { BOOST_TEST_MESSAGE("Testing barrier options against Babsiri's values..."); @@ -616,7 +619,7 @@ void BarrierOptionTest::testBabsiriValues() { } } -void BarrierOptionTest::testBeagleholeValues() { +BOOST_AUTO_TEST_CASE(testBeagleholeValues) { BOOST_TEST_MESSAGE("Testing barrier options against Beaglehole's values..."); @@ -708,100 +711,7 @@ void BarrierOptionTest::testBeagleholeValues() { } } -void BarrierOptionTest::testPerturbative() { - BOOST_TEST_MESSAGE("Testing perturbative engine for barrier options..."); - - Real S = 100.0; - Real rebate = 0.0; - Rate r = 0.03; - Rate q = 0.02; - - DayCounter dc = Actual360(); - Date today = Date::todaysDate(); - - ext::shared_ptr underlying = - ext::make_shared(S); - ext::shared_ptr qTS = flatRate(today, q, dc); - ext::shared_ptr rTS = flatRate(today, r, dc); - - std::vector dates(2); - std::vector vols(2); - - dates[0] = today + 90; vols[0] = 0.105; - dates[1] = today + 180; vols[1] = 0.11; - - ext::shared_ptr volTS = - ext::make_shared(today, dates, vols, dc); - - ext::shared_ptr stochProcess = - ext::make_shared( - Handle(underlying), - Handle(qTS), - Handle(rTS), - Handle(volTS)); - - Real strike = 101.0; - Real barrier = 101.0; - Date exDate = today+180; - - ext::shared_ptr exercise = - ext::make_shared(exDate); - ext::shared_ptr payoff = - ext::make_shared(Option::Put, strike); - - BarrierOption option(Barrier::UpOut, barrier, rebate, payoff, exercise); - - Natural order = 0; - bool zeroGamma = false; - ext::shared_ptr engine = - ext::make_shared(stochProcess, - order, zeroGamma); - - option.setPricingEngine(engine); - - Real calculated = option.NPV(); - Real expected = 0.897365; - Real tolerance = 1.0e-6; - if (std::fabs(calculated-expected) > tolerance) { - BOOST_ERROR("Failed to reproduce expected value" - << "\n calculated: " << std::setprecision(8) << calculated - << "\n expected: " << std::setprecision(8) << expected); - } - - order = 1; - engine = ext::make_shared(stochProcess, - order, - zeroGamma); - - option.setPricingEngine(engine); - - calculated = option.NPV(); - expected = 0.894374; - if (std::fabs(calculated-expected) > tolerance) { - BOOST_ERROR("Failed to reproduce expected value" - << "\n calculated: " << std::setprecision(8) << calculated - << "\n expected: " << std::setprecision(8) << expected); - } - - /* Too slow, skip - order = 2; - engine = ext::make_shared(stochProcess, - order, - zeroGamma); - - option.setPricingEngine(engine); - - calculated = option.NPV(); - expected = 0.8943769; - if (std::fabs(calculated-expected) > tolerance) { - BOOST_ERROR("Failed to reproduce expected value" - << "\n calculated: " << std::setprecision(8) << calculated - << "\n expected: " << std::setprecision(8) << expected); - } - */ -} - -void BarrierOptionTest::testLocalVolAndHestonComparison() { +BOOST_AUTO_TEST_CASE(testLocalVolAndHestonComparison) { BOOST_TEST_MESSAGE("Testing local volatility and Heston FD engines " "for barrier options..."); @@ -936,316 +846,92 @@ void BarrierOptionTest::testLocalVolAndHestonComparison() { } } +BOOST_AUTO_TEST_CASE(testOldDividendBarrierOption) { + BOOST_TEST_MESSAGE("Testing old-style barrier option pricing with discrete dividends..."); -void BarrierOptionTest::testVannaVolgaSimpleBarrierValues() { - BOOST_TEST_MESSAGE("Testing barrier FX options against Vanna/Volga values..."); + const DayCounter dc = Actual365Fixed(); - using namespace barrier_option_test; + const Date today(11, February, 2018); + const Date maturity = today + Period(1, Years); + Settings::instance().evaluationDate() = today; - BarrierFxOptionData values[] = { + const Real spot = 100.0; + const Real strike = 105.0; + const Real rebate = 5.0; - //barrierType,barrier,rebate,type,strike,s,q,r,t,vol25Put,volAtm,vol25Call,vol, result, tol - { Barrier::UpOut,1.5,0, Option::Call,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0.148127, 1.0e-4}, - { Barrier::UpOut,1.5,0, Option::Call,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0.075943, 1.0e-4}, - { Barrier::UpOut,1.5,0, Option::Call,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0.0274771, 1.0e-4}, - { Barrier::UpOut,1.5,0, Option::Call,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0.00573, 1.0e-4}, - { Barrier::UpOut,1.5,0, Option::Call,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0.00012, 1.0e-4}, + const Real barriers[] = { 80.0, 120.0 }; + const Barrier::Type barrierTypes[] = { Barrier::DownOut, Barrier::UpOut }; - { Barrier::UpOut,1.5,0, Option::Put,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0.00697606, 1.0e-4}, - { Barrier::UpOut,1.5,0, Option::Put,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0.020078, 1.0e-4}, - { Barrier::UpOut,1.5,0, Option::Put,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0.0489395, 1.0e-4}, - { Barrier::UpOut,1.5,0, Option::Put,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0.0969877, 1.0e-4}, - { Barrier::UpOut,1.5,0, Option::Put,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0.157, 1.0e-4}, + const Rate r = 0.05; + const Rate q = 0.0; + const Volatility v = 0.02; - { Barrier::UpIn,1.5,0, Option::Call,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0.0322202, 1.0e-4}, - { Barrier::UpIn,1.5,0, Option::Call,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0.0241491, 1.0e-4}, - { Barrier::UpIn,1.5,0, Option::Call,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0.0164275, 1.0e-4}, - { Barrier::UpIn,1.5,0, Option::Call,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0.01, 1.0e-4}, - { Barrier::UpIn,1.5,0, Option::Call,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0.00489, 1.0e-4}, + const Handle s0(ext::make_shared(spot)); + const Handle qTS(flatRate(today, q, dc)); + const Handle rTS(flatRate(today, r, dc)); + const Handle volTS(flatVol(today, v, dc)); - { Barrier::UpIn,1.5,0, Option::Put,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0.000560713, 1.0e-4}, - { Barrier::UpIn,1.5,0, Option::Put,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0.000546804, 1.0e-4}, - { Barrier::UpIn,1.5,0, Option::Put,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0.000130649, 1.0e-4}, - { Barrier::UpIn,1.5,0, Option::Put,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0.000300828, 1.0e-4}, - { Barrier::UpIn,1.5,0, Option::Put,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0.00135, 1.0e-4}, + const ext::shared_ptr bsProcess = + ext::make_shared(s0, qTS, rTS, volTS); - { Barrier::DownOut,1.1,0, Option::Call,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0.17746, 1.0e-4}, - { Barrier::DownOut,1.1,0, Option::Call,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0.0994142, 1.0e-4}, - { Barrier::DownOut,1.1,0, Option::Call,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0.0439, 1.0e-4}, - { Barrier::DownOut,1.1,0, Option::Call,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0.01574, 1.0e-4}, - { Barrier::DownOut,1.1,0, Option::Call,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0.00501, 1.0e-4}, + const ext::shared_ptr douglas = + ext::make_shared( + bsProcess, 100, 100, 0, FdmSchemeDesc::Douglas()); - { Barrier::DownOut,1.3,0, Option::Call,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0.00612, 1.0e-4}, - { Barrier::DownOut,1.3,0, Option::Call,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0.00426, 1.0e-4}, - { Barrier::DownOut,1.3,0, Option::Call,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0.00257, 1.0e-4}, - { Barrier::DownOut,1.3,0, Option::Call,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0.00122, 1.0e-4}, - { Barrier::DownOut,1.3,0, Option::Call,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0.00045, 1.0e-4}, + const ext::shared_ptr crankNicolson = + ext::make_shared( + bsProcess, 100, 100, 0, FdmSchemeDesc::CrankNicolson()); - { Barrier::DownOut,1.1,0, Option::Put,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0.00022, 1.0e-4}, - { Barrier::DownOut,1.1,0, Option::Put,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0.00284, 1.0e-4}, - { Barrier::DownOut,1.1,0, Option::Put,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0.02032, 1.0e-4}, - { Barrier::DownOut,1.1,0, Option::Put,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0.058235, 1.0e-4}, - { Barrier::DownOut,1.1,0, Option::Put,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0.109432, 1.0e-4}, + const ext::shared_ptr craigSnyed = + ext::make_shared( + bsProcess, 100, 100, 0, FdmSchemeDesc::CraigSneyd()); - { Barrier::DownOut,1.3,0, Option::Put,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0, 1.0e-4}, - { Barrier::DownOut,1.3,0, Option::Put,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0, 1.0e-4}, - { Barrier::DownOut,1.3,0, Option::Put,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0, 1.0e-4}, - { Barrier::DownOut,1.3,0, Option::Put,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0.00017, 1.0e-4}, - { Barrier::DownOut,1.3,0, Option::Put,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0.00083, 1.0e-4}, + const ext::shared_ptr hundsdorfer = + ext::make_shared( + bsProcess, 100, 100, 0, FdmSchemeDesc::Hundsdorfer()); - { Barrier::DownIn,1.1,0, Option::Call,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0.00289, 1.0e-4}, - { Barrier::DownIn,1.1,0, Option::Call,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0.00067784, 1.0e-4}, - { Barrier::DownIn,1.1,0, Option::Call,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0, 1.0e-4}, - { Barrier::DownIn,1.1,0, Option::Call,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0, 1.0e-4}, - { Barrier::DownIn,1.1,0, Option::Call,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0, 1.0e-4}, + const ext::shared_ptr mol = + ext::make_shared( + bsProcess, 100, 100, 0, FdmSchemeDesc::MethodOfLines()); - { Barrier::DownIn,1.3,0, Option::Call,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0.17423, 1.0e-4}, - { Barrier::DownIn,1.3,0, Option::Call,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0.09584, 1.0e-4}, - { Barrier::DownIn,1.3,0, Option::Call,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0.04133, 1.0e-4}, - { Barrier::DownIn,1.3,0, Option::Call,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0.01452, 1.0e-4}, - { Barrier::DownIn,1.3,0, Option::Call,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0.00456, 1.0e-4}, + const ext::shared_ptr trPDF2 = + ext::make_shared( + bsProcess, 100, 100, 0, FdmSchemeDesc::TrBDF2()); - { Barrier::DownIn,1.1,0, Option::Put,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0.00732, 1.0e-4}, - { Barrier::DownIn,1.1,0, Option::Put,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0.01778, 1.0e-4}, - { Barrier::DownIn,1.1,0, Option::Put,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0.02875, 1.0e-4}, - { Barrier::DownIn,1.1,0, Option::Put,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0.0390535, 1.0e-4}, - { Barrier::DownIn,1.1,0, Option::Put,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0.0489236, 1.0e-4}, + const ext::shared_ptr hestonEngine = + ext::make_shared( + ext::make_shared( + ext::make_shared( + rTS, qTS, s0, v*v, 1.0, v*v, 0.005, 0.0)), 50, 101, 3); - { Barrier::DownIn,1.3,0, Option::Put,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0.00753, 1.0e-4}, - { Barrier::DownIn,1.3,0, Option::Put,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0.02062, 1.0e-4}, - { Barrier::DownIn,1.3,0, Option::Put,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0.04907, 1.0e-4}, - { Barrier::DownIn,1.3,0, Option::Put,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0.09711, 1.0e-4}, - { Barrier::DownIn,1.3,0, Option::Put,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0.15752, 1.0e-4}, + const ext::shared_ptr engines[] = { + douglas, crankNicolson, + trPDF2, craigSnyed, hundsdorfer, mol, hestonEngine + }; - { Barrier::UpOut,1.6,0, Option::Call,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511,0.20493, 1.0e-4}, - { Barrier::UpOut,1.6,0, Option::Call,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089,0.105577, 1.0e-4}, - { Barrier::UpOut,1.6,0, Option::Call,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444,0.0358872, 1.0e-4}, - { Barrier::UpOut,1.6,0, Option::Call,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197,0.00634958, 1.0e-4}, - { Barrier::UpOut,1.6,0, Option::Call,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261,0, 1.0e-4}, + const ext::shared_ptr payoff = + ext::make_shared(Option::Put, strike); - { Barrier::UpOut,1.6,0, Option::Put,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511,0.0108218, 1.0e-4}, - { Barrier::UpOut,1.6,0, Option::Put,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089,0.0313339, 1.0e-4}, - { Barrier::UpOut,1.6,0, Option::Put,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444,0.0751237, 1.0e-4}, - { Barrier::UpOut,1.6,0, Option::Put,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197,0.153407, 1.0e-4}, - { Barrier::UpOut,1.6,0, Option::Put,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261,0.253767, 1.0e-4}, + const ext::shared_ptr exercise = + ext::make_shared(maturity); - { Barrier::UpIn,1.6,0, Option::Call,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511,0.05402, 1.0e-4}, - { Barrier::UpIn,1.6,0, Option::Call,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089,0.0410069, 1.0e-4}, - { Barrier::UpIn,1.6,0, Option::Call,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444,0.0279562, 1.0e-4}, - { Barrier::UpIn,1.6,0, Option::Call,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197,0.0173055, 1.0e-4}, - { Barrier::UpIn,1.6,0, Option::Call,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261,0.00764, 1.0e-4}, + const Real divAmount = 30; + const Date divDate = today + Period(6, Months); - { Barrier::UpIn,1.6,0, Option::Put,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511,0.000962737, 1.0e-4}, - { Barrier::UpIn,1.6,0, Option::Put,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089,0.00102637, 1.0e-4}, - { Barrier::UpIn,1.6,0, Option::Put,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444,0.000419834, 1.0e-4}, - { Barrier::UpIn,1.6,0, Option::Put,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197,0.00159277, 1.0e-4}, - { Barrier::UpIn,1.6,0, Option::Put,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261,0.00473629, 1.0e-4}, + const Real expected[] = { + rTS->discount(divDate)*rebate, + (*payoff)( + (spot - divAmount*rTS->discount(divDate))/rTS->discount(maturity)) + *rTS->discount(maturity) + }; - { Barrier::DownOut,1,0, Option::Call,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511,0.255098, 1.0e-4}, - { Barrier::DownOut,1,0, Option::Call,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089,0.145701, 1.0e-4}, - { Barrier::DownOut,1,0, Option::Call,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444,0.06384, 1.0e-4}, - { Barrier::DownOut,1,0, Option::Call,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197,0.02366, 1.0e-4}, - { Barrier::DownOut,1,0, Option::Call,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261,0.00764, 1.0e-4}, + const Real relTol = 1e-4; + for (Size i=0; i < LENGTH(barriers); ++i) { + for (const auto& engine : engines) { + const Real barrier = barriers[i]; + const Barrier::Type barrierType = barrierTypes[i]; - { Barrier::DownOut,1.3,0, Option::Call,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511,0.00592, 1.0e-4}, - { Barrier::DownOut,1.3,0, Option::Call,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089,0.00421, 1.0e-4}, - { Barrier::DownOut,1.3,0, Option::Call,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444,0.00256, 1.0e-4}, - { Barrier::DownOut,1.3,0, Option::Call,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197,0.0012, 1.0e-4}, - { Barrier::DownOut,1.3,0, Option::Call,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261,0.0004, 1.0e-4}, - - { Barrier::DownOut,1,0, Option::Put,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511,0, 1.0e-4}, - { Barrier::DownOut,1,0, Option::Put,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089,0.00280549, 1.0e-4}, - { Barrier::DownOut,1,0, Option::Put,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444,0.0279945, 1.0e-4}, - { Barrier::DownOut,1,0, Option::Put,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197,0.0896352, 1.0e-4}, - { Barrier::DownOut,1,0, Option::Put,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261,0.175182, 1.0e-4}, - - { Barrier::DownOut,1.3,0, Option::Put,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511, 0.00000, 1.0e-4}, - { Barrier::DownOut,1.3,0, Option::Put,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089, 0.00000, 1.0e-4}, - { Barrier::DownOut,1.3,0, Option::Put,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444, 0.00000, 1.0e-4}, - { Barrier::DownOut,1.3,0, Option::Put,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197,0.0002, 1.0e-4}, - { Barrier::DownOut,1.3,0, Option::Put,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261,0.00096, 1.0e-4}, - - { Barrier::DownIn,1,0, Option::Call,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511,0.00384783, 1.0e-4}, - { Barrier::DownIn,1,0, Option::Call,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089,0.000883232, 1.0e-4}, - { Barrier::DownIn,1,0, Option::Call,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444,0, 1.0e-4}, - { Barrier::DownIn,1,0, Option::Call,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197, 0.00000, 1.0e-4}, - { Barrier::DownIn,1,0, Option::Call,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261, 0.00000, 1.0e-4}, - - { Barrier::DownIn,1.3,0, Option::Call,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511,0.25302, 1.0e-4}, - { Barrier::DownIn,1.3,0, Option::Call,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089,0.14238, 1.0e-4}, - { Barrier::DownIn,1.3,0, Option::Call,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444,0.06128, 1.0e-4}, - { Barrier::DownIn,1.3,0, Option::Call,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197,0.02245, 1.0e-4}, - { Barrier::DownIn,1.3,0, Option::Call,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261,0.00725, 1.0e-4}, - - { Barrier::DownIn,1,0, Option::Put,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511,0.01178, 1.0e-4}, - { Barrier::DownIn,1,0, Option::Put,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089,0.0295548, 1.0e-4}, - { Barrier::DownIn,1,0, Option::Put,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444,0.047549, 1.0e-4}, - { Barrier::DownIn,1,0, Option::Put,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197,0.0653642, 1.0e-4}, - { Barrier::DownIn,1,0, Option::Put,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261,0.0833221, 1.0e-4}, - - { Barrier::DownIn,1.3,0, Option::Put,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511,0.01178, 1.0e-4}, - { Barrier::DownIn,1.3,0, Option::Put,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089,0.03236, 1.0e-4}, - { Barrier::DownIn,1.3,0, Option::Put,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444,0.07554, 1.0e-4}, - { Barrier::DownIn,1.3,0, Option::Put,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197,0.15479, 1.0e-4}, - { Barrier::DownIn,1.3,0, Option::Put,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261,0.25754, 1.0e-4}, - - }; - - DayCounter dc = Actual365Fixed(); - Date today(5, March, 2013); - Settings::instance().evaluationDate() = today; - - ext::shared_ptr spot = ext::make_shared(0.0); - ext::shared_ptr qRate = ext::make_shared(0.0); - ext::shared_ptr qTS = flatRate(today, qRate, dc); - ext::shared_ptr rRate = ext::make_shared(0.0); - ext::shared_ptr rTS = flatRate(today, rRate, dc); - ext::shared_ptr vol25Put = ext::make_shared(0.0); - ext::shared_ptr volAtm = ext::make_shared(0.0); - ext::shared_ptr vol25Call = ext::make_shared(0.0); - - for (auto& value : values) { - - spot->setValue(value.s); - qRate->setValue(value.q); - rRate->setValue(value.r); - vol25Put->setValue(value.vol25Put); - volAtm->setValue(value.volAtm); - vol25Call->setValue(value.vol25Call); - - ext::shared_ptr payoff = - ext::make_shared(value.type, value.strike); - - Date exDate = today + timeToDays(value.t, 365); - ext::shared_ptr exercise = - ext::make_shared(exDate); - - Handle volAtmQuote = Handle(ext::make_shared( - Handle(volAtm), DeltaVolQuote::Fwd, value.t, DeltaVolQuote::AtmDeltaNeutral)); - - Handle vol25PutQuote(Handle(ext::make_shared( - -0.25, Handle(vol25Put), value.t, DeltaVolQuote::Fwd))); - - Handle vol25CallQuote(Handle(ext::make_shared( - 0.25, Handle(vol25Call), value.t, DeltaVolQuote::Fwd))); - - BarrierOption barrierOption(value.barrierType, value.barrier, value.rebate, payoff, - exercise); - - Real bsVanillaPrice = - blackFormula(value.type, value.strike, - spot->value() * qTS->discount(value.t) / rTS->discount(value.t), - value.v * std::sqrt(value.t), rTS->discount(value.t)); - ext::shared_ptr vannaVolgaEngine = - ext::make_shared( - volAtmQuote, - vol25PutQuote, - vol25CallQuote, - Handle (spot), - Handle (rTS), - Handle (qTS), - true, - bsVanillaPrice); - barrierOption.setPricingEngine(vannaVolgaEngine); - - Real calculated = barrierOption.NPV(); - Real expected = value.result; - Real error = std::fabs(calculated-expected); - if (error > value.tol) { - REPORT_FX_FAILURE("value", value.barrierType, value.barrier, value.rebate, payoff, - exercise, value.s, value.q, value.r, today, value.vol25Put, - value.volAtm, value.vol25Call, value.v, expected, calculated, error, - value.tol); - } - } -} - -void BarrierOptionTest::testOldDividendBarrierOption() { - BOOST_TEST_MESSAGE("Testing old-style barrier option pricing with discrete dividends..."); - - const DayCounter dc = Actual365Fixed(); - - const Date today(11, February, 2018); - const Date maturity = today + Period(1, Years); - Settings::instance().evaluationDate() = today; - - const Real spot = 100.0; - const Real strike = 105.0; - const Real rebate = 5.0; - - const Real barriers[] = { 80.0, 120.0 }; - const Barrier::Type barrierTypes[] = { Barrier::DownOut, Barrier::UpOut }; - - const Rate r = 0.05; - const Rate q = 0.0; - const Volatility v = 0.02; - - const Handle s0(ext::make_shared(spot)); - const Handle qTS(flatRate(today, q, dc)); - const Handle rTS(flatRate(today, r, dc)); - const Handle volTS(flatVol(today, v, dc)); - - const ext::shared_ptr bsProcess = - ext::make_shared(s0, qTS, rTS, volTS); - - const ext::shared_ptr douglas = - ext::make_shared( - bsProcess, 100, 100, 0, FdmSchemeDesc::Douglas()); - - const ext::shared_ptr crankNicolson = - ext::make_shared( - bsProcess, 100, 100, 0, FdmSchemeDesc::CrankNicolson()); - - const ext::shared_ptr craigSnyed = - ext::make_shared( - bsProcess, 100, 100, 0, FdmSchemeDesc::CraigSneyd()); - - const ext::shared_ptr hundsdorfer = - ext::make_shared( - bsProcess, 100, 100, 0, FdmSchemeDesc::Hundsdorfer()); - - const ext::shared_ptr mol = - ext::make_shared( - bsProcess, 100, 100, 0, FdmSchemeDesc::MethodOfLines()); - - const ext::shared_ptr trPDF2 = - ext::make_shared( - bsProcess, 100, 100, 0, FdmSchemeDesc::TrBDF2()); - - const ext::shared_ptr hestonEngine = - ext::make_shared( - ext::make_shared( - ext::make_shared( - rTS, qTS, s0, v*v, 1.0, v*v, 0.005, 0.0)), 50, 101, 3); - - const ext::shared_ptr engines[] = { - douglas, crankNicolson, - trPDF2, craigSnyed, hundsdorfer, mol, hestonEngine - }; - - const ext::shared_ptr payoff = - ext::make_shared(Option::Put, strike); - - const ext::shared_ptr exercise = - ext::make_shared(maturity); - - const Real divAmount = 30; - const Date divDate = today + Period(6, Months); - - const Real expected[] = { - rTS->discount(divDate)*rebate, - (*payoff)( - (spot - divAmount*rTS->discount(divDate))/rTS->discount(maturity)) - *rTS->discount(maturity) - }; - - const Real relTol = 1e-4; - for (Size i=0; i < LENGTH(barriers); ++i) { - for (const auto& engine : engines) { - const Real barrier = barriers[i]; - const Barrier::Type barrierType = barrierTypes[i]; - - QL_DEPRECATED_DISABLE_WARNING + QL_DEPRECATED_DISABLE_WARNING DividendBarrierOption barrierOption( barrierType, barrier, rebate, payoff, exercise, @@ -1275,7 +961,7 @@ void BarrierOptionTest::testOldDividendBarrierOption() { } } -void BarrierOptionTest::testDividendBarrierOption() { +BOOST_AUTO_TEST_CASE(testDividendBarrierOption) { BOOST_TEST_MESSAGE("Testing barrier option pricing with discrete dividends..."); DayCounter dc = Actual365Fixed(); @@ -1381,8 +1067,7 @@ void BarrierOptionTest::testDividendBarrierOption() { } } - -void BarrierOptionTest::testDividendBarrierOptionWithDividendsPastMaturity() { +BOOST_AUTO_TEST_CASE(testDividendBarrierOptionWithDividendsPastMaturity) { BOOST_TEST_MESSAGE("Testing barrier option pricing with discrete dividends past maturity..."); DayCounter dc = Actual365Fixed(); @@ -1466,7 +1151,7 @@ void BarrierOptionTest::testDividendBarrierOptionWithDividendsPastMaturity() { } } -void BarrierOptionTest::testBarrierAndDividendEngine() { +BOOST_AUTO_TEST_CASE(testBarrierAndDividendEngine) { BOOST_TEST_MESSAGE("Testing the use of a single engine for barrier and dividend options..."); auto today = Date(1, January, 2023); @@ -1506,7 +1191,7 @@ void BarrierOptionTest::testBarrierAndDividendEngine() { } } -void BarrierOptionTest::testImpliedVolatility() { +BOOST_AUTO_TEST_CASE(testImpliedVolatility) { BOOST_TEST_MESSAGE("Testing implied volatility for barrier options..."); DayCounter dc = Actual365Fixed(); @@ -1585,7 +1270,7 @@ void BarrierOptionTest::testImpliedVolatility() { } } -void BarrierOptionTest::testLowVolatility() { +BOOST_AUTO_TEST_CASE(testLowVolatility) { BOOST_TEST_MESSAGE("Testing barrier options with low volatility value..."); DayCounter dc = Actual365Fixed(); @@ -1681,25 +1366,326 @@ void BarrierOptionTest::testLowVolatility() { check( 95.0, Option::Call, 99.0, Barrier::DownIn, 4.0, 0.01, 0.04, 2.0); // fwd = 97, call, ITM } -test_suite* BarrierOptionTest::suite() { - auto* suite = BOOST_TEST_SUITE("Barrier option tests"); - suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testParity)); - suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testHaugValues)); - suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testBabsiriValues)); - suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testBeagleholeValues)); - suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testLocalVolAndHestonComparison)); - suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testOldDividendBarrierOption)); - suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testDividendBarrierOption)); - suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testDividendBarrierOptionWithDividendsPastMaturity)); - suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testBarrierAndDividendEngine)); - suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testImpliedVolatility)); - suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testLowVolatility)); - return suite; +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(BarrierOptionExperimentalTest) + +BOOST_AUTO_TEST_CASE(testPerturbative) { + BOOST_TEST_MESSAGE("Testing perturbative engine for barrier options..."); + + Real S = 100.0; + Real rebate = 0.0; + Rate r = 0.03; + Rate q = 0.02; + + DayCounter dc = Actual360(); + Date today = Date::todaysDate(); + + ext::shared_ptr underlying = + ext::make_shared(S); + ext::shared_ptr qTS = flatRate(today, q, dc); + ext::shared_ptr rTS = flatRate(today, r, dc); + + std::vector dates(2); + std::vector vols(2); + + dates[0] = today + 90; vols[0] = 0.105; + dates[1] = today + 180; vols[1] = 0.11; + + ext::shared_ptr volTS = + ext::make_shared(today, dates, vols, dc); + + ext::shared_ptr stochProcess = + ext::make_shared( + Handle(underlying), + Handle(qTS), + Handle(rTS), + Handle(volTS)); + + Real strike = 101.0; + Real barrier = 101.0; + Date exDate = today+180; + + ext::shared_ptr exercise = + ext::make_shared(exDate); + ext::shared_ptr payoff = + ext::make_shared(Option::Put, strike); + + BarrierOption option(Barrier::UpOut, barrier, rebate, payoff, exercise); + + Natural order = 0; + bool zeroGamma = false; + ext::shared_ptr engine = + ext::make_shared(stochProcess, + order, zeroGamma); + + option.setPricingEngine(engine); + + Real calculated = option.NPV(); + Real expected = 0.897365; + Real tolerance = 1.0e-6; + if (std::fabs(calculated-expected) > tolerance) { + BOOST_ERROR("Failed to reproduce expected value" + << "\n calculated: " << std::setprecision(8) << calculated + << "\n expected: " << std::setprecision(8) << expected); + } + + order = 1; + engine = ext::make_shared(stochProcess, + order, + zeroGamma); + + option.setPricingEngine(engine); + + calculated = option.NPV(); + expected = 0.894374; + if (std::fabs(calculated-expected) > tolerance) { + BOOST_ERROR("Failed to reproduce expected value" + << "\n calculated: " << std::setprecision(8) << calculated + << "\n expected: " << std::setprecision(8) << expected); + } + + /* Too slow, skip + order = 2; + engine = ext::make_shared(stochProcess, + order, + zeroGamma); + + option.setPricingEngine(engine); + + calculated = option.NPV(); + expected = 0.8943769; + if (std::fabs(calculated-expected) > tolerance) { + BOOST_ERROR("Failed to reproduce expected value" + << "\n calculated: " << std::setprecision(8) << calculated + << "\n expected: " << std::setprecision(8) << expected); + } + */ } -test_suite* BarrierOptionTest::experimental() { - auto* suite = BOOST_TEST_SUITE("Barrier option experimental tests"); - suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testPerturbative)); - suite->add(QUANTLIB_TEST_CASE(&BarrierOptionTest::testVannaVolgaSimpleBarrierValues)); - return suite; +BOOST_AUTO_TEST_CASE(testVannaVolgaSimpleBarrierValues) { + BOOST_TEST_MESSAGE("Testing barrier FX options against Vanna/Volga values..."); + + using namespace barrier_option_test; + + BarrierFxOptionData values[] = { + + //barrierType,barrier,rebate,type,strike,s,q,r,t,vol25Put,volAtm,vol25Call,vol, result, tol + { Barrier::UpOut,1.5,0, Option::Call,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0.148127, 1.0e-4}, + { Barrier::UpOut,1.5,0, Option::Call,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0.075943, 1.0e-4}, + { Barrier::UpOut,1.5,0, Option::Call,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0.0274771, 1.0e-4}, + { Barrier::UpOut,1.5,0, Option::Call,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0.00573, 1.0e-4}, + { Barrier::UpOut,1.5,0, Option::Call,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0.00012, 1.0e-4}, + + { Barrier::UpOut,1.5,0, Option::Put,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0.00697606, 1.0e-4}, + { Barrier::UpOut,1.5,0, Option::Put,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0.020078, 1.0e-4}, + { Barrier::UpOut,1.5,0, Option::Put,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0.0489395, 1.0e-4}, + { Barrier::UpOut,1.5,0, Option::Put,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0.0969877, 1.0e-4}, + { Barrier::UpOut,1.5,0, Option::Put,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0.157, 1.0e-4}, + + { Barrier::UpIn,1.5,0, Option::Call,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0.0322202, 1.0e-4}, + { Barrier::UpIn,1.5,0, Option::Call,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0.0241491, 1.0e-4}, + { Barrier::UpIn,1.5,0, Option::Call,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0.0164275, 1.0e-4}, + { Barrier::UpIn,1.5,0, Option::Call,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0.01, 1.0e-4}, + { Barrier::UpIn,1.5,0, Option::Call,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0.00489, 1.0e-4}, + + { Barrier::UpIn,1.5,0, Option::Put,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0.000560713, 1.0e-4}, + { Barrier::UpIn,1.5,0, Option::Put,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0.000546804, 1.0e-4}, + { Barrier::UpIn,1.5,0, Option::Put,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0.000130649, 1.0e-4}, + { Barrier::UpIn,1.5,0, Option::Put,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0.000300828, 1.0e-4}, + { Barrier::UpIn,1.5,0, Option::Put,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0.00135, 1.0e-4}, + + { Barrier::DownOut,1.1,0, Option::Call,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0.17746, 1.0e-4}, + { Barrier::DownOut,1.1,0, Option::Call,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0.0994142, 1.0e-4}, + { Barrier::DownOut,1.1,0, Option::Call,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0.0439, 1.0e-4}, + { Barrier::DownOut,1.1,0, Option::Call,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0.01574, 1.0e-4}, + { Barrier::DownOut,1.1,0, Option::Call,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0.00501, 1.0e-4}, + + { Barrier::DownOut,1.3,0, Option::Call,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0.00612, 1.0e-4}, + { Barrier::DownOut,1.3,0, Option::Call,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0.00426, 1.0e-4}, + { Barrier::DownOut,1.3,0, Option::Call,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0.00257, 1.0e-4}, + { Barrier::DownOut,1.3,0, Option::Call,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0.00122, 1.0e-4}, + { Barrier::DownOut,1.3,0, Option::Call,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0.00045, 1.0e-4}, + + { Barrier::DownOut,1.1,0, Option::Put,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0.00022, 1.0e-4}, + { Barrier::DownOut,1.1,0, Option::Put,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0.00284, 1.0e-4}, + { Barrier::DownOut,1.1,0, Option::Put,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0.02032, 1.0e-4}, + { Barrier::DownOut,1.1,0, Option::Put,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0.058235, 1.0e-4}, + { Barrier::DownOut,1.1,0, Option::Put,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0.109432, 1.0e-4}, + + { Barrier::DownOut,1.3,0, Option::Put,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0, 1.0e-4}, + { Barrier::DownOut,1.3,0, Option::Put,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0, 1.0e-4}, + { Barrier::DownOut,1.3,0, Option::Put,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0, 1.0e-4}, + { Barrier::DownOut,1.3,0, Option::Put,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0.00017, 1.0e-4}, + { Barrier::DownOut,1.3,0, Option::Put,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0.00083, 1.0e-4}, + + { Barrier::DownIn,1.1,0, Option::Call,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0.00289, 1.0e-4}, + { Barrier::DownIn,1.1,0, Option::Call,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0.00067784, 1.0e-4}, + { Barrier::DownIn,1.1,0, Option::Call,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0, 1.0e-4}, + { Barrier::DownIn,1.1,0, Option::Call,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0, 1.0e-4}, + { Barrier::DownIn,1.1,0, Option::Call,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0, 1.0e-4}, + + { Barrier::DownIn,1.3,0, Option::Call,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0.17423, 1.0e-4}, + { Barrier::DownIn,1.3,0, Option::Call,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0.09584, 1.0e-4}, + { Barrier::DownIn,1.3,0, Option::Call,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0.04133, 1.0e-4}, + { Barrier::DownIn,1.3,0, Option::Call,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0.01452, 1.0e-4}, + { Barrier::DownIn,1.3,0, Option::Call,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0.00456, 1.0e-4}, + + { Barrier::DownIn,1.1,0, Option::Put,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0.00732, 1.0e-4}, + { Barrier::DownIn,1.1,0, Option::Put,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0.01778, 1.0e-4}, + { Barrier::DownIn,1.1,0, Option::Put,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0.02875, 1.0e-4}, + { Barrier::DownIn,1.1,0, Option::Put,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0.0390535, 1.0e-4}, + { Barrier::DownIn,1.1,0, Option::Put,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0.0489236, 1.0e-4}, + + { Barrier::DownIn,1.3,0, Option::Put,1.13321,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.11638,0.00753, 1.0e-4}, + { Barrier::DownIn,1.3,0, Option::Put,1.22687,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.10088,0.02062, 1.0e-4}, + { Barrier::DownIn,1.3,0, Option::Put,1.31179,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08925,0.04907, 1.0e-4}, + { Barrier::DownIn,1.3,0, Option::Put,1.38843,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08463,0.09711, 1.0e-4}, + { Barrier::DownIn,1.3,0, Option::Put,1.46047,1.30265,0.0003541,0.0033871,1,0.10087,0.08925,0.08463,0.08412,0.15752, 1.0e-4}, + + { Barrier::UpOut,1.6,0, Option::Call,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511,0.20493, 1.0e-4}, + { Barrier::UpOut,1.6,0, Option::Call,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089,0.105577, 1.0e-4}, + { Barrier::UpOut,1.6,0, Option::Call,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444,0.0358872, 1.0e-4}, + { Barrier::UpOut,1.6,0, Option::Call,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197,0.00634958, 1.0e-4}, + { Barrier::UpOut,1.6,0, Option::Call,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261,0, 1.0e-4}, + + { Barrier::UpOut,1.6,0, Option::Put,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511,0.0108218, 1.0e-4}, + { Barrier::UpOut,1.6,0, Option::Put,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089,0.0313339, 1.0e-4}, + { Barrier::UpOut,1.6,0, Option::Put,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444,0.0751237, 1.0e-4}, + { Barrier::UpOut,1.6,0, Option::Put,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197,0.153407, 1.0e-4}, + { Barrier::UpOut,1.6,0, Option::Put,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261,0.253767, 1.0e-4}, + + { Barrier::UpIn,1.6,0, Option::Call,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511,0.05402, 1.0e-4}, + { Barrier::UpIn,1.6,0, Option::Call,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089,0.0410069, 1.0e-4}, + { Barrier::UpIn,1.6,0, Option::Call,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444,0.0279562, 1.0e-4}, + { Barrier::UpIn,1.6,0, Option::Call,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197,0.0173055, 1.0e-4}, + { Barrier::UpIn,1.6,0, Option::Call,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261,0.00764, 1.0e-4}, + + { Barrier::UpIn,1.6,0, Option::Put,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511,0.000962737, 1.0e-4}, + { Barrier::UpIn,1.6,0, Option::Put,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089,0.00102637, 1.0e-4}, + { Barrier::UpIn,1.6,0, Option::Put,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444,0.000419834, 1.0e-4}, + { Barrier::UpIn,1.6,0, Option::Put,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197,0.00159277, 1.0e-4}, + { Barrier::UpIn,1.6,0, Option::Put,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261,0.00473629, 1.0e-4}, + + { Barrier::DownOut,1,0, Option::Call,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511,0.255098, 1.0e-4}, + { Barrier::DownOut,1,0, Option::Call,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089,0.145701, 1.0e-4}, + { Barrier::DownOut,1,0, Option::Call,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444,0.06384, 1.0e-4}, + { Barrier::DownOut,1,0, Option::Call,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197,0.02366, 1.0e-4}, + { Barrier::DownOut,1,0, Option::Call,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261,0.00764, 1.0e-4}, + + { Barrier::DownOut,1.3,0, Option::Call,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511,0.00592, 1.0e-4}, + { Barrier::DownOut,1.3,0, Option::Call,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089,0.00421, 1.0e-4}, + { Barrier::DownOut,1.3,0, Option::Call,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444,0.00256, 1.0e-4}, + { Barrier::DownOut,1.3,0, Option::Call,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197,0.0012, 1.0e-4}, + { Barrier::DownOut,1.3,0, Option::Call,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261,0.0004, 1.0e-4}, + + { Barrier::DownOut,1,0, Option::Put,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511,0, 1.0e-4}, + { Barrier::DownOut,1,0, Option::Put,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089,0.00280549, 1.0e-4}, + { Barrier::DownOut,1,0, Option::Put,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444,0.0279945, 1.0e-4}, + { Barrier::DownOut,1,0, Option::Put,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197,0.0896352, 1.0e-4}, + { Barrier::DownOut,1,0, Option::Put,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261,0.175182, 1.0e-4}, + + { Barrier::DownOut,1.3,0, Option::Put,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511, 0.00000, 1.0e-4}, + { Barrier::DownOut,1.3,0, Option::Put,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089, 0.00000, 1.0e-4}, + { Barrier::DownOut,1.3,0, Option::Put,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444, 0.00000, 1.0e-4}, + { Barrier::DownOut,1.3,0, Option::Put,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197,0.0002, 1.0e-4}, + { Barrier::DownOut,1.3,0, Option::Put,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261,0.00096, 1.0e-4}, + + { Barrier::DownIn,1,0, Option::Call,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511,0.00384783, 1.0e-4}, + { Barrier::DownIn,1,0, Option::Call,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089,0.000883232, 1.0e-4}, + { Barrier::DownIn,1,0, Option::Call,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444,0, 1.0e-4}, + { Barrier::DownIn,1,0, Option::Call,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197, 0.00000, 1.0e-4}, + { Barrier::DownIn,1,0, Option::Call,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261, 0.00000, 1.0e-4}, + + { Barrier::DownIn,1.3,0, Option::Call,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511,0.25302, 1.0e-4}, + { Barrier::DownIn,1.3,0, Option::Call,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089,0.14238, 1.0e-4}, + { Barrier::DownIn,1.3,0, Option::Call,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444,0.06128, 1.0e-4}, + { Barrier::DownIn,1.3,0, Option::Call,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197,0.02245, 1.0e-4}, + { Barrier::DownIn,1.3,0, Option::Call,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261,0.00725, 1.0e-4}, + + { Barrier::DownIn,1,0, Option::Put,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511,0.01178, 1.0e-4}, + { Barrier::DownIn,1,0, Option::Put,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089,0.0295548, 1.0e-4}, + { Barrier::DownIn,1,0, Option::Put,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444,0.047549, 1.0e-4}, + { Barrier::DownIn,1,0, Option::Put,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197,0.0653642, 1.0e-4}, + { Barrier::DownIn,1,0, Option::Put,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261,0.0833221, 1.0e-4}, + + { Barrier::DownIn,1.3,0, Option::Put,1.06145,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.12511,0.01178, 1.0e-4}, + { Barrier::DownIn,1.3,0, Option::Put,1.19545,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.1089,0.03236, 1.0e-4}, + { Barrier::DownIn,1.3,0, Option::Put,1.32238,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09444,0.07554, 1.0e-4}, + { Barrier::DownIn,1.3,0, Option::Put,1.44298,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09197,0.15479, 1.0e-4}, + { Barrier::DownIn,1.3,0, Option::Put,1.56345,1.30265,0.0009418,0.0039788,2,0.10891,0.09525,0.09197,0.09261,0.25754, 1.0e-4}, + + }; + + DayCounter dc = Actual365Fixed(); + Date today(5, March, 2013); + Settings::instance().evaluationDate() = today; + + ext::shared_ptr spot = ext::make_shared(0.0); + ext::shared_ptr qRate = ext::make_shared(0.0); + ext::shared_ptr qTS = flatRate(today, qRate, dc); + ext::shared_ptr rRate = ext::make_shared(0.0); + ext::shared_ptr rTS = flatRate(today, rRate, dc); + ext::shared_ptr vol25Put = ext::make_shared(0.0); + ext::shared_ptr volAtm = ext::make_shared(0.0); + ext::shared_ptr vol25Call = ext::make_shared(0.0); + + for (auto& value : values) { + + spot->setValue(value.s); + qRate->setValue(value.q); + rRate->setValue(value.r); + vol25Put->setValue(value.vol25Put); + volAtm->setValue(value.volAtm); + vol25Call->setValue(value.vol25Call); + + ext::shared_ptr payoff = + ext::make_shared(value.type, value.strike); + + Date exDate = today + timeToDays(value.t, 365); + ext::shared_ptr exercise = + ext::make_shared(exDate); + + Handle volAtmQuote = Handle(ext::make_shared( + Handle(volAtm), DeltaVolQuote::Fwd, value.t, DeltaVolQuote::AtmDeltaNeutral)); + + Handle vol25PutQuote(Handle(ext::make_shared( + -0.25, Handle(vol25Put), value.t, DeltaVolQuote::Fwd))); + + Handle vol25CallQuote(Handle(ext::make_shared( + 0.25, Handle(vol25Call), value.t, DeltaVolQuote::Fwd))); + + BarrierOption barrierOption(value.barrierType, value.barrier, value.rebate, payoff, + exercise); + + Real bsVanillaPrice = + blackFormula(value.type, value.strike, + spot->value() * qTS->discount(value.t) / rTS->discount(value.t), + value.v * std::sqrt(value.t), rTS->discount(value.t)); + ext::shared_ptr vannaVolgaEngine = + ext::make_shared( + volAtmQuote, + vol25PutQuote, + vol25CallQuote, + Handle (spot), + Handle (rTS), + Handle (qTS), + true, + bsVanillaPrice); + barrierOption.setPricingEngine(vannaVolgaEngine); + + Real calculated = barrierOption.NPV(); + Real expected = value.result; + Real error = std::fabs(calculated-expected); + if (error > value.tol) { + REPORT_FX_FAILURE("value", value.barrierType, value.barrier, value.rebate, payoff, + exercise, value.s, value.q, value.r, today, value.vol25Put, + value.volAtm, value.vol25Call, value.v, expected, calculated, error, + value.tol); + } + } } + +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/test-suite/barrieroption.hpp b/test-suite/barrieroption.hpp deleted file mode 100644 index 3383eeb6ed4..00000000000 --- a/test-suite/barrieroption.hpp +++ /dev/null @@ -1,50 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2003 Neil Firth - - This file is part of QuantLib, a free-software/open-source library - for financial quantitative analysts and developers - http://quantlib.org/ - - QuantLib is free software: you can redistribute it and/or modify it - under the terms of the QuantLib license. You should have received a - copy of the license along with this program; if not, please email - . The license is also available online at - . - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the license for more details. -*/ - -#ifndef quantlib_test_barrier_option_hpp -#define quantlib_test_barrier_option_hpp - -#include - -/* remember to document new and/or updated tests in the Doxygen - comment block of the corresponding class */ - -class BarrierOptionTest { - public: - static void testParity(); - static void testHaugValues(); - static void testBabsiriValues(); - static void testBeagleholeValues(); - static void testPerturbative(); - static void testLocalVolAndHestonComparison(); - static void testVannaVolgaSimpleBarrierValues(); - static void testVannaVolgaDoubleBarrierValues(); - static void testOldDividendBarrierOption(); - static void testDividendBarrierOption(); - static void testDividendBarrierOptionWithDividendsPastMaturity(); - static void testBarrierAndDividendEngine(); - static void testImpliedVolatility(); - static void testLowVolatility(); - - static boost::unit_test_framework::test_suite* suite(); - static boost::unit_test_framework::test_suite* experimental(); -}; - - -#endif diff --git a/test-suite/basismodels.cpp b/test-suite/basismodels.cpp index f555c16899c..d4390b928f8 100644 --- a/test-suite/basismodels.cpp +++ b/test-suite/basismodels.cpp @@ -17,7 +17,7 @@ FOR A PARTICULAR PURPOSE. See the license for more details. */ -#include "basismodels.hpp" +#include "toplevelfixture.hpp" #include "utilities.hpp" #include #include @@ -224,19 +224,22 @@ namespace { } +BOOST_FIXTURE_TEST_SUITE(QuantLibTest, TopLevelFixture) -void BasismodelsTest::testSwaptioncfsContCompSpread() { +BOOST_AUTO_TEST_SUITE(BasismodelsExperimentalTest) + +BOOST_AUTO_TEST_CASE(testSwaptioncfsContCompSpread) { BOOST_TEST_MESSAGE( "Testing deterministic tenor basis model with continuous compounded spreads..."); testSwaptioncfs(true); } -void BasismodelsTest::testSwaptioncfsSimpleCompSpread() { +BOOST_AUTO_TEST_CASE(testSwaptioncfsSimpleCompSpread) { BOOST_TEST_MESSAGE("Testing deterministic tenor basis model with simple compounded spreads..."); testSwaptioncfs(false); } -void BasismodelsTest::testTenoroptionletvts() { +BOOST_AUTO_TEST_CASE(testTenoroptionletvts) { BOOST_TEST_MESSAGE("Testing volatility transformation for caplets/floorlets..."); // market data and floating rate index Real spread = 0.01; @@ -320,7 +323,7 @@ void BasismodelsTest::testTenoroptionletvts() { } } -void BasismodelsTest::testTenorswaptionvts() { +BOOST_AUTO_TEST_CASE(testTenorswaptionvts) { BOOST_TEST_MESSAGE("Testing volatility transformation for swaptions..."); // market data and floating rate index Real spread = 0.01; @@ -397,12 +400,6 @@ void BasismodelsTest::testTenorswaptionvts() { } } +BOOST_AUTO_TEST_SUITE_END() -test_suite* BasismodelsTest::suite() { - auto* suite = BOOST_TEST_SUITE("Basismodels tests"); - suite->add(QUANTLIB_TEST_CASE(&BasismodelsTest::testSwaptioncfsContCompSpread)); - suite->add(QUANTLIB_TEST_CASE(&BasismodelsTest::testSwaptioncfsSimpleCompSpread)); - suite->add(QUANTLIB_TEST_CASE(&BasismodelsTest::testTenoroptionletvts)); - suite->add(QUANTLIB_TEST_CASE(&BasismodelsTest::testTenorswaptionvts)); - return suite; -} +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/test-suite/basismodels.hpp b/test-suite/basismodels.hpp deleted file mode 100644 index bd49b9fa742..00000000000 --- a/test-suite/basismodels.hpp +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2018 Sebastian Schlenkrich - - This file is part of QuantLib, a free-software/open-source library - for financial quantitative analysts and developers - http://quantlib.org/ - - QuantLib is free software: you can redistribute it and/or modify it - under the terms of the QuantLib license. You should have received a - copy of the license along with this program; if not, please email - . The license is also available online at - . - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the license for more details. -*/ - -#ifndef quantlib_test_basismodels_hpp -#define quantlib_test_basismodels_hpp - -#include - -/* remember to document new and/or updated tests in the Doxygen - comment block of the corresponding class */ - -class BasismodelsTest { - public: - static void testSwaptioncfsContCompSpread(); - static void testSwaptioncfsSimpleCompSpread(); - static void testTenoroptionletvts(); - static void testTenorswaptionvts(); - static boost::unit_test_framework::test_suite* suite(); -}; - - -#endif diff --git a/test-suite/basisswapratehelpers.cpp b/test-suite/basisswapratehelpers.cpp index bd42e8a162b..8b9affad4bc 100644 --- a/test-suite/basisswapratehelpers.cpp +++ b/test-suite/basisswapratehelpers.cpp @@ -17,7 +17,7 @@ FOR A PARTICULAR PURPOSE. See the license for more details. */ -#include "basisswapratehelpers.hpp" +#include "toplevelfixture.hpp" #include "utilities.hpp" #include #include @@ -210,39 +210,34 @@ namespace basisswapratehelpers_test { } +BOOST_FIXTURE_TEST_SUITE(QuantLibTest, TopLevelFixture) -void BasisSwapRateHelpersTest::testIborIborBaseCurveBootstrap() { +BOOST_AUTO_TEST_SUITE(BasisSwapRateHelpersExperimentalTest) + +BOOST_AUTO_TEST_CASE(testIborIborBaseCurveBootstrap) { BOOST_TEST_MESSAGE("Testing IBOR-IBOR basis-swap rate helpers (base curve bootstrap)..."); basisswapratehelpers_test::testIborIborBootstrap(true); } -void BasisSwapRateHelpersTest::testIborIborOtherCurveBootstrap() { +BOOST_AUTO_TEST_CASE(testIborIborOtherCurveBootstrap) { BOOST_TEST_MESSAGE("Testing IBOR-IBOR basis-swap rate helpers (other curve bootstrap)..."); basisswapratehelpers_test::testIborIborBootstrap(false); } -void BasisSwapRateHelpersTest::testOvernightIborBootstrap() { +BOOST_AUTO_TEST_CASE(testOvernightIborBootstrap) { BOOST_TEST_MESSAGE("Testing overnight-IBOR basis-swap rate helpers..."); basisswapratehelpers_test::testOvernightIborBootstrap(false); } -void BasisSwapRateHelpersTest::testOvernightIborBootstrapWithDiscountCurve() { +BOOST_AUTO_TEST_CASE(testOvernightIborBootstrapWithDiscountCurve) { BOOST_TEST_MESSAGE("Testing overnight-IBOR basis-swap rate helpers with external discount curve..."); basisswapratehelpers_test::testOvernightIborBootstrap(true); } +BOOST_AUTO_TEST_SUITE_END() -test_suite* BasisSwapRateHelpersTest::suite() { - auto* suite = BOOST_TEST_SUITE("Basis swap rate helpers tests"); - - suite->add(QUANTLIB_TEST_CASE(&BasisSwapRateHelpersTest::testIborIborBaseCurveBootstrap)); - suite->add(QUANTLIB_TEST_CASE(&BasisSwapRateHelpersTest::testIborIborOtherCurveBootstrap)); - suite->add(QUANTLIB_TEST_CASE(&BasisSwapRateHelpersTest::testOvernightIborBootstrap)); - suite->add(QUANTLIB_TEST_CASE(&BasisSwapRateHelpersTest::testOvernightIborBootstrapWithDiscountCurve)); - - return suite; -} +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/test-suite/basisswapratehelpers.hpp b/test-suite/basisswapratehelpers.hpp deleted file mode 100644 index f97fbe8d35b..00000000000 --- a/test-suite/basisswapratehelpers.hpp +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2021 StatPro Italia srl - - This file is part of QuantLib, a free-software/open-source library - for financial quantitative analysts and developers - http://quantlib.org/ - - QuantLib is free software: you can redistribute it and/or modify it - under the terms of the QuantLib license. You should have received a - copy of the license along with this program; if not, please email - . The license is also available online at - . - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the license for more details. -*/ - -#ifndef quantlib_test_basisswapratehelpers_hpp -#define quantlib_test_basisswapratehelpers_hpp - -#include - -/* remember to document new and/or updated tests in the Doxygen - comment block of the corresponding class */ - -class BasisSwapRateHelpersTest { - public: - static void testIborIborBaseCurveBootstrap(); - static void testIborIborOtherCurveBootstrap(); - static void testOvernightIborBootstrap(); - static void testOvernightIborBootstrapWithDiscountCurve(); - - static boost::unit_test_framework::test_suite* suite(); -}; - -#endif diff --git a/test-suite/basketoption.cpp b/test-suite/basketoption.cpp index 79513f4e77c..c9a192ff673 100644 --- a/test-suite/basketoption.cpp +++ b/test-suite/basketoption.cpp @@ -19,7 +19,8 @@ FOR A PARTICULAR PURPOSE. See the license for more details. */ -#include "basketoption.hpp" +#include "speedlevel.hpp" +#include "toplevelfixture.hpp" #include "utilities.hpp" #include #include @@ -167,10 +168,53 @@ namespace { Real amValue; }; + BasketOptionOneData oneDataValues[] = { + // type, strike, spot, q, r, t, vol, value, tol + { Option::Put, 100.00, 80.00, 0.0, 0.06, 0.5, 0.4, 21.6059, 1e-2 }, + { Option::Put, 100.00, 85.00, 0.0, 0.06, 0.5, 0.4, 18.0374, 1e-2 }, + { Option::Put, 100.00, 90.00, 0.0, 0.06, 0.5, 0.4, 14.9187, 1e-2 }, + { Option::Put, 100.00, 95.00, 0.0, 0.06, 0.5, 0.4, 12.2314, 1e-2 }, + { Option::Put, 100.00, 100.00, 0.0, 0.06, 0.5, 0.4, 9.9458, 1e-2 }, + { Option::Put, 100.00, 105.00, 0.0, 0.06, 0.5, 0.4, 8.0281, 1e-2 }, + { Option::Put, 100.00, 110.00, 0.0, 0.06, 0.5, 0.4, 6.4352, 1e-2 }, + { Option::Put, 100.00, 115.00, 0.0, 0.06, 0.5, 0.4, 5.1265, 1e-2 }, + { Option::Put, 100.00, 120.00, 0.0, 0.06, 0.5, 0.4, 4.0611, 1e-2 }, + + // Longstaff Schwartz 1D example + // use constant and three Laguerre polynomials + // 100,000 paths and 50 timesteps per year + { Option::Put, 40.00, 36.00, 0.0, 0.06, 1.0, 0.2, 4.478, 1e-2 }, + { Option::Put, 40.00, 36.00, 0.0, 0.06, 2.0, 0.2, 4.840, 1e-2 }, + { Option::Put, 40.00, 36.00, 0.0, 0.06, 1.0, 0.4, 7.101, 1e-2 }, + { Option::Put, 40.00, 36.00, 0.0, 0.06, 2.0, 0.4, 8.508, 1e-2 }, + + { Option::Put, 40.00, 38.00, 0.0, 0.06, 1.0, 0.2, 3.250, 1e-2 }, + { Option::Put, 40.00, 38.00, 0.0, 0.06, 2.0, 0.2, 3.745, 1e-2 }, + { Option::Put, 40.00, 38.00, 0.0, 0.06, 1.0, 0.4, 6.148, 1e-2 }, + { Option::Put, 40.00, 38.00, 0.0, 0.06, 2.0, 0.4, 7.670, 1e-2 }, + + { Option::Put, 40.00, 40.00, 0.0, 0.06, 1.0, 0.2, 2.314, 1e-2 }, + { Option::Put, 40.00, 40.00, 0.0, 0.06, 2.0, 0.2, 2.885, 1e-2 }, + { Option::Put, 40.00, 40.00, 0.0, 0.06, 1.0, 0.4, 5.312, 1e-2 }, + { Option::Put, 40.00, 40.00, 0.0, 0.06, 2.0, 0.4, 6.920, 1e-2 }, + + { Option::Put, 40.00, 42.00, 0.0, 0.06, 1.0, 0.2, 1.617, 1e-2 }, + { Option::Put, 40.00, 42.00, 0.0, 0.06, 2.0, 0.2, 2.212, 1e-2 }, + { Option::Put, 40.00, 42.00, 0.0, 0.06, 1.0, 0.4, 4.582, 1e-2 }, + { Option::Put, 40.00, 42.00, 0.0, 0.06, 2.0, 0.4, 6.248, 1e-2 }, + + { Option::Put, 40.00, 44.00, 0.0, 0.06, 1.0, 0.2, 1.110, 1e-2 }, + { Option::Put, 40.00, 44.00, 0.0, 0.06, 2.0, 0.2, 1.690, 1e-2 }, + { Option::Put, 40.00, 44.00, 0.0, 0.06, 1.0, 0.4, 3.948, 1e-2 }, + { Option::Put, 40.00, 44.00, 0.0, 0.06, 2.0, 0.4, 5.647, 1e-2 } + }; } +BOOST_FIXTURE_TEST_SUITE(QuantLibTest, TopLevelFixture) + +BOOST_AUTO_TEST_SUITE(BasketOptionTest) -void BasketOptionTest::testEuroTwoValues() { +BOOST_AUTO_TEST_CASE(testEuroTwoValues) { BOOST_TEST_MESSAGE("Testing two-asset European basket options..."); @@ -377,7 +421,7 @@ void BasketOptionTest::testEuroTwoValues() { } } -void BasketOptionTest::testBarraquandThreeValues() { +BOOST_AUTO_TEST_CASE(testBarraquandThreeValues, *precondition(if_speed(Slow))) { BOOST_TEST_MESSAGE("Testing three-asset basket options " "against Barraquand's values..."); @@ -580,7 +624,7 @@ void BasketOptionTest::testBarraquandThreeValues() { } } -void BasketOptionTest::testTavellaValues() { +BOOST_AUTO_TEST_CASE(testTavellaValues) { BOOST_TEST_MESSAGE("Testing three-asset American basket options " "against Tavella's values..."); @@ -697,53 +741,22 @@ void BasketOptionTest::testTavellaValues() { } } -namespace { - BasketOptionOneData oneDataValues[] = { - // type, strike, spot, q, r, t, vol, value, tol - { Option::Put, 100.00, 80.00, 0.0, 0.06, 0.5, 0.4, 21.6059, 1e-2 }, - { Option::Put, 100.00, 85.00, 0.0, 0.06, 0.5, 0.4, 18.0374, 1e-2 }, - { Option::Put, 100.00, 90.00, 0.0, 0.06, 0.5, 0.4, 14.9187, 1e-2 }, - { Option::Put, 100.00, 95.00, 0.0, 0.06, 0.5, 0.4, 12.2314, 1e-2 }, - { Option::Put, 100.00, 100.00, 0.0, 0.06, 0.5, 0.4, 9.9458, 1e-2 }, - { Option::Put, 100.00, 105.00, 0.0, 0.06, 0.5, 0.4, 8.0281, 1e-2 }, - { Option::Put, 100.00, 110.00, 0.0, 0.06, 0.5, 0.4, 6.4352, 1e-2 }, - { Option::Put, 100.00, 115.00, 0.0, 0.06, 0.5, 0.4, 5.1265, 1e-2 }, - { Option::Put, 100.00, 120.00, 0.0, 0.06, 0.5, 0.4, 4.0611, 1e-2 }, +BOOST_AUTO_TEST_SUITE(BasketOptionAmericanTest, *precondition(if_speed(Fast))) - // Longstaff Schwartz 1D example - // use constant and three Laguerre polynomials - // 100,000 paths and 50 timesteps per year - { Option::Put, 40.00, 36.00, 0.0, 0.06, 1.0, 0.2, 4.478, 1e-2 }, - { Option::Put, 40.00, 36.00, 0.0, 0.06, 2.0, 0.2, 4.840, 1e-2 }, - { Option::Put, 40.00, 36.00, 0.0, 0.06, 1.0, 0.4, 7.101, 1e-2 }, - { Option::Put, 40.00, 36.00, 0.0, 0.06, 2.0, 0.4, 8.508, 1e-2 }, +struct sliceOne { static const int from{0}, to{5}; }; +struct sliceTwo { static const int from{5}, to{11}; }; +struct sliceThree { static const int from{11}, to{17}; }; +struct sliceFour { static const int from{17}, to{23}; }; +struct sliceFive { static const int from{23}, to{29}; }; - { Option::Put, 40.00, 38.00, 0.0, 0.06, 1.0, 0.2, 3.250, 1e-2 }, - { Option::Put, 40.00, 38.00, 0.0, 0.06, 2.0, 0.2, 3.745, 1e-2 }, - { Option::Put, 40.00, 38.00, 0.0, 0.06, 1.0, 0.4, 6.148, 1e-2 }, - { Option::Put, 40.00, 38.00, 0.0, 0.06, 2.0, 0.4, 7.670, 1e-2 }, +using slices = boost::mpl::vector; - { Option::Put, 40.00, 40.00, 0.0, 0.06, 1.0, 0.2, 2.314, 1e-2 }, - { Option::Put, 40.00, 40.00, 0.0, 0.06, 2.0, 0.2, 2.885, 1e-2 }, - { Option::Put, 40.00, 40.00, 0.0, 0.06, 1.0, 0.4, 5.312, 1e-2 }, - { Option::Put, 40.00, 40.00, 0.0, 0.06, 2.0, 0.4, 6.920, 1e-2 }, - - { Option::Put, 40.00, 42.00, 0.0, 0.06, 1.0, 0.2, 1.617, 1e-2 }, - { Option::Put, 40.00, 42.00, 0.0, 0.06, 2.0, 0.2, 2.212, 1e-2 }, - { Option::Put, 40.00, 42.00, 0.0, 0.06, 1.0, 0.4, 4.582, 1e-2 }, - { Option::Put, 40.00, 42.00, 0.0, 0.06, 2.0, 0.4, 6.248, 1e-2 }, - - { Option::Put, 40.00, 44.00, 0.0, 0.06, 1.0, 0.2, 1.110, 1e-2 }, - { Option::Put, 40.00, 44.00, 0.0, 0.06, 2.0, 0.2, 1.690, 1e-2 }, - { Option::Put, 40.00, 44.00, 0.0, 0.06, 1.0, 0.4, 3.948, 1e-2 }, - { Option::Put, 40.00, 44.00, 0.0, 0.06, 2.0, 0.4, 5.647, 1e-2 } - }; -} - -void BasketOptionTest::testOneDAmericanValues(std::size_t from, std::size_t to) { +BOOST_AUTO_TEST_CASE_TEMPLATE(testOneDAmericanValues, T, slices) { + const int from = T::from; + const int to = T::to; BOOST_TEST_MESSAGE("Testing basket American options against 1-D case " - "from " << from << " to " << to-1 << "..."); + "from " << from << " to " << to - 1 << "..."); DayCounter dc = Actual360(); Date today = Date::todaysDate(); @@ -769,37 +782,35 @@ void BasketOptionTest::testOneDAmericanValues(std::size_t from, std::size_t to) Handle(rTS), Handle(volTS1))); - std::vector > procs = {stochProcess1}; + std::vector> procs = {stochProcess1}; Matrix correlation(1, 1, 1.0); ext::shared_ptr process( - new StochasticProcessArray(procs,correlation)); + new StochasticProcessArray(procs, correlation)); ext::shared_ptr mcLSMCEngine = MakeMCAmericanBasketEngine<>(process) - .withSteps(timeSteps) - .withAntitheticVariate() - .withSamples(requiredSamples) - .withCalibrationSamples(requiredSamples/4) - .withSeed(seed); + .withSteps(timeSteps) + .withAntitheticVariate() + .withSamples(requiredSamples) + .withCalibrationSamples(requiredSamples / 4) + .withSeed(seed); - for (Size i=from; i payoff(new - PlainVanillaPayoff(oneDataValues[i].type, oneDataValues[i].strike)); + for (Size i = from; i < to; i++) { + ext::shared_ptr payoff( + new PlainVanillaPayoff(oneDataValues[i].type, oneDataValues[i].strike)); Date exDate = today + timeToDays(oneDataValues[i].t); - ext::shared_ptr exercise(new AmericanExercise(today, - exDate)); + ext::shared_ptr exercise(new AmericanExercise(today, exDate)); spot1 ->setValue(oneDataValues[i].s); vol1 ->setValue(oneDataValues[i].v); rRate ->setValue(oneDataValues[i].r); qRate ->setValue(oneDataValues[i].q); - BasketOption basketOption(// process, - basketTypeToPayoff(MaxBasket, payoff), - exercise); + BasketOption basketOption( // process, + basketTypeToPayoff(MaxBasket, payoff), exercise); basketOption.setPricingEngine(mcLSMCEngine); Real calculated = basketOption.NPV(); @@ -810,18 +821,20 @@ void BasketOptionTest::testOneDAmericanValues(std::size_t from, std::size_t to) if (relError > oneDataValues[i].tol) { BOOST_FAIL("expected value: " << oneDataValues[i].result << "\n" - << "calculated: " << calculated); + << "calculated: " << calculated); } - } } -/* This unit test is a a regression test to check for a crash in +BOOST_AUTO_TEST_SUITE_END() + +/* This unit test is a regression test to check for a crash in monte carlo if the required sample is odd. The crash occurred because the samples array size was off by one when antithetic paths were added. */ -void BasketOptionTest::testOddSamples() { + +BOOST_AUTO_TEST_CASE(testOddSamples) { BOOST_TEST_MESSAGE("Testing antithetic engine using odd sample number..."); @@ -902,7 +915,7 @@ void BasketOptionTest::testOddSamples() { } } -void BasketOptionTest::testLocalVolatilitySpreadOption() { +BOOST_AUTO_TEST_CASE(testLocalVolatilitySpreadOption, *precondition(if_speed(Fast))) { BOOST_TEST_MESSAGE("Testing 2D local-volatility spread-option pricing..."); @@ -968,7 +981,7 @@ void BasketOptionTest::testLocalVolatilitySpreadOption() { } } -void BasketOptionTest::test2DPDEGreeks() { +BOOST_AUTO_TEST_CASE(test2DPDEGreeks) { BOOST_TEST_MESSAGE("Testing Greeks of two-dimensional PDE engine..."); @@ -1043,28 +1056,6 @@ void BasketOptionTest::test2DPDEGreeks() { } } -test_suite* BasketOptionTest::suite(SpeedLevel speed) { - auto* suite = BOOST_TEST_SUITE("Basket option tests"); - - suite->add(QUANTLIB_TEST_CASE(&BasketOptionTest::testEuroTwoValues)); - suite->add(QUANTLIB_TEST_CASE(&BasketOptionTest::testTavellaValues)); +BOOST_AUTO_TEST_SUITE_END() - suite->add(QUANTLIB_TEST_CASE(&BasketOptionTest::testOddSamples)); - suite->add(QUANTLIB_TEST_CASE(&BasketOptionTest::test2DPDEGreeks)); - - if (speed <= Fast) { - suite->add(QUANTLIB_TEST_CASE(&BasketOptionTest::testLocalVolatilitySpreadOption)); - // unrolled to get different test names - suite->add(QUANTLIB_TEST_CASE([=](){ BasketOptionTest::testOneDAmericanValues( 0, 5); })); - suite->add(QUANTLIB_TEST_CASE([=](){ BasketOptionTest::testOneDAmericanValues( 5, 11); })); - suite->add(QUANTLIB_TEST_CASE([=](){ BasketOptionTest::testOneDAmericanValues(11, 17); })); - suite->add(QUANTLIB_TEST_CASE([=](){ BasketOptionTest::testOneDAmericanValues(17, 23); })); - suite->add(QUANTLIB_TEST_CASE([=](){ BasketOptionTest::testOneDAmericanValues(23, 29); })); - } - - if (speed == Slow) { - suite->add(QUANTLIB_TEST_CASE(&BasketOptionTest::testBarraquandThreeValues)); - } - - return suite; -} +BOOST_AUTO_TEST_SUITE_END() diff --git a/test-suite/basketoption.hpp b/test-suite/basketoption.hpp deleted file mode 100644 index 0dd31bc1f22..00000000000 --- a/test-suite/basketoption.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2004 Neil Firth - - This file is part of QuantLib, a free-software/open-source library - for financial quantitative analysts and developers - http://quantlib.org/ - - QuantLib is free software: you can redistribute it and/or modify it - under the terms of the QuantLib license. You should have received a - copy of the license along with this program; if not, please email - . The license is also available online at - . - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the license for more details. -*/ - -#ifndef quantlib_test_basket_option_hpp -#define quantlib_test_basket_option_hpp - -#include -#include -#include "speedlevel.hpp" - -/* remember to document new and/or updated tests in the Doxygen - comment block of the corresponding class */ - -class BasketOptionTest { - public: - static void testEuroTwoValues(); - static void testBarraquandThreeValues(); - static void testTavellaValues(); - static void testOneDAmericanValues(std::size_t from, std::size_t to); - static void testOddSamples(); - static void testLocalVolatilitySpreadOption(); - static void test2DPDEGreeks(); - static boost::unit_test_framework::test_suite* suite(SpeedLevel); -}; - - -#endif diff --git a/test-suite/batesmodel.cpp b/test-suite/batesmodel.cpp index fff6f386612..37fca3cfb15 100644 --- a/test-suite/batesmodel.cpp +++ b/test-suite/batesmodel.cpp @@ -18,7 +18,7 @@ FOR A PARTICULAR PURPOSE. See the license for more details. */ -#include "batesmodel.hpp" +#include "toplevelfixture.hpp" #include "utilities.hpp" #include #include @@ -54,10 +54,37 @@ namespace bates_model_test { return sse; } + struct HestonModelData { + const char* const name; + Real v0; + Real kappa; + Real theta; + Real sigma; + Real rho; + Real r; + Real q; + }; + + HestonModelData hestonModels[] = { + // ADI finite difference schemes for option pricing in the + // Heston model with correlation, K.J. in t'Hout and S. Foulon, + {"'t Hout case 1", 0.04, 1.5, 0.04, 0.3, -0.9, 0.025, 0.0}, + // Efficient numerical methods for pricing American options under + // stochastic volatility, Samuli Ikonen and Jari Toivanen, + {"Ikonen-Toivanen", 0.0625, 5, 0.16, 0.9, 0.1, 0.1, 0.0}, + // Not-so-complex logarithms in the Heston model, + // Christian Kahl and Peter Jäckel + {"Kahl-Jaeckel", 0.16, 1.0, 0.16, 2.0, -0.8, 0.0, 0.0}, + // self defined test cases + {"Equity case", 0.07, 2.0, 0.04, 0.55, -0.8, 0.03, 0.035 }, + }; } +BOOST_FIXTURE_TEST_SUITE(QuantLibTest, TopLevelFixture) + +BOOST_AUTO_TEST_SUITE(BatesModelTest) -void BatesModelTest::testAnalyticVsBlack() { +BOOST_AUTO_TEST_CASE(testAnalyticVsBlack) { BOOST_TEST_MESSAGE("Testing analytic Bates engine against Black formula..."); @@ -167,8 +194,7 @@ void BatesModelTest::testAnalyticVsBlack() { } } - -void BatesModelTest::testAnalyticAndMcVsJumpDiffusion() { +BOOST_AUTO_TEST_CASE(testAnalyticAndMcVsJumpDiffusion) { BOOST_TEST_MESSAGE("Testing analytic Bates engine against Merton-76 engine..."); @@ -265,34 +291,7 @@ void BatesModelTest::testAnalyticAndMcVsJumpDiffusion() { } } -namespace bates_model_test { - struct HestonModelData { - const char* const name; - Real v0; - Real kappa; - Real theta; - Real sigma; - Real rho; - Real r; - Real q; - }; - - HestonModelData hestonModels[] = { - // ADI finite difference schemes for option pricing in the - // Heston model with correlation, K.J. in t'Hout and S. Foulon, - {"'t Hout case 1", 0.04, 1.5, 0.04, 0.3, -0.9, 0.025, 0.0}, - // Efficient numerical methods for pricing American options under - // stochastic volatility, Samuli Ikonen and Jari Toivanen, - {"Ikonen-Toivanen", 0.0625, 5, 0.16, 0.9, 0.1, 0.1, 0.0}, - // Not-so-complex logarithms in the Heston model, - // Christian Kahl and Peter Jäckel - {"Kahl-Jaeckel", 0.16, 1.0, 0.16, 2.0, -0.8, 0.0, 0.0}, - // self defined test cases - {"Equity case", 0.07, 2.0, 0.04, 0.55, -0.8, 0.03, 0.035 }, - }; -} - -void BatesModelTest::testAnalyticVsMCPricing() { +BOOST_AUTO_TEST_CASE(testAnalyticVsMCPricing) { BOOST_TEST_MESSAGE("Testing analytic Bates engine against Monte-Carlo " "engine..."); @@ -365,7 +364,7 @@ void BatesModelTest::testAnalyticVsMCPricing() { } } -void BatesModelTest::testDAXCalibration() { +BOOST_AUTO_TEST_CASE(testDAXCalibration) { /* this example is taken from A. Sepp Pricing European-Style Options under Jump Diffusion Processes with Stochstic Volatility: Applications of Fourier Transform @@ -516,11 +515,6 @@ void BatesModelTest::testDAXCalibration() { } } -test_suite* BatesModelTest::suite() { - auto* suite = BOOST_TEST_SUITE("Bates model tests"); - suite->add(QUANTLIB_TEST_CASE(&BatesModelTest::testAnalyticVsBlack)); - suite->add(QUANTLIB_TEST_CASE(&BatesModelTest::testAnalyticAndMcVsJumpDiffusion)); - suite->add(QUANTLIB_TEST_CASE(&BatesModelTest::testAnalyticVsMCPricing)); - suite->add(QUANTLIB_TEST_CASE(&BatesModelTest::testDAXCalibration)); - return suite; -} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/test-suite/batesmodel.hpp b/test-suite/batesmodel.hpp deleted file mode 100644 index a7556763593..00000000000 --- a/test-suite/batesmodel.hpp +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2005 Klaus Spanderen - - This file is part of QuantLib, a free-software/open-source library - for financial quantitative analysts and developers - http://quantlib.org/ - - QuantLib is free software: you can redistribute it and/or modify it - under the terms of the QuantLib license. You should have received a - copy of the license along with this program; if not, please email - . The license is also available online at - . - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the license for more details. -*/ - -#ifndef quantlib_test_bates_model_hpp -#define quantlib_test_bates_model_hpp - -#include - -/* remember to document new and/or updated tests in the Doxygen - comment block of the corresponding class */ - -class BatesModelTest { - public: - static void testAnalyticVsBlack(); - static void testAnalyticAndMcVsJumpDiffusion(); - static void testAnalyticVsMCPricing(); - static void testDAXCalibration(); - static boost::unit_test_framework::test_suite* suite(); -}; - - -#endif diff --git a/test-suite/bermudanswaption.cpp b/test-suite/bermudanswaption.cpp index eda05067aa1..90ac0756202 100644 --- a/test-suite/bermudanswaption.cpp +++ b/test-suite/bermudanswaption.cpp @@ -19,7 +19,8 @@ FOR A PARTICULAR PURPOSE. See the license for more details. */ -#include "bermudanswaption.hpp" +#include "speedlevel.hpp" +#include "toplevelfixture.hpp" #include "utilities.hpp" #include #include @@ -106,8 +107,11 @@ namespace bermudan_swaption_test { } +BOOST_FIXTURE_TEST_SUITE(QuantLibTest, TopLevelFixture) -void BermudanSwaptionTest::testCachedValues() { +BOOST_AUTO_TEST_SUITE(BermudanSwaptionTest) + +BOOST_AUTO_TEST_CASE(testCachedValues) { BOOST_TEST_MESSAGE( "Testing Bermudan swaption with HW model against cached values..."); @@ -235,7 +239,7 @@ void BermudanSwaptionTest::testCachedValues() { << "expected: " << otmValue); } -void BermudanSwaptionTest::testCachedG2Values() { +BOOST_AUTO_TEST_CASE(testCachedG2Values, *precondition(if_speed(Fast))) { BOOST_TEST_MESSAGE( "Testing Bermudan swaption with G2 model against cached values..."); @@ -312,7 +316,7 @@ void BermudanSwaptionTest::testCachedG2Values() { } } -void BermudanSwaptionTest::testTreeEngineTimeSnapping() { +BOOST_AUTO_TEST_CASE(testTreeEngineTimeSnapping) { BOOST_TEST_MESSAGE("Testing snap of exercise dates for discretized swaption..."); Date today = Date(8, Jul, 2021); @@ -373,15 +377,6 @@ void BermudanSwaptionTest::testTreeEngineTimeSnapping() { } } -test_suite* BermudanSwaptionTest::suite(SpeedLevel speed) { - auto* suite = BOOST_TEST_SUITE("Bermudan swaption tests"); - - suite->add(QUANTLIB_TEST_CASE(&BermudanSwaptionTest::testCachedValues)); - suite->add(QUANTLIB_TEST_CASE(&BermudanSwaptionTest::testTreeEngineTimeSnapping)); +BOOST_AUTO_TEST_SUITE_END() - if (speed <= Fast) { - suite->add(QUANTLIB_TEST_CASE(&BermudanSwaptionTest::testCachedG2Values)); - } - - return suite; -} +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/test-suite/binaryoption.cpp b/test-suite/binaryoption.cpp index 582dd73d533..9124d0ac2b6 100644 --- a/test-suite/binaryoption.cpp +++ b/test-suite/binaryoption.cpp @@ -17,7 +17,7 @@ FOR A PARTICULAR PURPOSE. See the license for more details. */ -#include "binaryoption.hpp" +#include "toplevelfixture.hpp" #include "utilities.hpp" #include #include @@ -88,8 +88,11 @@ namespace binary_option_test { }; } +BOOST_FIXTURE_TEST_SUITE(QuantLibTest, TopLevelFixture) -void BinaryOptionTest::testCashOrNothingHaugValues() { +BOOST_AUTO_TEST_SUITE(BinaryOptionTest) + +BOOST_AUTO_TEST_CASE(testCashOrNothingHaugValues) { BOOST_TEST_MESSAGE("Testing cash-or-nothing barrier options against Haug's values..."); @@ -188,7 +191,7 @@ void BinaryOptionTest::testCashOrNothingHaugValues() { } } -void BinaryOptionTest::testAssetOrNothingHaugValues() { +BOOST_AUTO_TEST_CASE(testAssetOrNothingHaugValues) { BOOST_TEST_MESSAGE("Testing asset-or-nothing barrier options against Haug's values..."); @@ -269,9 +272,6 @@ void BinaryOptionTest::testAssetOrNothingHaugValues() { } } -test_suite* BinaryOptionTest::suite() { - auto* suite = BOOST_TEST_SUITE("Binary"); - suite->add(QUANTLIB_TEST_CASE(&BinaryOptionTest::testCashOrNothingHaugValues)); - suite->add(QUANTLIB_TEST_CASE(&BinaryOptionTest::testAssetOrNothingHaugValues)); - return suite; -} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/test-suite/binaryoption.hpp b/test-suite/binaryoption.hpp deleted file mode 100644 index cd6aaef560f..00000000000 --- a/test-suite/binaryoption.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2014 Thema Consulting SA - - This file is part of QuantLib, a free-software/open-source library - for financial quantitative analysts and developers - http://quantlib.org/ - - QuantLib is free software: you can redistribute it and/or modify it - under the terms of the QuantLib license. You should have received a - copy of the license along with this program; if not, please email - . The license is also available online at - . - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the license for more details. -*/ - -#ifndef quantlib_test_binary_option_hpp -#define quantlib_test_binary_option_hpp - -#include - -/* remember to document new and/or updated tests in the Doxygen - comment block of the corresponding class */ - -class BinaryOptionTest { - public: - static void testCashOrNothingHaugValues(); - static void testAssetOrNothingHaugValues(); - static boost::unit_test_framework::test_suite* suite(); -}; - - -#endif diff --git a/test-suite/blackdeltacalculator.cpp b/test-suite/blackdeltacalculator.cpp index f019fefaea8..89c5c08c493 100644 --- a/test-suite/blackdeltacalculator.cpp +++ b/test-suite/blackdeltacalculator.cpp @@ -17,7 +17,7 @@ FOR A PARTICULAR PURPOSE. See the license for more details. */ -#include "blackdeltacalculator.hpp" +#include "toplevelfixture.hpp" #include "utilities.hpp" #include #include @@ -61,8 +61,11 @@ namespace black_delta_calculator_test { } +BOOST_FIXTURE_TEST_SUITE(QuantLibTest, TopLevelFixture) -void BlackDeltaCalculatorTest::testDeltaValues(){ +BOOST_AUTO_TEST_SUITE(BlackDeltaCalculatorExperimentalTest) + +BOOST_AUTO_TEST_CASE(testDeltaValues){ BOOST_TEST_MESSAGE("Testing delta calculator values..."); @@ -147,7 +150,7 @@ void BlackDeltaCalculatorTest::testDeltaValues(){ } } -void BlackDeltaCalculatorTest::testDeltaPriceConsistency() { +BOOST_AUTO_TEST_CASE(testDeltaPriceConsistency) { BOOST_TEST_MESSAGE("Testing premium-adjusted delta price consistency..."); @@ -312,7 +315,7 @@ void BlackDeltaCalculatorTest::testDeltaPriceConsistency() { } } -void BlackDeltaCalculatorTest::testPutCallParity(){ +BOOST_AUTO_TEST_CASE(testPutCallParity){ BOOST_TEST_MESSAGE("Testing put-call parity for deltas..."); @@ -517,7 +520,7 @@ void BlackDeltaCalculatorTest::testPutCallParity(){ } } -void BlackDeltaCalculatorTest::testAtmCalcs(){ +BOOST_AUTO_TEST_CASE(testAtmCalcs){ BOOST_TEST_MESSAGE("Testing delta-neutral ATM quotations..."); @@ -679,17 +682,7 @@ void BlackDeltaCalculatorTest::testAtmCalcs(){ } } +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() - -test_suite* BlackDeltaCalculatorTest::suite() { - auto* suite = BOOST_TEST_SUITE("Black delta calculator tests"); - suite->add(QUANTLIB_TEST_CASE(&BlackDeltaCalculatorTest::testDeltaValues)); - suite->add(QUANTLIB_TEST_CASE( - &BlackDeltaCalculatorTest::testDeltaPriceConsistency)); - suite->add(QUANTLIB_TEST_CASE( - &BlackDeltaCalculatorTest::testPutCallParity)); - suite->add(QUANTLIB_TEST_CASE(&BlackDeltaCalculatorTest::testAtmCalcs)); - - return suite; -} diff --git a/test-suite/blackdeltacalculator.hpp b/test-suite/blackdeltacalculator.hpp deleted file mode 100644 index de3d435d793..00000000000 --- a/test-suite/blackdeltacalculator.hpp +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2010 Dimitri Reiswich - - This file is part of QuantLib, a free-software/open-source library - for financial quantitative analysts and developers - http://quantlib.org/ - - QuantLib is free software: you can redistribute it and/or modify it - under the terms of the QuantLib license. You should have received a - copy of the license along with this program; if not, please email - . The license is also available online at - . - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the license for more details. -*/ - -#ifndef quantlib_black_delta_calculator_test_hpp -#define quantlib_black_delta_calculator_test_hpp - -#include - -/* remember to document new and/or updated tests in the Doxygen - comment block of the corresponding class */ - -class BlackDeltaCalculatorTest { - public: - static void testDeltaValues(); - static void testDeltaPriceConsistency(); - static void testPutCallParity(); - static void testAtmCalcs(); - - static boost::unit_test_framework::test_suite* suite(); -}; - -#endif diff --git a/test-suite/blackformula.cpp b/test-suite/blackformula.cpp index 3bae78bc706..067158b7764 100644 --- a/test-suite/blackformula.cpp +++ b/test-suite/blackformula.cpp @@ -21,7 +21,7 @@ */ -#include "blackformula.hpp" +#include "toplevelfixture.hpp" #include "utilities.hpp" #include #include @@ -29,8 +29,11 @@ using namespace QuantLib; using namespace boost::unit_test_framework; +BOOST_FIXTURE_TEST_SUITE(QuantLibTest, TopLevelFixture) -void BlackFormulaTest::testBachelierImpliedVol(){ +BOOST_AUTO_TEST_SUITE(BlackFormulaTest) + +BOOST_AUTO_TEST_CASE(testBachelierImpliedVol){ BOOST_TEST_MESSAGE("Testing Bachelier implied vol..."); @@ -58,7 +61,7 @@ void BlackFormulaTest::testBachelierImpliedVol(){ } } -void BlackFormulaTest::testChambersImpliedVol() { +BOOST_AUTO_TEST_CASE(testChambersImpliedVol) { BOOST_TEST_MESSAGE("Testing Chambers-Nawalkha implied vol approximation..."); @@ -107,7 +110,7 @@ void BlackFormulaTest::testChambersImpliedVol() { } } -void BlackFormulaTest::testRadoicicStefanicaImpliedVol() { +BOOST_AUTO_TEST_CASE(testRadoicicStefanicaImpliedVol) { BOOST_TEST_MESSAGE( "Testing Radoicic-Stefanica implied vol approximation..."); @@ -154,7 +157,7 @@ void BlackFormulaTest::testRadoicicStefanicaImpliedVol() { } } -void BlackFormulaTest::testRadoicicStefanicaLowerBound() { +BOOST_AUTO_TEST_CASE(testRadoicicStefanicaLowerBound) { BOOST_TEST_MESSAGE("Testing Radoicic-Stefanica lower bound..."); @@ -195,7 +198,7 @@ void BlackFormulaTest::testRadoicicStefanicaLowerBound() { } } -void BlackFormulaTest::testImpliedVolAdaptiveSuccessiveOverRelaxation() { +BOOST_AUTO_TEST_CASE(testImpliedVolAdaptiveSuccessiveOverRelaxation) { BOOST_TEST_MESSAGE("Testing implied volatility calculation via " "adaptive successive over-relaxation..."); @@ -302,7 +305,7 @@ void assertBlackFormulaForwardDerivative( } } -void BlackFormulaTest::testBlackFormulaForwardDerivative() { +BOOST_AUTO_TEST_CASE(testBlackFormulaForwardDerivative) { BOOST_TEST_MESSAGE("Testing forward derivative of the Black formula..."); @@ -317,7 +320,7 @@ void BlackFormulaTest::testBlackFormulaForwardDerivative() { assertBlackFormulaForwardDerivative(Option::Put, strikes, vol); } -void BlackFormulaTest::testBlackFormulaForwardDerivativeWithZeroStrike() { +BOOST_AUTO_TEST_CASE(testBlackFormulaForwardDerivativeWithZeroStrike) { BOOST_TEST_MESSAGE("Testing forward derivative of the Black formula " "with zero strike..."); @@ -329,7 +332,7 @@ void BlackFormulaTest::testBlackFormulaForwardDerivativeWithZeroStrike() { assertBlackFormulaForwardDerivative(Option::Put, strikes, vol); } -void BlackFormulaTest::testBlackFormulaForwardDerivativeWithZeroVolatility() { +BOOST_AUTO_TEST_CASE(testBlackFormulaForwardDerivativeWithZeroVolatility) { BOOST_TEST_MESSAGE("Testing forward derivative of the Black formula " "with zero volatility..."); @@ -392,7 +395,7 @@ void assertBachelierBlackFormulaForwardDerivative( } } -void BlackFormulaTest::testBachelierBlackFormulaForwardDerivative() { +BOOST_AUTO_TEST_CASE(testBachelierBlackFormulaForwardDerivative) { BOOST_TEST_MESSAGE("Testing forward derivative of the " "Bachelier Black formula..."); @@ -412,7 +415,7 @@ void BlackFormulaTest::testBachelierBlackFormulaForwardDerivative() { assertBachelierBlackFormulaForwardDerivative(Option::Put, strikes, vol); } -void BlackFormulaTest::testBachelierBlackFormulaForwardDerivativeWithZeroVolatility() { +BOOST_AUTO_TEST_CASE(testBachelierBlackFormulaForwardDerivativeWithZeroVolatility) { BOOST_TEST_MESSAGE("Testing forward derivative of the Bachelier Black formula " "with zero volatility..."); @@ -432,29 +435,6 @@ void BlackFormulaTest::testBachelierBlackFormulaForwardDerivativeWithZeroVolatil assertBachelierBlackFormulaForwardDerivative(Option::Put, strikes, vol); } -test_suite* BlackFormulaTest::suite() { - auto* suite = BOOST_TEST_SUITE("Black formula tests"); - - suite->add(QUANTLIB_TEST_CASE( - &BlackFormulaTest::testBachelierImpliedVol)); - suite->add(QUANTLIB_TEST_CASE( - &BlackFormulaTest::testChambersImpliedVol)); - suite->add(QUANTLIB_TEST_CASE( - &BlackFormulaTest::testRadoicicStefanicaImpliedVol)); - suite->add(QUANTLIB_TEST_CASE( - &BlackFormulaTest::testRadoicicStefanicaLowerBound)); - suite->add(QUANTLIB_TEST_CASE( - &BlackFormulaTest::testImpliedVolAdaptiveSuccessiveOverRelaxation)); - suite->add(QUANTLIB_TEST_CASE( - &BlackFormulaTest::testBlackFormulaForwardDerivative)); - suite->add(QUANTLIB_TEST_CASE( - &BlackFormulaTest::testBlackFormulaForwardDerivativeWithZeroStrike)); - suite->add(QUANTLIB_TEST_CASE( - &BlackFormulaTest::testBlackFormulaForwardDerivativeWithZeroVolatility)); - suite->add(QUANTLIB_TEST_CASE( - &BlackFormulaTest::testBachelierBlackFormulaForwardDerivative)); - suite->add(QUANTLIB_TEST_CASE( - &BlackFormulaTest::testBachelierBlackFormulaForwardDerivativeWithZeroVolatility)); - - return suite; -} +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/test-suite/blackformula.hpp b/test-suite/blackformula.hpp deleted file mode 100644 index cf9ae00da26..00000000000 --- a/test-suite/blackformula.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2013 Gary Kennedy - Copyright (C) 2015 Peter Caspers - Copyright (C) 2017 Klaus Spanderen - Copyright (C) 2020 Marcin Rybacki - - This file is part of QuantLib, a free-software/open-source library - for financial quantitative analysts and developers - http://quantlib.org/ - - QuantLib is free software: you can redistribute it and/or modify it - under the terms of the QuantLib license. You should have received a - copy of the license along with this program; if not, please email - . The license is also available online at - . - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the license for more details. -*/ - -#ifndef quantlib_test_blackformula_hpp -#define quantlib_test_blackformula_hpp - -#include - - -class BlackFormulaTest { - public: - static void testBachelierImpliedVol(); - static void testChambersImpliedVol(); - static void testRadoicicStefanicaImpliedVol(); - static void testRadoicicStefanicaLowerBound(); - static void testImpliedVolAdaptiveSuccessiveOverRelaxation(); - static void testBlackFormulaForwardDerivative(); - static void testBlackFormulaForwardDerivativeWithZeroStrike(); - static void testBlackFormulaForwardDerivativeWithZeroVolatility(); - static void testBachelierBlackFormulaForwardDerivative(); - static void testBachelierBlackFormulaForwardDerivativeWithZeroVolatility(); - - static boost::unit_test_framework::test_suite* suite(); -}; - - -#endif diff --git a/test-suite/bondforward.cpp b/test-suite/bondforward.cpp index 3d2fc0b79ed..06c84a1cbf2 100644 --- a/test-suite/bondforward.cpp +++ b/test-suite/bondforward.cpp @@ -16,7 +16,7 @@ FOR A PARTICULAR PURPOSE. See the license for more details. */ -#include "bondforward.hpp" +#include "toplevelfixture.hpp" #include "utilities.hpp" #include #include @@ -64,7 +64,11 @@ namespace bond_forward_test { } } -void BondForwardTest::testFuturesPriceReplication() { +BOOST_FIXTURE_TEST_SUITE(QuantLibTest, TopLevelFixture) + +BOOST_AUTO_TEST_SUITE(BondForwardTest) + +BOOST_AUTO_TEST_CASE(testFuturesPriceReplication) { BOOST_TEST_MESSAGE("Testing futures price replication..."); using namespace bond_forward_test; @@ -94,7 +98,7 @@ void BondForwardTest::testFuturesPriceReplication() { << " expected: " << expectedFuturesPrice << "\n"); } -void BondForwardTest::testCleanForwardPriceReplication() { +BOOST_AUTO_TEST_CASE(testCleanForwardPriceReplication) { BOOST_TEST_MESSAGE("Testing clean forward price replication..."); using namespace bond_forward_test; @@ -123,7 +127,7 @@ void BondForwardTest::testCleanForwardPriceReplication() { << " expected: " << expectedFwdCleanPrice << "\n"); } -void BondForwardTest::testThatForwardValueIsEqualToSpotValueIfNoIncome() { +BOOST_AUTO_TEST_CASE(testThatForwardValueIsEqualToSpotValueIfNoIncome) { BOOST_TEST_MESSAGE( "Testing that forward value is equal to spot value if no income..."); @@ -153,12 +157,6 @@ void BondForwardTest::testThatForwardValueIsEqualToSpotValueIfNoIncome() { << " underlying bond: " << underlyingDirtyPrice << "\n"); } -test_suite* BondForwardTest::suite() { - auto* suite = BOOST_TEST_SUITE("Bond forward tests"); +BOOST_AUTO_TEST_SUITE_END() - suite->add(QUANTLIB_TEST_CASE(&BondForwardTest::testFuturesPriceReplication)); - suite->add(QUANTLIB_TEST_CASE(&BondForwardTest::testCleanForwardPriceReplication)); - suite->add(QUANTLIB_TEST_CASE( - &BondForwardTest::testThatForwardValueIsEqualToSpotValueIfNoIncome)); - return suite; -} \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/test-suite/bondforward.hpp b/test-suite/bondforward.hpp deleted file mode 100644 index 79f486f4f05..00000000000 --- a/test-suite/bondforward.hpp +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2022 Marcin Rybacki - - This file is part of QuantLib, a free-software/open-source library - for financial quantitative analysts and developers - http://quantlib.org/ - - QuantLib is free software: you can redistribute it and/or modify it - under the terms of the QuantLib license. You should have received a - copy of the license along with this program; if not, please email - . The license is also available online at - . - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the license for more details. -*/ - -#ifndef quantlib_test_bond_forward_hpp -#define quantlib_test_bond_forward_hpp - -#include - -/* remember to document new and/or updated tests in the Doxygen - comment block of the corresponding class */ - -class BondForwardTest { - public: - static void testFuturesPriceReplication(); - static void testCleanForwardPriceReplication(); - static void testThatForwardValueIsEqualToSpotValueIfNoIncome(); - - static boost::unit_test_framework::test_suite* suite(); -}; - - -#endif \ No newline at end of file diff --git a/test-suite/bonds.cpp b/test-suite/bonds.cpp index c48e82a46cd..afbd748ad39 100644 --- a/test-suite/bonds.cpp +++ b/test-suite/bonds.cpp @@ -19,7 +19,7 @@ FOR A PARTICULAR PURPOSE. See the license for more details. */ -#include "bonds.hpp" +#include "toplevelfixture.hpp" #include "utilities.hpp" #include #include @@ -91,7 +91,11 @@ namespace bonds_test { } -void BondTest::testYield() { +BOOST_FIXTURE_TEST_SUITE(QuantLibTest, TopLevelFixture) + +BOOST_AUTO_TEST_SUITE(BondsTest) + +BOOST_AUTO_TEST_CASE(testYield) { BOOST_TEST_MESSAGE("Testing consistency of bond price/yield calculation..."); @@ -191,7 +195,7 @@ void BondTest::testYield() { } } -void BondTest::testAtmRate() { +BOOST_AUTO_TEST_CASE(testAtmRate) { BOOST_TEST_MESSAGE("Testing consistency of bond price/ATM rate calculation..."); @@ -251,7 +255,7 @@ void BondTest::testAtmRate() { } } -void BondTest::testZspread() { +BOOST_AUTO_TEST_CASE(testZspread) { BOOST_TEST_MESSAGE("Testing consistency of bond price/z-spread calculation..."); @@ -330,9 +334,7 @@ void BondTest::testZspread() { } } - - -void BondTest::testTheoretical() { +BOOST_AUTO_TEST_CASE(testTheoretical) { BOOST_TEST_MESSAGE("Testing theoretical bond price/yield calculation..."); @@ -412,8 +414,7 @@ void BondTest::testTheoretical() { } } - -void BondTest::testCached() { +BOOST_AUTO_TEST_CASE(testCached) { BOOST_TEST_MESSAGE( "Testing bond price/yield calculation against cached values..."); @@ -685,9 +686,7 @@ void BondTest::testCached() { ); } - - -void BondTest::testCachedZero() { +BOOST_AUTO_TEST_CASE(testCachedZero) { BOOST_TEST_MESSAGE("Testing zero-coupon bond prices against cached values..."); @@ -769,8 +768,7 @@ void BondTest::testCachedZero() { } } - -void BondTest::testCachedFixed() { +BOOST_AUTO_TEST_CASE(testCachedFixed) { BOOST_TEST_MESSAGE("Testing fixed-coupon bond prices against cached values..."); @@ -869,8 +867,7 @@ void BondTest::testCachedFixed() { } } - -void BondTest::testCachedFloating() { +BOOST_AUTO_TEST_CASE(testCachedFloating) { BOOST_TEST_MESSAGE("Testing floating-rate bond prices against cached values..."); @@ -1011,7 +1008,7 @@ void BondTest::testCachedFloating() { } } -void BondTest::testBrazilianCached() { +BOOST_AUTO_TEST_CASE(testBrazilianCached) { BOOST_TEST_MESSAGE( "Testing Brazilian public bond prices against Andima cached values..."); @@ -1100,7 +1097,7 @@ void BondTest::testBrazilianCached() { } } -void BondTest::testExCouponGilt() { +BOOST_AUTO_TEST_CASE(testExCouponGilt) { BOOST_TEST_MESSAGE( "Testing ex-coupon UK Gilt price against market values..."); /* UK Gilts have an exCouponDate 7 business days before the coupon @@ -1228,8 +1225,7 @@ void BondTest::testExCouponGilt() { } } - -void BondTest::testExCouponAustralianBond() { +BOOST_AUTO_TEST_CASE(testExCouponAustralianBond) { BOOST_TEST_MESSAGE( "Testing ex-coupon Australian bond price against market values..."); /* Australian Government Bonds have an exCouponDate 7 calendar @@ -1362,7 +1358,7 @@ void BondTest::testExCouponAustralianBond() { /// This requires the use of the Schedule to be constructed /// with a custom date vector /// -void BondTest::testBondFromScheduleWithDateVector() +BOOST_AUTO_TEST_CASE(testBondFromScheduleWithDateVector) { BOOST_TEST_MESSAGE("Testing South African R2048 bond price using Schedule constructor with Date vector..."); //When pricing bond from Yield To Maturity, use NullCalendar() @@ -1439,7 +1435,7 @@ void BondTest::testBondFromScheduleWithDateVector() } } -void BondTest::testFixedBondWithGivenDates() { +BOOST_AUTO_TEST_CASE(testFixedBondWithGivenDates) { BOOST_TEST_MESSAGE("Testing fixed-coupon bond built on schedule with given dates..."); @@ -1561,7 +1557,7 @@ void BondTest::testFixedBondWithGivenDates() { } } -void BondTest::testRiskyBondWithGivenDates() { +BOOST_AUTO_TEST_CASE(testRiskyBondWithGivenDates) { BOOST_TEST_MESSAGE("Testing risky bond engine..."); @@ -1627,8 +1623,7 @@ void BondTest::testRiskyBondWithGivenDates() { } } - -void BondTest::testFixedRateBondWithArbitrarySchedule() { +BOOST_AUTO_TEST_CASE(testFixedRateBondWithArbitrarySchedule) { BOOST_TEST_MESSAGE("Testing fixed-rate bond with arbitrary schedule..."); Calendar calendar = NullCalendar(); @@ -1666,8 +1661,7 @@ void BondTest::testFixedRateBondWithArbitrarySchedule() { BOOST_CHECK_NO_THROW(bond.cleanPrice()); } - -void BondTest::testThirty360BondWithSettlementOn31st(){ +BOOST_AUTO_TEST_CASE(testThirty360BondWithSettlementOn31st){ BOOST_TEST_MESSAGE( "Testing Thirty/360 bond with settlement on 31st of the month..."); @@ -1711,25 +1705,6 @@ void BondTest::testThirty360BondWithSettlementOn31st(){ ASSERT_CLOSE("accrued", settlement, accrued, 0.7, 1e-6); } -test_suite* BondTest::suite() { - auto* suite = BOOST_TEST_SUITE("Bond tests"); - - suite->add(QUANTLIB_TEST_CASE(&BondTest::testYield)); - suite->add(QUANTLIB_TEST_CASE(&BondTest::testAtmRate)); - suite->add(QUANTLIB_TEST_CASE(&BondTest::testZspread)); - suite->add(QUANTLIB_TEST_CASE(&BondTest::testTheoretical)); - suite->add(QUANTLIB_TEST_CASE(&BondTest::testCached)); - suite->add(QUANTLIB_TEST_CASE(&BondTest::testCachedZero)); - suite->add(QUANTLIB_TEST_CASE(&BondTest::testCachedFixed)); - suite->add(QUANTLIB_TEST_CASE(&BondTest::testCachedFloating)); - suite->add(QUANTLIB_TEST_CASE(&BondTest::testBrazilianCached)); - suite->add(QUANTLIB_TEST_CASE(&BondTest::testFixedBondWithGivenDates)); - suite->add(QUANTLIB_TEST_CASE(&BondTest::testRiskyBondWithGivenDates)); - suite->add(QUANTLIB_TEST_CASE(&BondTest::testExCouponGilt)); - suite->add(QUANTLIB_TEST_CASE(&BondTest::testExCouponAustralianBond)); - suite->add(QUANTLIB_TEST_CASE(&BondTest::testBondFromScheduleWithDateVector)); - suite->add(QUANTLIB_TEST_CASE(&BondTest::testFixedRateBondWithArbitrarySchedule)); - suite->add(QUANTLIB_TEST_CASE(&BondTest::testThirty360BondWithSettlementOn31st)); - return suite; -} +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/test-suite/bonds.hpp b/test-suite/bonds.hpp deleted file mode 100644 index bb72791f26b..00000000000 --- a/test-suite/bonds.hpp +++ /dev/null @@ -1,50 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2004 StatPro Italia srl - - This file is part of QuantLib, a free-software/open-source library - for financial quantitative analysts and developers - http://quantlib.org/ - - QuantLib is free software: you can redistribute it and/or modify it - under the terms of the QuantLib license. You should have received a - copy of the license along with this program; if not, please email - . The license is also available online at - . - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the license for more details. -*/ - -#ifndef quantlib_test_bonds_hpp -#define quantlib_test_bonds_hpp - -#include - -/* remember to document new and/or updated tests in the Doxygen - comment block of the corresponding class */ - -class BondTest { - public: - static void testYield(); - static void testAtmRate(); - static void testZspread(); - static void testTheoretical(); - static void testCached(); - static void testCachedZero(); - static void testCachedFixed(); - static void testCachedFloating(); - static void testBrazilianCached(); - static void testFixedBondWithGivenDates(); - static void testRiskyBondWithGivenDates(); - static void testExCouponGilt(); - static void testExCouponAustralianBond(); - static void testBondFromScheduleWithDateVector(); - static void testFixedRateBondWithArbitrarySchedule(); - static void testThirty360BondWithSettlementOn31st(); - static boost::unit_test_framework::test_suite* suite(); -}; - - -#endif diff --git a/test-suite/brownianbridge.cpp b/test-suite/brownianbridge.cpp index c62687d91dd..bd53c766a81 100644 --- a/test-suite/brownianbridge.cpp +++ b/test-suite/brownianbridge.cpp @@ -17,7 +17,7 @@ FOR A PARTICULAR PURPOSE. See the license for more details. */ -#include "brownianbridge.hpp" +#include "toplevelfixture.hpp" #include "utilities.hpp" #include #include @@ -58,8 +58,11 @@ namespace { } +BOOST_FIXTURE_TEST_SUITE(QuantLibTest, TopLevelFixture) -void BrownianBridgeTest::testVariates() { +BOOST_AUTO_TEST_SUITE(BrownianBridgeTest) + +BOOST_AUTO_TEST_CASE(testVariates) { BOOST_TEST_MESSAGE("Testing Brownian-bridge variates..."); std::vector