1 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 3 /* 4 Copyright (C) 2002, 2003, 2004 Ferdinando Ametrano 5 Copyright (C) 2002, 2003 RiskMap srl 6 Copyright (C) 2003, 2004, 2005, 2007 StatPro Italia srl 7 Copyright (C) 2007 Affine Group Limited 8 9 This file is part of QuantLib, a free-software/open-source library 10 for financial quantitative analysts and developers - http://quantlib.org/ 11 12 QuantLib is free software: you can redistribute it and/or modify it 13 under the terms of the QuantLib license. You should have received a 14 copy of the license along with this program; if not, please email 15 <quantlib-dev@lists.sf.net>. The license is also available online at 16 <http://quantlib.org/license.shtml>. 17 18 This program is distributed in the hope that it will be useful, but WITHOUT 19 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 20 FOR A PARTICULAR PURPOSE. See the license for more details. 21 */ 22 23 /*! \file binomialengine.hpp 24 \brief Binomial option engine 25 */ 26 27 #ifndef quantlib_binomial_engine_hpp 28 #define quantlib_binomial_engine_hpp 29 30 #include <ql/methods/lattices/binomialtree.hpp> 31 #include <ql/methods/lattices/bsmlattice.hpp> 32 #include <ql/math/distributions/normaldistribution.hpp> 33 #include <ql/pricingengines/vanilla/discretizedvanillaoption.hpp> 34 #include <ql/pricingengines/greeks.hpp> 35 #include <ql/processes/blackscholesprocess.hpp> 36 #include <ql/termstructures/yield/flatforward.hpp> 37 #include <ql/termstructures/volatility/equityfx/blackconstantvol.hpp> 38 39 namespace QuantLib { 40 41 //! Pricing engine for vanilla options using binomial trees 42 /*! \ingroup vanillaengines 43 44 \test the correctness of the returned values is tested by 45 checking it against analytic results. 46 47 \todo Greeks are not overly accurate. They could be improved 48 by building a tree so that it has three points at the 49 current time. The value would be fetched from the middle 50 one, while the two side points would be used for 51 estimating partial derivatives. 52 */ 53 template <class T> 54 class BinomialVanillaEngine : public VanillaOption::engine { 55 public: BinomialVanillaEngine(const ext::shared_ptr<GeneralizedBlackScholesProcess> & process,Size timeSteps)56 BinomialVanillaEngine( 57 const ext::shared_ptr<GeneralizedBlackScholesProcess>& process, 58 Size timeSteps) 59 : process_(process), timeSteps_(timeSteps) { 60 QL_REQUIRE(timeSteps >= 2, 61 "at least 2 time steps required, " 62 << timeSteps << " provided"); 63 registerWith(process_); 64 } 65 void calculate() const; 66 private: 67 ext::shared_ptr<GeneralizedBlackScholesProcess> process_; 68 Size timeSteps_; 69 }; 70 71 72 // template definitions 73 74 template <class T> calculate() const75 void BinomialVanillaEngine<T>::calculate() const { 76 77 DayCounter rfdc = process_->riskFreeRate()->dayCounter(); 78 DayCounter divdc = process_->dividendYield()->dayCounter(); 79 DayCounter voldc = process_->blackVolatility()->dayCounter(); 80 Calendar volcal = process_->blackVolatility()->calendar(); 81 82 Real s0 = process_->stateVariable()->value(); 83 QL_REQUIRE(s0 > 0.0, "negative or null underlying given"); 84 Volatility v = process_->blackVolatility()->blackVol( 85 arguments_.exercise->lastDate(), s0); 86 Date maturityDate = arguments_.exercise->lastDate(); 87 Rate r = process_->riskFreeRate()->zeroRate(maturityDate, 88 rfdc, Continuous, NoFrequency); 89 Rate q = process_->dividendYield()->zeroRate(maturityDate, 90 divdc, Continuous, NoFrequency); 91 Date referenceDate = process_->riskFreeRate()->referenceDate(); 92 93 // binomial trees with constant coefficient 94 Handle<YieldTermStructure> flatRiskFree( 95 ext::shared_ptr<YieldTermStructure>( 96 new FlatForward(referenceDate, r, rfdc))); 97 Handle<YieldTermStructure> flatDividends( 98 ext::shared_ptr<YieldTermStructure>( 99 new FlatForward(referenceDate, q, divdc))); 100 Handle<BlackVolTermStructure> flatVol( 101 ext::shared_ptr<BlackVolTermStructure>( 102 new BlackConstantVol(referenceDate, volcal, v, voldc))); 103 104 ext::shared_ptr<PlainVanillaPayoff> payoff = 105 ext::dynamic_pointer_cast<PlainVanillaPayoff>(arguments_.payoff); 106 QL_REQUIRE(payoff, "non-plain payoff given"); 107 108 Time maturity = rfdc.yearFraction(referenceDate, maturityDate); 109 110 ext::shared_ptr<StochasticProcess1D> bs( 111 new GeneralizedBlackScholesProcess( 112 process_->stateVariable(), 113 flatDividends, flatRiskFree, flatVol)); 114 115 TimeGrid grid(maturity, timeSteps_); 116 117 ext::shared_ptr<T> tree(new T(bs, maturity, timeSteps_, 118 payoff->strike())); 119 120 ext::shared_ptr<BlackScholesLattice<T> > lattice( 121 new BlackScholesLattice<T>(tree, r, maturity, timeSteps_)); 122 123 DiscretizedVanillaOption option(arguments_, *process_, grid); 124 125 option.initialize(lattice, maturity); 126 127 // Partial derivatives calculated from various points in the 128 // binomial tree 129 // (see J.C.Hull, "Options, Futures and other derivatives", 6th edition, pp 397/398) 130 131 // Rollback to third-last step, and get underlying prices (s2) & 132 // option values (p2) at this point 133 option.rollback(grid[2]); 134 Array va2(option.values()); 135 QL_ENSURE(va2.size() == 3, "Expect 3 nodes in grid at second step"); 136 Real p2u = va2[2]; // up 137 Real p2m = va2[1]; // mid 138 Real p2d = va2[0]; // down (low) 139 Real s2u = lattice->underlying(2, 2); // up price 140 Real s2m = lattice->underlying(2, 1); // middle price 141 Real s2d = lattice->underlying(2, 0); // down (low) price 142 143 // calculate gamma by taking the first derivate of the two deltas 144 Real delta2u = (p2u - p2m)/(s2u-s2m); 145 Real delta2d = (p2m-p2d)/(s2m-s2d); 146 Real gamma = (delta2u - delta2d) / ((s2u-s2d)/2); 147 148 // Rollback to second-last step, and get option values (p1) at 149 // this point 150 option.rollback(grid[1]); 151 Array va(option.values()); 152 QL_ENSURE(va.size() == 2, "Expect 2 nodes in grid at first step"); 153 Real p1u = va[1]; 154 Real p1d = va[0]; 155 Real s1u = lattice->underlying(1, 1); // up (high) price 156 Real s1d = lattice->underlying(1, 0); // down (low) price 157 158 Real delta = (p1u - p1d) / (s1u - s1d); 159 160 // Finally, rollback to t=0 161 option.rollback(0.0); 162 Real p0 = option.presentValue(); 163 164 // Store results 165 results_.value = p0; 166 results_.delta = delta; 167 results_.gamma = gamma; 168 results_.theta = blackScholesTheta(process_, 169 results_.value, 170 results_.delta, 171 results_.gamma); 172 } 173 174 } 175 176 177 #endif 178