QuantLib: a free/open-source library for quantitative finance
Fully annotated sources - version 1.32
Loading...
Searching...
No Matches
fittedbonddiscountcurve.cpp
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2009 Ferdinando Ametrano
5 Copyright (C) 2007 Allen Kuo
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/cashflows/cashflows.hpp>
22#include <ql/math/optimization/constraint.hpp>
23#include <ql/math/optimization/costfunction.hpp>
24#include <ql/math/optimization/simplex.hpp>
25#include <ql/pricingengines/bond/bondfunctions.hpp>
26#include <ql/termstructures/yield/fittedbonddiscountcurve.hpp>
27#include <ql/time/daycounters/simpledaycounter.hpp>
28#include <ql/utilities/dataformatters.hpp>
29#include <utility>
30
31using std::vector;
32
33namespace QuantLib {
34
35 class FittedBondDiscountCurve::FittingMethod::FittingCost
36 : public CostFunction {
37 friend class FittedBondDiscountCurve::FittingMethod;
38 public:
39 explicit FittingCost(
40 FittedBondDiscountCurve::FittingMethod* fittingMethod);
41 Real value(const Array& x) const override;
42 Array values(const Array& x) const override;
43
44 private:
45 FittedBondDiscountCurve::FittingMethod* fittingMethod_;
46 };
47
48
50 Natural settlementDays,
51 const Calendar& calendar,
52 vector<ext::shared_ptr<BondHelper> > bondHelpers,
53 const DayCounter& dayCounter,
54 const FittingMethod& fittingMethod,
55 Real accuracy,
56 Size maxEvaluations,
57 Array guess,
58 Real simplexLambda,
59 Size maxStationaryStateIterations)
60 : YieldTermStructure(settlementDays, calendar, dayCounter), accuracy_(accuracy),
61 maxEvaluations_(maxEvaluations), simplexLambda_(simplexLambda),
62 maxStationaryStateIterations_(maxStationaryStateIterations), guessSolution_(std::move(guess)),
63 bondHelpers_(std::move(bondHelpers)), fittingMethod_(fittingMethod) {
64 fittingMethod_->curve_ = this;
65 setup();
66 }
67
68
70 const Date& referenceDate,
71 vector<ext::shared_ptr<BondHelper> > bondHelpers,
72 const DayCounter& dayCounter,
73 const FittingMethod& fittingMethod,
74 Real accuracy,
75 Size maxEvaluations,
76 Array guess,
77 Real simplexLambda,
78 Size maxStationaryStateIterations)
79 : YieldTermStructure(referenceDate, Calendar(), dayCounter), accuracy_(accuracy),
80 maxEvaluations_(maxEvaluations), simplexLambda_(simplexLambda),
81 maxStationaryStateIterations_(maxStationaryStateIterations), guessSolution_(std::move(guess)),
82 bondHelpers_(std::move(bondHelpers)), fittingMethod_(fittingMethod) {
83
84 fittingMethod_->curve_ = this;
85 setup();
86 }
87
88
90
91 QL_REQUIRE(!bondHelpers_.empty(), "no bondHelpers given");
92
94 Date refDate = referenceDate();
95
96 // double check bond quotes still valid and/or instruments not expired
97 for (Size i=0; i<bondHelpers_.size(); ++i) {
98 ext::shared_ptr<Bond> bond = bondHelpers_[i]->bond();
99 QL_REQUIRE(bondHelpers_[i]->quote()->isValid(),
100 io::ordinal(i+1) << " bond (maturity: " <<
101 bond->maturityDate() << ") has an invalid price quote");
102 Date bondSettlement = bond->settlementDate();
103 QL_REQUIRE(bondSettlement>=refDate,
104 io::ordinal(i+1) << " bond settlemente date (" <<
105 bondSettlement << ") before curve reference date (" <<
106 refDate << ")");
107 QL_REQUIRE(BondFunctions::isTradable(*bond, bondSettlement),
108 io::ordinal(i+1) << " bond non tradable at " <<
109 bondSettlement << " settlement date (maturity"
110 " being " << bond->maturityDate() << ")");
111 maxDate_ = std::max(maxDate_, bondHelpers_[i]->pillarDate());
112 bondHelpers_[i]->setTermStructure(
113 const_cast<FittedBondDiscountCurve*>(this));
114 }
115 fittingMethod_->init();
116 fittingMethod_->calculate();
117 }
118
119
121 bool constrainAtZero,
122 const Array& weights,
123 ext::shared_ptr<OptimizationMethod> optimizationMethod,
124 Array l2,
125 const Real minCutoffTime,
126 const Real maxCutoffTime)
127 : constrainAtZero_(constrainAtZero), weights_(weights), l2_(std::move(l2)),
128 calculateWeights_(weights.empty()), optimizationMethod_(std::move(optimizationMethod)),
129 minCutoffTime_(minCutoffTime), maxCutoffTime_(maxCutoffTime) {}
130
132 // yield conventions
133 DayCounter yieldDC = curve_->dayCounter();
134 Compounding yieldComp = Compounded;
135 Frequency yieldFreq = Annual;
136
137 Size n = curve_->bondHelpers_.size();
138 costFunction_ = ext::make_shared<FittingCost>(this);
139
140 for (auto& bondHelper : curve_->bondHelpers_) {
141 bondHelper->setTermStructure(curve_);
142 }
143
144 if (calculateWeights_) {
145 if (weights_.empty())
146 weights_ = Array(n);
147
148 Real squaredSum = 0.0;
149 for (Size i=0; i<curve_->bondHelpers_.size(); ++i) {
150 ext::shared_ptr<Bond> bond = curve_->bondHelpers_[i]->bond();
151
152 Real cleanPrice = curve_->bondHelpers_[i]->quote()->value();
153
154 Date bondSettlement = bond->settlementDate();
155 Rate ytm = BondFunctions::yield(*bond, cleanPrice,
156 yieldDC, yieldComp, yieldFreq,
157 bondSettlement);
158
159 Time dur = BondFunctions::duration(*bond, ytm,
160 yieldDC, yieldComp, yieldFreq,
162 bondSettlement);
163 weights_[i] = 1.0/dur;
164 squaredSum += weights_[i]*weights_[i];
165 }
166 weights_ /= std::sqrt(squaredSum);
167 }
168
169 QL_REQUIRE(weights_.size() == n,
170 "Given weights do not cover all boostrapping helpers");
171
172 if (!l2_.empty()) {
173 QL_REQUIRE(l2_.size() == size(),
174 "Given penalty factors do not cover all parameters");
175 }
176 }
177
179
180 FittingCost& costFunction = *costFunction_;
181 Constraint constraint = NoConstraint();
182
183 // start with the guess solution, if it exists
184 Array x(size(), 0.0);
185 if (!curve_->guessSolution_.empty()) {
186 x = curve_->guessSolution_;
187 }
188
189 if (curve_->maxEvaluations_ == 0)
190 {
191 // Don't calculate, simply use the given parameters to provide a fitted curve.
192 // This turns the fittedbonddiscountcurve into an evaluator of the parametric
193 // curve, for example allowing to use the parameters for a credit spread curve
194 // calculated with bonds in one currency to be coupled to a discount curve in
195 // another currency.
196
197 QL_REQUIRE(!curve_->guessSolution_.empty(), "no guess provided");
198
199 solution_ = curve_->guessSolution_;
200
201 numberOfIterations_ = 0;
202 costValue_ = costFunction.value(solution_);
203 errorCode_ = EndCriteria::None;
204
205 return;
206 }
207
208 //workaround for backwards compatibility
209 ext::shared_ptr<OptimizationMethod> optimization = optimizationMethod_;
210 if(!optimization){
211 optimization = ext::make_shared<Simplex>(curve_->simplexLambda_);
212 }
213 Problem problem(costFunction, constraint, x);
214
215 Real rootEpsilon = curve_->accuracy_;
216 Real functionEpsilon = curve_->accuracy_;
217 Real gradientNormEpsilon = curve_->accuracy_;
218
219 EndCriteria endCriteria(curve_->maxEvaluations_,
220 curve_->maxStationaryStateIterations_,
221 rootEpsilon,
222 functionEpsilon,
223 gradientNormEpsilon);
224
225 errorCode_ = optimization->minimize(problem,endCriteria);
226 solution_ = problem.currentValue();
227
228 numberOfIterations_ = problem.functionEvaluation();
229 costValue_ = problem.functionValue();
230
231 // save the results as the guess solution, in case of recalculation
232 curve_->guessSolution_ = solution_;
233 }
234
235
236 FittedBondDiscountCurve::FittingMethod::FittingCost::FittingCost(
238 : fittingMethod_(fittingMethod) {}
239
240
241 Real FittedBondDiscountCurve::FittingMethod::FittingCost::value(
242 const Array& x) const {
243 Real squaredError = 0.0;
244 Array vals = values(x);
245 for (Real val : vals) {
246 squaredError += val;
247 }
248 return squaredError;
249 }
250
251 Array FittedBondDiscountCurve::FittingMethod::FittingCost::values(const Array &x) const {
252 Size n = fittingMethod_->curve_->bondHelpers_.size();
253 Size N = fittingMethod_->l2_.size();
254
255 // set solution so that fittingMethod_->curve_ represents the current trial
256 // the final solution will be set in FittingMethod::calculate() later on
257 fittingMethod_->solution_ = x;
258
259 Array values(n + N);
260 for (Size i=0; i<n; ++i) {
261 ext::shared_ptr<BondHelper> helper = fittingMethod_->curve_->bondHelpers_[i];
262 Real error = helper->impliedQuote() - helper->quote()->value();
263 Real weightedError = fittingMethod_->weights_[i] * error;
264 values[i] = weightedError * weightedError;
265 }
266
267 if (N != 0) {
268 for (Size i = 0; i < N; ++i) {
269 Real error = x[i] - fittingMethod_->curve_->guessSolution_[i];
270 values[i + n] = fittingMethod_->l2_[i] * error * error;
271 }
272 }
273 return values;
274 }
275
276}
1-D array used in linear algebra.
Definition: array.hpp:52
calendar class
Definition: calendar.hpp:61
Base constraint class.
Definition: constraint.hpp:35
Concrete date class.
Definition: date.hpp:125
static Date minDate()
earliest allowed date
Definition: date.cpp:766
day counter class
Definition: daycounter.hpp:44
Criteria to end optimization process:
Definition: endcriteria.hpp:40
Base fitting method used to construct a fitted bond discount curve.
virtual void init()
rerun every time instruments/referenceDate changes
FittingMethod(bool constrainAtZero=true, const Array &weights=Array(), ext::shared_ptr< OptimizationMethod > optimizationMethod=ext::shared_ptr< OptimizationMethod >(), Array l2=Array(), Real minCutoffTime=0.0, Real maxCutoffTime=QL_MAX_REAL)
constructors
Discount curve fitted to a set of fixed-coupon bonds.
std::vector< ext::shared_ptr< BondHelper > > bondHelpers_
FittedBondDiscountCurve(Natural settlementDays, const Calendar &calendar, std::vector< ext::shared_ptr< BondHelper > > bonds, const DayCounter &dayCounter, const FittingMethod &fittingMethod, Real accuracy=1.0e-10, Size maxEvaluations=10000, Array guess=Array(), Real simplexLambda=1.0, Size maxStationaryStateIterations=100)
reference date based on current evaluation date
No constraint.
Definition: constraint.hpp:79
Constrained optimization problem.
Definition: problem.hpp:42
const Array & currentValue() const
current value of the local minimum
Definition: problem.hpp:81
Real functionValue() const
value of cost function
Definition: problem.hpp:88
Integer functionEvaluation() const
number of evaluation of cost function
Definition: problem.hpp:97
virtual const Date & referenceDate() const
the date at which discount = 1.0 and/or variance = 0.0
Interest-rate term structure.
Frequency
Frequency of events.
Definition: frequency.hpp:37
@ Annual
once a year
Definition: frequency.hpp:39
detail::ordinal_holder ordinal(Size)
outputs naturals as 1st, 2nd, 3rd...
Real Time
continuous quantity with 1-year units
Definition: types.hpp:62
QL_REAL Real
real number
Definition: types.hpp:50
unsigned QL_INTEGER Natural
positive integer
Definition: types.hpp:43
Real Rate
interest rates
Definition: types.hpp:70
std::size_t Size
size of a container
Definition: types.hpp:58
Definition: any.hpp:35
Compounding
Interest rate coumpounding rule.
Definition: compounding.hpp:32
STL namespace.
static Rate yield(const Bond &bond, Real price, const DayCounter &dayCounter, Compounding compounding, Frequency frequency, Date settlementDate=Date(), Real accuracy=1.0e-10, Size maxIterations=100, Rate guess=0.05, Bond::Price::Type priceType=Bond::Price::Clean)
static bool isTradable(const Bond &bond, Date settlementDate=Date())
static Time duration(const Bond &bond, const InterestRate &yield, Duration::Type type=Duration::Modified, Date settlementDate=Date())