1 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 
3 /*
4  Copyright (C) 2001, 2002, 2003 Sadruddin Rejeb
5  Copyright (C) 2013 Peter Caspers
6 
7  This file is part of QuantLib, a free-software/open-source library
8  for financial quantitative analysts and developers - http://quantlib.org/
9 
10  QuantLib is free software: you can redistribute it and/or modify it
11  under the terms of the QuantLib license.  You should have received a
12  copy of the license along with this program; if not, please email
13  <quantlib-dev@lists.sf.net>. The license is also available online at
14  <http://quantlib.org/license.shtml>.
15 
16  This program is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18  FOR A PARTICULAR PURPOSE.  See the license for more details.
19 */
20 
21 #include <ql/pricingengines/swaption/jamshidianswaptionengine.hpp>
22 #include <ql/math/solvers1d/brent.hpp>
23 
24 namespace QuantLib {
25 
26     class JamshidianSwaptionEngine::rStarFinder {
27       public:
rStarFinder(const ext::shared_ptr<OneFactorAffineModel> & model,Real nominal,Time maturity,Time valueTime,const std::vector<Time> & fixedPayTimes,const std::vector<Real> & amounts)28         rStarFinder(const ext::shared_ptr<OneFactorAffineModel>& model,
29                     Real nominal,
30                     Time maturity,
31                     Time valueTime,
32                     const std::vector<Time>& fixedPayTimes,
33                     const std::vector<Real>& amounts)
34         : strike_(nominal), maturity_(maturity), valueTime_(valueTime), times_(fixedPayTimes), amounts_(amounts), model_(model) {}
35 
operator ()(Rate x) const36         Real operator()(Rate x) const {
37             Real value = strike_;
38             Real B = model_->discountBond(maturity_, valueTime_, x);
39             Size size = times_.size();
40             for (Size i=0; i<size; i++) {
41                 Real dbValue =
42                     model_->discountBond(maturity_, times_[i], x) / B;
43                 value -= amounts_[i]*dbValue;
44             }
45             return value;
46         }
47       private:
48         Real strike_;
49         Time maturity_,valueTime_;
50         std::vector<Time> times_;
51         const std::vector<Real>& amounts_;
52         const ext::shared_ptr<OneFactorAffineModel>& model_;
53     };
54 
calculate() const55     void JamshidianSwaptionEngine::calculate() const {
56 
57         QL_REQUIRE(arguments_.settlementMethod != Settlement::ParYieldCurve,
58                    "cash settled (ParYieldCurve) swaptions not priced with "
59                    "JamshidianSwaptionEngine");
60 
61         QL_REQUIRE(arguments_.exercise->type() == Exercise::European,
62                    "cannot use the Jamshidian decomposition "
63                    "on exotic swaptions");
64 
65         QL_REQUIRE(arguments_.swap->spread() == 0.0, "non zero spread (" << arguments_.swap->spread() << ") not allowed"); // PC
66 
67         Date referenceDate;
68         DayCounter dayCounter;
69 
70         ext::shared_ptr<TermStructureConsistentModel> tsmodel =
71             ext::dynamic_pointer_cast<TermStructureConsistentModel>(*model_);
72         if (tsmodel != 0) {
73             referenceDate = tsmodel->termStructure()->referenceDate();
74             dayCounter = tsmodel->termStructure()->dayCounter();
75         } else {
76             referenceDate = termStructure_->referenceDate();
77             dayCounter = termStructure_->dayCounter();
78         }
79 
80         std::vector<Real> amounts(arguments_.fixedCoupons);
81         amounts.back() += arguments_.nominal;
82 
83         Real maturity = dayCounter.yearFraction(referenceDate,
84                                                 arguments_.exercise->date(0));
85 
86         std::vector<Time> fixedPayTimes(arguments_.fixedPayDates.size());
87         Time valueTime = dayCounter.yearFraction(referenceDate,arguments_.fixedResetDates[0]);
88         for (Size i=0; i<fixedPayTimes.size(); i++)
89             fixedPayTimes[i] = dayCounter.yearFraction(referenceDate,
90                                                        arguments_.fixedPayDates[i]);
91 
92         rStarFinder finder(*model_, arguments_.nominal, maturity, valueTime,
93                            fixedPayTimes, amounts);
94         Brent s1d;
95         Rate minStrike = -10.0;
96         Rate maxStrike = 10.0;
97         s1d.setMaxEvaluations(10000);
98         s1d.setLowerBound(minStrike);
99         s1d.setUpperBound(maxStrike);
100         Rate rStar = s1d.solve(finder, 1e-8, 0.05, minStrike, maxStrike);
101 
102         Option::Type w = arguments_.type==VanillaSwap::Payer ?
103                                                 Option::Put : Option::Call;
104         Size size = arguments_.fixedCoupons.size();
105 
106         Real value = 0.0;
107         Real B = model_->discountBond(maturity, valueTime, rStar);
108         for (Size i=0; i<size; i++) {
109             Real fixedPayTime =
110                 dayCounter.yearFraction(referenceDate,
111                                         arguments_.fixedPayDates[i]);
112             Real strike = model_->discountBond(maturity,
113                                                fixedPayTime,
114                                                rStar) / B;
115             // Looks like the swaption decomposed into individual options adjusted for maturity. Each individual option is valued by Hull-White (or other one-factor model).
116             Real dboValue = model_->discountBondOption(
117                                                w, strike, maturity, valueTime,
118                                                fixedPayTime);
119             value += amounts[i]*dboValue;
120         }
121         results_.value = value;
122     }
123 
124 }
125 
126