/* -*- 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 replicatingvarianceswapengine.hpp \brief Replicating engine for variance swaps */ #ifndef quantlib_replicating_varianceswap_engine_hpp #define quantlib_replicating_varianceswap_engine_hpp #include #include #include namespace QuantLib { //! Variance-swap pricing engine using replicating cost, /*! as described in Demeterfi, Derman, Kamal & Zou, "A Guide to Volatility and Variance Swaps", 1999 \ingroup forwardengines \test returned fair variances verified against results from literature */ class ReplicatingVarianceSwapEngine : public VarianceSwap::engine { public: typedef std::vector StrikesType; // constructor ReplicatingVarianceSwapEngine( const Real dk = 5.0, const StrikesType& callStrikes = StrikesType(), const StrikesType& putStrikes = StrikesType(), const boost::shared_ptr& engine = boost::shared_ptr()); void calculate() const; protected: // helper methods void computeOptionWeights(const StrikesType&, const Option::Type) const; Real computeLogPayoff(const Real, const Real) const; Real computeReplicatingPortfolio() const; Rate riskFreeRate() const; DiscountFactor riskFreeDiscount() const; Real underlying() const; Time residualTime() const; private: const Real dk_; const StrikesType callStrikes_; const StrikesType putStrikes_; boost::shared_ptr optionEngine_; }; // inline definitions inline ReplicatingVarianceSwapEngine::ReplicatingVarianceSwapEngine( const Real dk, const StrikesType& callStrikes, const StrikesType& putStrikes, const boost::shared_ptr& engine) : dk_(dk), callStrikes_(callStrikes), putStrikes_(putStrikes), optionEngine_(engine) { QL_REQUIRE(!callStrikes.empty() && !putStrikes.empty(), "no strike(s) given"); QL_REQUIRE(*std::min_element(putStrikes.begin(),putStrikes.end())>0.0, "min put strike must be positive"); QL_REQUIRE(*std::min_element(callStrikes.begin(), callStrikes.end())== *std::max_element(putStrikes.begin(), putStrikes.end()), "min call and max put strikes differ"); if (!engine) optionEngine_ = boost::shared_ptr( new AnalyticEuropeanEngine); } inline void ReplicatingVarianceSwapEngine::computeOptionWeights( const StrikesType& availStrikes, const Option::Type type) const { if (availStrikes.empty()) return; StrikesType strikes = availStrikes; // add end-strike for piecewise approximation switch (type) { case Option::Call: std::sort(strikes.begin(), strikes.end()); strikes.push_back(strikes.back() + dk_); break; case Option::Put: std::sort(strikes.begin(), strikes.end(), std::greater()); strikes.push_back(std::max(strikes.back() - dk_, 0.0)); break; default: QL_FAIL("invalid option type"); } // remove duplicate strikes StrikesType::iterator last = std::unique(strikes.begin(), strikes.end()); strikes.erase(last, strikes.end()); // compute weights Real f = strikes.front(); Real slope, prevSlope = 0.0; for (StrikesType::const_iterator k=strikes.begin(); // added end-strike discarded k payoff( new PlainVanillaPayoff(type, *k)); if ( k == strikes.begin() ) results_.optionWeights.push_back(std::make_pair(payoff,slope)); else results_.optionWeights.push_back( std::make_pair(payoff, slope - prevSlope)); prevSlope = slope; } } inline Real ReplicatingVarianceSwapEngine::computeLogPayoff( const Real strike, const Real callPutStrikeBoundary) const { Real f = callPutStrikeBoundary; return (2.0/residualTime()) * (((strike - f)/f) - std::log(strike/f)); } inline Real ReplicatingVarianceSwapEngine::computeReplicatingPortfolio() const { boost::shared_ptr exercise( new EuropeanExercise(arguments_.maturityDate)); Real optionsValue = 0.0; for (results_.iterator=results_.optionWeights.begin(); results_.iterator payoff = results_.iterator->first; EuropeanOption option(arguments_.stochasticProcess, payoff, exercise); option.setPricingEngine(optionEngine_); Real weight = results_.iterator->second; optionsValue += option.NPV() * weight; } Real f = results_.optionWeights.front().first->strike(); return 2.0 * riskFreeRate() - 2.0/residualTime() * (((underlying()/riskFreeDiscount() - f)/f) + std::log(f/underlying())) + optionsValue/riskFreeDiscount(); } // calculate fair variance via replicating portfolio inline void ReplicatingVarianceSwapEngine::calculate() const { computeOptionWeights(callStrikes_, Option::Call); computeOptionWeights(putStrikes_, Option::Put); results_.fairVariance = computeReplicatingPortfolio(); } inline Real ReplicatingVarianceSwapEngine::underlying() const { return arguments_.stochasticProcess->stateVariable()->value(); } inline Time ReplicatingVarianceSwapEngine::residualTime() const { return arguments_.stochasticProcess->time(arguments_.maturityDate); } inline Rate ReplicatingVarianceSwapEngine::riskFreeRate() const { boost::shared_ptr process = boost::dynamic_pointer_cast( arguments_.stochasticProcess); QL_REQUIRE(process, "Black-Scholes process required"); return process->riskFreeRate()->zeroRate(residualTime(), Continuous, NoFrequency, true); } inline DiscountFactor ReplicatingVarianceSwapEngine::riskFreeDiscount() const { boost::shared_ptr process = boost::dynamic_pointer_cast( arguments_.stochasticProcess); QL_REQUIRE(process, "Black-Scholes process required"); return process->riskFreeRate()->discount(residualTime()); } } #endif