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