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