/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* Copyright (C) 2006 Warren Chou 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 mcvarianceswapengine.hpp \brief Monte Carlo variance-swap engine */ #ifndef quantlib_mc_varianceswap_engine_hpp #define quantlib_mc_varianceswap_engine_hpp #include #include #include #include namespace QuantLib { //! Variance-swap pricing engine using Monte Carlo simulation, /*! as described in Demeterfi, Derman, Kamal & Zou, "A Guide to Volatility and Variance Swaps", 1999 \ingroup forwardengines \todo define tolerance of numerical integral and incorporate it in errorEstimate \test returned fair variances checked for consistency with implied volatility curve. */ template class MCVarianceSwapEngine : public VarianceSwap::engine, public McSimulation { public: typedef typename McSimulation::path_generator_type path_generator_type; typedef typename McSimulation::path_pricer_type path_pricer_type; typedef typename McSimulation::stats_type stats_type; // constructor MCVarianceSwapEngine(Size timeSteps, Size timeStepsPerYear, bool brownianBridge, bool antitheticVariate, Size requiredSamples, Real requiredTolerance, Size maxSamples, BigNatural seed); // calculate fair variance via Monte Carlo void calculate() const { McSimulation::calculate(requiredTolerance_, requiredSamples_, maxSamples_); results_.fairVariance = this->mcModel_->sampleAccumulator().mean(); if (RNG::allowsErrorEstimate) results_.errorEstimate = this->mcModel_->sampleAccumulator().errorEstimate(); } protected: // McSimulation implementation boost::shared_ptr pathPricer() const; TimeGrid timeGrid() const; boost::shared_ptr pathGenerator() const { Size dimensions = arguments_.stochasticProcess->factors(); TimeGrid grid = timeGrid(); typename RNG::rsg_type gen = RNG::make_sequence_generator(dimensions*(grid.size()-1),seed_); return boost::shared_ptr( new path_generator_type(arguments_.stochasticProcess, grid, gen, brownianBridge_)); } // data members Size timeSteps_, timeStepsPerYear_; Size requiredSamples_, maxSamples_; Real requiredTolerance_; bool brownianBridge_; BigNatural seed_; }; //! Monte Carlo variance-swap engine factory template class MakeMCVarianceSwapEngine { public: MakeMCVarianceSwapEngine(); // named parameters MakeMCVarianceSwapEngine& withSteps(Size steps); MakeMCVarianceSwapEngine& withStepsPerYear(Size steps); MakeMCVarianceSwapEngine& withBrownianBridge(bool b = true); MakeMCVarianceSwapEngine& withSamples(Size samples); MakeMCVarianceSwapEngine& withTolerance(Real tolerance); MakeMCVarianceSwapEngine& withMaxSamples(Size samples); MakeMCVarianceSwapEngine& withSeed(BigNatural seed); MakeMCVarianceSwapEngine& withAntitheticVariate(bool b = true); // conversion to pricing engine operator boost::shared_ptr() const; private: bool antithetic_; Size steps_, stepsPerYear_, samples_, maxSamples_; Real tolerance_; bool brownianBridge_; BigNatural seed_; }; class FairVariancePathPricer : public PathPricer { public: FairVariancePathPricer( const boost::shared_ptr& process) : process_(process) {} Real operator()(const Path& path) const; private: boost::shared_ptr process_; }; // inline definitions template inline MCVarianceSwapEngine::MCVarianceSwapEngine(Size timeSteps, Size timeStepsPerYear, bool brownianBridge, bool antitheticVariate, Size requiredSamples, Real requiredTolerance, Size maxSamples, BigNatural seed) : McSimulation(antitheticVariate, false), timeSteps_(timeSteps), timeStepsPerYear_(timeStepsPerYear), requiredSamples_(requiredSamples), maxSamples_(maxSamples), requiredTolerance_(requiredTolerance), brownianBridge_(brownianBridge), seed_(seed) {} template inline TimeGrid MCVarianceSwapEngine::timeGrid() const { Time t = this->arguments_.stochasticProcess->time( this->arguments_.maturityDate); if (timeSteps_ != Null()) { return TimeGrid(t, this->timeSteps_); } else if (timeStepsPerYear_ != Null()) { Size steps = static_cast(timeStepsPerYear_*t); return TimeGrid(t, std::max(steps, 1)); } else { QL_FAIL("time steps not specified"); } } template inline boost::shared_ptr< QL_TYPENAME MCVarianceSwapEngine::path_pricer_type> MCVarianceSwapEngine::pathPricer() const { boost::shared_ptr process = boost::dynamic_pointer_cast( this->arguments_.stochasticProcess); QL_REQUIRE(process, "Black-Scholes process required"); return boost::shared_ptr< QL_TYPENAME MCVarianceSwapEngine::path_pricer_type>( new FairVariancePathPricer(this->arguments_.stochasticProcess)); } template inline MakeMCVarianceSwapEngine::MakeMCVarianceSwapEngine() : antithetic_(false), steps_(Null()), stepsPerYear_(Null()), samples_(Null()), maxSamples_(Null()), tolerance_(Null()), brownianBridge_(false), seed_(0) {} template inline MakeMCVarianceSwapEngine& MakeMCVarianceSwapEngine::withSteps(Size steps) { steps_ = steps; return *this; } template inline MakeMCVarianceSwapEngine& MakeMCVarianceSwapEngine::withStepsPerYear(Size steps) { stepsPerYear_ = steps; return *this; } template inline MakeMCVarianceSwapEngine& MakeMCVarianceSwapEngine::withSamples(Size samples) { QL_REQUIRE(tolerance_ == Null(), "tolerance already set"); samples_ = samples; return *this; } template inline MakeMCVarianceSwapEngine& MakeMCVarianceSwapEngine::withTolerance(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 MakeMCVarianceSwapEngine& MakeMCVarianceSwapEngine::withMaxSamples(Size samples) { maxSamples_ = samples; return *this; } template inline MakeMCVarianceSwapEngine& MakeMCVarianceSwapEngine::withSeed(BigNatural seed) { seed_ = seed; return *this; } template inline MakeMCVarianceSwapEngine& MakeMCVarianceSwapEngine::withBrownianBridge(bool brownianBridge) { brownianBridge_ = brownianBridge; return *this; } template inline MakeMCVarianceSwapEngine& MakeMCVarianceSwapEngine::withAntitheticVariate(bool b) { antithetic_ = b; return *this; } template inline MakeMCVarianceSwapEngine:: operator boost::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 boost::shared_ptr(new MCVarianceSwapEngine(steps_, stepsPerYear_, brownianBridge_, antithetic_, samples_, tolerance_, maxSamples_, seed_)); } namespace detail { class Integrand : std::unary_function { public: Integrand(const Path& path, const boost::shared_ptr& process) : path_(path), process_(process) {} Real operator()(Time t) const { Size i = static_cast(t/path_.timeGrid().dt(0)); Real sigma = process_->diffusion(t,path_[i]); return sigma*sigma; } private: Path path_; boost::shared_ptr process_; }; } inline Real FairVariancePathPricer::operator()(const Path& path) const { QL_REQUIRE(path.length() > 0, "the path cannot be empty"); Time t0 = path.timeGrid().front(); Time t = path.timeGrid().back(); Time dt = path.timeGrid().dt(0); SegmentIntegral integrator(static_cast(t/dt)); detail::Integrand f(path, process_); return integrator(f,t0,t)/t; } } #endif