QuantLib: a free/open-source library for quantitative finance
fully annotated source code - version 1.38
Loading...
Searching...
No Matches
fittedbonddiscountcurve.cpp
Go to the documentation of this file.
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
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
89 Natural settlementDays,
90 const Calendar& calendar,
91 const FittingMethod& fittingMethod,
92 Array parameters,
93 Date maxDate,
94 const DayCounter& dayCounter)
95 : YieldTermStructure(settlementDays, calendar, dayCounter), accuracy_(1e-10),
96 maxEvaluations_(0), guessSolution_(std::move(parameters)),
97 maxDate_(maxDate), fittingMethod_(fittingMethod) {
98
99 fittingMethod_->curve_ = this;
100 setup();
101 }
102
104 const Date& referenceDate,
105 const FittingMethod& fittingMethod,
106 Array parameters,
107 Date maxDate,
108 const DayCounter& dayCounter)
109 : YieldTermStructure(referenceDate, Calendar(), dayCounter), accuracy_(1e-10),
110 maxEvaluations_(0), guessSolution_(std::move(parameters)),
111 maxDate_(maxDate), fittingMethod_(fittingMethod) {
112
113 fittingMethod_->curve_ = this;
114 setup();
115 }
116
117
119 QL_REQUIRE(guess.empty() || guess.size() == fittingMethod_->size(), "guess is of wrong size");
120 guessSolution_ = guess;
121 update();
122 }
123
124
126
127 if (maxEvaluations_!= 0) {
128 // we need to fit, so we require helpers
129 QL_REQUIRE(!bondHelpers_.empty(), "no bond helpers given");
130 }
131
132 if (maxEvaluations_ == 0) {
133 // no fit, but we need either an explicit max date or
134 // helpers from which to deduce it
135 QL_REQUIRE(maxDate_ != Date() || !bondHelpers_.empty(),
136 "no bond helpers or max date given");
137 }
138
139 if (!bondHelpers_.empty()) {
141 Date refDate = referenceDate();
142
143 // double check bond quotes still valid and/or instruments not expired
144 for (Size i=0; i<bondHelpers_.size(); ++i) {
145 ext::shared_ptr<Bond> bond = bondHelpers_[i]->bond();
146 QL_REQUIRE(bondHelpers_[i]->quote()->isValid(),
147 io::ordinal(i+1) << " bond (maturity: " <<
148 bond->maturityDate() << ") has an invalid price quote");
149 Date bondSettlement = bond->settlementDate();
150 QL_REQUIRE(bondSettlement>=refDate,
151 io::ordinal(i+1) << " bond settlemente date (" <<
152 bondSettlement << ") before curve reference date (" <<
153 refDate << ")");
154 QL_REQUIRE(BondFunctions::isTradable(*bond, bondSettlement),
155 io::ordinal(i+1) << " bond non tradable at " <<
156 bondSettlement << " settlement date (maturity"
157 " being " << bond->maturityDate() << ")");
158 maxDate_ = std::max(maxDate_, bondHelpers_[i]->pillarDate());
159 bondHelpers_[i]->setTermStructure(
160 const_cast<FittedBondDiscountCurve*>(this));
161 }
162 }
163
164 fittingMethod_->init();
165 fittingMethod_->calculate();
166 }
167
168
170 bool constrainAtZero,
171 const Array& weights,
172 ext::shared_ptr<OptimizationMethod> optimizationMethod,
173 Array l2,
174 const Real minCutoffTime,
175 const Real maxCutoffTime,
176 Constraint constraint)
177 : constrainAtZero_(constrainAtZero), weights_(weights), l2_(std::move(l2)),
178 calculateWeights_(weights.empty()), optimizationMethod_(std::move(optimizationMethod)),
179 constraint_(std::move(constraint)),
180 minCutoffTime_(minCutoffTime), maxCutoffTime_(maxCutoffTime) {
181 if (constraint_.empty())
183 }
184
186
187 if (curve_->maxEvaluations_ == 0)
188 return; // we can skip the rest
189
190 // yield conventions
191 DayCounter yieldDC = curve_->dayCounter();
192 Compounding yieldComp = Compounded;
193 Frequency yieldFreq = Annual;
194
195 Size n = curve_->bondHelpers_.size();
196 costFunction_ = ext::make_shared<FittingCost>(this);
197
198 for (auto& bondHelper : curve_->bondHelpers_) {
199 bondHelper->setTermStructure(curve_);
200 }
201
202 if (calculateWeights_) {
203 if (weights_.empty())
204 weights_ = Array(n);
205
206 Real squaredSum = 0.0;
207 for (Size i=0; i<curve_->bondHelpers_.size(); ++i) {
208 ext::shared_ptr<Bond> bond = curve_->bondHelpers_[i]->bond();
209
210 Real amount = curve_->bondHelpers_[i]->quote()->value();
211 Bond::Price price(amount, curve_->bondHelpers_[i]->priceType());
212
213 Date bondSettlement = bond->settlementDate();
214 Rate ytm = BondFunctions::yield(*bond, price,
215 yieldDC, yieldComp, yieldFreq,
216 bondSettlement);
217
218 Time dur = BondFunctions::duration(*bond, ytm,
219 yieldDC, yieldComp, yieldFreq,
221 bondSettlement);
222 weights_[i] = 1.0/dur;
223 squaredSum += weights_[i]*weights_[i];
224 }
225 weights_ /= std::sqrt(squaredSum);
226 }
227
228 QL_REQUIRE(weights_.size() == n,
229 "Given weights do not cover all boostrapping helpers");
230
231 if (!l2_.empty()) {
232 QL_REQUIRE(l2_.size() == size(),
233 "Given penalty factors do not cover all parameters");
234
235 QL_REQUIRE(!curve_->guessSolution_.empty(), "L2 penalty requires a guess");
236 }
237 }
238
240
241 if (curve_->maxEvaluations_ == 0)
242 {
243 // Don't calculate, simply use the given parameters to
244 // provide a fitted curve. This turns the instance into
245 // an evaluator of the parametric curve, for example
246 // allowing to use the parameters for a credit spread
247 // curve calculated with bonds in one currency to be
248 // coupled to a discount curve in another currency.
249
250 QL_REQUIRE(curve_->guessSolution_.size() == size(),
251 "wrong number of parameters");
252
253 solution_ = curve_->guessSolution_;
254
255 numberOfIterations_ = 0;
256 costValue_ = Null<Real>();
257 errorCode_ = EndCriteria::None;
258
259 return;
260 }
261
262 FittingCost& costFunction = *costFunction_;
263
264 // start with the guess solution, if it exists
265 Array x(size(), 0.0);
266 if (!curve_->guessSolution_.empty()) {
267 QL_REQUIRE(curve_->guessSolution_.size() == size(), "wrong size for guess");
268 x = curve_->guessSolution_;
269 }
270
271 // workaround for backwards compatibility
272 ext::shared_ptr<OptimizationMethod> optimization = optimizationMethod_;
273 if (!optimization) {
274 optimization = ext::make_shared<Simplex>(curve_->simplexLambda_);
275 }
276 Problem problem(costFunction, constraint_, x);
277
278 Real rootEpsilon = curve_->accuracy_;
279 Real functionEpsilon = curve_->accuracy_;
280 Real gradientNormEpsilon = curve_->accuracy_;
281
282 EndCriteria endCriteria(curve_->maxEvaluations_,
283 curve_->maxStationaryStateIterations_,
284 rootEpsilon,
285 functionEpsilon,
286 gradientNormEpsilon);
287
288 errorCode_ = optimization->minimize(problem,endCriteria);
289 solution_ = problem.currentValue();
290
291 numberOfIterations_ = problem.functionEvaluation();
292 costValue_ = problem.functionValue();
293
294 // save the results as the guess solution, in case of recalculation
295 curve_->guessSolution_ = solution_;
296 }
297
298
299 FittedBondDiscountCurve::FittingMethod::FittingCost::FittingCost(
301 : fittingMethod_(fittingMethod) {}
302
303
304 Real FittedBondDiscountCurve::FittingMethod::FittingCost::value(
305 const Array& x) const {
306 Real squaredError = 0.0;
307 Array vals = values(x);
308 for (Real val : vals) {
309 squaredError += val;
310 }
311 return squaredError;
312 }
313
314 Array FittedBondDiscountCurve::FittingMethod::FittingCost::values(const Array &x) const {
315 Size n = fittingMethod_->curve_->bondHelpers_.size();
316 Size N = fittingMethod_->l2_.size();
317
318 // set solution so that fittingMethod_->curve_ represents the current trial
319 // the final solution will be set in FittingMethod::calculate() later on
320 fittingMethod_->solution_ = x;
321
322 Array values(n + N);
323 for (Size i=0; i<n; ++i) {
324 ext::shared_ptr<BondHelper> helper = fittingMethod_->curve_->bondHelpers_[i];
325 Real error = helper->impliedQuote() - helper->quote()->value();
326 Real weightedError = fittingMethod_->weights_[i] * error;
327 values[i] = weightedError * weightedError;
328 }
329
330 if (N != 0) {
331 for (Size i = 0; i < N; ++i) {
332 Real error = x[i] - fittingMethod_->curve_->guessSolution_[i];
333 values[i + n] = fittingMethod_->l2_[i] * error * error;
334 }
335 }
336 return values;
337 }
338
339}
bond functions
ZeroSpreadedTermStructure curve_
Definition: cashflows.cpp:1187
Cash-flow analysis functions.
1-D array used in linear algebra.
Definition: array.hpp:52
bool empty() const
whether the array is empty
Definition: array.hpp:487
Size size() const
dimension of the array
Definition: array.hpp:483
Bond price information.
Definition: bond.hpp:62
calendar class
Definition: calendar.hpp:61
Base constraint class.
Definition: constraint.hpp:35
bool empty() const
Definition: constraint.hpp:56
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, Constraint constraint=NoConstraint())
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
template class providing a null value for a given type.
Definition: null.hpp:59
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.
Abstract constraint class.
Optimization cost function class.
output manipulators
#define QL_REQUIRE(condition, message)
throw an error if the given pre-condition is not verified
Definition: errors.hpp:117
discount curve fitted to a set of bonds
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:37
Compounding
Interest rate coumpounding rule.
Definition: compounding.hpp:32
STL namespace.
Simple day counter for reproducing theoretical calculations.
Simplex optimization method.
static bool isTradable(const Bond &bond, Date settlementDate=Date())
static Rate yield(const Bond &bond, Bond::Price price, const DayCounter &dayCounter, Compounding compounding, Frequency frequency, Date settlementDate=Date(), Real accuracy=1.0e-10, Size maxIterations=100, Rate guess=0.05)
static Time duration(const Bond &bond, const InterestRate &yield, Duration::Type type=Duration::Modified, Date settlementDate=Date())