1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2009 Nathan Abbott
5 Copyright (C) 2007, 2008, 2009, 2010 Ferdinando Ametrano
6 Copyright (C) 2007 Chiara Fornarola
7 Copyright (C) 2008 Simon Ibbotson
8 Copyright (C) 2004 M-Dimension Consulting Inc.
9 Copyright (C) 2005, 2006, 2007, 2008, 2009 StatPro Italia srl
10 Copyright (C) 2004 Jeff Yu
11
12 This file is part of QuantLib, a free-software/open-source library
13 for financial quantitative analysts and developers - http://quantlib.org/
14
15 QuantLib is free software: you can redistribute it and/or modify it
16 under the terms of the QuantLib license. You should have received a
17 copy of the license along with this program; if not, please email
18 <quantlib-dev@lists.sf.net>. The license is also available online at
19 <http://quantlib.org/license.shtml>.
20
21 This program is distributed in the hope that it will be useful, but WITHOUT
22 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
23 FOR A PARTICULAR PURPOSE. See the license for more details.
24*/
25
26#include <ql/math/solvers1d/newtonsafe.hpp>
27#include <ql/pricingengines/bond/bondfunctions.hpp>
28
29namespace QuantLib {
30
31 Date BondFunctions::startDate(const Bond& bond) {
32 return CashFlows::startDate(leg: bond.cashflows());
33 }
34
35 Date BondFunctions::maturityDate(const Bond& bond) {
36 return CashFlows::maturityDate(leg: bond.cashflows());
37 }
38
39 bool BondFunctions::isTradable(const Bond& bond,
40 Date settlement) {
41 if (settlement == Date())
42 settlement = bond.settlementDate();
43
44 return bond.notional(d: settlement)!=0.0;
45 }
46
47 Leg::const_reverse_iterator
48 BondFunctions::previousCashFlow(const Bond& bond,
49 Date settlement) {
50 if (settlement == Date())
51 settlement = bond.settlementDate();
52
53 return CashFlows::previousCashFlow(leg: bond.cashflows(),
54 includeSettlementDateFlows: false, settlementDate: settlement);
55 }
56
57 Leg::const_iterator BondFunctions::nextCashFlow(const Bond& bond,
58 Date settlement) {
59 if (settlement == Date())
60 settlement = bond.settlementDate();
61
62 return CashFlows::nextCashFlow(leg: bond.cashflows(),
63 includeSettlementDateFlows: false, settlementDate: settlement);
64 }
65
66 Date BondFunctions::previousCashFlowDate(const Bond& bond,
67 Date settlement) {
68 if (settlement == Date())
69 settlement = bond.settlementDate();
70
71 return CashFlows::previousCashFlowDate(leg: bond.cashflows(),
72 includeSettlementDateFlows: false, settlementDate: settlement);
73 }
74
75 Date BondFunctions::nextCashFlowDate(const Bond& bond,
76 Date settlement) {
77 if (settlement == Date())
78 settlement = bond.settlementDate();
79
80 return CashFlows::nextCashFlowDate(leg: bond.cashflows(),
81 includeSettlementDateFlows: false, settlementDate: settlement);
82 }
83
84 Real BondFunctions::previousCashFlowAmount(const Bond& bond,
85 Date settlement) {
86 if (settlement == Date())
87 settlement = bond.settlementDate();
88
89 return CashFlows::previousCashFlowAmount(leg: bond.cashflows(),
90 includeSettlementDateFlows: false, settlementDate: settlement);
91 }
92
93 Real BondFunctions::nextCashFlowAmount(const Bond& bond,
94 Date settlement) {
95 if (settlement == Date())
96 settlement = bond.settlementDate();
97
98 return CashFlows::nextCashFlowAmount(leg: bond.cashflows(),
99 includeSettlementDateFlows: false, settlementDate: settlement);
100 }
101
102 Rate BondFunctions::previousCouponRate(const Bond& bond,
103 Date settlement) {
104 if (settlement == Date())
105 settlement = bond.settlementDate();
106
107 return CashFlows::previousCouponRate(leg: bond.cashflows(),
108 includeSettlementDateFlows: false, settlementDate: settlement);
109 }
110
111 Rate BondFunctions::nextCouponRate(const Bond& bond,
112 Date settlement) {
113 if (settlement == Date())
114 settlement = bond.settlementDate();
115
116 return CashFlows::nextCouponRate(leg: bond.cashflows(),
117 includeSettlementDateFlows: false, settlementDate: settlement);
118 }
119
120 Date BondFunctions::accrualStartDate(const Bond& bond,
121 Date settlement) {
122 if (settlement == Date())
123 settlement = bond.settlementDate();
124
125 QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
126 "non tradable at " << settlement <<
127 " (maturity being " << bond.maturityDate() << ")");
128
129 return CashFlows::accrualStartDate(leg: bond.cashflows(),
130 includeSettlementDateFlows: false, settlDate: settlement);
131 }
132
133 Date BondFunctions::accrualEndDate(const Bond& bond,
134 Date settlement) {
135 if (settlement == Date())
136 settlement = bond.settlementDate();
137
138 QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
139 "non tradable at " << settlement <<
140 " (maturity being " << bond.maturityDate() << ")");
141
142 return CashFlows::accrualEndDate(leg: bond.cashflows(),
143 includeSettlementDateFlows: false, settlementDate: settlement);
144 }
145
146 Date BondFunctions::referencePeriodStart(const Bond& bond,
147 Date settlement) {
148 if (settlement == Date())
149 settlement = bond.settlementDate();
150
151 QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
152 "non tradable at " << settlement <<
153 " (maturity being " << bond.maturityDate() << ")");
154
155 return CashFlows::referencePeriodStart(leg: bond.cashflows(),
156 includeSettlementDateFlows: false, settlDate: settlement);
157 }
158
159 Date BondFunctions::referencePeriodEnd(const Bond& bond,
160 Date settlement) {
161 if (settlement == Date())
162 settlement = bond.settlementDate();
163
164 QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
165 "non tradable at " << settlement <<
166 " (maturity being " << bond.maturityDate() << ")");
167
168 return CashFlows::referencePeriodEnd(leg: bond.cashflows(),
169 includeSettlementDateFlows: false, settlDate: settlement);
170 }
171
172 Time BondFunctions::accrualPeriod(const Bond& bond,
173 Date settlement) {
174 if (settlement == Date())
175 settlement = bond.settlementDate();
176
177 QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
178 "non tradable at " << settlement <<
179 " (maturity being " << bond.maturityDate() << ")");
180
181 return CashFlows::accrualPeriod(leg: bond.cashflows(),
182 includeSettlementDateFlows: false, settlementDate: settlement);
183 }
184
185 Date::serial_type BondFunctions::accrualDays(const Bond& bond,
186 Date settlement) {
187 if (settlement == Date())
188 settlement = bond.settlementDate();
189
190 QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
191 "non tradable at " << settlement <<
192 " (maturity being " << bond.maturityDate() << ")");
193
194 return CashFlows::accrualDays(leg: bond.cashflows(),
195 includeSettlementDateFlows: false, settlementDate: settlement);
196 }
197
198 Time BondFunctions::accruedPeriod(const Bond& bond,
199 Date settlement) {
200 if (settlement == Date())
201 settlement = bond.settlementDate();
202
203 QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
204 "non tradable at " << settlement <<
205 " (maturity being " << bond.maturityDate() << ")");
206
207 return CashFlows::accruedPeriod(leg: bond.cashflows(),
208 includeSettlementDateFlows: false, settlementDate: settlement);
209 }
210
211 Date::serial_type BondFunctions::accruedDays(const Bond& bond,
212 Date settlement) {
213 if (settlement == Date())
214 settlement = bond.settlementDate();
215
216 QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
217 "non tradable at " << settlement <<
218 " (maturity being " << bond.maturityDate() << ")");
219
220 return CashFlows::accruedDays(leg: bond.cashflows(),
221 includeSettlementDateFlows: false, settlementDate: settlement);
222 }
223
224 Real BondFunctions::accruedAmount(const Bond& bond,
225 Date settlement) {
226 if (settlement == Date())
227 settlement = bond.settlementDate();
228
229 if (!BondFunctions::isTradable(bond, settlement))
230 return 0.0;
231
232 return CashFlows::accruedAmount(leg: bond.cashflows(),
233 includeSettlementDateFlows: false, settlementDate: settlement) *
234 100.0 / bond.notional(d: settlement);
235 }
236
237
238
239 Real BondFunctions::cleanPrice(const Bond& bond,
240 const YieldTermStructure& discountCurve,
241 Date settlement) {
242 if (settlement == Date())
243 settlement = bond.settlementDate();
244
245 QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
246 "non tradable at " << settlement <<
247 " settlement date (maturity being " <<
248 bond.maturityDate() << ")");
249
250 Real dirtyPrice = CashFlows::npv(leg: bond.cashflows(), discountCurve,
251 includeSettlementDateFlows: false, settlementDate: settlement) *
252 100.0 / bond.notional(d: settlement);
253 return dirtyPrice - bond.accruedAmount(d: settlement);
254 }
255
256 Real BondFunctions::bps(const Bond& bond,
257 const YieldTermStructure& discountCurve,
258 Date settlement) {
259 if (settlement == Date())
260 settlement = bond.settlementDate();
261
262 QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
263 "non tradable at " << settlement <<
264 " (maturity being " << bond.maturityDate() << ")");
265
266 return CashFlows::bps(leg: bond.cashflows(), discountCurve,
267 includeSettlementDateFlows: false, settlementDate: settlement) *
268 100.0 / bond.notional(d: settlement);
269 }
270
271 Rate BondFunctions::atmRate(const Bond& bond,
272 const YieldTermStructure& discountCurve,
273 Date settlement,
274 Real cleanPrice) {
275 if (settlement == Date())
276 settlement = bond.settlementDate();
277
278 QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
279 "non tradable at " << settlement <<
280 " (maturity being " << bond.maturityDate() << ")");
281
282 Real dirtyPrice = cleanPrice==Null<Real>() ? Null<Real>() :
283 cleanPrice + bond.accruedAmount(d: settlement);
284 Real currentNotional = bond.notional(d: settlement);
285 Real npv = dirtyPrice==Null<Real>() ? Null<Real>() :
286 dirtyPrice/100.0 * currentNotional;
287
288 return CashFlows::atmRate(leg: bond.cashflows(), discountCurve,
289 includeSettlementDateFlows: false, settlementDate: settlement, npvDate: settlement,
290 npv);
291 }
292
293 Real BondFunctions::cleanPrice(const Bond& bond,
294 const InterestRate& yield,
295 Date settlement) {
296 return dirtyPrice(bond, yield, settlementDate: settlement) - bond.accruedAmount(d: settlement);
297 }
298
299 Real BondFunctions::cleanPrice(const Bond& bond,
300 Rate yield,
301 const DayCounter& dayCounter,
302 Compounding compounding,
303 Frequency frequency,
304 Date settlement) {
305 InterestRate y(yield, dayCounter, compounding, frequency);
306 return cleanPrice(bond, yield: y, settlement);
307 }
308
309 Real BondFunctions::dirtyPrice(const Bond& bond,
310 const InterestRate& yield,
311 Date settlement) {
312 if (settlement == Date())
313 settlement = bond.settlementDate();
314
315 QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
316 "non tradable at " << settlement <<
317 " (maturity being " << bond.maturityDate() << ")");
318
319 Real dirtyPrice = CashFlows::npv(leg: bond.cashflows(), yield,
320 includeSettlementDateFlows: false, settlementDate: settlement) *
321 100.0 / bond.notional(d: settlement);
322 return dirtyPrice;
323 }
324
325 Real BondFunctions::dirtyPrice(const Bond& bond,
326 Rate yield,
327 const DayCounter& dayCounter,
328 Compounding compounding,
329 Frequency frequency,
330 Date settlement) {
331 InterestRate y(yield, dayCounter, compounding, frequency);
332 return dirtyPrice(bond, yield: y, settlement);
333 }
334
335 Real BondFunctions::bps(const Bond& bond,
336 const InterestRate& yield,
337 Date settlement) {
338 if (settlement == Date())
339 settlement = bond.settlementDate();
340
341 QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
342 "non tradable at " << settlement <<
343 " (maturity being " << bond.maturityDate() << ")");
344
345 return CashFlows::bps(leg: bond.cashflows(), yield,
346 includeSettlementDateFlows: false, settlementDate: settlement) *
347 100.0 / bond.notional(d: settlement);
348 }
349
350 Real BondFunctions::bps(const Bond& bond,
351 Rate yield,
352 const DayCounter& dayCounter,
353 Compounding compounding,
354 Frequency frequency,
355 Date settlement) {
356 InterestRate y(yield, dayCounter, compounding, frequency);
357 return bps(bond, yield: y, settlement);
358 }
359
360 Rate BondFunctions::yield(const Bond& bond,
361 Real price,
362 const DayCounter& dayCounter,
363 Compounding compounding,
364 Frequency frequency,
365 Date settlement,
366 Real accuracy,
367 Size maxIterations,
368 Rate guess,
369 Bond::Price::Type priceType) {
370 NewtonSafe solver;
371 solver.setMaxEvaluations(maxIterations);
372 return yield<NewtonSafe>(solver, bond, price, dayCounter,
373 compounding, frequency, settlementDate: settlement,
374 accuracy, guess, priceType);
375 }
376
377 Time BondFunctions::duration(const Bond& bond,
378 const InterestRate& yield,
379 Duration::Type type,
380 Date settlement) {
381 if (settlement == Date())
382 settlement = bond.settlementDate();
383
384 QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
385 "non tradable at " << settlement <<
386 " (maturity being " << bond.maturityDate() << ")");
387
388 return CashFlows::duration(leg: bond.cashflows(), yield,
389 type,
390 includeSettlementDateFlows: false, settlementDate: settlement);
391 }
392
393 Time BondFunctions::duration(const Bond& bond,
394 Rate yield,
395 const DayCounter& dayCounter,
396 Compounding compounding,
397 Frequency frequency,
398 Duration::Type type,
399 Date settlement) {
400 InterestRate y(yield, dayCounter, compounding, frequency);
401 return duration(bond, yield: y, type, settlement);
402 }
403
404 Real BondFunctions::convexity(const Bond& bond,
405 const InterestRate& yield,
406 Date settlement) {
407 if (settlement == Date())
408 settlement = bond.settlementDate();
409
410 QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
411 "non tradable at " << settlement <<
412 " (maturity being " << bond.maturityDate() << ")");
413
414 return CashFlows::convexity(leg: bond.cashflows(), yield,
415 includeSettlementDateFlows: false, settlementDate: settlement);
416 }
417
418 Real BondFunctions::convexity(const Bond& bond,
419 Rate yield,
420 const DayCounter& dayCounter,
421 Compounding compounding,
422 Frequency frequency,
423 Date settlement) {
424 InterestRate y(yield, dayCounter, compounding, frequency);
425 return convexity(bond, yield: y, settlement);
426 }
427
428 Real BondFunctions::basisPointValue(const Bond& bond,
429 const InterestRate& yield,
430 Date settlement) {
431 if (settlement == Date())
432 settlement = bond.settlementDate();
433
434 QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
435 "non tradable at " << settlement <<
436 " (maturity being " << bond.maturityDate() << ")");
437
438 return CashFlows::basisPointValue(leg: bond.cashflows(), yield,
439 includeSettlementDateFlows: false, settlementDate: settlement);
440 }
441
442 Real BondFunctions::basisPointValue(const Bond& bond,
443 Rate yield,
444 const DayCounter& dayCounter,
445 Compounding compounding,
446 Frequency frequency,
447 Date settlement) {
448 InterestRate y(yield, dayCounter, compounding, frequency);
449 return CashFlows::basisPointValue(leg: bond.cashflows(), yield: y,
450 includeSettlementDateFlows: false, settlementDate: settlement);
451 }
452
453 Real BondFunctions::yieldValueBasisPoint(const Bond& bond,
454 const InterestRate& yield,
455 Date settlement) {
456 if (settlement == Date())
457 settlement = bond.settlementDate();
458
459 QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
460 "non tradable at " << settlement <<
461 " (maturity being " << bond.maturityDate() << ")");
462
463 return CashFlows::yieldValueBasisPoint(leg: bond.cashflows(), yield,
464 includeSettlementDateFlows: false, settlementDate: settlement);
465 }
466
467 Real BondFunctions::yieldValueBasisPoint(const Bond& bond,
468 Rate yield,
469 const DayCounter& dayCounter,
470 Compounding compounding,
471 Frequency frequency,
472 Date settlement) {
473 InterestRate y(yield, dayCounter, compounding, frequency);
474 return CashFlows::yieldValueBasisPoint(leg: bond.cashflows(), yield: y,
475 includeSettlementDateFlows: false, settlementDate: settlement);
476 }
477
478 Real BondFunctions::cleanPrice(const Bond& bond,
479 const ext::shared_ptr<YieldTermStructure>& d,
480 Spread zSpread,
481 const DayCounter& dc,
482 Compounding comp,
483 Frequency freq,
484 Date settlement) {
485 if (settlement == Date())
486 settlement = bond.settlementDate();
487
488 QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
489 "non tradable at " << settlement <<
490 " (maturity being " << bond.maturityDate() << ")");
491
492 Real dirtyPrice = CashFlows::npv(leg: bond.cashflows(), discount: d,
493 zSpread, dayCounter: dc, compounding: comp, frequency: freq,
494 includeSettlementDateFlows: false, settlementDate: settlement) *
495 100.0 / bond.notional(d: settlement);
496 return dirtyPrice - bond.accruedAmount(d: settlement);
497 }
498
499 Spread BondFunctions::zSpread(const Bond& bond,
500 Real cleanPrice,
501 const ext::shared_ptr<YieldTermStructure>& d,
502 const DayCounter& dayCounter,
503 Compounding compounding,
504 Frequency frequency,
505 Date settlement,
506 Real accuracy,
507 Size maxIterations,
508 Rate guess) {
509 if (settlement == Date())
510 settlement = bond.settlementDate();
511
512 QL_REQUIRE(BondFunctions::isTradable(bond, settlement),
513 "non tradable at " << settlement <<
514 " (maturity being " << bond.maturityDate() << ")");
515
516 Real dirtyPrice = cleanPrice + bond.accruedAmount(d: settlement);
517 dirtyPrice /= 100.0 / bond.notional(d: settlement);
518
519 return CashFlows::zSpread(leg: bond.cashflows(),
520 d,
521 npv: dirtyPrice,
522 dayCounter, compounding, frequency,
523 includeSettlementDateFlows: false, settlementDate: settlement, npvDate: settlement,
524 accuracy, maxIterations, guess);
525 }
526
527}
528

source code of quantlib/ql/pricingengines/bond/bondfunctions.cpp