QuantLib: a free/open-source library for quantitative finance
fully annotated source code - version 1.34
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
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 amount = curve_->bondHelpers_[i]->quote()->value();
153 Bond::Price price(amount, curve_->bondHelpers_[i]->priceType());
154
155 Date bondSettlement = bond->settlementDate();
156 Rate ytm = BondFunctions::yield(*bond, price,
157 yieldDC, yieldComp, yieldFreq,
158 bondSettlement);
159
160 Time dur = BondFunctions::duration(*bond, ytm,
161 yieldDC, yieldComp, yieldFreq,
163 bondSettlement);
164 weights_[i] = 1.0/dur;
165 squaredSum += weights_[i]*weights_[i];
166 }
167 weights_ /= std::sqrt(squaredSum);
168 }
169
170 QL_REQUIRE(weights_.size() == n,
171 "Given weights do not cover all boostrapping helpers");
172
173 if (!l2_.empty()) {
174 QL_REQUIRE(l2_.size() == size(),
175 "Given penalty factors do not cover all parameters");
176
177 QL_REQUIRE(!curve_->guessSolution_.empty(), "L2 penalty requires a guess");
178 }
179 }
180
182
183 FittingCost& costFunction = *costFunction_;
184 Constraint constraint = NoConstraint();
185
186 // start with the guess solution, if it exists
187 Array x(size(), 0.0);
188 if (!curve_->guessSolution_.empty()) {
189 x = curve_->guessSolution_;
190 }
191
192 if (curve_->maxEvaluations_ == 0)
193 {
194 // Don't calculate, simply use the given parameters to provide a fitted curve.
195 // This turns the fittedbonddiscountcurve into an evaluator of the parametric
196 // curve, for example allowing to use the parameters for a credit spread curve
197 // calculated with bonds in one currency to be coupled to a discount curve in
198 // another currency.
199
200 QL_REQUIRE(!curve_->guessSolution_.empty(), "no guess provided");
201
202 solution_ = curve_->guessSolution_;
203
204 numberOfIterations_ = 0;
205 costValue_ = costFunction.value(solution_);
206 errorCode_ = EndCriteria::None;
207
208 return;
209 }
210
211 //workaround for backwards compatibility
212 ext::shared_ptr<OptimizationMethod> optimization = optimizationMethod_;
213 if(!optimization){
214 optimization = ext::make_shared<Simplex>(curve_->simplexLambda_);
215 }
216 Problem problem(costFunction, constraint, x);
217
218 Real rootEpsilon = curve_->accuracy_;
219 Real functionEpsilon = curve_->accuracy_;
220 Real gradientNormEpsilon = curve_->accuracy_;
221
222 EndCriteria endCriteria(curve_->maxEvaluations_,
223 curve_->maxStationaryStateIterations_,
224 rootEpsilon,
225 functionEpsilon,
226 gradientNormEpsilon);
227
228 errorCode_ = optimization->minimize(problem,endCriteria);
229 solution_ = problem.currentValue();
230
231 numberOfIterations_ = problem.functionEvaluation();
232 costValue_ = problem.functionValue();
233
234 // save the results as the guess solution, in case of recalculation
235 curve_->guessSolution_ = solution_;
236 }
237
238
239 FittedBondDiscountCurve::FittingMethod::FittingCost::FittingCost(
241 : fittingMethod_(fittingMethod) {}
242
243
244 Real FittedBondDiscountCurve::FittingMethod::FittingCost::value(
245 const Array& x) const {
246 Real squaredError = 0.0;
247 Array vals = values(x);
248 for (Real val : vals) {
249 squaredError += val;
250 }
251 return squaredError;
252 }
253
254 Array FittedBondDiscountCurve::FittingMethod::FittingCost::values(const Array &x) const {
255 Size n = fittingMethod_->curve_->bondHelpers_.size();
256 Size N = fittingMethod_->l2_.size();
257
258 // set solution so that fittingMethod_->curve_ represents the current trial
259 // the final solution will be set in FittingMethod::calculate() later on
260 fittingMethod_->solution_ = x;
261
262 Array values(n + N);
263 for (Size i=0; i<n; ++i) {
264 ext::shared_ptr<BondHelper> helper = fittingMethod_->curve_->bondHelpers_[i];
265 Real error = helper->impliedQuote() - helper->quote()->value();
266 Real weightedError = fittingMethod_->weights_[i] * error;
267 values[i] = weightedError * weightedError;
268 }
269
270 if (N != 0) {
271 for (Size i = 0; i < N; ++i) {
272 Real error = x[i] - fittingMethod_->curve_->guessSolution_[i];
273 values[i + n] = fittingMethod_->l2_[i] * error * error;
274 }
275 }
276 return values;
277 }
278
279}
bond functions
ZeroSpreadedTermStructure curve_
Definition: cashflows.cpp:1187
Cash-flow analysis functions.
1-D array used in linear algebra.
Definition: array.hpp:52
Bond price information.
Definition: bond.hpp:62
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.
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:35
Compounding
Interest rate coumpounding rule.
Definition: compounding.hpp:32
STL namespace.
Simple day counter for reproducing theoretical calculations.
Simplex optimization method.
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())