QuantLib: a free/open-source library for quantitative finance
Fully annotated sources - version 1.32
Loading...
Searching...
No Matches
assetswap.cpp
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4 Copyright (C) 2006, 2007 Chiara Fornarola
5 Copyright (C) 2007, 2009, 2011 Ferdinando Ametrano
6 Copyright (C) 2007, 2009 StatPro Italia srl
7
8 This file is part of QuantLib, a free-software/open-source library
9 for financial quantitative analysts and developers - http://quantlib.org/
10
11 QuantLib is free software: you can redistribute it and/or modify it
12 under the terms of the QuantLib license. You should have received a
13 copy of the license along with this program; if not, please email
14 <quantlib-dev@lists.sf.net>. The license is also available online at
15 <http://quantlib.org/license.shtml>.
16
17 This program is distributed in the hope that it will be useful, but WITHOUT
18 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19 FOR A PARTICULAR PURPOSE. See the license for more details.
20*/
21
22#include <ql/cashflows/cashflowvectors.hpp>
23#include <ql/cashflows/couponpricer.hpp>
24#include <ql/cashflows/fixedratecoupon.hpp>
25#include <ql/cashflows/iborcoupon.hpp>
26#include <ql/cashflows/simplecashflow.hpp>
27#include <ql/instruments/assetswap.hpp>
28#include <ql/pricingengines/swap/discountingswapengine.hpp>
29#include <utility>
30
31using std::vector;
32
33namespace QuantLib {
34
36 ext::shared_ptr<Bond> bond,
37 Real bondCleanPrice,
38 Real nonParRepayment,
39 Real gearing,
40 const ext::shared_ptr<IborIndex>& iborIndex,
41 Spread spread,
42 const DayCounter& floatingDayCounter,
43 Date dealMaturity,
44 bool payBondCoupon)
45 : Swap(2), bond_(std::move(bond)), bondCleanPrice_(bondCleanPrice),
46 nonParRepayment_(nonParRepayment), spread_(spread), parSwap_(parSwap) {
47 Schedule tempSch(bond_->settlementDate(),
48 bond_->maturityDate(),
49 iborIndex->tenor(),
50 iborIndex->fixingCalendar(),
51 iborIndex->businessDayConvention(),
52 iborIndex->businessDayConvention(),
54 false); // endOfMonth
55 if (dealMaturity==Date())
56 dealMaturity = bond_->maturityDate();
57 QL_REQUIRE(dealMaturity <= tempSch.dates().back(),
58 "deal maturity " << dealMaturity <<
59 " cannot be later than (adjusted) bond maturity " <<
60 tempSch.dates().back());
61 QL_REQUIRE(dealMaturity > tempSch.dates()[0],
62 "deal maturity " << dealMaturity <<
63 " must be later than swap start date " <<
64 tempSch.dates()[0]);
65
66 // the following might become an input parameter
67 BusinessDayConvention paymentAdjustment = Following;
68
69 Date finalDate = tempSch.calendar().adjust(
70 dealMaturity, paymentAdjustment);
71 Schedule schedule = tempSch.until(finalDate);
72
73 // bondCleanPrice must be the (forward) clean price
74 // at the floating schedule start date
75 upfrontDate_ = schedule.startDate();
76 Real dirtyPrice = bondCleanPrice_ +
77 bond_->accruedAmount(upfrontDate_);
78
79 Real notional = bond_->notional(upfrontDate_);
80 /* In the market asset swap, the bond is purchased in return for
81 payment of the full price. The notional of the floating leg is
82 then scaled by the full price. */
83 if (!parSwap_)
84 notional *= dirtyPrice/100.0;
85
86 if (floatingDayCounter==DayCounter())
87 legs_[1] = IborLeg(schedule, iborIndex)
88 .withNotionals(notional)
89 .withPaymentAdjustment(paymentAdjustment)
90 .withGearings(gearing)
92 else
93 legs_[1] = IborLeg(schedule, iborIndex)
94 .withNotionals(notional)
95 .withPaymentDayCounter(floatingDayCounter)
96 .withPaymentAdjustment(paymentAdjustment)
97 .withGearings(gearing)
99
100 Leg::const_iterator i;
101 for (i=legs_[1].begin(); i<legs_[1].end(); ++i)
102 registerWith(*i);
103
104 const Leg& bondLeg = bond_->cashflows();
105 // skip bond redemption
106 for (i = bondLeg.begin(); i<bondLeg.end()-1 && (*i)->date()<=dealMaturity; ++i) {
107 // whatever might be the choice for the discounting engine
108 // bond flows on upfrontDate_ must be discarded
109 bool upfrontDateBondFlows = false;
110 if (!(*i)->hasOccurred(upfrontDate_, upfrontDateBondFlows))
111 legs_[0].push_back(*i);
112 }
113 // if the first skipped cashflow is not the redemption
114 // and it is a coupon then add the accrued coupon
115 if (i<bondLeg.end()-1) {
116 ext::shared_ptr<Coupon> c = ext::dynamic_pointer_cast<Coupon>(*i);
117 if (c != nullptr) {
118 ext::shared_ptr<CashFlow> accruedCoupon(new
119 SimpleCashFlow(c->accruedAmount(dealMaturity), finalDate));
120 legs_[0].push_back(accruedCoupon);
121 }
122 }
123 // add the nonParRepayment_
124 ext::shared_ptr<CashFlow> nonParRepaymentFlow(new
125 SimpleCashFlow(nonParRepayment_, finalDate));
126 legs_[0].push_back(nonParRepaymentFlow);
127
128 QL_REQUIRE(!legs_[0].empty(),
129 "empty bond leg to start with");
130
131 // special flows
132 if (parSwap_) {
133 // upfront on the floating leg
134 Real upfront = (dirtyPrice-100.0)/100.0*notional;
135 ext::shared_ptr<CashFlow> upfrontCashFlow(new
136 SimpleCashFlow(upfront, upfrontDate_));
137 legs_[1].insert(legs_[1].begin(), upfrontCashFlow);
138 // backpayment on the floating leg
139 // (accounts for non-par redemption, if any)
140 Real backPayment = notional;
141 ext::shared_ptr<CashFlow> backPaymentCashFlow(new
142 SimpleCashFlow(backPayment, finalDate));
143 legs_[1].push_back(backPaymentCashFlow);
144 } else {
145 // final notional exchange
146 ext::shared_ptr<CashFlow> finalCashFlow (new
147 SimpleCashFlow(notional, finalDate));
148 legs_[1].push_back(finalCashFlow);
149 }
150
151 QL_REQUIRE(!legs_[0].empty(), "empty bond leg");
152 for (i=legs_[0].begin(); i<legs_[0].end(); ++i)
153 registerWith(*i);
154
155 if (payBondCoupon) {
156 payer_[0]=-1.0;
157 payer_[1]=+1.0;
158 } else {
159 payer_[0]=+1.0;
160 payer_[1]=-1.0;
161 }
162 }
163
164 AssetSwap::AssetSwap(bool payBondCoupon,
165 ext::shared_ptr<Bond> bond,
166 Real bondCleanPrice,
167 const ext::shared_ptr<IborIndex>& iborIndex,
168 Spread spread,
169 const Schedule& floatSchedule,
170 const DayCounter& floatingDayCounter,
171 bool parSwap)
172 : Swap(2), bond_(std::move(bond)), bondCleanPrice_(bondCleanPrice), nonParRepayment_(100),
173 spread_(spread), parSwap_(parSwap) {
174 Schedule schedule = floatSchedule;
175 if (floatSchedule.empty())
176 schedule = Schedule(bond_->settlementDate(),
177 bond_->maturityDate(),
178 iborIndex->tenor(),
179 iborIndex->fixingCalendar(),
180 iborIndex->businessDayConvention(),
181 iborIndex->businessDayConvention(),
183 false); // endOfMonth
184
185 // the following might become an input parameter
186 BusinessDayConvention paymentAdjustment = Following;
187
188 Date finalDate = schedule.calendar().adjust(
189 schedule.endDate(), paymentAdjustment);
190 Date adjBondMaturityDate = schedule.calendar().adjust(
191 bond_->maturityDate(), paymentAdjustment);
192
193 QL_REQUIRE(finalDate==adjBondMaturityDate,
194 "adjusted schedule end date (" <<
195 finalDate <<
196 ") must be equal to adjusted bond maturity date (" <<
197 adjBondMaturityDate << ")");
198
199 // bondCleanPrice must be the (forward) clean price
200 // at the floating schedule start date
201 upfrontDate_ = schedule.startDate();
202 Real dirtyPrice = bondCleanPrice_ +
203 bond_->accruedAmount(upfrontDate_);
204
205 Real notional = bond_->notional(upfrontDate_);
206 /* In the market asset swap, the bond is purchased in return for
207 payment of the full price. The notional of the floating leg is
208 then scaled by the full price. */
209 if (!parSwap_)
210 notional *= dirtyPrice/100.0;
211
212 if (floatingDayCounter==DayCounter())
213 legs_[1] = IborLeg(schedule, iborIndex)
214 .withNotionals(notional)
215 .withPaymentAdjustment(paymentAdjustment)
217 else
218 legs_[1] = IborLeg(schedule, iborIndex)
219 .withNotionals(notional)
220 .withPaymentDayCounter(floatingDayCounter)
221 .withPaymentAdjustment(paymentAdjustment)
223
224 for (Leg::const_iterator i=legs_[1].begin(); i<legs_[1].end(); ++i)
225 registerWith(*i);
226
227 const Leg& bondLeg = bond_->cashflows();
228 for (auto i = bondLeg.begin(); i < bondLeg.end(); ++i) {
229 // whatever might be the choice for the discounting engine
230 // bond flows on upfrontDate_ must be discarded
231 bool upfrontDateBondFlows = false;
232 if (!(*i)->hasOccurred(upfrontDate_, upfrontDateBondFlows))
233 legs_[0].push_back(*i);
234 }
235
236 QL_REQUIRE(!legs_[0].empty(),
237 "empty bond leg to start with");
238
239 // special flows
240 if (parSwap_) {
241 // upfront on the floating leg
242 Real upfront = (dirtyPrice-100.0)/100.0*notional;
243 ext::shared_ptr<CashFlow> upfrontCashFlow(new
244 SimpleCashFlow(upfront, upfrontDate_));
245 legs_[1].insert(legs_[1].begin(), upfrontCashFlow);
246 // backpayment on the floating leg
247 // (accounts for non-par redemption, if any)
248 Real backPayment = notional;
249 ext::shared_ptr<CashFlow> backPaymentCashFlow(new
250 SimpleCashFlow(backPayment, finalDate));
251 legs_[1].push_back(backPaymentCashFlow);
252 } else {
253 // final notional exchange
254 ext::shared_ptr<CashFlow> finalCashFlow(new
255 SimpleCashFlow(notional, finalDate));
256 legs_[1].push_back(finalCashFlow);
257 }
258
259 QL_REQUIRE(!legs_[0].empty(), "empty bond leg");
260 for (Leg::const_iterator i=legs_[0].begin(); i<legs_[0].end(); ++i)
261 registerWith(*i);
262
263 if (payBondCoupon) {
264 payer_[0]=-1.0;
265 payer_[1]=+1.0;
266 } else {
267 payer_[0]=+1.0;
268 payer_[1]=-1.0;
269 }
270 }
271
273
275
276 auto* arguments = dynamic_cast<AssetSwap::arguments*>(args);
277
278 if (arguments == nullptr) // it's a swap engine...
279 return;
280
281 const Leg& fixedCoupons = bondLeg();
282
284 vector<Date>(fixedCoupons.size());
285 arguments->fixedCoupons = vector<Real>(fixedCoupons.size());
286
287 for (Size i=0; i<fixedCoupons.size(); ++i) {
288 ext::shared_ptr<FixedRateCoupon> coupon =
289 ext::dynamic_pointer_cast<FixedRateCoupon>(fixedCoupons[i]);
290
291 arguments->fixedPayDates[i] = coupon->date();
292 arguments->fixedResetDates[i] = coupon->accrualStartDate();
293 arguments->fixedCoupons[i] = coupon->amount();
294 }
295
296 const Leg& floatingCoupons = floatingLeg();
297
300 vector<Date>(floatingCoupons.size());
302 vector<Time>(floatingCoupons.size());
304 vector<Spread>(floatingCoupons.size());
305
306 for (Size i=0; i<floatingCoupons.size(); ++i) {
307 ext::shared_ptr<FloatingRateCoupon> coupon =
308 ext::dynamic_pointer_cast<FloatingRateCoupon>(floatingCoupons[i]);
309
310 arguments->floatingResetDates[i] = coupon->accrualStartDate();
311 arguments->floatingPayDates[i] = coupon->date();
312 arguments->floatingFixingDates[i] = coupon->fixingDate();
313 arguments->floatingAccrualTimes[i] = coupon->accrualPeriod();
314 arguments->floatingSpreads[i] = coupon->spread();
315 }
316 }
317
319 static const Spread basisPoint = 1.0e-4;
320 calculate();
321 if (fairSpread_ != Null<Spread>()) {
322 return fairSpread_;
323 } else if (legBPS_.size() > 1 && legBPS_[1] != Null<Spread>()) {
324 fairSpread_ = spread_ - NPV_/legBPS_[1]*basisPoint;
325 return fairSpread_;
326 } else {
327 QL_FAIL("fair spread not available");
328 }
329 }
330
332 calculate();
333 QL_REQUIRE(legBPS_.size() > 1 && legBPS_[1] != Null<Real>(),
334 "floating-leg BPS not available");
335 return legBPS_[1];
336 }
337
339 calculate();
340 QL_REQUIRE(legNPV_.size() > 1 && legNPV_[1] != Null<Real>(),
341 "floating-leg NPV not available");
342 return legNPV_[1];
343 }
344
346 calculate();
347 if (fairCleanPrice_ != Null<Real>()) {
348 return fairCleanPrice_;
349 } else {
350 QL_REQUIRE(startDiscounts_[1]!=Null<DiscountFactor>(),
351 "fair clean price not available for seasoned deal");
352 Real notional = bond_->notional(upfrontDate_);
353 if (parSwap_) {
355 NPV_*npvDateDiscount_/startDiscounts_[1]/(notional/100.0);
356 } else {
357 Real accruedAmount = bond_->accruedAmount(upfrontDate_);
358 Real dirtyPrice = bondCleanPrice_ + accruedAmount;
359 Real fairDirtyPrice = - legNPV_[0]/legNPV_[1] * dirtyPrice;
360 fairCleanPrice_ = fairDirtyPrice - accruedAmount;
361 }
362
363 return fairCleanPrice_;
364 }
365 }
366
368 calculate();
371 } else {
372 QL_REQUIRE(endDiscounts_[1]!=Null<DiscountFactor>(),
373 "fair non par repayment not available for expired leg");
374 Real notional = bond_->notional(upfrontDate_);
376 NPV_*npvDateDiscount_/endDiscounts_[1]/(notional/100.0);
378 }
379 }
380
386 }
387
390 const auto* results = dynamic_cast<const AssetSwap::results*>(r);
391 if (results != nullptr) {
395 } else {
399 }
400 }
401
403 QL_REQUIRE(fixedResetDates.size() == fixedPayDates.size(),
404 "number of fixed start dates different from "
405 "number of fixed payment dates");
406 QL_REQUIRE(fixedPayDates.size() == fixedCoupons.size(),
407 "number of fixed payment dates different from "
408 "number of fixed coupon amounts");
409 QL_REQUIRE(floatingResetDates.size() == floatingPayDates.size(),
410 "number of floating start dates different from "
411 "number of floating payment dates");
412 QL_REQUIRE(floatingFixingDates.size() == floatingPayDates.size(),
413 "number of floating fixing dates different from "
414 "number of floating payment dates");
415 QL_REQUIRE(floatingAccrualTimes.size() == floatingPayDates.size(),
416 "number of floating accrual times different from "
417 "number of floating payment dates");
418 QL_REQUIRE(floatingSpreads.size() == floatingPayDates.size(),
419 "number of floating spreads different from "
420 "number of floating payment dates");
421 }
422
428 }
429
430}
Arguments for asset swap calculation
Definition: assetswap.hpp:108
std::vector< Date > floatingResetDates
Definition: assetswap.hpp:115
std::vector< Spread > floatingSpreads
Definition: assetswap.hpp:118
std::vector< Date > floatingFixingDates
Definition: assetswap.hpp:116
std::vector< Date > fixedPayDates
Definition: assetswap.hpp:112
std::vector< Date > fixedResetDates
Definition: assetswap.hpp:111
void validate() const override
Definition: assetswap.cpp:402
std::vector< Time > floatingAccrualTimes
Definition: assetswap.hpp:114
std::vector< Real > fixedCoupons
Definition: assetswap.hpp:113
std::vector< Date > floatingPayDates
Definition: assetswap.hpp:117
Results from simple swap calculation
Definition: assetswap.hpp:123
Real fairCleanPrice() const
Definition: assetswap.cpp:345
const Leg & floatingLeg() const
Definition: assetswap.hpp:89
Spread fairSpread() const
Definition: assetswap.cpp:318
Real floatingLegBPS() const
Definition: assetswap.cpp:331
Real fairNonParRepayment() const
Definition: assetswap.cpp:367
AssetSwap(bool payBondCoupon, ext::shared_ptr< Bond > bond, Real bondCleanPrice, const ext::shared_ptr< IborIndex > &iborIndex, Spread spread, const Schedule &floatSchedule=Schedule(), const DayCounter &floatingDayCount=DayCounter(), bool parAssetSwap=true)
Definition: assetswap.cpp:164
void setupArguments(PricingEngine::arguments *args) const override
Definition: assetswap.cpp:272
const Leg & bondLeg() const
Definition: assetswap.hpp:88
ext::shared_ptr< Bond > bond_
Definition: assetswap.hpp:96
void setupExpired() const override
Definition: assetswap.cpp:381
Real floatingLegNPV() const
Definition: assetswap.cpp:338
void fetchResults(const PricingEngine::results *) const override
Definition: assetswap.cpp:388
Spread spread() const
Definition: assetswap.hpp:83
bool payBondCoupon() const
Definition: assetswap.hpp:87
Date adjust(const Date &, BusinessDayConvention convention=Following) const
Definition: calendar.cpp:84
Concrete date class.
Definition: date.hpp:125
day counter class
Definition: daycounter.hpp:44
helper class building a sequence of capped/floored ibor-rate coupons
Definition: iborcoupon.hpp:133
IborLeg & withSpreads(Spread spread)
Definition: iborcoupon.cpp:208
IborLeg & withPaymentAdjustment(BusinessDayConvention)
Definition: iborcoupon.cpp:173
IborLeg & withPaymentDayCounter(const DayCounter &)
Definition: iborcoupon.cpp:168
IborLeg & withNotionals(Real notional)
Definition: iborcoupon.cpp:158
IborLeg & withGearings(Real gearing)
Definition: iborcoupon.cpp:198
void calculate() const override
Definition: instrument.hpp:129
template class providing a null value for a given type.
Definition: null.hpp:76
std::pair< iterator, bool > registerWith(const ext::shared_ptr< Observable > &)
Definition: observable.hpp:228
Payment schedule.
Definition: schedule.hpp:40
const Date & endDate() const
Definition: schedule.hpp:184
const Calendar & calendar() const
Definition: schedule.hpp:176
const std::vector< Date > & dates() const
Definition: schedule.hpp:75
bool empty() const
Definition: schedule.hpp:82
const Date & startDate() const
Definition: schedule.hpp:180
Schedule until(const Date &truncationDate) const
Definition: schedule.cpp:459
Predetermined cash flow.
void reset() override
Definition: swap.cpp:176
Interest rate swap.
Definition: swap.hpp:41
DiscountFactor npvDateDiscount_
Definition: swap.hpp:138
void setupArguments(PricingEngine::arguments *) const override
Definition: swap.cpp:87
std::vector< Leg > legs_
Definition: swap.hpp:133
std::vector< Real > legNPV_
Definition: swap.hpp:135
std::vector< Real > legBPS_
Definition: swap.hpp:136
std::vector< DiscountFactor > startDiscounts_
Definition: swap.hpp:137
void setupExpired() const override
Definition: swap.cpp:78
void fetchResults(const PricingEngine::results *) const override
Definition: swap.cpp:95
std::vector< DiscountFactor > endDiscounts_
Definition: swap.hpp:137
std::vector< Real > payer_
Definition: swap.hpp:134
BusinessDayConvention
Business Day conventions.
QL_REAL Real
real number
Definition: types.hpp:50
Real Spread
spreads on interest rates
Definition: types.hpp:74
std::size_t Size
size of a container
Definition: types.hpp:58
Definition: any.hpp:35
std::vector< ext::shared_ptr< CashFlow > > Leg
Sequence of cash-flows.
Definition: cashflow.hpp:78
STL namespace.