From 12ee4d7c7d3eb5c126196c4a5ced3ad9e8cbb95c Mon Sep 17 00:00:00 2001 From: w31ha0 Date: Sat, 2 May 2020 01:45:18 -0700 Subject: [PATCH 1/4] Initial Commit for all 4 Monte Carlo engines for lookbacks Renaming Change int to Size Remove unused variables Fix lambda value Fix test failure --- QuantLib.vcxproj | 8 + QuantLib.vcxproj.filters | 24 ++ ql/CMakeLists.txt | 8 + ql/instruments/payoffs.cpp | 11 + ql/instruments/payoffs.hpp | 1 + ql/pricingengines/lookback/Makefile.am | 12 +- ql/pricingengines/lookback/all.hpp | 4 + .../lookback/mclookbackfixedengine.cpp | 68 ++++ .../lookback/mclookbackfixedengine.hpp | 285 ++++++++++++++ .../lookback/mclookbackfloatingengine.cpp | 66 ++++ .../lookback/mclookbackfloatingengine.hpp | 284 ++++++++++++++ .../lookback/mclookbackpartialfixedengine.cpp | 71 ++++ .../lookback/mclookbackpartialfixedengine.hpp | 290 ++++++++++++++ .../mclookbackpartialfloatingengine.cpp | 69 ++++ .../mclookbackpartialfloatingengine.hpp | 288 ++++++++++++++ test-suite/lookbackoptions.cpp | 357 +++++++++++++----- test-suite/lookbackoptions.hpp | 1 + 17 files changed, 1754 insertions(+), 93 deletions(-) create mode 100644 ql/pricingengines/lookback/mclookbackfixedengine.cpp create mode 100644 ql/pricingengines/lookback/mclookbackfixedengine.hpp create mode 100644 ql/pricingengines/lookback/mclookbackfloatingengine.cpp create mode 100644 ql/pricingengines/lookback/mclookbackfloatingengine.hpp create mode 100644 ql/pricingengines/lookback/mclookbackpartialfixedengine.cpp create mode 100644 ql/pricingengines/lookback/mclookbackpartialfixedengine.hpp create mode 100644 ql/pricingengines/lookback/mclookbackpartialfloatingengine.cpp create mode 100644 ql/pricingengines/lookback/mclookbackpartialfloatingengine.hpp diff --git a/QuantLib.vcxproj b/QuantLib.vcxproj index fdf23733ad2..2e62a51048e 100644 --- a/QuantLib.vcxproj +++ b/QuantLib.vcxproj @@ -1508,6 +1508,10 @@ + + + + @@ -2522,6 +2526,10 @@ + + + + diff --git a/QuantLib.vcxproj.filters b/QuantLib.vcxproj.filters index c69f5e89feb..e0ab5bc8128 100644 --- a/QuantLib.vcxproj.filters +++ b/QuantLib.vcxproj.filters @@ -2568,6 +2568,18 @@ pricingengines\lookback + + pricingengines\lookback + + + pricingengines\lookback + + + pricingengines\lookback + + + pricingengines\lookback + pricingengines\bond @@ -5652,6 +5664,18 @@ pricingengines\lookback + + pricingengines\lookback + + + pricingengines\lookback + + + pricingengines\lookback + + + pricingengines\lookback + pricingengines\bond diff --git a/ql/CMakeLists.txt b/ql/CMakeLists.txt index 0b391aaf3ff..199f5663893 100644 --- a/ql/CMakeLists.txt +++ b/ql/CMakeLists.txt @@ -687,6 +687,10 @@ set(QuantLib_SRC pricingengines/lookback/analyticcontinuousfloatinglookback.cpp pricingengines/lookback/analyticcontinuouspartialfixedlookback.cpp pricingengines/lookback/analyticcontinuouspartialfloatinglookback.cpp + pricingengines/lookback/mclookbackpartialfixedengine.cpp + pricingengines/lookback/mclookbackfixedengine.cpp + pricingengines/lookback/mclookbackpartialfloatingengine.cpp + pricingengines/lookback/mclookbackfloatingengine.cpp pricingengines/swap/cvaswapengine.cpp pricingengines/swap/discountingswapengine.cpp pricingengines/swap/discretizedswap.cpp @@ -1940,6 +1944,10 @@ set(QuantLib_HDR pricingengines/lookback/analyticcontinuousfloatinglookback.hpp pricingengines/lookback/analyticcontinuouspartialfixedlookback.hpp pricingengines/lookback/analyticcontinuouspartialfloatinglookback.hpp + pricingengines/lookback/mclookbackpartialfixedengine.hpp + pricingengines/lookback/mclookbackfixedengine.hpp + pricingengines/lookback/mclookbackpartialfloatingengine.hpp + pricingengines/lookback/mclookbackfloatingengine.hpp pricingengines/mclongstaffschwartzengine.hpp pricingengines/mcsimulation.hpp pricingengines/quanto/all.hpp diff --git a/ql/instruments/payoffs.cpp b/ql/instruments/payoffs.cpp index cadfdf39b9d..956e36ada29 100644 --- a/ql/instruments/payoffs.cpp +++ b/ql/instruments/payoffs.cpp @@ -63,6 +63,17 @@ namespace QuantLib { QL_FAIL("floating payoff not handled"); } + Real FloatingTypePayoff::operator()(Real price, Real strike) const { + switch (type_) { + case Option::Call: + return std::max(price - strike,0.0); + case Option::Put: + return std::max(strike - price,0.0); + default: + QL_FAIL("unknown/illegal option type"); + } + } + std::string StrikedTypePayoff::description() const { std::ostringstream result; result << TypePayoff::description() << ", " << diff --git a/ql/instruments/payoffs.hpp b/ql/instruments/payoffs.hpp index 85fd657830b..bf0659bd180 100644 --- a/ql/instruments/payoffs.hpp +++ b/ql/instruments/payoffs.hpp @@ -78,6 +78,7 @@ namespace QuantLib { //! \name Payoff interface //@{ std::string name() const { return "FloatingType";} + Real operator()(Real price, Real strike) const; Real operator()(Real price) const; virtual void accept(AcyclicVisitor&); //@} diff --git a/ql/pricingengines/lookback/Makefile.am b/ql/pricingengines/lookback/Makefile.am index 05636a68665..9aadbf46aff 100644 --- a/ql/pricingengines/lookback/Makefile.am +++ b/ql/pricingengines/lookback/Makefile.am @@ -7,13 +7,21 @@ this_include_HEADERS = \ analyticcontinuousfixedlookback.hpp \ analyticcontinuousfloatinglookback.hpp \ analyticcontinuouspartialfixedlookback.hpp \ - analyticcontinuouspartialfloatinglookback.hpp + analyticcontinuouspartialfloatinglookback.hpp \ + mclookbackpartialfixedengine.hpp \ + mclookbackfixedengine.hpp \ + mclookbackfloatingengine.hpp \ + mclookbackpartialfloatingengine.hpp cpp_files = \ analyticcontinuousfixedlookback.cpp \ analyticcontinuousfloatinglookback.cpp \ analyticcontinuouspartialfixedlookback.cpp \ - analyticcontinuouspartialfloatinglookback.cpp + analyticcontinuouspartialfloatinglookback.cpp \ + mclookbackpartialfixedengine.cpp \ + mclookbackfixedengine.cpp \ + mclookbackfloatingengine.cpp \ + mclookbackpartialfloatingengine.cpp if UNITY_BUILD diff --git a/ql/pricingengines/lookback/all.hpp b/ql/pricingengines/lookback/all.hpp index ac69dc567c5..9c7f2ed18d6 100644 --- a/ql/pricingengines/lookback/all.hpp +++ b/ql/pricingengines/lookback/all.hpp @@ -5,4 +5,8 @@ #include #include #include +#include +#include +#include +#include diff --git a/ql/pricingengines/lookback/mclookbackfixedengine.cpp b/ql/pricingengines/lookback/mclookbackfixedengine.cpp new file mode 100644 index 00000000000..da17738d21a --- /dev/null +++ b/ql/pricingengines/lookback/mclookbackfixedengine.cpp @@ -0,0 +1,68 @@ +/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + Copyright (C) 2020 Lew Wei Hao + + 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 "mclookbackfixedengine.hpp" + +namespace QuantLib { + + LookbackFixedPathPricer::LookbackFixedPathPricer( + Option::Type type, + Real strike, + const std::vector& discounts) + : payoff_(type, strike), discounts_(discounts) { + QL_REQUIRE(strike>=0.0, + "strike less than zero not allowed"); + } + + Real LookbackFixedPathPricer::operator()(const Path& path) const { + Size n = path.length(); + QL_REQUIRE(n>1, "the path cannot be empty"); + Real underlying; + Size i; + Real winnerUnderlying; + + switch (payoff_.optionType()) { + case Option::Put: + winnerUnderlying = INT_MAX; + + for (i = 0; i < n - 1; i++) { + underlying = path[i + 1]; + if (underlying < winnerUnderlying){ + winnerUnderlying = underlying; + } + } + break; + case Option::Call: + winnerUnderlying = 0; + + for (i = 0; i < n - 1; i++) { + underlying = path[i + 1]; + if (underlying > winnerUnderlying){ + winnerUnderlying = underlying; + } + } + break; + default: + QL_FAIL("unknown option type"); + } + + return payoff_(winnerUnderlying) * discounts_.back(); + } + +} diff --git a/ql/pricingengines/lookback/mclookbackfixedengine.hpp b/ql/pricingengines/lookback/mclookbackfixedengine.hpp new file mode 100644 index 00000000000..96251ebce09 --- /dev/null +++ b/ql/pricingengines/lookback/mclookbackfixedengine.hpp @@ -0,0 +1,285 @@ +/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + Copyright (C) 2020 Lew Wei Hao + + 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 mclookbackfixedengine.hpp + \brief Monte Carlo lookback fixed engine +*/ + +#ifndef quantlib_mc_lookback_fixed_engines_hpp +#define quantlib_mc_lookback_fixed_engines_hpp + +#include +#include +#include +#include + +namespace QuantLib { + + template + class MCLookbackFixedEngine : public ContinuousFixedLookbackOption::engine, + public McSimulation { + public: + typedef typename McSimulation::path_generator_type + path_generator_type; + typedef typename McSimulation::path_pricer_type + path_pricer_type; + // constructor + MCLookbackFixedEngine( + const ext::shared_ptr& process, + Size timeSteps, + Size timeStepsPerYear, + bool brownianBridge, + bool antithetic, + Size requiredSamples, + Real requiredTolerance, + Size maxSamples, + BigNatural seed); + void calculate() const { + Real spot = process_->x0(); + QL_REQUIRE(spot >= 0.0, "negative or null underlying given"); + McSimulation::calculate(requiredTolerance_, + requiredSamples_, + maxSamples_); + results_.value = this->mcModel_->sampleAccumulator().mean(); + if (RNG::allowsErrorEstimate) + results_.errorEstimate = + this->mcModel_->sampleAccumulator().errorEstimate(); + } + protected: + // McSimulation implementation + TimeGrid timeGrid() const; + ext::shared_ptr pathGenerator() const { + TimeGrid grid = timeGrid(); + typename RNG::rsg_type gen = + RNG::make_sequence_generator(grid.size()-1,seed_); + return ext::shared_ptr( + new path_generator_type(process_, + grid, gen, brownianBridge_)); + } + ext::shared_ptr pathPricer() const; + // data members + ext::shared_ptr process_; + Size timeSteps_, timeStepsPerYear_; + Size requiredSamples_, maxSamples_; + Real requiredTolerance_; + bool antithetic_; + bool brownianBridge_; + BigNatural seed_; + }; + + //! Monte Carlo lookback-option engine factory + template + class MakeMCLookbackFixedEngine { + public: + explicit MakeMCLookbackFixedEngine( + const ext::shared_ptr&); + // named parameters + MakeMCLookbackFixedEngine& withSteps(Size steps); + MakeMCLookbackFixedEngine& withStepsPerYear(Size steps); + MakeMCLookbackFixedEngine& withBrownianBridge(bool b = true); + MakeMCLookbackFixedEngine& withAntitheticVariate(bool b = true); + MakeMCLookbackFixedEngine& withSamples(Size samples); + MakeMCLookbackFixedEngine& withAbsoluteTolerance(Real tolerance); + MakeMCLookbackFixedEngine& withMaxSamples(Size samples); + MakeMCLookbackFixedEngine& withSeed(BigNatural seed); + // conversion to pricing engine + operator ext::shared_ptr() const; + private: + ext::shared_ptr process_; + bool brownianBridge_, antithetic_; + Size steps_, stepsPerYear_, samples_, maxSamples_; + Real tolerance_; + BigNatural seed_; + }; + + class LookbackFixedPathPricer : public PathPricer { + public: + LookbackFixedPathPricer(Option::Type type, + Real strike, + const std::vector& discounts); + Real operator()(const Path& path) const; + private: + PlainVanillaPayoff payoff_; + std::vector discounts_; + }; + + // template definitions + + template + inline MCLookbackFixedEngine::MCLookbackFixedEngine( + const ext::shared_ptr& process, + Size timeSteps, + Size timeStepsPerYear, + bool brownianBridge, + bool antitheticVariate, + Size requiredSamples, + Real requiredTolerance, + Size maxSamples, + BigNatural seed) + : McSimulation(antitheticVariate, false), + process_(process), timeSteps_(timeSteps), + timeStepsPerYear_(timeStepsPerYear), + requiredSamples_(requiredSamples), maxSamples_(maxSamples), + requiredTolerance_(requiredTolerance), + brownianBridge_(brownianBridge), seed_(seed) { + QL_REQUIRE(timeSteps != Null() || + timeStepsPerYear != Null(), + "no time steps provided"); + QL_REQUIRE(timeSteps == Null() || + timeStepsPerYear == Null(), + "both time steps and time steps per year were provided"); + QL_REQUIRE(timeSteps != 0, + "timeSteps must be positive, " << timeSteps << + " not allowed"); + QL_REQUIRE(timeStepsPerYear != 0, + "timeStepsPerYear must be positive, " << timeStepsPerYear << + " not allowed"); + registerWith(process_); + } + + template + inline TimeGrid MCLookbackFixedEngine::timeGrid() const { + + Time residualTime = process_->time(arguments_.exercise->lastDate()); + if (timeSteps_ != Null()) { + return TimeGrid(residualTime, timeSteps_); + } else if (timeStepsPerYear_ != Null()) { + Size steps = static_cast(timeStepsPerYear_*residualTime); + return TimeGrid(residualTime, std::max(steps, 1)); + } else { + QL_FAIL("time steps not specified"); + } + } + + template + inline + ext::shared_ptr::path_pricer_type> + MCLookbackFixedEngine::pathPricer() const { + ext::shared_ptr payoff = + ext::dynamic_pointer_cast(arguments_.payoff); + QL_REQUIRE(payoff, "non-plain payoff given"); + + TimeGrid grid = timeGrid(); + std::vector discounts(grid.size()); + for (Size i=0; iriskFreeRate()->discount(grid[i]); + + return ext::shared_ptr< + typename MCLookbackFixedEngine::path_pricer_type>( + new LookbackFixedPathPricer( + payoff->optionType(), + payoff->strike(), + discounts)); + } + + template + inline MakeMCLookbackFixedEngine::MakeMCLookbackFixedEngine( + const ext::shared_ptr& process) + : process_(process), + brownianBridge_(false), antithetic_(false), + steps_(Null()), stepsPerYear_(Null()), + samples_(Null()), maxSamples_(Null()), + tolerance_(Null()), seed_(0) {} + + template + inline MakeMCLookbackFixedEngine& + MakeMCLookbackFixedEngine::withSteps(Size steps) { + steps_ = steps; + return *this; + } + + template + inline MakeMCLookbackFixedEngine& + MakeMCLookbackFixedEngine::withStepsPerYear(Size steps) { + stepsPerYear_ = steps; + return *this; + } + + template + inline MakeMCLookbackFixedEngine& + MakeMCLookbackFixedEngine::withBrownianBridge(bool brownianBridge) { + brownianBridge_ = brownianBridge; + return *this; + } + + template + inline MakeMCLookbackFixedEngine& + MakeMCLookbackFixedEngine::withAntitheticVariate(bool b) { + antithetic_ = b; + return *this; + } + + template + inline MakeMCLookbackFixedEngine& + MakeMCLookbackFixedEngine::withSamples(Size samples) { + QL_REQUIRE(tolerance_ == Null(), + "tolerance already set"); + samples_ = samples; + return *this; + } + + template + inline MakeMCLookbackFixedEngine& + MakeMCLookbackFixedEngine::withAbsoluteTolerance(Real tolerance) { + QL_REQUIRE(samples_ == Null(), + "number of samples already set"); + QL_REQUIRE(RNG::allowsErrorEstimate, + "chosen random generator policy " + "does not allow an error estimate"); + tolerance_ = tolerance; + return *this; + } + + template + inline MakeMCLookbackFixedEngine& + MakeMCLookbackFixedEngine::withMaxSamples(Size samples) { + maxSamples_ = samples; + return *this; + } + + template + inline MakeMCLookbackFixedEngine& + MakeMCLookbackFixedEngine::withSeed(BigNatural seed) { + seed_ = seed; + return *this; + } + + template + inline MakeMCLookbackFixedEngine::operator ext::shared_ptr() + const { + QL_REQUIRE(steps_ != Null() || stepsPerYear_ != Null(), + "number of steps not given"); + QL_REQUIRE(steps_ == Null() || stepsPerYear_ == Null(), + "number of steps overspecified"); + + return ext::shared_ptr(new MCLookbackFixedEngine(process_, + steps_, + stepsPerYear_, + brownianBridge_, + antithetic_, + samples_, + tolerance_, + maxSamples_, + seed_)); + } + +} + + +#endif diff --git a/ql/pricingengines/lookback/mclookbackfloatingengine.cpp b/ql/pricingengines/lookback/mclookbackfloatingengine.cpp new file mode 100644 index 00000000000..24cf0b47ce3 --- /dev/null +++ b/ql/pricingengines/lookback/mclookbackfloatingengine.cpp @@ -0,0 +1,66 @@ +/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + Copyright (C) 2020 Lew Wei Hao + + 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 "mclookbackfloatingengine.hpp" + +namespace QuantLib { + + LookbackFloatingPathPricer::LookbackFloatingPathPricer( + Option::Type type, + const std::vector& discounts) + : payoff_(type), discounts_(discounts) { + } + + Real LookbackFloatingPathPricer::operator()(const Path& path) const { + Size n = path.length(); + QL_REQUIRE(n>1, "the path cannot be empty"); + Real underlying; + Real terminalPrice = path.back(); + Size i; + Real winnerStrike; + + switch (payoff_.optionType()) { + case Option::Call: + winnerStrike = INT_MAX; + + for (i = 0; i < n - 1; i++) { + underlying = path[i + 1]; + if (underlying < winnerStrike){ + winnerStrike = underlying; + } + } + break; + case Option::Put: + winnerStrike = 0; + + for (i = 0; i < n - 1; i++) { + underlying = path[i + 1]; + if (underlying > winnerStrike){ + winnerStrike = underlying; + } + } + break; + default: + QL_FAIL("unknown option type"); + } + + return payoff_(terminalPrice, winnerStrike) * discounts_.back(); + } + +} diff --git a/ql/pricingengines/lookback/mclookbackfloatingengine.hpp b/ql/pricingengines/lookback/mclookbackfloatingengine.hpp new file mode 100644 index 00000000000..f225cf3c7ca --- /dev/null +++ b/ql/pricingengines/lookback/mclookbackfloatingengine.hpp @@ -0,0 +1,284 @@ +/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + Copyright (C) 2020 Lew Wei Hao + + 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 mclookbackfloatingengine.hpp + \brief Monte Carlo lookback floating engine +*/ + +#ifndef quantlib_mc_lookback_floating_engines_hpp +#define quantlib_mc_lookback_floating_engines_hpp + +#include +#include +#include +#include + +namespace QuantLib { + + template + class MCLookbackFloatingEngine : public ContinuousFloatingLookbackOption::engine, + public McSimulation { + public: + typedef typename McSimulation::path_generator_type + path_generator_type; + typedef typename McSimulation::path_pricer_type + path_pricer_type; + // constructor + MCLookbackFloatingEngine( + const ext::shared_ptr& process, + Size timeSteps, + Size timeStepsPerYear, + bool brownianBridge, + bool antithetic, + Size requiredSamples, + Real requiredTolerance, + Size maxSamples, + BigNatural seed); + void calculate() const { + Real spot = process_->x0(); + QL_REQUIRE(spot >= 0.0, "negative or null underlying given"); + McSimulation::calculate(requiredTolerance_, + requiredSamples_, + maxSamples_); + results_.value = this->mcModel_->sampleAccumulator().mean(); + if (RNG::allowsErrorEstimate) + results_.errorEstimate = + this->mcModel_->sampleAccumulator().errorEstimate(); + } + protected: + // McSimulation implementation + TimeGrid timeGrid() const; + ext::shared_ptr pathGenerator() const { + TimeGrid grid = timeGrid(); + typename RNG::rsg_type gen = + RNG::make_sequence_generator(grid.size()-1,seed_); + return ext::shared_ptr( + new path_generator_type(process_, + grid, gen, brownianBridge_)); + } + ext::shared_ptr pathPricer() const; + // data members + ext::shared_ptr process_; + Size timeSteps_, timeStepsPerYear_; + Size requiredSamples_, maxSamples_; + Real requiredTolerance_; + bool antithetic_; + bool brownianBridge_; + BigNatural seed_; + }; + + //! Monte Carlo lookback-option engine factory + template + class MakeMCLookbackFloatingEngine { + public: + explicit MakeMCLookbackFloatingEngine( + const ext::shared_ptr&); + // named parameters + MakeMCLookbackFloatingEngine& withSteps(Size steps); + MakeMCLookbackFloatingEngine& withStepsPerYear(Size steps); + MakeMCLookbackFloatingEngine& withBrownianBridge(bool b = true); + MakeMCLookbackFloatingEngine& withAntitheticVariate(bool b = true); + MakeMCLookbackFloatingEngine& withSamples(Size samples); + MakeMCLookbackFloatingEngine& withAbsoluteTolerance(Real tolerance); + MakeMCLookbackFloatingEngine& withMaxSamples(Size samples); + MakeMCLookbackFloatingEngine& withSeed(BigNatural seed); + // conversion to pricing engine + operator ext::shared_ptr() const; + private: + ext::shared_ptr process_; + bool brownianBridge_, antithetic_; + Size steps_, stepsPerYear_, samples_, maxSamples_; + Real tolerance_; + BigNatural seed_; + }; + + class LookbackFloatingPathPricer : public PathPricer { + public: + LookbackFloatingPathPricer( + Option::Type type, + const std::vector& discounts); + Real operator()(const Path& path) const; + private: + FloatingTypePayoff payoff_; + std::vector discounts_; + }; + + // template definitions + + template + inline MCLookbackFloatingEngine::MCLookbackFloatingEngine( + const ext::shared_ptr& process, + Size timeSteps, + Size timeStepsPerYear, + bool brownianBridge, + bool antitheticVariate, + Size requiredSamples, + Real requiredTolerance, + Size maxSamples, + BigNatural seed) + : McSimulation(antitheticVariate, false), + process_(process), timeSteps_(timeSteps), + timeStepsPerYear_(timeStepsPerYear), + requiredSamples_(requiredSamples), maxSamples_(maxSamples), + requiredTolerance_(requiredTolerance), + brownianBridge_(brownianBridge), seed_(seed) { + QL_REQUIRE(timeSteps != Null() || + timeStepsPerYear != Null(), + "no time steps provided"); + QL_REQUIRE(timeSteps == Null() || + timeStepsPerYear == Null(), + "both time steps and time steps per year were provided"); + QL_REQUIRE(timeSteps != 0, + "timeSteps must be positive, " << timeSteps << + " not allowed"); + QL_REQUIRE(timeStepsPerYear != 0, + "timeStepsPerYear must be positive, " << timeStepsPerYear << + " not allowed"); + registerWith(process_); + } + + template + inline TimeGrid MCLookbackFloatingEngine::timeGrid() const { + + Time residualTime = process_->time(arguments_.exercise->lastDate()); + if (timeSteps_ != Null()) { + return TimeGrid(residualTime, timeSteps_); + } else if (timeStepsPerYear_ != Null()) { + Size steps = static_cast(timeStepsPerYear_*residualTime); + return TimeGrid(residualTime, std::max(steps, 1)); + } else { + QL_FAIL("time steps not specified"); + } + } + + template + inline + ext::shared_ptr::path_pricer_type> + MCLookbackFloatingEngine::pathPricer() const { + ext::shared_ptr payoff = + ext::dynamic_pointer_cast(arguments_.payoff); + QL_REQUIRE(payoff, "non-plain payoff given"); + + TimeGrid grid = timeGrid(); + std::vector discounts(grid.size()); + for (Size i=0; iriskFreeRate()->discount(grid[i]); + + return ext::shared_ptr< + typename MCLookbackFloatingEngine::path_pricer_type>( + new LookbackFloatingPathPricer( + payoff->optionType(), + discounts)); + } + + template + inline MakeMCLookbackFloatingEngine::MakeMCLookbackFloatingEngine( + const ext::shared_ptr& process) + : process_(process), + brownianBridge_(false), antithetic_(false), + steps_(Null()), stepsPerYear_(Null()), + samples_(Null()), maxSamples_(Null()), + tolerance_(Null()), seed_(0) {} + + template + inline MakeMCLookbackFloatingEngine& + MakeMCLookbackFloatingEngine::withSteps(Size steps) { + steps_ = steps; + return *this; + } + + template + inline MakeMCLookbackFloatingEngine& + MakeMCLookbackFloatingEngine::withStepsPerYear(Size steps) { + stepsPerYear_ = steps; + return *this; + } + + template + inline MakeMCLookbackFloatingEngine& + MakeMCLookbackFloatingEngine::withBrownianBridge(bool brownianBridge) { + brownianBridge_ = brownianBridge; + return *this; + } + + template + inline MakeMCLookbackFloatingEngine& + MakeMCLookbackFloatingEngine::withAntitheticVariate(bool b) { + antithetic_ = b; + return *this; + } + + template + inline MakeMCLookbackFloatingEngine& + MakeMCLookbackFloatingEngine::withSamples(Size samples) { + QL_REQUIRE(tolerance_ == Null(), + "tolerance already set"); + samples_ = samples; + return *this; + } + + template + inline MakeMCLookbackFloatingEngine& + MakeMCLookbackFloatingEngine::withAbsoluteTolerance(Real tolerance) { + QL_REQUIRE(samples_ == Null(), + "number of samples already set"); + QL_REQUIRE(RNG::allowsErrorEstimate, + "chosen random generator policy " + "does not allow an error estimate"); + tolerance_ = tolerance; + return *this; + } + + template + inline MakeMCLookbackFloatingEngine& + MakeMCLookbackFloatingEngine::withMaxSamples(Size samples) { + maxSamples_ = samples; + return *this; + } + + template + inline MakeMCLookbackFloatingEngine& + MakeMCLookbackFloatingEngine::withSeed(BigNatural seed) { + seed_ = seed; + return *this; + } + + template + inline MakeMCLookbackFloatingEngine::operator ext::shared_ptr() + const { + QL_REQUIRE(steps_ != Null() || stepsPerYear_ != Null(), + "number of steps not given"); + QL_REQUIRE(steps_ == Null() || stepsPerYear_ == Null(), + "number of steps overspecified"); + + return ext::shared_ptr(new MCLookbackFloatingEngine(process_, + steps_, + stepsPerYear_, + brownianBridge_, + antithetic_, + samples_, + tolerance_, + maxSamples_, + seed_)); + } + +} + + +#endif diff --git a/ql/pricingengines/lookback/mclookbackpartialfixedengine.cpp b/ql/pricingengines/lookback/mclookbackpartialfixedengine.cpp new file mode 100644 index 00000000000..6ae1368de8e --- /dev/null +++ b/ql/pricingengines/lookback/mclookbackpartialfixedengine.cpp @@ -0,0 +1,71 @@ +/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + Copyright (C) 2020 Lew Wei Hao + + 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 "mclookbackpartialfixedengine.hpp" + +namespace QuantLib { + + LookbackPartialFixedPathPricer::LookbackPartialFixedPathPricer( + Time lookbackStart, + Option::Type type, + Real strike, + const std::vector& discounts) + : lookbackStart_(lookbackStart), payoff_(type, strike), discounts_(discounts) { + QL_REQUIRE(strike>=0.0, + "strike less than zero not allowed"); + } + + Real LookbackPartialFixedPathPricer::operator()(const Path& path) const { + Size n = path.length(); + QL_REQUIRE(n>1, "the path cannot be empty"); + Real underlying; + TimeGrid timeGrid = path.timeGrid(); + Size lookbackStart = timeGrid.closestIndex(lookbackStart_); + Size i; + Real winnerUnderlying; + + switch (payoff_.optionType()) { + case Option::Put: + winnerUnderlying = INT_MAX; + + for (i = lookbackStart; i < n-1; i++) { + underlying = path[i + 1]; + if (underlying < winnerUnderlying){ + winnerUnderlying = underlying; + } + } + break; + case Option::Call: + winnerUnderlying = 0; + + for (i = lookbackStart; i < n-1; i++) { + underlying = path[i + 1]; + if (underlying > winnerUnderlying){ + winnerUnderlying = underlying; + } + } + break; + default: + QL_FAIL("unknown option type"); + } + + return payoff_(winnerUnderlying) * discounts_.back(); + } + +} diff --git a/ql/pricingengines/lookback/mclookbackpartialfixedengine.hpp b/ql/pricingengines/lookback/mclookbackpartialfixedengine.hpp new file mode 100644 index 00000000000..37f93cfa495 --- /dev/null +++ b/ql/pricingengines/lookback/mclookbackpartialfixedengine.hpp @@ -0,0 +1,290 @@ +/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + Copyright (C) 2020 Lew Wei Hao + + 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 mclookbackpartialfixedengine.hpp + \brief Monte Carlo lookback partial fixed engine +*/ + +#ifndef quantlib_mc_lookback_partial_fixed_engines_hpp +#define quantlib_mc_lookback_partial_fixed_engines_hpp + +#include +#include +#include +#include + +namespace QuantLib { + + template + class MCLookbackPartialFixedEngine : public ContinuousPartialFixedLookbackOption::engine, + public McSimulation { + public: + typedef typename McSimulation::path_generator_type + path_generator_type; + typedef typename McSimulation::path_pricer_type + path_pricer_type; + // constructor + MCLookbackPartialFixedEngine( + const ext::shared_ptr& process, + Size timeSteps, + Size timeStepsPerYear, + bool brownianBridge, + bool antithetic, + Size requiredSamples, + Real requiredTolerance, + Size maxSamples, + BigNatural seed); + void calculate() const { + Real spot = process_->x0(); + QL_REQUIRE(spot >= 0.0, "negative or null underlying given"); + McSimulation::calculate(requiredTolerance_, + requiredSamples_, + maxSamples_); + results_.value = this->mcModel_->sampleAccumulator().mean(); + if (RNG::allowsErrorEstimate) + results_.errorEstimate = + this->mcModel_->sampleAccumulator().errorEstimate(); + } + protected: + // McSimulation implementation + TimeGrid timeGrid() const; + ext::shared_ptr pathGenerator() const { + TimeGrid grid = timeGrid(); + typename RNG::rsg_type gen = + RNG::make_sequence_generator(grid.size()-1,seed_); + return ext::shared_ptr( + new path_generator_type(process_, + grid, gen, brownianBridge_)); + } + ext::shared_ptr pathPricer() const; + // data members + ext::shared_ptr process_; + Size timeSteps_, timeStepsPerYear_; + Size requiredSamples_, maxSamples_; + Real requiredTolerance_; + bool antithetic_; + bool brownianBridge_; + BigNatural seed_; + }; + + //! Monte Carlo lookback-option engine factory + template + class MakeMCLookbackPartialFixedEngine { + public: + explicit MakeMCLookbackPartialFixedEngine( + const ext::shared_ptr&); + // named parameters + MakeMCLookbackPartialFixedEngine& withSteps(Size steps); + MakeMCLookbackPartialFixedEngine& withStepsPerYear(Size steps); + MakeMCLookbackPartialFixedEngine& withBrownianBridge(bool b = true); + MakeMCLookbackPartialFixedEngine& withAntitheticVariate(bool b = true); + MakeMCLookbackPartialFixedEngine& withSamples(Size samples); + MakeMCLookbackPartialFixedEngine& withAbsoluteTolerance(Real tolerance); + MakeMCLookbackPartialFixedEngine& withMaxSamples(Size samples); + MakeMCLookbackPartialFixedEngine& withSeed(BigNatural seed); + // conversion to pricing engine + operator ext::shared_ptr() const; + private: + ext::shared_ptr process_; + bool brownianBridge_, antithetic_; + Size steps_, stepsPerYear_, samples_, maxSamples_; + Real tolerance_; + BigNatural seed_; + }; + + class LookbackPartialFixedPathPricer : public PathPricer { + public: + LookbackPartialFixedPathPricer(Time lookbackStart, + Option::Type type, + Real strike, + const std::vector& discounts); + Real operator()(const Path& path) const; + private: + Time lookbackStart_; + PlainVanillaPayoff payoff_; + std::vector discounts_; + }; + + // template definitions + + template + inline MCLookbackPartialFixedEngine::MCLookbackPartialFixedEngine( + const ext::shared_ptr& process, + Size timeSteps, + Size timeStepsPerYear, + bool brownianBridge, + bool antitheticVariate, + Size requiredSamples, + Real requiredTolerance, + Size maxSamples, + BigNatural seed) + : McSimulation(antitheticVariate, false), + process_(process), timeSteps_(timeSteps), + timeStepsPerYear_(timeStepsPerYear), + requiredSamples_(requiredSamples), maxSamples_(maxSamples), + requiredTolerance_(requiredTolerance), + brownianBridge_(brownianBridge), seed_(seed) { + QL_REQUIRE(timeSteps != Null() || + timeStepsPerYear != Null(), + "no time steps provided"); + QL_REQUIRE(timeSteps == Null() || + timeStepsPerYear == Null(), + "both time steps and time steps per year were provided"); + QL_REQUIRE(timeSteps != 0, + "timeSteps must be positive, " << timeSteps << + " not allowed"); + QL_REQUIRE(timeStepsPerYear != 0, + "timeStepsPerYear must be positive, " << timeStepsPerYear << + " not allowed"); + registerWith(process_); + } + + template + inline TimeGrid MCLookbackPartialFixedEngine::timeGrid() const { + + Time residualTime = process_->time(arguments_.exercise->lastDate()); + if (timeSteps_ != Null()) { + return TimeGrid(residualTime, timeSteps_); + } else if (timeStepsPerYear_ != Null()) { + Size steps = static_cast(timeStepsPerYear_*residualTime); + return TimeGrid(residualTime, std::max(steps, 1)); + } else { + QL_FAIL("time steps not specified"); + } + } + + template + inline + ext::shared_ptr::path_pricer_type> + MCLookbackPartialFixedEngine::pathPricer() const { + ext::shared_ptr payoff = + ext::dynamic_pointer_cast(arguments_.payoff); + QL_REQUIRE(payoff, "non-plain payoff given"); + + TimeGrid grid = timeGrid(); + std::vector discounts(grid.size()); + for (Size i=0; iriskFreeRate()->discount(grid[i]); + + Time lookbackStart = process_->time(arguments_.lookbackPeriodStart); + + return ext::shared_ptr< + typename MCLookbackPartialFixedEngine::path_pricer_type>( + new LookbackPartialFixedPathPricer( + lookbackStart, + payoff->optionType(), + payoff->strike(), + discounts)); + } + + template + inline MakeMCLookbackPartialFixedEngine::MakeMCLookbackPartialFixedEngine( + const ext::shared_ptr& process) + : process_(process), + brownianBridge_(false), antithetic_(false), + steps_(Null()), stepsPerYear_(Null()), + samples_(Null()), maxSamples_(Null()), + tolerance_(Null()), seed_(0) {} + + template + inline MakeMCLookbackPartialFixedEngine& + MakeMCLookbackPartialFixedEngine::withSteps(Size steps) { + steps_ = steps; + return *this; + } + + template + inline MakeMCLookbackPartialFixedEngine& + MakeMCLookbackPartialFixedEngine::withStepsPerYear(Size steps) { + stepsPerYear_ = steps; + return *this; + } + + template + inline MakeMCLookbackPartialFixedEngine& + MakeMCLookbackPartialFixedEngine::withBrownianBridge(bool brownianBridge) { + brownianBridge_ = brownianBridge; + return *this; + } + + template + inline MakeMCLookbackPartialFixedEngine& + MakeMCLookbackPartialFixedEngine::withAntitheticVariate(bool b) { + antithetic_ = b; + return *this; + } + + template + inline MakeMCLookbackPartialFixedEngine& + MakeMCLookbackPartialFixedEngine::withSamples(Size samples) { + QL_REQUIRE(tolerance_ == Null(), + "tolerance already set"); + samples_ = samples; + return *this; + } + + template + inline MakeMCLookbackPartialFixedEngine& + MakeMCLookbackPartialFixedEngine::withAbsoluteTolerance(Real tolerance) { + QL_REQUIRE(samples_ == Null(), + "number of samples already set"); + QL_REQUIRE(RNG::allowsErrorEstimate, + "chosen random generator policy " + "does not allow an error estimate"); + tolerance_ = tolerance; + return *this; + } + + template + inline MakeMCLookbackPartialFixedEngine& + MakeMCLookbackPartialFixedEngine::withMaxSamples(Size samples) { + maxSamples_ = samples; + return *this; + } + + template + inline MakeMCLookbackPartialFixedEngine& + MakeMCLookbackPartialFixedEngine::withSeed(BigNatural seed) { + seed_ = seed; + return *this; + } + + template + inline MakeMCLookbackPartialFixedEngine::operator ext::shared_ptr() + const { + QL_REQUIRE(steps_ != Null() || stepsPerYear_ != Null(), + "number of steps not given"); + QL_REQUIRE(steps_ == Null() || stepsPerYear_ == Null(), + "number of steps overspecified"); + + return ext::shared_ptr(new MCLookbackPartialFixedEngine(process_, + steps_, + stepsPerYear_, + brownianBridge_, + antithetic_, + samples_, + tolerance_, + maxSamples_, + seed_)); + } + +} + + +#endif diff --git a/ql/pricingengines/lookback/mclookbackpartialfloatingengine.cpp b/ql/pricingengines/lookback/mclookbackpartialfloatingengine.cpp new file mode 100644 index 00000000000..2952c20e172 --- /dev/null +++ b/ql/pricingengines/lookback/mclookbackpartialfloatingengine.cpp @@ -0,0 +1,69 @@ +/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + Copyright (C) 2020 Lew Wei Hao + + 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 "mclookbackpartialfloatingengine.hpp" + +namespace QuantLib { + + LookbackPartialFloatingPathPricer::LookbackPartialFloatingPathPricer( + Time lookbackEnd, + Option::Type type, + const std::vector& discounts) + : lookbackEnd_(lookbackEnd), payoff_(type), discounts_(discounts) { + } + + Real LookbackPartialFloatingPathPricer::operator()(const Path& path) const { + Size n = path.length(); + QL_REQUIRE(n>1, "the path cannot be empty"); + Real underlying; + TimeGrid timeGrid = path.timeGrid(); + Size lookbackEnd = timeGrid.closestIndex(lookbackEnd_); + Real terminalPrice = path.back(); + Size i; + Real winnerStrike; + + switch (payoff_.optionType()) { + case Option::Call: + winnerStrike = INT_MAX; + + for (i = 0; i < lookbackEnd; i++) { + underlying = path[i + 1]; + if (underlying < winnerStrike){ + winnerStrike = underlying; + } + } + break; + case Option::Put: + winnerStrike = 0; + + for (i = 0; i < lookbackEnd; i++) { + underlying = path[i + 1]; + if (underlying > winnerStrike){ + winnerStrike = underlying; + } + } + break; + default: + QL_FAIL("unknown option type"); + } + + return payoff_(terminalPrice, winnerStrike) * discounts_.back(); + } + +} diff --git a/ql/pricingengines/lookback/mclookbackpartialfloatingengine.hpp b/ql/pricingengines/lookback/mclookbackpartialfloatingengine.hpp new file mode 100644 index 00000000000..ffefe5b4a45 --- /dev/null +++ b/ql/pricingengines/lookback/mclookbackpartialfloatingengine.hpp @@ -0,0 +1,288 @@ +/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + Copyright (C) 2020 Lew Wei Hao + + 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 mclookbackpartialfloatingengine.hpp + \brief Monte Carlo lookback partial floating engine +*/ + +#ifndef quantlib_mc_partial_floating_lookback_engines_hpp +#define quantlib_mc_partial_floating_lookback_engines_hpp + +#include +#include +#include +#include + +namespace QuantLib { + + template + class MCLookbackPartialFloatingEngine : public ContinuousPartialFloatingLookbackOption::engine, + public McSimulation { + public: + typedef typename McSimulation::path_generator_type + path_generator_type; + typedef typename McSimulation::path_pricer_type + path_pricer_type; + // constructor + MCLookbackPartialFloatingEngine( + const ext::shared_ptr& process, + Size timeSteps, + Size timeStepsPerYear, + bool brownianBridge, + bool antithetic, + Size requiredSamples, + Real requiredTolerance, + Size maxSamples, + BigNatural seed); + void calculate() const { + Real spot = process_->x0(); + QL_REQUIRE(spot >= 0.0, "negative or null underlying given"); + McSimulation::calculate(requiredTolerance_, + requiredSamples_, + maxSamples_); + results_.value = this->mcModel_->sampleAccumulator().mean(); + if (RNG::allowsErrorEstimate) + results_.errorEstimate = + this->mcModel_->sampleAccumulator().errorEstimate(); + } + protected: + // McSimulation implementation + TimeGrid timeGrid() const; + ext::shared_ptr pathGenerator() const { + TimeGrid grid = timeGrid(); + typename RNG::rsg_type gen = + RNG::make_sequence_generator(grid.size()-1,seed_); + return ext::shared_ptr( + new path_generator_type(process_, + grid, gen, brownianBridge_)); + } + ext::shared_ptr pathPricer() const; + // data members + ext::shared_ptr process_; + Size timeSteps_, timeStepsPerYear_; + Size requiredSamples_, maxSamples_; + Real requiredTolerance_; + bool antithetic_; + bool brownianBridge_; + BigNatural seed_; + }; + + //! Monte Carlo lookback-option engine factory + template + class MakeMCLookbackPartialFloatingEngine { + public: + explicit MakeMCLookbackPartialFloatingEngine( + const ext::shared_ptr&); + // named parameters + MakeMCLookbackPartialFloatingEngine& withSteps(Size steps); + MakeMCLookbackPartialFloatingEngine& withStepsPerYear(Size steps); + MakeMCLookbackPartialFloatingEngine& withBrownianBridge(bool b = true); + MakeMCLookbackPartialFloatingEngine& withAntitheticVariate(bool b = true); + MakeMCLookbackPartialFloatingEngine& withSamples(Size samples); + MakeMCLookbackPartialFloatingEngine& withAbsoluteTolerance(Real tolerance); + MakeMCLookbackPartialFloatingEngine& withMaxSamples(Size samples); + MakeMCLookbackPartialFloatingEngine& withSeed(BigNatural seed); + // conversion to pricing engine + operator ext::shared_ptr() const; + private: + ext::shared_ptr process_; + bool brownianBridge_, antithetic_; + Size steps_, stepsPerYear_, samples_, maxSamples_; + Real tolerance_; + BigNatural seed_; + }; + + class LookbackPartialFloatingPathPricer : public PathPricer { + public: + LookbackPartialFloatingPathPricer(Time lookbackEnd, + Option::Type type, + const std::vector& discounts); + Real operator()(const Path& path) const; + private: + Time lookbackEnd_; + FloatingTypePayoff payoff_; + std::vector discounts_; + }; + + // template definitions + + template + inline MCLookbackPartialFloatingEngine::MCLookbackPartialFloatingEngine( + const ext::shared_ptr& process, + Size timeSteps, + Size timeStepsPerYear, + bool brownianBridge, + bool antitheticVariate, + Size requiredSamples, + Real requiredTolerance, + Size maxSamples, + BigNatural seed) + : McSimulation(antitheticVariate, false), + process_(process), timeSteps_(timeSteps), + timeStepsPerYear_(timeStepsPerYear), + requiredSamples_(requiredSamples), maxSamples_(maxSamples), + requiredTolerance_(requiredTolerance), + brownianBridge_(brownianBridge), seed_(seed) { + QL_REQUIRE(timeSteps != Null() || + timeStepsPerYear != Null(), + "no time steps provided"); + QL_REQUIRE(timeSteps == Null() || + timeStepsPerYear == Null(), + "both time steps and time steps per year were provided"); + QL_REQUIRE(timeSteps != 0, + "timeSteps must be positive, " << timeSteps << + " not allowed"); + QL_REQUIRE(timeStepsPerYear != 0, + "timeStepsPerYear must be positive, " << timeStepsPerYear << + " not allowed"); + registerWith(process_); + } + + template + inline TimeGrid MCLookbackPartialFloatingEngine::timeGrid() const { + + Time residualTime = process_->time(arguments_.exercise->lastDate()); + if (timeSteps_ != Null()) { + return TimeGrid(residualTime, timeSteps_); + } else if (timeStepsPerYear_ != Null()) { + Size steps = static_cast(timeStepsPerYear_*residualTime); + return TimeGrid(residualTime, std::max(steps, 1)); + } else { + QL_FAIL("time steps not specified"); + } + } + + template + inline + ext::shared_ptr::path_pricer_type> + MCLookbackPartialFloatingEngine::pathPricer() const { + ext::shared_ptr payoff = + ext::dynamic_pointer_cast(arguments_.payoff); + QL_REQUIRE(payoff, "non-plain payoff given"); + + TimeGrid grid = timeGrid(); + std::vector discounts(grid.size()); + for (Size i=0; iriskFreeRate()->discount(grid[i]); + + Time lookbackEnd = process_->time(arguments_.lookbackPeriodEnd); + + return ext::shared_ptr< + typename MCLookbackPartialFloatingEngine::path_pricer_type>( + new LookbackPartialFloatingPathPricer( + lookbackEnd, + payoff->optionType(), + discounts)); + } + + template + inline MakeMCLookbackPartialFloatingEngine::MakeMCLookbackPartialFloatingEngine( + const ext::shared_ptr& process) + : process_(process), + brownianBridge_(false), antithetic_(false), + steps_(Null()), stepsPerYear_(Null()), + samples_(Null()), maxSamples_(Null()), + tolerance_(Null()), seed_(0) {} + + template + inline MakeMCLookbackPartialFloatingEngine& + MakeMCLookbackPartialFloatingEngine::withSteps(Size steps) { + steps_ = steps; + return *this; + } + + template + inline MakeMCLookbackPartialFloatingEngine& + MakeMCLookbackPartialFloatingEngine::withStepsPerYear(Size steps) { + stepsPerYear_ = steps; + return *this; + } + + template + inline MakeMCLookbackPartialFloatingEngine& + MakeMCLookbackPartialFloatingEngine::withBrownianBridge(bool brownianBridge) { + brownianBridge_ = brownianBridge; + return *this; + } + + template + inline MakeMCLookbackPartialFloatingEngine& + MakeMCLookbackPartialFloatingEngine::withAntitheticVariate(bool b) { + antithetic_ = b; + return *this; + } + + template + inline MakeMCLookbackPartialFloatingEngine& + MakeMCLookbackPartialFloatingEngine::withSamples(Size samples) { + QL_REQUIRE(tolerance_ == Null(), + "tolerance already set"); + samples_ = samples; + return *this; + } + + template + inline MakeMCLookbackPartialFloatingEngine& + MakeMCLookbackPartialFloatingEngine::withAbsoluteTolerance(Real tolerance) { + QL_REQUIRE(samples_ == Null(), + "number of samples already set"); + QL_REQUIRE(RNG::allowsErrorEstimate, + "chosen random generator policy " + "does not allow an error estimate"); + tolerance_ = tolerance; + return *this; + } + + template + inline MakeMCLookbackPartialFloatingEngine& + MakeMCLookbackPartialFloatingEngine::withMaxSamples(Size samples) { + maxSamples_ = samples; + return *this; + } + + template + inline MakeMCLookbackPartialFloatingEngine& + MakeMCLookbackPartialFloatingEngine::withSeed(BigNatural seed) { + seed_ = seed; + return *this; + } + + template + inline MakeMCLookbackPartialFloatingEngine::operator ext::shared_ptr() + const { + QL_REQUIRE(steps_ != Null() || stepsPerYear_ != Null(), + "number of steps not given"); + QL_REQUIRE(steps_ == Null() || stepsPerYear_ == Null(), + "number of steps overspecified"); + + return ext::shared_ptr(new MCLookbackPartialFloatingEngine(process_, + steps_, + stepsPerYear_, + brownianBridge_, + antithetic_, + samples_, + tolerance_, + maxSamples_, + seed_)); + } + +} + + +#endif diff --git a/test-suite/lookbackoptions.cpp b/test-suite/lookbackoptions.cpp index 31f37307e31..23acebd1529 100644 --- a/test-suite/lookbackoptions.cpp +++ b/test-suite/lookbackoptions.cpp @@ -27,6 +27,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -74,6 +78,14 @@ using namespace boost::unit_test_framework; << " error: " << error << "\n" \ << " tolerance: " << tolerance); +#undef REPORT_FAILURE_MC +#define REPORT_FAILURE_MC(optionType, analytical, monteCarlo, tolerance) \ + BOOST_ERROR( \ + "Analytical and MC " << optionType << " values differed by more than tolerance" << "\n" \ + << " Analytical: " << analytical << "\n" \ + << " Monte Carlo: " << monteCarlo << "\n" \ + << " tolerance: " << tolerance); + namespace { struct LookbackOptionData { @@ -284,51 +296,51 @@ void LookbackOptionTest::testAnalyticContinuousPartialFloatingLookback() { //type, strike, minmax, s, q, r, t, v, l, t1, result, tol { Option::Call, 0, 90, 90, 0, 0.06, 1, 0.1, 1, 0.25, 8.6524, 1.0e-4}, - { Option::Call, 0, 90, 90, 0, 0.06, 1, 0.1, 1, 0.5, 9.2128, 1.0e-4}, - { Option::Call, 0, 90, 90, 0, 0.06, 1, 0.1, 1, 0.75, 9.5567, 1.0e-4}, - - { Option::Call, 0, 110, 110, 0, 0.06, 1, 0.1, 1, 0.25, 10.5751, 1.0e-4}, - { Option::Call, 0, 110, 110, 0, 0.06, 1, 0.1, 1, 0.5, 11.2601, 1.0e-4}, - { Option::Call, 0, 110, 110, 0, 0.06, 1, 0.1, 1, 0.75, 11.6804, 1.0e-4}, - - { Option::Call, 0, 90, 90, 0, 0.06, 1, 0.2, 1, 0.25, 13.3402, 1.0e-4}, - { Option::Call, 0, 90, 90, 0, 0.06, 1, 0.2, 1, 0.5, 14.5121, 1.0e-4}, - { Option::Call, 0, 90, 90, 0, 0.06, 1, 0.2, 1, 0.75, 15.314, 1.0e-4}, - - { Option::Call, 0, 110, 110, 0, 0.06, 1, 0.2, 1, 0.25, 16.3047, 1.0e-4}, - { Option::Call, 0, 110, 110, 0, 0.06, 1, 0.2, 1, 0.5, 17.737, 1.0e-4}, - { Option::Call, 0, 110, 110, 0, 0.06, 1, 0.2, 1, 0.75, 18.7171, 1.0e-4}, - - { Option::Call, 0, 90, 90, 0, 0.06, 1, 0.3, 1, 0.25, 17.9831, 1.0e-4}, - { Option::Call, 0, 90, 90, 0, 0.06, 1, 0.3, 1, 0.5, 19.6618, 1.0e-4}, - { Option::Call, 0, 90, 90, 0, 0.06, 1, 0.3, 1, 0.75, 20.8493, 1.0e-4}, - - { Option::Call, 0, 110, 110, 0, 0.06, 1, 0.3, 1, 0.25, 21.9793, 1.0e-4}, - { Option::Call, 0, 110, 110, 0, 0.06, 1, 0.3, 1, 0.5, 24.0311, 1.0e-4}, - { Option::Call, 0, 110, 110, 0, 0.06, 1, 0.3, 1, 0.75, 25.4825, 1.0e-4}, - - { Option::Put, 0, 90, 90, 0, 0.06, 1, 0.1, 1, 0.25, 2.7189, 1.0e-4}, - { Option::Put, 0, 90, 90, 0, 0.06, 1, 0.1, 1, 0.5, 3.4639, 1.0e-4}, - { Option::Put, 0, 90, 90, 0, 0.06, 1, 0.1, 1, 0.75, 4.1912, 1.0e-4}, - - { Option::Put, 0, 110, 110, 0, 0.06, 1, 0.1, 1, 0.25, 3.3231, 1.0e-4}, - { Option::Put, 0, 110, 110, 0, 0.06, 1, 0.1, 1, 0.5, 4.2336, 1.0e-4}, - { Option::Put, 0, 110, 110, 0, 0.06, 1, 0.1, 1, 0.75, 5.1226, 1.0e-4}, - - { Option::Put, 0, 90, 90, 0, 0.06, 1, 0.2, 1, 0.25, 7.9153, 1.0e-4}, - { Option::Put, 0, 90, 90, 0, 0.06, 1, 0.2, 1, 0.5, 9.5825, 1.0e-4}, - { Option::Put, 0, 90, 90, 0, 0.06, 1, 0.2, 1, 0.75, 11.0362, 1.0e-4}, - - { Option::Put, 0, 110, 110, 0, 0.06, 1, 0.2, 1, 0.25, 9.6743, 1.0e-4}, - { Option::Put, 0, 110, 110, 0, 0.06, 1, 0.2, 1, 0.5, 11.7119, 1.0e-4}, - { Option::Put, 0, 110, 110, 0, 0.06, 1, 0.2, 1, 0.75, 13.4887, 1.0e-4}, - - { Option::Put, 0, 90, 90, 0, 0.06, 1, 0.3, 1, 0.25, 13.4719, 1.0e-4}, - { Option::Put, 0, 90, 90, 0, 0.06, 1, 0.3, 1, 0.5, 16.1495, 1.0e-4}, - { Option::Put, 0, 90, 90, 0, 0.06, 1, 0.3, 1, 0.75, 18.4071, 1.0e-4}, - - { Option::Put, 0, 110, 110, 0, 0.06, 1, 0.3, 1, 0.25, 16.4657, 1.0e-4}, - { Option::Put, 0, 110, 110, 0, 0.06, 1, 0.3, 1, 0.5, 19.7383, 1.0e-4}, + { Option::Call, 0, 90, 90, 0, 0.06, 1, 0.1, 1, 0.5, 9.2128, 1.0e-4}, + { Option::Call, 0, 90, 90, 0, 0.06, 1, 0.1, 1, 0.75, 9.5567, 1.0e-4}, + + { Option::Call, 0, 110, 110, 0, 0.06, 1, 0.1, 1, 0.25, 10.5751, 1.0e-4}, + { Option::Call, 0, 110, 110, 0, 0.06, 1, 0.1, 1, 0.5, 11.2601, 1.0e-4}, + { Option::Call, 0, 110, 110, 0, 0.06, 1, 0.1, 1, 0.75, 11.6804, 1.0e-4}, + + { Option::Call, 0, 90, 90, 0, 0.06, 1, 0.2, 1, 0.25, 13.3402, 1.0e-4}, + { Option::Call, 0, 90, 90, 0, 0.06, 1, 0.2, 1, 0.5, 14.5121, 1.0e-4}, + { Option::Call, 0, 90, 90, 0, 0.06, 1, 0.2, 1, 0.75, 15.314, 1.0e-4}, + + { Option::Call, 0, 110, 110, 0, 0.06, 1, 0.2, 1, 0.25, 16.3047, 1.0e-4}, + { Option::Call, 0, 110, 110, 0, 0.06, 1, 0.2, 1, 0.5, 17.737, 1.0e-4}, + { Option::Call, 0, 110, 110, 0, 0.06, 1, 0.2, 1, 0.75, 18.7171, 1.0e-4}, + + { Option::Call, 0, 90, 90, 0, 0.06, 1, 0.3, 1, 0.25, 17.9831, 1.0e-4}, + { Option::Call, 0, 90, 90, 0, 0.06, 1, 0.3, 1, 0.5, 19.6618, 1.0e-4}, + { Option::Call, 0, 90, 90, 0, 0.06, 1, 0.3, 1, 0.75, 20.8493, 1.0e-4}, + + { Option::Call, 0, 110, 110, 0, 0.06, 1, 0.3, 1, 0.25, 21.9793, 1.0e-4}, + { Option::Call, 0, 110, 110, 0, 0.06, 1, 0.3, 1, 0.5, 24.0311, 1.0e-4}, + { Option::Call, 0, 110, 110, 0, 0.06, 1, 0.3, 1, 0.75, 25.4825, 1.0e-4}, + + { Option::Put, 0, 90, 90, 0, 0.06, 1, 0.1, 1, 0.25, 2.7189, 1.0e-4}, + { Option::Put, 0, 90, 90, 0, 0.06, 1, 0.1, 1, 0.5, 3.4639, 1.0e-4}, + { Option::Put, 0, 90, 90, 0, 0.06, 1, 0.1, 1, 0.75, 4.1912, 1.0e-4}, + + { Option::Put, 0, 110, 110, 0, 0.06, 1, 0.1, 1, 0.25, 3.3231, 1.0e-4}, + { Option::Put, 0, 110, 110, 0, 0.06, 1, 0.1, 1, 0.5, 4.2336, 1.0e-4}, + { Option::Put, 0, 110, 110, 0, 0.06, 1, 0.1, 1, 0.75, 5.1226, 1.0e-4}, + + { Option::Put, 0, 90, 90, 0, 0.06, 1, 0.2, 1, 0.25, 7.9153, 1.0e-4}, + { Option::Put, 0, 90, 90, 0, 0.06, 1, 0.2, 1, 0.5, 9.5825, 1.0e-4}, + { Option::Put, 0, 90, 90, 0, 0.06, 1, 0.2, 1, 0.75, 11.0362, 1.0e-4}, + + { Option::Put, 0, 110, 110, 0, 0.06, 1, 0.2, 1, 0.25, 9.6743, 1.0e-4}, + { Option::Put, 0, 110, 110, 0, 0.06, 1, 0.2, 1, 0.5, 11.7119, 1.0e-4}, + { Option::Put, 0, 110, 110, 0, 0.06, 1, 0.2, 1, 0.75, 13.4887, 1.0e-4}, + + { Option::Put, 0, 90, 90, 0, 0.06, 1, 0.3, 1, 0.25, 13.4719, 1.0e-4}, + { Option::Put, 0, 90, 90, 0, 0.06, 1, 0.3, 1, 0.5, 16.1495, 1.0e-4}, + { Option::Put, 0, 90, 90, 0, 0.06, 1, 0.3, 1, 0.75, 18.4071, 1.0e-4}, + + { Option::Put, 0, 110, 110, 0, 0.06, 1, 0.3, 1, 0.25, 16.4657, 1.0e-4}, + { Option::Put, 0, 110, 110, 0, 0.06, 1, 0.3, 1, 0.5, 19.7383, 1.0e-4}, { Option::Put, 0, 110, 110, 0, 0.06, 1, 0.3, 1, 0.75, 22.4976, 1.0e-4} }; @@ -394,52 +406,52 @@ void LookbackOptionTest::testAnalyticContinuousPartialFixedLookback() { LookbackOptionData values[] = { // data from "Option Pricing Formulas, Second Edition", Haug, 2006, pg.148 //type, strike, minmax, s, q, r, t, v, l, t1, result, tol - { Option::Call, 90, 0, 100, 0, 0.06, 1, 0.1, 0, 0.25, 20.2845, 1.0e-4}, - { Option::Call, 90, 0, 100, 0, 0.06, 1, 0.1, 0, 0.5, 19.6239, 1.0e-4}, - { Option::Call, 90, 0, 100, 0, 0.06, 1, 0.1, 0, 0.75, 18.6244, 1.0e-4}, - - { Option::Call, 110, 0, 100, 0, 0.06, 1, 0.1, 0, 0.25, 4.0432, 1.0e-4}, - { Option::Call, 110, 0, 100, 0, 0.06, 1, 0.1, 0, 0.5, 3.958, 1.0e-4}, - { Option::Call, 110, 0, 100, 0, 0.06, 1, 0.1, 0, 0.75, 3.7015, 1.0e-4}, - - { Option::Call, 90, 0, 100, 0, 0.06, 1, 0.2, 0, 0.25, 27.5385, 1.0e-4}, - { Option::Call, 90, 0, 100, 0, 0.06, 1, 0.2, 0, 0.5, 25.8126, 1.0e-4}, - { Option::Call, 90, 0, 100, 0, 0.06, 1, 0.2, 0, 0.75, 23.4957, 1.0e-4}, - - { Option::Call, 110, 0, 100, 0, 0.06, 1, 0.2, 0, 0.25, 11.4895, 1.0e-4}, - { Option::Call, 110, 0, 100, 0, 0.06, 1, 0.2, 0, 0.5, 10.8995, 1.0e-4}, - { Option::Call, 110, 0, 100, 0, 0.06, 1, 0.2, 0, 0.75, 9.8244, 1.0e-4}, - - { Option::Call, 90, 0, 100, 0, 0.06, 1, 0.3, 0, 0.25, 35.4578, 1.0e-4}, - { Option::Call, 90, 0, 100, 0, 0.06, 1, 0.3, 0, 0.5, 32.7172, 1.0e-4}, - { Option::Call, 90, 0, 100, 0, 0.06, 1, 0.3, 0, 0.75, 29.1473, 1.0e-4}, - - { Option::Call, 110, 0, 100, 0, 0.06, 1, 0.3, 0, 0.25, 19.725, 1.0e-4}, - { Option::Call, 110, 0, 100, 0, 0.06, 1, 0.3, 0, 0.5, 18.4025, 1.0e-4}, - { Option::Call, 110, 0, 100, 0, 0.06, 1, 0.3, 0, 0.75, 16.2976, 1.0e-4}, - - { Option::Put, 90, 0, 100, 0, 0.06, 1, 0.1, 0, 0.25, 0.4973, 1.0e-4}, - { Option::Put, 90, 0, 100, 0, 0.06, 1, 0.1, 0, 0.5, 0.4632, 1.0e-4}, - { Option::Put, 90, 0, 100, 0, 0.06, 1, 0.1, 0, 0.75, 0.3863, 1.0e-4}, - - { Option::Put, 110, 0, 100, 0, 0.06, 1, 0.1, 0, 0.25, 12.6978, 1.0e-4}, - { Option::Put, 110, 0, 100, 0, 0.06, 1, 0.1, 0, 0.5, 10.9492, 1.0e-4}, - { Option::Put, 110, 0, 100, 0, 0.06, 1, 0.1, 0, 0.75, 9.1555, 1.0e-4}, - - { Option::Put, 90, 0, 100, 0, 0.06, 1, 0.2, 0, 0.25, 4.5863, 1.0e-4}, - { Option::Put, 90, 0, 100, 0, 0.06, 1, 0.2, 0, 0.5, 4.1925, 1.0e-4}, - { Option::Put, 90, 0, 100, 0, 0.06, 1, 0.2, 0, 0.75, 3.5831, 1.0e-4}, - - { Option::Put, 110, 0, 100, 0, 0.06, 1, 0.2, 0, 0.25, 19.0255, 1.0e-4}, - { Option::Put, 110, 0, 100, 0, 0.06, 1, 0.2, 0, 0.5, 16.9433, 1.0e-4}, - { Option::Put, 110, 0, 100, 0, 0.06, 1, 0.2, 0, 0.75, 14.6505, 1.0e-4}, - - { Option::Put, 90, 0, 100, 0, 0.06, 1, 0.3, 0, 0.25, 9.9348, 1.0e-4}, - { Option::Put, 90, 0, 100, 0, 0.06, 1, 0.3, 0, 0.5, 9.1111, 1.0e-4}, - { Option::Put, 90, 0, 100, 0, 0.06, 1, 0.3, 0, 0.75, 7.9267, 1.0e-4}, - - { Option::Put, 110, 0, 100, 0, 0.06, 1, 0.3, 0, 0.25, 25.2112, 1.0e-4}, - { Option::Put, 110, 0, 100, 0, 0.06, 1, 0.3, 0, 0.5, 22.8217, 1.0e-4}, + { Option::Call, 90, 0, 100, 0, 0.06, 1, 0.1, 0, 0.25, 20.2845, 1.0e-4}, + { Option::Call, 90, 0, 100, 0, 0.06, 1, 0.1, 0, 0.5, 19.6239, 1.0e-4}, + { Option::Call, 90, 0, 100, 0, 0.06, 1, 0.1, 0, 0.75, 18.6244, 1.0e-4}, + + { Option::Call, 110, 0, 100, 0, 0.06, 1, 0.1, 0, 0.25, 4.0432, 1.0e-4}, + { Option::Call, 110, 0, 100, 0, 0.06, 1, 0.1, 0, 0.5, 3.958, 1.0e-4}, + { Option::Call, 110, 0, 100, 0, 0.06, 1, 0.1, 0, 0.75, 3.7015, 1.0e-4}, + + { Option::Call, 90, 0, 100, 0, 0.06, 1, 0.2, 0, 0.25, 27.5385, 1.0e-4}, + { Option::Call, 90, 0, 100, 0, 0.06, 1, 0.2, 0, 0.5, 25.8126, 1.0e-4}, + { Option::Call, 90, 0, 100, 0, 0.06, 1, 0.2, 0, 0.75, 23.4957, 1.0e-4}, + + { Option::Call, 110, 0, 100, 0, 0.06, 1, 0.2, 0, 0.25, 11.4895, 1.0e-4}, + { Option::Call, 110, 0, 100, 0, 0.06, 1, 0.2, 0, 0.5, 10.8995, 1.0e-4}, + { Option::Call, 110, 0, 100, 0, 0.06, 1, 0.2, 0, 0.75, 9.8244, 1.0e-4}, + + { Option::Call, 90, 0, 100, 0, 0.06, 1, 0.3, 0, 0.25, 35.4578, 1.0e-4}, + { Option::Call, 90, 0, 100, 0, 0.06, 1, 0.3, 0, 0.5, 32.7172, 1.0e-4}, + { Option::Call, 90, 0, 100, 0, 0.06, 1, 0.3, 0, 0.75, 29.1473, 1.0e-4}, + + { Option::Call, 110, 0, 100, 0, 0.06, 1, 0.3, 0, 0.25, 19.725, 1.0e-4}, + { Option::Call, 110, 0, 100, 0, 0.06, 1, 0.3, 0, 0.5, 18.4025, 1.0e-4}, + { Option::Call, 110, 0, 100, 0, 0.06, 1, 0.3, 0, 0.75, 16.2976, 1.0e-4}, + + { Option::Put, 90, 0, 100, 0, 0.06, 1, 0.1, 0, 0.25, 0.4973, 1.0e-4}, + { Option::Put, 90, 0, 100, 0, 0.06, 1, 0.1, 0, 0.5, 0.4632, 1.0e-4}, + { Option::Put, 90, 0, 100, 0, 0.06, 1, 0.1, 0, 0.75, 0.3863, 1.0e-4}, + + { Option::Put, 110, 0, 100, 0, 0.06, 1, 0.1, 0, 0.25, 12.6978, 1.0e-4}, + { Option::Put, 110, 0, 100, 0, 0.06, 1, 0.1, 0, 0.5, 10.9492, 1.0e-4}, + { Option::Put, 110, 0, 100, 0, 0.06, 1, 0.1, 0, 0.75, 9.1555, 1.0e-4}, + + { Option::Put, 90, 0, 100, 0, 0.06, 1, 0.2, 0, 0.25, 4.5863, 1.0e-4}, + { Option::Put, 90, 0, 100, 0, 0.06, 1, 0.2, 0, 0.5, 4.1925, 1.0e-4}, + { Option::Put, 90, 0, 100, 0, 0.06, 1, 0.2, 0, 0.75, 3.5831, 1.0e-4}, + + { Option::Put, 110, 0, 100, 0, 0.06, 1, 0.2, 0, 0.25, 19.0255, 1.0e-4}, + { Option::Put, 110, 0, 100, 0, 0.06, 1, 0.2, 0, 0.5, 16.9433, 1.0e-4}, + { Option::Put, 110, 0, 100, 0, 0.06, 1, 0.2, 0, 0.75, 14.6505, 1.0e-4}, + + { Option::Put, 90, 0, 100, 0, 0.06, 1, 0.3, 0, 0.25, 9.9348, 1.0e-4}, + { Option::Put, 90, 0, 100, 0, 0.06, 1, 0.3, 0, 0.5, 9.1111, 1.0e-4}, + { Option::Put, 90, 0, 100, 0, 0.06, 1, 0.3, 0, 0.75, 7.9267, 1.0e-4}, + + { Option::Put, 110, 0, 100, 0, 0.06, 1, 0.3, 0, 0.25, 25.2112, 1.0e-4}, + { Option::Put, 110, 0, 100, 0, 0.06, 1, 0.3, 0, 0.5, 22.8217, 1.0e-4}, { Option::Put, 110, 0, 100, 0, 0.06, 1, 0.3, 0, 0.75, 20.0566, 1.0e-4} }; @@ -494,6 +506,167 @@ void LookbackOptionTest::testAnalyticContinuousPartialFixedLookback() { } } +void LookbackOptionTest::testMonteCarloLookback() { + Real tolerance = 0.01; + + DayCounter dc = Actual360(); + Date today = Date::todaysDate(); + + Real strike = 90; + Real t = 1; + Real t1= 0.25; + + Date exDate = today + Integer(t*360+0.5); + ext::shared_ptr exercise(new EuropeanExercise(exDate)); + + ext::shared_ptr spot(new SimpleQuote(0.0)); + ext::shared_ptr qRate(new SimpleQuote(0.0)); + ext::shared_ptr qTS = flatRate(today, qRate, dc); + ext::shared_ptr rRate(new SimpleQuote(0.0)); + ext::shared_ptr rTS = flatRate(today, rRate, dc); + ext::shared_ptr vol(new SimpleQuote(0.0)); + ext::shared_ptr volTS = flatVol(today, vol, dc); + + spot ->setValue(100); + qRate->setValue(0); + rRate->setValue(0.06); + vol ->setValue(0.1); + + ext::shared_ptr payoff( + new PlainVanillaPayoff(Option::Call, strike)); + + ext::shared_ptr stochProcess( + new BlackScholesMertonProcess( + Handle(spot), + Handle(qTS), + Handle(rTS), + Handle(volTS))); + + ext::shared_ptr engine( + new AnalyticContinuousPartialFixedLookbackEngine(stochProcess)); + + /** + * Partial Fixed + * **/ + + Date lookbackStart = today + Integer(t1*360+0.5); + ContinuousPartialFixedLookbackOption partialFixedLookback(lookbackStart, + payoff, + exercise); + partialFixedLookback.setPricingEngine(engine); + + Real analytical = partialFixedLookback.NPV(); + + ext::shared_ptr mcpartialfixedengine = + MakeMCLookbackPartialFixedEngine(stochProcess) + .withSteps(1000) + .withAntitheticVariate() + .withSeed(1) + .withAbsoluteTolerance(0.1); + + partialFixedLookback.setPricingEngine(mcpartialfixedengine); + Real monteCarlo = partialFixedLookback.NPV(); + + Real percentageDiff = std::abs(analytical - monteCarlo) / analytical; + + if (percentageDiff > tolerance){ + REPORT_FAILURE_MC("Partial Fixed", analytical, monteCarlo, tolerance); + } + + /** + * Fixed + * **/ + + Real minMax = 100; + + ContinuousFixedLookbackOption fixedLookback(minMax, + payoff, + exercise); + ext::shared_ptr analyticalfixedengine( + new AnalyticContinuousFixedLookbackEngine(stochProcess)); + fixedLookback.setPricingEngine(analyticalfixedengine); + analytical = fixedLookback.NPV(); + + ext::shared_ptr mcfixedengine = + MakeMCLookbackFixedEngine(stochProcess) + .withSteps(1000) + .withAntitheticVariate() + .withSeed(1) + .withAbsoluteTolerance(0.1); + fixedLookback.setPricingEngine(mcfixedengine); + monteCarlo = fixedLookback.NPV(); + + percentageDiff = std::abs(analytical - monteCarlo) / analytical; + + if (percentageDiff > tolerance){ + REPORT_FAILURE_MC("Fixed", analytical, monteCarlo, tolerance); + } + + /** + * Partial Floating + * **/ + + Real lambda = 1; + Date lookbackEnd = today + Integer(t1*360+0.5); + + ext::shared_ptr floatingPayoff( + new FloatingTypePayoff(Option::Call)); + + ContinuousPartialFloatingLookbackOption partialFloating(minMax, + lambda, + lookbackEnd, + floatingPayoff, + exercise); + ext::shared_ptr analyticalpartialFloatingengine( + new AnalyticContinuousPartialFloatingLookbackEngine(stochProcess)); + partialFloating.setPricingEngine(analyticalpartialFloatingengine); + analytical = partialFloating.NPV(); + + ext::shared_ptr mcpartialfloatingengine = + MakeMCLookbackPartialFloatingEngine(stochProcess) + .withSteps(1000) + .withAntitheticVariate() + .withSeed(1) + .withAbsoluteTolerance(0.05); + partialFloating.setPricingEngine(mcpartialfloatingengine); + monteCarlo = partialFloating.NPV(); + + percentageDiff = std::abs(analytical - monteCarlo) / analytical; + + if (percentageDiff > tolerance){ + REPORT_FAILURE_MC("Partial Floating", analytical, monteCarlo, tolerance); + } + + /** + * Floating + * **/ + + ContinuousFloatingLookbackOption floating(minMax, + floatingPayoff, + exercise); + ext::shared_ptr analyticalFloatingengine( + new AnalyticContinuousFloatingLookbackEngine(stochProcess)); + floating.setPricingEngine(analyticalFloatingengine); + analytical = floating.NPV(); + + ext::shared_ptr mcfloatingengine = + MakeMCLookbackFloatingEngine(stochProcess) + .withSteps(1000) + .withAntitheticVariate() + .withSeed(1) + .withAbsoluteTolerance(0.05); + floating.setPricingEngine(mcfloatingengine); + monteCarlo = floating.NPV(); + + percentageDiff = std::abs(analytical - monteCarlo) / analytical; + + if (percentageDiff > tolerance){ + REPORT_FAILURE_MC("Floating", analytical, monteCarlo, tolerance); + } + +} + + test_suite* LookbackOptionTest::suite() { test_suite* suite = BOOST_TEST_SUITE("Lookback option tests"); @@ -505,6 +678,8 @@ test_suite* LookbackOptionTest::suite() { &LookbackOptionTest::testAnalyticContinuousPartialFloatingLookback)); suite->add(QUANTLIB_TEST_CASE( &LookbackOptionTest::testAnalyticContinuousPartialFixedLookback)); + suite->add(QUANTLIB_TEST_CASE( + &LookbackOptionTest::testMonteCarloLookback)); return suite; } diff --git a/test-suite/lookbackoptions.hpp b/test-suite/lookbackoptions.hpp index 4eb455b306f..2d4d2baef7c 100644 --- a/test-suite/lookbackoptions.hpp +++ b/test-suite/lookbackoptions.hpp @@ -31,6 +31,7 @@ class LookbackOptionTest { static void testAnalyticContinuousFixedLookback(); static void testAnalyticContinuousPartialFloatingLookback(); static void testAnalyticContinuousPartialFixedLookback(); + static void testMonteCarloLookback(); static boost::unit_test_framework::test_suite* suite(); }; From 89f2dfb218c14655cc5e8f72ab6580a2dcdac7d1 Mon Sep 17 00:00:00 2001 From: Luigi Ballabio Date: Fri, 8 May 2020 10:22:27 +0200 Subject: [PATCH 2/4] Also test Put options. --- test-suite/lookbackoptions.cpp | 221 +++++++++++++++++---------------- 1 file changed, 115 insertions(+), 106 deletions(-) diff --git a/test-suite/lookbackoptions.cpp b/test-suite/lookbackoptions.cpp index 23acebd1529..59f88946e71 100644 --- a/test-suite/lookbackoptions.cpp +++ b/test-suite/lookbackoptions.cpp @@ -79,12 +79,13 @@ using namespace boost::unit_test_framework; << " tolerance: " << tolerance); #undef REPORT_FAILURE_MC -#define REPORT_FAILURE_MC(optionType, analytical, monteCarlo, tolerance) \ +#define REPORT_FAILURE_MC(lookbackType, optionType, analytical, monteCarlo, tolerance) \ BOOST_ERROR( \ - "Analytical and MC " << optionType << " values differed by more than tolerance" << "\n" \ - << " Analytical: " << analytical << "\n" \ - << " Monte Carlo: " << monteCarlo << "\n" \ - << " tolerance: " << tolerance); + "Analytical and MC " << lookbackType << " " << optionType << " values differed by more than tolerance" << "\n" \ + << " Analytical: " << analytical << "\n" \ + << " Monte Carlo: " << monteCarlo << "\n" \ + << " tolerance: " << tolerance << "\n" \ + << " difference: " << std::abs(analytical - monteCarlo)); namespace { @@ -507,7 +508,7 @@ void LookbackOptionTest::testAnalyticContinuousPartialFixedLookback() { } void LookbackOptionTest::testMonteCarloLookback() { - Real tolerance = 0.01; + Real tolerance = 0.1; DayCounter dc = Actual360(); Date today = Date::todaysDate(); @@ -532,9 +533,6 @@ void LookbackOptionTest::testMonteCarloLookback() { rRate->setValue(0.06); vol ->setValue(0.1); - ext::shared_ptr payoff( - new PlainVanillaPayoff(Option::Call, strike)); - ext::shared_ptr stochProcess( new BlackScholesMertonProcess( Handle(spot), @@ -542,128 +540,139 @@ void LookbackOptionTest::testMonteCarloLookback() { Handle(rTS), Handle(volTS))); - ext::shared_ptr engine( - new AnalyticContinuousPartialFixedLookbackEngine(stochProcess)); + Option::Type types[] = { Option::Call, Option::Put }; + + for (Size i=0; i payoff(new PlainVanillaPayoff(type, strike)); - Date lookbackStart = today + Integer(t1*360+0.5); - ContinuousPartialFixedLookbackOption partialFixedLookback(lookbackStart, - payoff, - exercise); - partialFixedLookback.setPricingEngine(engine); + /** + * Partial Fixed + * **/ - Real analytical = partialFixedLookback.NPV(); + Date lookbackStart = today + Integer(t1*360+0.5); + ContinuousPartialFixedLookbackOption partialFixedLookback(lookbackStart, + payoff, + exercise); + ext::shared_ptr engine( + new AnalyticContinuousPartialFixedLookbackEngine(stochProcess)); + partialFixedLookback.setPricingEngine(engine); - ext::shared_ptr mcpartialfixedengine = - MakeMCLookbackPartialFixedEngine(stochProcess) - .withSteps(1000) - .withAntitheticVariate() - .withSeed(1) - .withAbsoluteTolerance(0.1); + Real analytical = partialFixedLookback.NPV(); - partialFixedLookback.setPricingEngine(mcpartialfixedengine); - Real monteCarlo = partialFixedLookback.NPV(); + ext::shared_ptr mcpartialfixedengine = + MakeMCLookbackPartialFixedEngine(stochProcess) + .withSteps(2000) + .withAntitheticVariate() + .withSeed(1) + .withAbsoluteTolerance(tolerance); - Real percentageDiff = std::abs(analytical - monteCarlo) / analytical; + partialFixedLookback.setPricingEngine(mcpartialfixedengine); + Real monteCarlo = partialFixedLookback.NPV(); - if (percentageDiff > tolerance){ - REPORT_FAILURE_MC("Partial Fixed", analytical, monteCarlo, tolerance); - } + Real diff = std::abs(analytical - monteCarlo); - /** - * Fixed - * **/ + if (diff > tolerance){ + REPORT_FAILURE_MC("Partial Fixed", type, analytical, monteCarlo, tolerance); + } - Real minMax = 100; + /** + * Fixed + * **/ - ContinuousFixedLookbackOption fixedLookback(minMax, - payoff, - exercise); - ext::shared_ptr analyticalfixedengine( - new AnalyticContinuousFixedLookbackEngine(stochProcess)); - fixedLookback.setPricingEngine(analyticalfixedengine); - analytical = fixedLookback.NPV(); - - ext::shared_ptr mcfixedengine = - MakeMCLookbackFixedEngine(stochProcess) - .withSteps(1000) + Real minMax = 100; + + ContinuousFixedLookbackOption fixedLookback(minMax, + payoff, + exercise); + ext::shared_ptr analyticalfixedengine( + new AnalyticContinuousFixedLookbackEngine(stochProcess)); + fixedLookback.setPricingEngine(analyticalfixedengine); + + analytical = fixedLookback.NPV(); + + ext::shared_ptr mcfixedengine = + MakeMCLookbackFixedEngine(stochProcess) + .withSteps(2000) .withAntitheticVariate() .withSeed(1) - .withAbsoluteTolerance(0.1); - fixedLookback.setPricingEngine(mcfixedengine); - monteCarlo = fixedLookback.NPV(); + .withAbsoluteTolerance(tolerance); - percentageDiff = std::abs(analytical - monteCarlo) / analytical; + fixedLookback.setPricingEngine(mcfixedengine); + monteCarlo = fixedLookback.NPV(); - if (percentageDiff > tolerance){ - REPORT_FAILURE_MC("Fixed", analytical, monteCarlo, tolerance); - } + diff = std::abs(analytical - monteCarlo); - /** - * Partial Floating - * **/ - - Real lambda = 1; - Date lookbackEnd = today + Integer(t1*360+0.5); - - ext::shared_ptr floatingPayoff( - new FloatingTypePayoff(Option::Call)); - - ContinuousPartialFloatingLookbackOption partialFloating(minMax, - lambda, - lookbackEnd, - floatingPayoff, - exercise); - ext::shared_ptr analyticalpartialFloatingengine( - new AnalyticContinuousPartialFloatingLookbackEngine(stochProcess)); - partialFloating.setPricingEngine(analyticalpartialFloatingengine); - analytical = partialFloating.NPV(); - - ext::shared_ptr mcpartialfloatingengine = - MakeMCLookbackPartialFloatingEngine(stochProcess) - .withSteps(1000) + if (diff > tolerance){ + REPORT_FAILURE_MC("Fixed", type, analytical, monteCarlo, tolerance); + } + + /** + * Partial Floating + * **/ + + Real lambda = 1; + Date lookbackEnd = today + Integer(t1*360+0.5); + + ext::shared_ptr floatingPayoff(new FloatingTypePayoff(type)); + + ContinuousPartialFloatingLookbackOption partialFloating(minMax, + lambda, + lookbackEnd, + floatingPayoff, + exercise); + ext::shared_ptr analyticalpartialFloatingengine( + new AnalyticContinuousPartialFloatingLookbackEngine(stochProcess)); + partialFloating.setPricingEngine(analyticalpartialFloatingengine); + + analytical = partialFloating.NPV(); + + ext::shared_ptr mcpartialfloatingengine = + MakeMCLookbackPartialFloatingEngine(stochProcess) + .withSteps(2000) .withAntitheticVariate() .withSeed(1) - .withAbsoluteTolerance(0.05); - partialFloating.setPricingEngine(mcpartialfloatingengine); - monteCarlo = partialFloating.NPV(); + .withAbsoluteTolerance(tolerance); - percentageDiff = std::abs(analytical - monteCarlo) / analytical; + partialFloating.setPricingEngine(mcpartialfloatingengine); + monteCarlo = partialFloating.NPV(); - if (percentageDiff > tolerance){ - REPORT_FAILURE_MC("Partial Floating", analytical, monteCarlo, tolerance); - } + diff = std::abs(analytical - monteCarlo); - /** - * Floating - * **/ - - ContinuousFloatingLookbackOption floating(minMax, - floatingPayoff, - exercise); - ext::shared_ptr analyticalFloatingengine( - new AnalyticContinuousFloatingLookbackEngine(stochProcess)); - floating.setPricingEngine(analyticalFloatingengine); - analytical = floating.NPV(); - - ext::shared_ptr mcfloatingengine = - MakeMCLookbackFloatingEngine(stochProcess) - .withSteps(1000) + if (diff > tolerance){ + REPORT_FAILURE_MC("Partial Floating", type, analytical, monteCarlo, tolerance); + } + + /** + * Floating + * **/ + + ContinuousFloatingLookbackOption floating(minMax, + floatingPayoff, + exercise); + ext::shared_ptr analyticalFloatingengine( + new AnalyticContinuousFloatingLookbackEngine(stochProcess)); + floating.setPricingEngine(analyticalFloatingengine); + + analytical = floating.NPV(); + + ext::shared_ptr mcfloatingengine = + MakeMCLookbackFloatingEngine(stochProcess) + .withSteps(2000) .withAntitheticVariate() .withSeed(1) - .withAbsoluteTolerance(0.05); - floating.setPricingEngine(mcfloatingengine); - monteCarlo = floating.NPV(); + .withAbsoluteTolerance(tolerance); - percentageDiff = std::abs(analytical - monteCarlo) / analytical; + floating.setPricingEngine(mcfloatingengine); + monteCarlo = floating.NPV(); - if (percentageDiff > tolerance){ - REPORT_FAILURE_MC("Floating", analytical, monteCarlo, tolerance); - } + diff = std::abs(analytical - monteCarlo); + if (diff > tolerance){ + REPORT_FAILURE_MC("Floating", type, analytical, monteCarlo, tolerance); + } + } } From 65b2213e9aae4360ad202207a7d6c91ff6ab15c0 Mon Sep 17 00:00:00 2001 From: Luigi Ballabio Date: Fri, 8 May 2020 10:14:42 +0200 Subject: [PATCH 3/4] Simplify path pricers. --- .../lookback/mclookbackfixedengine.cpp | 44 ++++++----------- .../lookback/mclookbackfixedengine.hpp | 10 ++-- .../lookback/mclookbackfloatingengine.cpp | 47 ++++++------------ .../lookback/mclookbackfloatingengine.hpp | 10 ++-- .../lookback/mclookbackpartialfixedengine.cpp | 48 +++++++------------ .../lookback/mclookbackpartialfixedengine.hpp | 10 ++-- .../mclookbackpartialfloatingengine.cpp | 48 +++++++------------ .../mclookbackpartialfloatingengine.hpp | 10 ++-- test-suite/lookbackoptions.cpp | 2 + 9 files changed, 79 insertions(+), 150 deletions(-) diff --git a/ql/pricingengines/lookback/mclookbackfixedengine.cpp b/ql/pricingengines/lookback/mclookbackfixedengine.cpp index da17738d21a..b76bf0b6da9 100644 --- a/ql/pricingengines/lookback/mclookbackfixedengine.cpp +++ b/ql/pricingengines/lookback/mclookbackfixedengine.cpp @@ -18,51 +18,35 @@ */ #include "mclookbackfixedengine.hpp" +#include namespace QuantLib { LookbackFixedPathPricer::LookbackFixedPathPricer( Option::Type type, Real strike, - const std::vector& discounts) - : payoff_(type, strike), discounts_(discounts) { + DiscountFactor discount) + : payoff_(type, strike), discount_(discount) { QL_REQUIRE(strike>=0.0, "strike less than zero not allowed"); } Real LookbackFixedPathPricer::operator()(const Path& path) const { - Size n = path.length(); - QL_REQUIRE(n>1, "the path cannot be empty"); - Real underlying; - Size i; - Real winnerUnderlying; + QL_REQUIRE(!path.empty(), "the path cannot be empty"); + Real underlying; switch (payoff_.optionType()) { - case Option::Put: - winnerUnderlying = INT_MAX; - - for (i = 0; i < n - 1; i++) { - underlying = path[i + 1]; - if (underlying < winnerUnderlying){ - winnerUnderlying = underlying; - } - } - break; - case Option::Call: - winnerUnderlying = 0; - - for (i = 0; i < n - 1; i++) { - underlying = path[i + 1]; - if (underlying > winnerUnderlying){ - winnerUnderlying = underlying; - } - } - break; - default: - QL_FAIL("unknown option type"); + case Option::Put: + underlying = *std::min_element(path.begin()+1, path.end()); + break; + case Option::Call: + underlying = *std::max_element(path.begin()+1, path.end()); + break; + default: + QL_FAIL("unknown option type"); } - return payoff_(winnerUnderlying) * discounts_.back(); + return payoff_(underlying) * discount_; } } diff --git a/ql/pricingengines/lookback/mclookbackfixedengine.hpp b/ql/pricingengines/lookback/mclookbackfixedengine.hpp index 96251ebce09..26722b82bb7 100644 --- a/ql/pricingengines/lookback/mclookbackfixedengine.hpp +++ b/ql/pricingengines/lookback/mclookbackfixedengine.hpp @@ -112,11 +112,11 @@ namespace QuantLib { public: LookbackFixedPathPricer(Option::Type type, Real strike, - const std::vector& discounts); + DiscountFactor discount); Real operator()(const Path& path) const; private: PlainVanillaPayoff payoff_; - std::vector discounts_; + DiscountFactor discount_; }; // template definitions @@ -176,16 +176,14 @@ namespace QuantLib { QL_REQUIRE(payoff, "non-plain payoff given"); TimeGrid grid = timeGrid(); - std::vector discounts(grid.size()); - for (Size i=0; iriskFreeRate()->discount(grid[i]); + DiscountFactor discount = process_->riskFreeRate()->discount(grid.back()); return ext::shared_ptr< typename MCLookbackFixedEngine::path_pricer_type>( new LookbackFixedPathPricer( payoff->optionType(), payoff->strike(), - discounts)); + discount)); } template diff --git a/ql/pricingengines/lookback/mclookbackfloatingengine.cpp b/ql/pricingengines/lookback/mclookbackfloatingengine.cpp index 24cf0b47ce3..3f08ed42a56 100644 --- a/ql/pricingengines/lookback/mclookbackfloatingengine.cpp +++ b/ql/pricingengines/lookback/mclookbackfloatingengine.cpp @@ -18,49 +18,32 @@ */ #include "mclookbackfloatingengine.hpp" +#include namespace QuantLib { LookbackFloatingPathPricer::LookbackFloatingPathPricer( Option::Type type, - const std::vector& discounts) - : payoff_(type), discounts_(discounts) { - } + const DiscountFactor discount) + : payoff_(type), discount_(discount) {} Real LookbackFloatingPathPricer::operator()(const Path& path) const { - Size n = path.length(); - QL_REQUIRE(n>1, "the path cannot be empty"); - Real underlying; - Real terminalPrice = path.back(); - Size i; - Real winnerStrike; + QL_REQUIRE(!path.empty(), "the path cannot be empty"); + Real terminalPrice = path.back(); + Real strike; switch (payoff_.optionType()) { - case Option::Call: - winnerStrike = INT_MAX; - - for (i = 0; i < n - 1; i++) { - underlying = path[i + 1]; - if (underlying < winnerStrike){ - winnerStrike = underlying; - } - } - break; - case Option::Put: - winnerStrike = 0; - - for (i = 0; i < n - 1; i++) { - underlying = path[i + 1]; - if (underlying > winnerStrike){ - winnerStrike = underlying; - } - } - break; - default: - QL_FAIL("unknown option type"); + case Option::Call: + strike = *std::min_element(path.begin()+1, path.end()); + break; + case Option::Put: + strike = *std::max_element(path.begin()+1, path.end()); + break; + default: + QL_FAIL("unknown option type"); } - return payoff_(terminalPrice, winnerStrike) * discounts_.back(); + return payoff_(terminalPrice, strike) * discount_; } } diff --git a/ql/pricingengines/lookback/mclookbackfloatingengine.hpp b/ql/pricingengines/lookback/mclookbackfloatingengine.hpp index f225cf3c7ca..459e6c09698 100644 --- a/ql/pricingengines/lookback/mclookbackfloatingengine.hpp +++ b/ql/pricingengines/lookback/mclookbackfloatingengine.hpp @@ -112,11 +112,11 @@ namespace QuantLib { public: LookbackFloatingPathPricer( Option::Type type, - const std::vector& discounts); + DiscountFactor discount); Real operator()(const Path& path) const; private: FloatingTypePayoff payoff_; - std::vector discounts_; + DiscountFactor discount_; }; // template definitions @@ -176,15 +176,13 @@ namespace QuantLib { QL_REQUIRE(payoff, "non-plain payoff given"); TimeGrid grid = timeGrid(); - std::vector discounts(grid.size()); - for (Size i=0; iriskFreeRate()->discount(grid[i]); + DiscountFactor discount = process_->riskFreeRate()->discount(grid.back()); return ext::shared_ptr< typename MCLookbackFloatingEngine::path_pricer_type>( new LookbackFloatingPathPricer( payoff->optionType(), - discounts)); + discount)); } template diff --git a/ql/pricingengines/lookback/mclookbackpartialfixedengine.cpp b/ql/pricingengines/lookback/mclookbackpartialfixedengine.cpp index 6ae1368de8e..aff295a3349 100644 --- a/ql/pricingengines/lookback/mclookbackpartialfixedengine.cpp +++ b/ql/pricingengines/lookback/mclookbackpartialfixedengine.cpp @@ -18,6 +18,7 @@ */ #include "mclookbackpartialfixedengine.hpp" +#include namespace QuantLib { @@ -25,47 +26,30 @@ namespace QuantLib { Time lookbackStart, Option::Type type, Real strike, - const std::vector& discounts) - : lookbackStart_(lookbackStart), payoff_(type, strike), discounts_(discounts) { + const DiscountFactor discount) + : lookbackStart_(lookbackStart), payoff_(type, strike), discount_(discount) { QL_REQUIRE(strike>=0.0, "strike less than zero not allowed"); } Real LookbackPartialFixedPathPricer::operator()(const Path& path) const { - Size n = path.length(); - QL_REQUIRE(n>1, "the path cannot be empty"); - Real underlying; - TimeGrid timeGrid = path.timeGrid(); - Size lookbackStart = timeGrid.closestIndex(lookbackStart_); - Size i; - Real winnerUnderlying; + QL_REQUIRE(!path.empty(), "the path cannot be empty"); + TimeGrid timeGrid = path.timeGrid(); + Size startIndex = timeGrid.closestIndex(lookbackStart_); + Real underlying; switch (payoff_.optionType()) { - case Option::Put: - winnerUnderlying = INT_MAX; - - for (i = lookbackStart; i < n-1; i++) { - underlying = path[i + 1]; - if (underlying < winnerUnderlying){ - winnerUnderlying = underlying; - } - } - break; - case Option::Call: - winnerUnderlying = 0; - - for (i = lookbackStart; i < n-1; i++) { - underlying = path[i + 1]; - if (underlying > winnerUnderlying){ - winnerUnderlying = underlying; - } - } - break; - default: - QL_FAIL("unknown option type"); + case Option::Put: + underlying = *std::min_element(path.begin()+startIndex+1, path.end()); + break; + case Option::Call: + underlying = *std::max_element(path.begin()+startIndex+1, path.end()); + break; + default: + QL_FAIL("unknown option type"); } - return payoff_(winnerUnderlying) * discounts_.back(); + return payoff_(underlying) * discount_; } } diff --git a/ql/pricingengines/lookback/mclookbackpartialfixedengine.hpp b/ql/pricingengines/lookback/mclookbackpartialfixedengine.hpp index 37f93cfa495..3dbe8b7571a 100644 --- a/ql/pricingengines/lookback/mclookbackpartialfixedengine.hpp +++ b/ql/pricingengines/lookback/mclookbackpartialfixedengine.hpp @@ -113,12 +113,12 @@ namespace QuantLib { LookbackPartialFixedPathPricer(Time lookbackStart, Option::Type type, Real strike, - const std::vector& discounts); + const DiscountFactor discount); Real operator()(const Path& path) const; private: Time lookbackStart_; PlainVanillaPayoff payoff_; - std::vector discounts_; + DiscountFactor discount_; }; // template definitions @@ -178,9 +178,7 @@ namespace QuantLib { QL_REQUIRE(payoff, "non-plain payoff given"); TimeGrid grid = timeGrid(); - std::vector discounts(grid.size()); - for (Size i=0; iriskFreeRate()->discount(grid[i]); + DiscountFactor discount = process_->riskFreeRate()->discount(grid.back()); Time lookbackStart = process_->time(arguments_.lookbackPeriodStart); @@ -190,7 +188,7 @@ namespace QuantLib { lookbackStart, payoff->optionType(), payoff->strike(), - discounts)); + discount)); } template diff --git a/ql/pricingengines/lookback/mclookbackpartialfloatingengine.cpp b/ql/pricingengines/lookback/mclookbackpartialfloatingengine.cpp index 2952c20e172..82bef963dc8 100644 --- a/ql/pricingengines/lookback/mclookbackpartialfloatingengine.cpp +++ b/ql/pricingengines/lookback/mclookbackpartialfloatingengine.cpp @@ -18,52 +18,36 @@ */ #include "mclookbackpartialfloatingengine.hpp" +#include namespace QuantLib { LookbackPartialFloatingPathPricer::LookbackPartialFloatingPathPricer( Time lookbackEnd, Option::Type type, - const std::vector& discounts) - : lookbackEnd_(lookbackEnd), payoff_(type), discounts_(discounts) { - } + DiscountFactor discount) + : lookbackEnd_(lookbackEnd), payoff_(type), discount_(discount) {} Real LookbackPartialFloatingPathPricer::operator()(const Path& path) const { - Size n = path.length(); - QL_REQUIRE(n>1, "the path cannot be empty"); - Real underlying; + QL_REQUIRE(!path.empty(), "the path cannot be empty"); + TimeGrid timeGrid = path.timeGrid(); - Size lookbackEnd = timeGrid.closestIndex(lookbackEnd_); + Size endIndex = timeGrid.closestIndex(lookbackEnd_); Real terminalPrice = path.back(); - Size i; - Real winnerStrike; + Real strike; switch (payoff_.optionType()) { - case Option::Call: - winnerStrike = INT_MAX; - - for (i = 0; i < lookbackEnd; i++) { - underlying = path[i + 1]; - if (underlying < winnerStrike){ - winnerStrike = underlying; - } - } - break; - case Option::Put: - winnerStrike = 0; - - for (i = 0; i < lookbackEnd; i++) { - underlying = path[i + 1]; - if (underlying > winnerStrike){ - winnerStrike = underlying; - } - } - break; - default: - QL_FAIL("unknown option type"); + case Option::Call: + strike = *std::min_element(path.begin()+1, path.begin()+endIndex+1); + break; + case Option::Put: + strike = *std::max_element(path.begin()+1, path.begin()+endIndex+1); + break; + default: + QL_FAIL("unknown option type"); } - return payoff_(terminalPrice, winnerStrike) * discounts_.back(); + return payoff_(terminalPrice, strike) * discount_; } } diff --git a/ql/pricingengines/lookback/mclookbackpartialfloatingengine.hpp b/ql/pricingengines/lookback/mclookbackpartialfloatingengine.hpp index ffefe5b4a45..f7e8b2c1c68 100644 --- a/ql/pricingengines/lookback/mclookbackpartialfloatingengine.hpp +++ b/ql/pricingengines/lookback/mclookbackpartialfloatingengine.hpp @@ -112,12 +112,12 @@ namespace QuantLib { public: LookbackPartialFloatingPathPricer(Time lookbackEnd, Option::Type type, - const std::vector& discounts); + DiscountFactor discount); Real operator()(const Path& path) const; private: Time lookbackEnd_; FloatingTypePayoff payoff_; - std::vector discounts_; + DiscountFactor discount_; }; // template definitions @@ -177,9 +177,7 @@ namespace QuantLib { QL_REQUIRE(payoff, "non-plain payoff given"); TimeGrid grid = timeGrid(); - std::vector discounts(grid.size()); - for (Size i=0; iriskFreeRate()->discount(grid[i]); + DiscountFactor discount = process_->riskFreeRate()->discount(grid.back()); Time lookbackEnd = process_->time(arguments_.lookbackPeriodEnd); @@ -188,7 +186,7 @@ namespace QuantLib { new LookbackPartialFloatingPathPricer( lookbackEnd, payoff->optionType(), - discounts)); + discount)); } template diff --git a/test-suite/lookbackoptions.cpp b/test-suite/lookbackoptions.cpp index 59f88946e71..daa22bfc883 100644 --- a/test-suite/lookbackoptions.cpp +++ b/test-suite/lookbackoptions.cpp @@ -508,6 +508,8 @@ void LookbackOptionTest::testAnalyticContinuousPartialFixedLookback() { } void LookbackOptionTest::testMonteCarloLookback() { + BOOST_TEST_MESSAGE("Testing Monte Carlo engines for lookback options..."); + Real tolerance = 0.1; DayCounter dc = Actual360(); From 10827f0a4a70ab9fa21c920e09e2ffda16d5263c Mon Sep 17 00:00:00 2001 From: Luigi Ballabio Date: Fri, 8 May 2020 15:56:17 +0200 Subject: [PATCH 4/4] Common template engine. --- QuantLib.vcxproj | 12 +- QuantLib.vcxproj.filters | 24 +- ql/CMakeLists.txt | 10 +- ql/pricingengines/lookback/Makefile.am | 10 +- ql/pricingengines/lookback/all.hpp | 5 +- .../lookback/mclookbackengine.cpp | 252 +++++++++++++++ ...loatingengine.hpp => mclookbackengine.hpp} | 206 +++++++------ .../lookback/mclookbackfixedengine.cpp | 52 ---- .../lookback/mclookbackfixedengine.hpp | 283 ----------------- .../lookback/mclookbackfloatingengine.cpp | 49 --- .../lookback/mclookbackpartialfixedengine.cpp | 55 ---- .../lookback/mclookbackpartialfixedengine.hpp | 288 ------------------ .../mclookbackpartialfloatingengine.cpp | 53 ---- .../mclookbackpartialfloatingengine.hpp | 286 ----------------- test-suite/lookbackoptions.cpp | 17 +- 15 files changed, 384 insertions(+), 1218 deletions(-) create mode 100644 ql/pricingengines/lookback/mclookbackengine.cpp rename ql/pricingengines/lookback/{mclookbackfloatingengine.hpp => mclookbackengine.hpp} (56%) delete mode 100644 ql/pricingengines/lookback/mclookbackfixedengine.cpp delete mode 100644 ql/pricingengines/lookback/mclookbackfixedengine.hpp delete mode 100644 ql/pricingengines/lookback/mclookbackfloatingengine.cpp delete mode 100644 ql/pricingengines/lookback/mclookbackpartialfixedengine.cpp delete mode 100644 ql/pricingengines/lookback/mclookbackpartialfixedengine.hpp delete mode 100644 ql/pricingengines/lookback/mclookbackpartialfloatingengine.cpp delete mode 100644 ql/pricingengines/lookback/mclookbackpartialfloatingengine.hpp diff --git a/QuantLib.vcxproj b/QuantLib.vcxproj index 2e62a51048e..1eda2b627ca 100644 --- a/QuantLib.vcxproj +++ b/QuantLib.vcxproj @@ -1508,10 +1508,7 @@ - - - - + @@ -2526,10 +2523,7 @@ - - - - + @@ -2755,4 +2749,4 @@ - \ No newline at end of file + diff --git a/QuantLib.vcxproj.filters b/QuantLib.vcxproj.filters index e0ab5bc8128..9c8e772103b 100644 --- a/QuantLib.vcxproj.filters +++ b/QuantLib.vcxproj.filters @@ -2568,16 +2568,7 @@ pricingengines\lookback - - pricingengines\lookback - - - pricingengines\lookback - - - pricingengines\lookback - - + pricingengines\lookback @@ -5664,16 +5655,7 @@ pricingengines\lookback - - pricingengines\lookback - - - pricingengines\lookback - - - pricingengines\lookback - - + pricingengines\lookback @@ -6973,4 +6955,4 @@ methods\finitedifferences\schemes - \ No newline at end of file + diff --git a/ql/CMakeLists.txt b/ql/CMakeLists.txt index 199f5663893..23900991f8d 100644 --- a/ql/CMakeLists.txt +++ b/ql/CMakeLists.txt @@ -687,10 +687,7 @@ set(QuantLib_SRC pricingengines/lookback/analyticcontinuousfloatinglookback.cpp pricingengines/lookback/analyticcontinuouspartialfixedlookback.cpp pricingengines/lookback/analyticcontinuouspartialfloatinglookback.cpp - pricingengines/lookback/mclookbackpartialfixedengine.cpp - pricingengines/lookback/mclookbackfixedengine.cpp - pricingengines/lookback/mclookbackpartialfloatingengine.cpp - pricingengines/lookback/mclookbackfloatingengine.cpp + pricingengines/lookback/mclookbackengine.cpp pricingengines/swap/cvaswapengine.cpp pricingengines/swap/discountingswapengine.cpp pricingengines/swap/discretizedswap.cpp @@ -1944,10 +1941,7 @@ set(QuantLib_HDR pricingengines/lookback/analyticcontinuousfloatinglookback.hpp pricingengines/lookback/analyticcontinuouspartialfixedlookback.hpp pricingengines/lookback/analyticcontinuouspartialfloatinglookback.hpp - pricingengines/lookback/mclookbackpartialfixedengine.hpp - pricingengines/lookback/mclookbackfixedengine.hpp - pricingengines/lookback/mclookbackpartialfloatingengine.hpp - pricingengines/lookback/mclookbackfloatingengine.hpp + pricingengines/lookback/mclookbackengine.hpp pricingengines/mclongstaffschwartzengine.hpp pricingengines/mcsimulation.hpp pricingengines/quanto/all.hpp diff --git a/ql/pricingengines/lookback/Makefile.am b/ql/pricingengines/lookback/Makefile.am index 9aadbf46aff..708f23e025f 100644 --- a/ql/pricingengines/lookback/Makefile.am +++ b/ql/pricingengines/lookback/Makefile.am @@ -8,20 +8,14 @@ this_include_HEADERS = \ analyticcontinuousfloatinglookback.hpp \ analyticcontinuouspartialfixedlookback.hpp \ analyticcontinuouspartialfloatinglookback.hpp \ - mclookbackpartialfixedengine.hpp \ - mclookbackfixedengine.hpp \ - mclookbackfloatingengine.hpp \ - mclookbackpartialfloatingengine.hpp + mclookbackengine.hpp cpp_files = \ analyticcontinuousfixedlookback.cpp \ analyticcontinuousfloatinglookback.cpp \ analyticcontinuouspartialfixedlookback.cpp \ analyticcontinuouspartialfloatinglookback.cpp \ - mclookbackpartialfixedengine.cpp \ - mclookbackfixedengine.cpp \ - mclookbackfloatingengine.cpp \ - mclookbackpartialfloatingengine.cpp + mclookbackengine.cpp if UNITY_BUILD diff --git a/ql/pricingengines/lookback/all.hpp b/ql/pricingengines/lookback/all.hpp index 9c7f2ed18d6..76092bbeccb 100644 --- a/ql/pricingengines/lookback/all.hpp +++ b/ql/pricingengines/lookback/all.hpp @@ -5,8 +5,5 @@ #include #include #include -#include -#include -#include -#include +#include diff --git a/ql/pricingengines/lookback/mclookbackengine.cpp b/ql/pricingengines/lookback/mclookbackengine.cpp new file mode 100644 index 00000000000..2362d2f7cd4 --- /dev/null +++ b/ql/pricingengines/lookback/mclookbackengine.cpp @@ -0,0 +1,252 @@ +/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + Copyright (C) 2020 Lew Wei Hao + + 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 + +namespace QuantLib { + + class LookbackFixedPathPricer : public PathPricer { + public: + LookbackFixedPathPricer(Option::Type type, + Real strike, + DiscountFactor discount); + Real operator()(const Path& path) const; + private: + PlainVanillaPayoff payoff_; + DiscountFactor discount_; + }; + + class LookbackPartialFixedPathPricer : public PathPricer { + public: + LookbackPartialFixedPathPricer(Time lookbackStart, + Option::Type type, + Real strike, + const DiscountFactor discount); + Real operator()(const Path& path) const; + private: + Time lookbackStart_; + PlainVanillaPayoff payoff_; + DiscountFactor discount_; + }; + + class LookbackFloatingPathPricer : public PathPricer { + public: + LookbackFloatingPathPricer(Option::Type type, + DiscountFactor discount); + Real operator()(const Path& path) const; + private: + FloatingTypePayoff payoff_; + DiscountFactor discount_; + }; + + class LookbackPartialFloatingPathPricer : public PathPricer { + public: + LookbackPartialFloatingPathPricer(Time lookbackEnd, + Option::Type type, + DiscountFactor discount); + Real operator()(const Path& path) const; + private: + Time lookbackEnd_; + FloatingTypePayoff payoff_; + DiscountFactor discount_; + }; + + namespace detail { + + ext::shared_ptr > + mc_lookback_path_pricer( + const ContinuousFixedLookbackOption::arguments& args, + const GeneralizedBlackScholesProcess& process, + DiscountFactor discount) { + ext::shared_ptr payoff = + ext::dynamic_pointer_cast(args.payoff); + QL_REQUIRE(payoff, "non-plain payoff given"); + + return ext::shared_ptr >( + new LookbackFixedPathPricer(payoff->optionType(), + payoff->strike(), + discount)); + } + + ext::shared_ptr > + mc_lookback_path_pricer( + const ContinuousPartialFixedLookbackOption::arguments& args, + const GeneralizedBlackScholesProcess& process, + DiscountFactor discount) { + ext::shared_ptr payoff = + ext::dynamic_pointer_cast(args.payoff); + QL_REQUIRE(payoff, "non-plain payoff given"); + + Time lookbackStart = process.time(args.lookbackPeriodStart); + + return ext::shared_ptr >( + new LookbackPartialFixedPathPricer(lookbackStart, + payoff->optionType(), + payoff->strike(), + discount)); + } + + ext::shared_ptr > + mc_lookback_path_pricer( + const ContinuousFloatingLookbackOption::arguments& args, + const GeneralizedBlackScholesProcess& process, + DiscountFactor discount) { + ext::shared_ptr payoff = + ext::dynamic_pointer_cast(args.payoff); + QL_REQUIRE(payoff, "non-floating payoff given"); + + return ext::shared_ptr >( + new LookbackFloatingPathPricer(payoff->optionType(), + discount)); + } + + ext::shared_ptr > + mc_lookback_path_pricer( + const ContinuousPartialFloatingLookbackOption::arguments& args, + const GeneralizedBlackScholesProcess& process, + DiscountFactor discount) { + ext::shared_ptr payoff = + ext::dynamic_pointer_cast(args.payoff); + QL_REQUIRE(payoff, "non-floating payoff given"); + + Time lookbackEnd = process.time(args.lookbackPeriodEnd); + + return ext::shared_ptr >( + new LookbackPartialFloatingPathPricer(lookbackEnd, + payoff->optionType(), + discount)); + } + + } + + + LookbackFixedPathPricer::LookbackFixedPathPricer( + Option::Type type, + Real strike, + DiscountFactor discount) + : payoff_(type, strike), discount_(discount) { + QL_REQUIRE(strike>=0.0, + "strike less than zero not allowed"); + } + + Real LookbackFixedPathPricer::operator()(const Path& path) const { + QL_REQUIRE(!path.empty(), "the path cannot be empty"); + + Real underlying; + switch (payoff_.optionType()) { + case Option::Put: + underlying = *std::min_element(path.begin()+1, path.end()); + break; + case Option::Call: + underlying = *std::max_element(path.begin()+1, path.end()); + break; + default: + QL_FAIL("unknown option type"); + } + + return payoff_(underlying) * discount_; + } + + + LookbackPartialFixedPathPricer::LookbackPartialFixedPathPricer( + Time lookbackStart, + Option::Type type, + Real strike, + const DiscountFactor discount) + : lookbackStart_(lookbackStart), payoff_(type, strike), discount_(discount) { + QL_REQUIRE(strike>=0.0, + "strike less than zero not allowed"); + } + + Real LookbackPartialFixedPathPricer::operator()(const Path& path) const { + QL_REQUIRE(!path.empty(), "the path cannot be empty"); + + TimeGrid timeGrid = path.timeGrid(); + Size startIndex = timeGrid.closestIndex(lookbackStart_); + Real underlying; + switch (payoff_.optionType()) { + case Option::Put: + underlying = *std::min_element(path.begin()+startIndex+1, path.end()); + break; + case Option::Call: + underlying = *std::max_element(path.begin()+startIndex+1, path.end()); + break; + default: + QL_FAIL("unknown option type"); + } + + return payoff_(underlying) * discount_; + } + + + LookbackFloatingPathPricer::LookbackFloatingPathPricer( + Option::Type type, + const DiscountFactor discount) + : payoff_(type), discount_(discount) {} + + Real LookbackFloatingPathPricer::operator()(const Path& path) const { + QL_REQUIRE(!path.empty(), "the path cannot be empty"); + + Real terminalPrice = path.back(); + Real strike; + switch (payoff_.optionType()) { + case Option::Call: + strike = *std::min_element(path.begin()+1, path.end()); + break; + case Option::Put: + strike = *std::max_element(path.begin()+1, path.end()); + break; + default: + QL_FAIL("unknown option type"); + } + + return payoff_(terminalPrice, strike) * discount_; + } + + + LookbackPartialFloatingPathPricer::LookbackPartialFloatingPathPricer( + Time lookbackEnd, + Option::Type type, + DiscountFactor discount) + : lookbackEnd_(lookbackEnd), payoff_(type), discount_(discount) {} + + Real LookbackPartialFloatingPathPricer::operator()(const Path& path) const { + QL_REQUIRE(!path.empty(), "the path cannot be empty"); + + TimeGrid timeGrid = path.timeGrid(); + Size endIndex = timeGrid.closestIndex(lookbackEnd_); + Real terminalPrice = path.back(); + Real strike; + + switch (payoff_.optionType()) { + case Option::Call: + strike = *std::min_element(path.begin()+1, path.begin()+endIndex+1); + break; + case Option::Put: + strike = *std::max_element(path.begin()+1, path.begin()+endIndex+1); + break; + default: + QL_FAIL("unknown option type"); + } + + return payoff_(terminalPrice, strike) * discount_; + } + +} diff --git a/ql/pricingengines/lookback/mclookbackfloatingengine.hpp b/ql/pricingengines/lookback/mclookbackengine.hpp similarity index 56% rename from ql/pricingengines/lookback/mclookbackfloatingengine.hpp rename to ql/pricingengines/lookback/mclookbackengine.hpp index 459e6c09698..e765c1a0529 100644 --- a/ql/pricingengines/lookback/mclookbackfloatingengine.hpp +++ b/ql/pricingengines/lookback/mclookbackengine.hpp @@ -17,12 +17,12 @@ FOR A PARTICULAR PURPOSE. See the license for more details. */ -/*! \file mclookbackfloatingengine.hpp - \brief Monte Carlo lookback floating engine +/*! \file mclookbackengine.hpp + \brief Monte Carlo lookback fixed engines */ -#ifndef quantlib_mc_lookback_floating_engines_hpp -#define quantlib_mc_lookback_floating_engines_hpp +#ifndef quantlib_mc_lookback_engines_hpp +#define quantlib_mc_lookback_engines_hpp #include #include @@ -31,16 +31,17 @@ namespace QuantLib { - template - class MCLookbackFloatingEngine : public ContinuousFloatingLookbackOption::engine, - public McSimulation { + //! Monte Carlo lookback-option engine + template + class MCLookbackEngine : public I::engine, + public McSimulation { public: typedef typename McSimulation::path_generator_type path_generator_type; typedef typename McSimulation::path_pricer_type path_pricer_type; // constructor - MCLookbackFloatingEngine( + MCLookbackEngine( const ext::shared_ptr& process, Size timeSteps, Size timeStepsPerYear, @@ -56,10 +57,10 @@ namespace QuantLib { McSimulation::calculate(requiredTolerance_, requiredSamples_, maxSamples_); - results_.value = this->mcModel_->sampleAccumulator().mean(); + this->results_.value = this->mcModel_->sampleAccumulator().mean(); if (RNG::allowsErrorEstimate) - results_.errorEstimate = - this->mcModel_->sampleAccumulator().errorEstimate(); + this->results_.errorEstimate = + this->mcModel_->sampleAccumulator().errorEstimate(); } protected: // McSimulation implementation @@ -83,21 +84,22 @@ namespace QuantLib { BigNatural seed_; }; + //! Monte Carlo lookback-option engine factory - template - class MakeMCLookbackFloatingEngine { + template + class MakeMCLookbackEngine { public: - explicit MakeMCLookbackFloatingEngine( + explicit MakeMCLookbackEngine( const ext::shared_ptr&); // named parameters - MakeMCLookbackFloatingEngine& withSteps(Size steps); - MakeMCLookbackFloatingEngine& withStepsPerYear(Size steps); - MakeMCLookbackFloatingEngine& withBrownianBridge(bool b = true); - MakeMCLookbackFloatingEngine& withAntitheticVariate(bool b = true); - MakeMCLookbackFloatingEngine& withSamples(Size samples); - MakeMCLookbackFloatingEngine& withAbsoluteTolerance(Real tolerance); - MakeMCLookbackFloatingEngine& withMaxSamples(Size samples); - MakeMCLookbackFloatingEngine& withSeed(BigNatural seed); + MakeMCLookbackEngine& withSteps(Size steps); + MakeMCLookbackEngine& withStepsPerYear(Size steps); + MakeMCLookbackEngine& withBrownianBridge(bool b = true); + MakeMCLookbackEngine& withAntitheticVariate(bool b = true); + MakeMCLookbackEngine& withSamples(Size samples); + MakeMCLookbackEngine& withAbsoluteTolerance(Real tolerance); + MakeMCLookbackEngine& withMaxSamples(Size samples); + MakeMCLookbackEngine& withSeed(BigNatural seed); // conversion to pricing engine operator ext::shared_ptr() const; private: @@ -108,21 +110,11 @@ namespace QuantLib { BigNatural seed_; }; - class LookbackFloatingPathPricer : public PathPricer { - public: - LookbackFloatingPathPricer( - Option::Type type, - DiscountFactor discount); - Real operator()(const Path& path) const; - private: - FloatingTypePayoff payoff_; - DiscountFactor discount_; - }; // template definitions - template - inline MCLookbackFloatingEngine::MCLookbackFloatingEngine( + template + inline MCLookbackEngine::MCLookbackEngine( const ext::shared_ptr& process, Size timeSteps, Size timeStepsPerYear, @@ -150,13 +142,14 @@ namespace QuantLib { QL_REQUIRE(timeStepsPerYear != 0, "timeStepsPerYear must be positive, " << timeStepsPerYear << " not allowed"); - registerWith(process_); + this->registerWith(process_); } - template - inline TimeGrid MCLookbackFloatingEngine::timeGrid() const { - Time residualTime = process_->time(arguments_.exercise->lastDate()); + template + inline TimeGrid MCLookbackEngine::timeGrid() const { + + Time residualTime = process_->time(this->arguments_.exercise->lastDate()); if (timeSteps_ != Null()) { return TimeGrid(residualTime, timeSteps_); } else if (timeStepsPerYear_ != Null()) { @@ -167,26 +160,52 @@ namespace QuantLib { } } - template - inline - ext::shared_ptr::path_pricer_type> - MCLookbackFloatingEngine::pathPricer() const { - ext::shared_ptr payoff = - ext::dynamic_pointer_cast(arguments_.payoff); - QL_REQUIRE(payoff, "non-plain payoff given"); - - TimeGrid grid = timeGrid(); - DiscountFactor discount = process_->riskFreeRate()->discount(grid.back()); - - return ext::shared_ptr< - typename MCLookbackFloatingEngine::path_pricer_type>( - new LookbackFloatingPathPricer( - payoff->optionType(), - discount)); - } - template - inline MakeMCLookbackFloatingEngine::MakeMCLookbackFloatingEngine( + namespace detail { + + // these functions are specialized for each of the instruments. + + ext::shared_ptr > + mc_lookback_path_pricer( + const ContinuousFixedLookbackOption::arguments& args, + const GeneralizedBlackScholesProcess& process, + DiscountFactor discount); + + ext::shared_ptr > + mc_lookback_path_pricer( + const ContinuousPartialFixedLookbackOption::arguments& args, + const GeneralizedBlackScholesProcess& process, + DiscountFactor discount); + + ext::shared_ptr > + mc_lookback_path_pricer( + const ContinuousFloatingLookbackOption::arguments& args, + const GeneralizedBlackScholesProcess& process, + DiscountFactor discount); + + ext::shared_ptr > + mc_lookback_path_pricer( + const ContinuousPartialFloatingLookbackOption::arguments& args, + const GeneralizedBlackScholesProcess& process, + DiscountFactor discount); + + } + + + template + inline ext::shared_ptr::path_pricer_type> + MCLookbackEngine::pathPricer() const { + TimeGrid grid = this->timeGrid(); + DiscountFactor discount = this->process_->riskFreeRate()->discount(grid.back()); + + return detail::mc_lookback_path_pricer(this->arguments_, + *(this->process_), + discount); + } + + + template + inline MakeMCLookbackEngine::MakeMCLookbackEngine( const ext::shared_ptr& process) : process_(process), brownianBridge_(false), antithetic_(false), @@ -194,46 +213,46 @@ namespace QuantLib { samples_(Null()), maxSamples_(Null()), tolerance_(Null()), seed_(0) {} - template - inline MakeMCLookbackFloatingEngine& - MakeMCLookbackFloatingEngine::withSteps(Size steps) { + template + inline MakeMCLookbackEngine& + MakeMCLookbackEngine::withSteps(Size steps) { steps_ = steps; return *this; } - template - inline MakeMCLookbackFloatingEngine& - MakeMCLookbackFloatingEngine::withStepsPerYear(Size steps) { + template + inline MakeMCLookbackEngine& + MakeMCLookbackEngine::withStepsPerYear(Size steps) { stepsPerYear_ = steps; return *this; } - template - inline MakeMCLookbackFloatingEngine& - MakeMCLookbackFloatingEngine::withBrownianBridge(bool brownianBridge) { + template + inline MakeMCLookbackEngine& + MakeMCLookbackEngine::withBrownianBridge(bool brownianBridge) { brownianBridge_ = brownianBridge; return *this; } - template - inline MakeMCLookbackFloatingEngine& - MakeMCLookbackFloatingEngine::withAntitheticVariate(bool b) { + template + inline MakeMCLookbackEngine& + MakeMCLookbackEngine::withAntitheticVariate(bool b) { antithetic_ = b; return *this; } - template - inline MakeMCLookbackFloatingEngine& - MakeMCLookbackFloatingEngine::withSamples(Size samples) { + template + inline MakeMCLookbackEngine& + MakeMCLookbackEngine::withSamples(Size samples) { QL_REQUIRE(tolerance_ == Null(), "tolerance already set"); samples_ = samples; return *this; } - template - inline MakeMCLookbackFloatingEngine& - MakeMCLookbackFloatingEngine::withAbsoluteTolerance(Real tolerance) { + template + inline MakeMCLookbackEngine& + MakeMCLookbackEngine::withAbsoluteTolerance(Real tolerance) { QL_REQUIRE(samples_ == Null(), "number of samples already set"); QL_REQUIRE(RNG::allowsErrorEstimate, @@ -243,40 +262,39 @@ namespace QuantLib { return *this; } - template - inline MakeMCLookbackFloatingEngine& - MakeMCLookbackFloatingEngine::withMaxSamples(Size samples) { + template + inline MakeMCLookbackEngine& + MakeMCLookbackEngine::withMaxSamples(Size samples) { maxSamples_ = samples; return *this; } - template - inline MakeMCLookbackFloatingEngine& - MakeMCLookbackFloatingEngine::withSeed(BigNatural seed) { + template + inline MakeMCLookbackEngine& + MakeMCLookbackEngine::withSeed(BigNatural seed) { seed_ = seed; return *this; } - template - inline MakeMCLookbackFloatingEngine::operator ext::shared_ptr() - const { + template + inline MakeMCLookbackEngine::operator ext::shared_ptr() const { QL_REQUIRE(steps_ != Null() || stepsPerYear_ != Null(), "number of steps not given"); QL_REQUIRE(steps_ == Null() || stepsPerYear_ == Null(), "number of steps overspecified"); - return ext::shared_ptr(new MCLookbackFloatingEngine(process_, - steps_, - stepsPerYear_, - brownianBridge_, - antithetic_, - samples_, - tolerance_, - maxSamples_, - seed_)); + return ext::shared_ptr( + new MCLookbackEngine(process_, + steps_, + stepsPerYear_, + brownianBridge_, + antithetic_, + samples_, + tolerance_, + maxSamples_, + seed_)); } } - #endif diff --git a/ql/pricingengines/lookback/mclookbackfixedengine.cpp b/ql/pricingengines/lookback/mclookbackfixedengine.cpp deleted file mode 100644 index b76bf0b6da9..00000000000 --- a/ql/pricingengines/lookback/mclookbackfixedengine.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2020 Lew Wei Hao - - 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 "mclookbackfixedengine.hpp" -#include - -namespace QuantLib { - - LookbackFixedPathPricer::LookbackFixedPathPricer( - Option::Type type, - Real strike, - DiscountFactor discount) - : payoff_(type, strike), discount_(discount) { - QL_REQUIRE(strike>=0.0, - "strike less than zero not allowed"); - } - - Real LookbackFixedPathPricer::operator()(const Path& path) const { - QL_REQUIRE(!path.empty(), "the path cannot be empty"); - - Real underlying; - switch (payoff_.optionType()) { - case Option::Put: - underlying = *std::min_element(path.begin()+1, path.end()); - break; - case Option::Call: - underlying = *std::max_element(path.begin()+1, path.end()); - break; - default: - QL_FAIL("unknown option type"); - } - - return payoff_(underlying) * discount_; - } - -} diff --git a/ql/pricingengines/lookback/mclookbackfixedengine.hpp b/ql/pricingengines/lookback/mclookbackfixedengine.hpp deleted file mode 100644 index 26722b82bb7..00000000000 --- a/ql/pricingengines/lookback/mclookbackfixedengine.hpp +++ /dev/null @@ -1,283 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2020 Lew Wei Hao - - 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 mclookbackfixedengine.hpp - \brief Monte Carlo lookback fixed engine -*/ - -#ifndef quantlib_mc_lookback_fixed_engines_hpp -#define quantlib_mc_lookback_fixed_engines_hpp - -#include -#include -#include -#include - -namespace QuantLib { - - template - class MCLookbackFixedEngine : public ContinuousFixedLookbackOption::engine, - public McSimulation { - public: - typedef typename McSimulation::path_generator_type - path_generator_type; - typedef typename McSimulation::path_pricer_type - path_pricer_type; - // constructor - MCLookbackFixedEngine( - const ext::shared_ptr& process, - Size timeSteps, - Size timeStepsPerYear, - bool brownianBridge, - bool antithetic, - Size requiredSamples, - Real requiredTolerance, - Size maxSamples, - BigNatural seed); - void calculate() const { - Real spot = process_->x0(); - QL_REQUIRE(spot >= 0.0, "negative or null underlying given"); - McSimulation::calculate(requiredTolerance_, - requiredSamples_, - maxSamples_); - results_.value = this->mcModel_->sampleAccumulator().mean(); - if (RNG::allowsErrorEstimate) - results_.errorEstimate = - this->mcModel_->sampleAccumulator().errorEstimate(); - } - protected: - // McSimulation implementation - TimeGrid timeGrid() const; - ext::shared_ptr pathGenerator() const { - TimeGrid grid = timeGrid(); - typename RNG::rsg_type gen = - RNG::make_sequence_generator(grid.size()-1,seed_); - return ext::shared_ptr( - new path_generator_type(process_, - grid, gen, brownianBridge_)); - } - ext::shared_ptr pathPricer() const; - // data members - ext::shared_ptr process_; - Size timeSteps_, timeStepsPerYear_; - Size requiredSamples_, maxSamples_; - Real requiredTolerance_; - bool antithetic_; - bool brownianBridge_; - BigNatural seed_; - }; - - //! Monte Carlo lookback-option engine factory - template - class MakeMCLookbackFixedEngine { - public: - explicit MakeMCLookbackFixedEngine( - const ext::shared_ptr&); - // named parameters - MakeMCLookbackFixedEngine& withSteps(Size steps); - MakeMCLookbackFixedEngine& withStepsPerYear(Size steps); - MakeMCLookbackFixedEngine& withBrownianBridge(bool b = true); - MakeMCLookbackFixedEngine& withAntitheticVariate(bool b = true); - MakeMCLookbackFixedEngine& withSamples(Size samples); - MakeMCLookbackFixedEngine& withAbsoluteTolerance(Real tolerance); - MakeMCLookbackFixedEngine& withMaxSamples(Size samples); - MakeMCLookbackFixedEngine& withSeed(BigNatural seed); - // conversion to pricing engine - operator ext::shared_ptr() const; - private: - ext::shared_ptr process_; - bool brownianBridge_, antithetic_; - Size steps_, stepsPerYear_, samples_, maxSamples_; - Real tolerance_; - BigNatural seed_; - }; - - class LookbackFixedPathPricer : public PathPricer { - public: - LookbackFixedPathPricer(Option::Type type, - Real strike, - DiscountFactor discount); - Real operator()(const Path& path) const; - private: - PlainVanillaPayoff payoff_; - DiscountFactor discount_; - }; - - // template definitions - - template - inline MCLookbackFixedEngine::MCLookbackFixedEngine( - const ext::shared_ptr& process, - Size timeSteps, - Size timeStepsPerYear, - bool brownianBridge, - bool antitheticVariate, - Size requiredSamples, - Real requiredTolerance, - Size maxSamples, - BigNatural seed) - : McSimulation(antitheticVariate, false), - process_(process), timeSteps_(timeSteps), - timeStepsPerYear_(timeStepsPerYear), - requiredSamples_(requiredSamples), maxSamples_(maxSamples), - requiredTolerance_(requiredTolerance), - brownianBridge_(brownianBridge), seed_(seed) { - QL_REQUIRE(timeSteps != Null() || - timeStepsPerYear != Null(), - "no time steps provided"); - QL_REQUIRE(timeSteps == Null() || - timeStepsPerYear == Null(), - "both time steps and time steps per year were provided"); - QL_REQUIRE(timeSteps != 0, - "timeSteps must be positive, " << timeSteps << - " not allowed"); - QL_REQUIRE(timeStepsPerYear != 0, - "timeStepsPerYear must be positive, " << timeStepsPerYear << - " not allowed"); - registerWith(process_); - } - - template - inline TimeGrid MCLookbackFixedEngine::timeGrid() const { - - Time residualTime = process_->time(arguments_.exercise->lastDate()); - if (timeSteps_ != Null()) { - return TimeGrid(residualTime, timeSteps_); - } else if (timeStepsPerYear_ != Null()) { - Size steps = static_cast(timeStepsPerYear_*residualTime); - return TimeGrid(residualTime, std::max(steps, 1)); - } else { - QL_FAIL("time steps not specified"); - } - } - - template - inline - ext::shared_ptr::path_pricer_type> - MCLookbackFixedEngine::pathPricer() const { - ext::shared_ptr payoff = - ext::dynamic_pointer_cast(arguments_.payoff); - QL_REQUIRE(payoff, "non-plain payoff given"); - - TimeGrid grid = timeGrid(); - DiscountFactor discount = process_->riskFreeRate()->discount(grid.back()); - - return ext::shared_ptr< - typename MCLookbackFixedEngine::path_pricer_type>( - new LookbackFixedPathPricer( - payoff->optionType(), - payoff->strike(), - discount)); - } - - template - inline MakeMCLookbackFixedEngine::MakeMCLookbackFixedEngine( - const ext::shared_ptr& process) - : process_(process), - brownianBridge_(false), antithetic_(false), - steps_(Null()), stepsPerYear_(Null()), - samples_(Null()), maxSamples_(Null()), - tolerance_(Null()), seed_(0) {} - - template - inline MakeMCLookbackFixedEngine& - MakeMCLookbackFixedEngine::withSteps(Size steps) { - steps_ = steps; - return *this; - } - - template - inline MakeMCLookbackFixedEngine& - MakeMCLookbackFixedEngine::withStepsPerYear(Size steps) { - stepsPerYear_ = steps; - return *this; - } - - template - inline MakeMCLookbackFixedEngine& - MakeMCLookbackFixedEngine::withBrownianBridge(bool brownianBridge) { - brownianBridge_ = brownianBridge; - return *this; - } - - template - inline MakeMCLookbackFixedEngine& - MakeMCLookbackFixedEngine::withAntitheticVariate(bool b) { - antithetic_ = b; - return *this; - } - - template - inline MakeMCLookbackFixedEngine& - MakeMCLookbackFixedEngine::withSamples(Size samples) { - QL_REQUIRE(tolerance_ == Null(), - "tolerance already set"); - samples_ = samples; - return *this; - } - - template - inline MakeMCLookbackFixedEngine& - MakeMCLookbackFixedEngine::withAbsoluteTolerance(Real tolerance) { - QL_REQUIRE(samples_ == Null(), - "number of samples already set"); - QL_REQUIRE(RNG::allowsErrorEstimate, - "chosen random generator policy " - "does not allow an error estimate"); - tolerance_ = tolerance; - return *this; - } - - template - inline MakeMCLookbackFixedEngine& - MakeMCLookbackFixedEngine::withMaxSamples(Size samples) { - maxSamples_ = samples; - return *this; - } - - template - inline MakeMCLookbackFixedEngine& - MakeMCLookbackFixedEngine::withSeed(BigNatural seed) { - seed_ = seed; - return *this; - } - - template - inline MakeMCLookbackFixedEngine::operator ext::shared_ptr() - const { - QL_REQUIRE(steps_ != Null() || stepsPerYear_ != Null(), - "number of steps not given"); - QL_REQUIRE(steps_ == Null() || stepsPerYear_ == Null(), - "number of steps overspecified"); - - return ext::shared_ptr(new MCLookbackFixedEngine(process_, - steps_, - stepsPerYear_, - brownianBridge_, - antithetic_, - samples_, - tolerance_, - maxSamples_, - seed_)); - } - -} - - -#endif diff --git a/ql/pricingengines/lookback/mclookbackfloatingengine.cpp b/ql/pricingengines/lookback/mclookbackfloatingengine.cpp deleted file mode 100644 index 3f08ed42a56..00000000000 --- a/ql/pricingengines/lookback/mclookbackfloatingengine.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2020 Lew Wei Hao - - 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 "mclookbackfloatingengine.hpp" -#include - -namespace QuantLib { - - LookbackFloatingPathPricer::LookbackFloatingPathPricer( - Option::Type type, - const DiscountFactor discount) - : payoff_(type), discount_(discount) {} - - Real LookbackFloatingPathPricer::operator()(const Path& path) const { - QL_REQUIRE(!path.empty(), "the path cannot be empty"); - - Real terminalPrice = path.back(); - Real strike; - switch (payoff_.optionType()) { - case Option::Call: - strike = *std::min_element(path.begin()+1, path.end()); - break; - case Option::Put: - strike = *std::max_element(path.begin()+1, path.end()); - break; - default: - QL_FAIL("unknown option type"); - } - - return payoff_(terminalPrice, strike) * discount_; - } - -} diff --git a/ql/pricingengines/lookback/mclookbackpartialfixedengine.cpp b/ql/pricingengines/lookback/mclookbackpartialfixedengine.cpp deleted file mode 100644 index aff295a3349..00000000000 --- a/ql/pricingengines/lookback/mclookbackpartialfixedengine.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2020 Lew Wei Hao - - 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 "mclookbackpartialfixedengine.hpp" -#include - -namespace QuantLib { - - LookbackPartialFixedPathPricer::LookbackPartialFixedPathPricer( - Time lookbackStart, - Option::Type type, - Real strike, - const DiscountFactor discount) - : lookbackStart_(lookbackStart), payoff_(type, strike), discount_(discount) { - QL_REQUIRE(strike>=0.0, - "strike less than zero not allowed"); - } - - Real LookbackPartialFixedPathPricer::operator()(const Path& path) const { - QL_REQUIRE(!path.empty(), "the path cannot be empty"); - - TimeGrid timeGrid = path.timeGrid(); - Size startIndex = timeGrid.closestIndex(lookbackStart_); - Real underlying; - switch (payoff_.optionType()) { - case Option::Put: - underlying = *std::min_element(path.begin()+startIndex+1, path.end()); - break; - case Option::Call: - underlying = *std::max_element(path.begin()+startIndex+1, path.end()); - break; - default: - QL_FAIL("unknown option type"); - } - - return payoff_(underlying) * discount_; - } - -} diff --git a/ql/pricingengines/lookback/mclookbackpartialfixedengine.hpp b/ql/pricingengines/lookback/mclookbackpartialfixedengine.hpp deleted file mode 100644 index 3dbe8b7571a..00000000000 --- a/ql/pricingengines/lookback/mclookbackpartialfixedengine.hpp +++ /dev/null @@ -1,288 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2020 Lew Wei Hao - - 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 mclookbackpartialfixedengine.hpp - \brief Monte Carlo lookback partial fixed engine -*/ - -#ifndef quantlib_mc_lookback_partial_fixed_engines_hpp -#define quantlib_mc_lookback_partial_fixed_engines_hpp - -#include -#include -#include -#include - -namespace QuantLib { - - template - class MCLookbackPartialFixedEngine : public ContinuousPartialFixedLookbackOption::engine, - public McSimulation { - public: - typedef typename McSimulation::path_generator_type - path_generator_type; - typedef typename McSimulation::path_pricer_type - path_pricer_type; - // constructor - MCLookbackPartialFixedEngine( - const ext::shared_ptr& process, - Size timeSteps, - Size timeStepsPerYear, - bool brownianBridge, - bool antithetic, - Size requiredSamples, - Real requiredTolerance, - Size maxSamples, - BigNatural seed); - void calculate() const { - Real spot = process_->x0(); - QL_REQUIRE(spot >= 0.0, "negative or null underlying given"); - McSimulation::calculate(requiredTolerance_, - requiredSamples_, - maxSamples_); - results_.value = this->mcModel_->sampleAccumulator().mean(); - if (RNG::allowsErrorEstimate) - results_.errorEstimate = - this->mcModel_->sampleAccumulator().errorEstimate(); - } - protected: - // McSimulation implementation - TimeGrid timeGrid() const; - ext::shared_ptr pathGenerator() const { - TimeGrid grid = timeGrid(); - typename RNG::rsg_type gen = - RNG::make_sequence_generator(grid.size()-1,seed_); - return ext::shared_ptr( - new path_generator_type(process_, - grid, gen, brownianBridge_)); - } - ext::shared_ptr pathPricer() const; - // data members - ext::shared_ptr process_; - Size timeSteps_, timeStepsPerYear_; - Size requiredSamples_, maxSamples_; - Real requiredTolerance_; - bool antithetic_; - bool brownianBridge_; - BigNatural seed_; - }; - - //! Monte Carlo lookback-option engine factory - template - class MakeMCLookbackPartialFixedEngine { - public: - explicit MakeMCLookbackPartialFixedEngine( - const ext::shared_ptr&); - // named parameters - MakeMCLookbackPartialFixedEngine& withSteps(Size steps); - MakeMCLookbackPartialFixedEngine& withStepsPerYear(Size steps); - MakeMCLookbackPartialFixedEngine& withBrownianBridge(bool b = true); - MakeMCLookbackPartialFixedEngine& withAntitheticVariate(bool b = true); - MakeMCLookbackPartialFixedEngine& withSamples(Size samples); - MakeMCLookbackPartialFixedEngine& withAbsoluteTolerance(Real tolerance); - MakeMCLookbackPartialFixedEngine& withMaxSamples(Size samples); - MakeMCLookbackPartialFixedEngine& withSeed(BigNatural seed); - // conversion to pricing engine - operator ext::shared_ptr() const; - private: - ext::shared_ptr process_; - bool brownianBridge_, antithetic_; - Size steps_, stepsPerYear_, samples_, maxSamples_; - Real tolerance_; - BigNatural seed_; - }; - - class LookbackPartialFixedPathPricer : public PathPricer { - public: - LookbackPartialFixedPathPricer(Time lookbackStart, - Option::Type type, - Real strike, - const DiscountFactor discount); - Real operator()(const Path& path) const; - private: - Time lookbackStart_; - PlainVanillaPayoff payoff_; - DiscountFactor discount_; - }; - - // template definitions - - template - inline MCLookbackPartialFixedEngine::MCLookbackPartialFixedEngine( - const ext::shared_ptr& process, - Size timeSteps, - Size timeStepsPerYear, - bool brownianBridge, - bool antitheticVariate, - Size requiredSamples, - Real requiredTolerance, - Size maxSamples, - BigNatural seed) - : McSimulation(antitheticVariate, false), - process_(process), timeSteps_(timeSteps), - timeStepsPerYear_(timeStepsPerYear), - requiredSamples_(requiredSamples), maxSamples_(maxSamples), - requiredTolerance_(requiredTolerance), - brownianBridge_(brownianBridge), seed_(seed) { - QL_REQUIRE(timeSteps != Null() || - timeStepsPerYear != Null(), - "no time steps provided"); - QL_REQUIRE(timeSteps == Null() || - timeStepsPerYear == Null(), - "both time steps and time steps per year were provided"); - QL_REQUIRE(timeSteps != 0, - "timeSteps must be positive, " << timeSteps << - " not allowed"); - QL_REQUIRE(timeStepsPerYear != 0, - "timeStepsPerYear must be positive, " << timeStepsPerYear << - " not allowed"); - registerWith(process_); - } - - template - inline TimeGrid MCLookbackPartialFixedEngine::timeGrid() const { - - Time residualTime = process_->time(arguments_.exercise->lastDate()); - if (timeSteps_ != Null()) { - return TimeGrid(residualTime, timeSteps_); - } else if (timeStepsPerYear_ != Null()) { - Size steps = static_cast(timeStepsPerYear_*residualTime); - return TimeGrid(residualTime, std::max(steps, 1)); - } else { - QL_FAIL("time steps not specified"); - } - } - - template - inline - ext::shared_ptr::path_pricer_type> - MCLookbackPartialFixedEngine::pathPricer() const { - ext::shared_ptr payoff = - ext::dynamic_pointer_cast(arguments_.payoff); - QL_REQUIRE(payoff, "non-plain payoff given"); - - TimeGrid grid = timeGrid(); - DiscountFactor discount = process_->riskFreeRate()->discount(grid.back()); - - Time lookbackStart = process_->time(arguments_.lookbackPeriodStart); - - return ext::shared_ptr< - typename MCLookbackPartialFixedEngine::path_pricer_type>( - new LookbackPartialFixedPathPricer( - lookbackStart, - payoff->optionType(), - payoff->strike(), - discount)); - } - - template - inline MakeMCLookbackPartialFixedEngine::MakeMCLookbackPartialFixedEngine( - const ext::shared_ptr& process) - : process_(process), - brownianBridge_(false), antithetic_(false), - steps_(Null()), stepsPerYear_(Null()), - samples_(Null()), maxSamples_(Null()), - tolerance_(Null()), seed_(0) {} - - template - inline MakeMCLookbackPartialFixedEngine& - MakeMCLookbackPartialFixedEngine::withSteps(Size steps) { - steps_ = steps; - return *this; - } - - template - inline MakeMCLookbackPartialFixedEngine& - MakeMCLookbackPartialFixedEngine::withStepsPerYear(Size steps) { - stepsPerYear_ = steps; - return *this; - } - - template - inline MakeMCLookbackPartialFixedEngine& - MakeMCLookbackPartialFixedEngine::withBrownianBridge(bool brownianBridge) { - brownianBridge_ = brownianBridge; - return *this; - } - - template - inline MakeMCLookbackPartialFixedEngine& - MakeMCLookbackPartialFixedEngine::withAntitheticVariate(bool b) { - antithetic_ = b; - return *this; - } - - template - inline MakeMCLookbackPartialFixedEngine& - MakeMCLookbackPartialFixedEngine::withSamples(Size samples) { - QL_REQUIRE(tolerance_ == Null(), - "tolerance already set"); - samples_ = samples; - return *this; - } - - template - inline MakeMCLookbackPartialFixedEngine& - MakeMCLookbackPartialFixedEngine::withAbsoluteTolerance(Real tolerance) { - QL_REQUIRE(samples_ == Null(), - "number of samples already set"); - QL_REQUIRE(RNG::allowsErrorEstimate, - "chosen random generator policy " - "does not allow an error estimate"); - tolerance_ = tolerance; - return *this; - } - - template - inline MakeMCLookbackPartialFixedEngine& - MakeMCLookbackPartialFixedEngine::withMaxSamples(Size samples) { - maxSamples_ = samples; - return *this; - } - - template - inline MakeMCLookbackPartialFixedEngine& - MakeMCLookbackPartialFixedEngine::withSeed(BigNatural seed) { - seed_ = seed; - return *this; - } - - template - inline MakeMCLookbackPartialFixedEngine::operator ext::shared_ptr() - const { - QL_REQUIRE(steps_ != Null() || stepsPerYear_ != Null(), - "number of steps not given"); - QL_REQUIRE(steps_ == Null() || stepsPerYear_ == Null(), - "number of steps overspecified"); - - return ext::shared_ptr(new MCLookbackPartialFixedEngine(process_, - steps_, - stepsPerYear_, - brownianBridge_, - antithetic_, - samples_, - tolerance_, - maxSamples_, - seed_)); - } - -} - - -#endif diff --git a/ql/pricingengines/lookback/mclookbackpartialfloatingengine.cpp b/ql/pricingengines/lookback/mclookbackpartialfloatingengine.cpp deleted file mode 100644 index 82bef963dc8..00000000000 --- a/ql/pricingengines/lookback/mclookbackpartialfloatingengine.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2020 Lew Wei Hao - - 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 "mclookbackpartialfloatingengine.hpp" -#include - -namespace QuantLib { - - LookbackPartialFloatingPathPricer::LookbackPartialFloatingPathPricer( - Time lookbackEnd, - Option::Type type, - DiscountFactor discount) - : lookbackEnd_(lookbackEnd), payoff_(type), discount_(discount) {} - - Real LookbackPartialFloatingPathPricer::operator()(const Path& path) const { - QL_REQUIRE(!path.empty(), "the path cannot be empty"); - - TimeGrid timeGrid = path.timeGrid(); - Size endIndex = timeGrid.closestIndex(lookbackEnd_); - Real terminalPrice = path.back(); - Real strike; - - switch (payoff_.optionType()) { - case Option::Call: - strike = *std::min_element(path.begin()+1, path.begin()+endIndex+1); - break; - case Option::Put: - strike = *std::max_element(path.begin()+1, path.begin()+endIndex+1); - break; - default: - QL_FAIL("unknown option type"); - } - - return payoff_(terminalPrice, strike) * discount_; - } - -} diff --git a/ql/pricingengines/lookback/mclookbackpartialfloatingengine.hpp b/ql/pricingengines/lookback/mclookbackpartialfloatingengine.hpp deleted file mode 100644 index f7e8b2c1c68..00000000000 --- a/ql/pricingengines/lookback/mclookbackpartialfloatingengine.hpp +++ /dev/null @@ -1,286 +0,0 @@ -/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ - -/* - Copyright (C) 2020 Lew Wei Hao - - 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 mclookbackpartialfloatingengine.hpp - \brief Monte Carlo lookback partial floating engine -*/ - -#ifndef quantlib_mc_partial_floating_lookback_engines_hpp -#define quantlib_mc_partial_floating_lookback_engines_hpp - -#include -#include -#include -#include - -namespace QuantLib { - - template - class MCLookbackPartialFloatingEngine : public ContinuousPartialFloatingLookbackOption::engine, - public McSimulation { - public: - typedef typename McSimulation::path_generator_type - path_generator_type; - typedef typename McSimulation::path_pricer_type - path_pricer_type; - // constructor - MCLookbackPartialFloatingEngine( - const ext::shared_ptr& process, - Size timeSteps, - Size timeStepsPerYear, - bool brownianBridge, - bool antithetic, - Size requiredSamples, - Real requiredTolerance, - Size maxSamples, - BigNatural seed); - void calculate() const { - Real spot = process_->x0(); - QL_REQUIRE(spot >= 0.0, "negative or null underlying given"); - McSimulation::calculate(requiredTolerance_, - requiredSamples_, - maxSamples_); - results_.value = this->mcModel_->sampleAccumulator().mean(); - if (RNG::allowsErrorEstimate) - results_.errorEstimate = - this->mcModel_->sampleAccumulator().errorEstimate(); - } - protected: - // McSimulation implementation - TimeGrid timeGrid() const; - ext::shared_ptr pathGenerator() const { - TimeGrid grid = timeGrid(); - typename RNG::rsg_type gen = - RNG::make_sequence_generator(grid.size()-1,seed_); - return ext::shared_ptr( - new path_generator_type(process_, - grid, gen, brownianBridge_)); - } - ext::shared_ptr pathPricer() const; - // data members - ext::shared_ptr process_; - Size timeSteps_, timeStepsPerYear_; - Size requiredSamples_, maxSamples_; - Real requiredTolerance_; - bool antithetic_; - bool brownianBridge_; - BigNatural seed_; - }; - - //! Monte Carlo lookback-option engine factory - template - class MakeMCLookbackPartialFloatingEngine { - public: - explicit MakeMCLookbackPartialFloatingEngine( - const ext::shared_ptr&); - // named parameters - MakeMCLookbackPartialFloatingEngine& withSteps(Size steps); - MakeMCLookbackPartialFloatingEngine& withStepsPerYear(Size steps); - MakeMCLookbackPartialFloatingEngine& withBrownianBridge(bool b = true); - MakeMCLookbackPartialFloatingEngine& withAntitheticVariate(bool b = true); - MakeMCLookbackPartialFloatingEngine& withSamples(Size samples); - MakeMCLookbackPartialFloatingEngine& withAbsoluteTolerance(Real tolerance); - MakeMCLookbackPartialFloatingEngine& withMaxSamples(Size samples); - MakeMCLookbackPartialFloatingEngine& withSeed(BigNatural seed); - // conversion to pricing engine - operator ext::shared_ptr() const; - private: - ext::shared_ptr process_; - bool brownianBridge_, antithetic_; - Size steps_, stepsPerYear_, samples_, maxSamples_; - Real tolerance_; - BigNatural seed_; - }; - - class LookbackPartialFloatingPathPricer : public PathPricer { - public: - LookbackPartialFloatingPathPricer(Time lookbackEnd, - Option::Type type, - DiscountFactor discount); - Real operator()(const Path& path) const; - private: - Time lookbackEnd_; - FloatingTypePayoff payoff_; - DiscountFactor discount_; - }; - - // template definitions - - template - inline MCLookbackPartialFloatingEngine::MCLookbackPartialFloatingEngine( - const ext::shared_ptr& process, - Size timeSteps, - Size timeStepsPerYear, - bool brownianBridge, - bool antitheticVariate, - Size requiredSamples, - Real requiredTolerance, - Size maxSamples, - BigNatural seed) - : McSimulation(antitheticVariate, false), - process_(process), timeSteps_(timeSteps), - timeStepsPerYear_(timeStepsPerYear), - requiredSamples_(requiredSamples), maxSamples_(maxSamples), - requiredTolerance_(requiredTolerance), - brownianBridge_(brownianBridge), seed_(seed) { - QL_REQUIRE(timeSteps != Null() || - timeStepsPerYear != Null(), - "no time steps provided"); - QL_REQUIRE(timeSteps == Null() || - timeStepsPerYear == Null(), - "both time steps and time steps per year were provided"); - QL_REQUIRE(timeSteps != 0, - "timeSteps must be positive, " << timeSteps << - " not allowed"); - QL_REQUIRE(timeStepsPerYear != 0, - "timeStepsPerYear must be positive, " << timeStepsPerYear << - " not allowed"); - registerWith(process_); - } - - template - inline TimeGrid MCLookbackPartialFloatingEngine::timeGrid() const { - - Time residualTime = process_->time(arguments_.exercise->lastDate()); - if (timeSteps_ != Null()) { - return TimeGrid(residualTime, timeSteps_); - } else if (timeStepsPerYear_ != Null()) { - Size steps = static_cast(timeStepsPerYear_*residualTime); - return TimeGrid(residualTime, std::max(steps, 1)); - } else { - QL_FAIL("time steps not specified"); - } - } - - template - inline - ext::shared_ptr::path_pricer_type> - MCLookbackPartialFloatingEngine::pathPricer() const { - ext::shared_ptr payoff = - ext::dynamic_pointer_cast(arguments_.payoff); - QL_REQUIRE(payoff, "non-plain payoff given"); - - TimeGrid grid = timeGrid(); - DiscountFactor discount = process_->riskFreeRate()->discount(grid.back()); - - Time lookbackEnd = process_->time(arguments_.lookbackPeriodEnd); - - return ext::shared_ptr< - typename MCLookbackPartialFloatingEngine::path_pricer_type>( - new LookbackPartialFloatingPathPricer( - lookbackEnd, - payoff->optionType(), - discount)); - } - - template - inline MakeMCLookbackPartialFloatingEngine::MakeMCLookbackPartialFloatingEngine( - const ext::shared_ptr& process) - : process_(process), - brownianBridge_(false), antithetic_(false), - steps_(Null()), stepsPerYear_(Null()), - samples_(Null()), maxSamples_(Null()), - tolerance_(Null()), seed_(0) {} - - template - inline MakeMCLookbackPartialFloatingEngine& - MakeMCLookbackPartialFloatingEngine::withSteps(Size steps) { - steps_ = steps; - return *this; - } - - template - inline MakeMCLookbackPartialFloatingEngine& - MakeMCLookbackPartialFloatingEngine::withStepsPerYear(Size steps) { - stepsPerYear_ = steps; - return *this; - } - - template - inline MakeMCLookbackPartialFloatingEngine& - MakeMCLookbackPartialFloatingEngine::withBrownianBridge(bool brownianBridge) { - brownianBridge_ = brownianBridge; - return *this; - } - - template - inline MakeMCLookbackPartialFloatingEngine& - MakeMCLookbackPartialFloatingEngine::withAntitheticVariate(bool b) { - antithetic_ = b; - return *this; - } - - template - inline MakeMCLookbackPartialFloatingEngine& - MakeMCLookbackPartialFloatingEngine::withSamples(Size samples) { - QL_REQUIRE(tolerance_ == Null(), - "tolerance already set"); - samples_ = samples; - return *this; - } - - template - inline MakeMCLookbackPartialFloatingEngine& - MakeMCLookbackPartialFloatingEngine::withAbsoluteTolerance(Real tolerance) { - QL_REQUIRE(samples_ == Null(), - "number of samples already set"); - QL_REQUIRE(RNG::allowsErrorEstimate, - "chosen random generator policy " - "does not allow an error estimate"); - tolerance_ = tolerance; - return *this; - } - - template - inline MakeMCLookbackPartialFloatingEngine& - MakeMCLookbackPartialFloatingEngine::withMaxSamples(Size samples) { - maxSamples_ = samples; - return *this; - } - - template - inline MakeMCLookbackPartialFloatingEngine& - MakeMCLookbackPartialFloatingEngine::withSeed(BigNatural seed) { - seed_ = seed; - return *this; - } - - template - inline MakeMCLookbackPartialFloatingEngine::operator ext::shared_ptr() - const { - QL_REQUIRE(steps_ != Null() || stepsPerYear_ != Null(), - "number of steps not given"); - QL_REQUIRE(steps_ == Null() || stepsPerYear_ == Null(), - "number of steps overspecified"); - - return ext::shared_ptr(new MCLookbackPartialFloatingEngine(process_, - steps_, - stepsPerYear_, - brownianBridge_, - antithetic_, - samples_, - tolerance_, - maxSamples_, - seed_)); - } - -} - - -#endif diff --git a/test-suite/lookbackoptions.cpp b/test-suite/lookbackoptions.cpp index daa22bfc883..832dd566b6b 100644 --- a/test-suite/lookbackoptions.cpp +++ b/test-suite/lookbackoptions.cpp @@ -27,10 +27,7 @@ #include #include #include -#include -#include -#include -#include +#include #include #include #include @@ -564,7 +561,8 @@ void LookbackOptionTest::testMonteCarloLookback() { Real analytical = partialFixedLookback.NPV(); ext::shared_ptr mcpartialfixedengine = - MakeMCLookbackPartialFixedEngine(stochProcess) + MakeMCLookbackEngine + (stochProcess) .withSteps(2000) .withAntitheticVariate() .withSeed(1) @@ -595,7 +593,8 @@ void LookbackOptionTest::testMonteCarloLookback() { analytical = fixedLookback.NPV(); ext::shared_ptr mcfixedengine = - MakeMCLookbackFixedEngine(stochProcess) + MakeMCLookbackEngine + (stochProcess) .withSteps(2000) .withAntitheticVariate() .withSeed(1) @@ -631,7 +630,8 @@ void LookbackOptionTest::testMonteCarloLookback() { analytical = partialFloating.NPV(); ext::shared_ptr mcpartialfloatingengine = - MakeMCLookbackPartialFloatingEngine(stochProcess) + MakeMCLookbackEngine + (stochProcess) .withSteps(2000) .withAntitheticVariate() .withSeed(1) @@ -660,7 +660,8 @@ void LookbackOptionTest::testMonteCarloLookback() { analytical = floating.NPV(); ext::shared_ptr mcfloatingengine = - MakeMCLookbackFloatingEngine(stochProcess) + MakeMCLookbackEngine + (stochProcess) .withSteps(2000) .withAntitheticVariate() .withSeed(1)