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