Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
currencyswap.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2016 Quaternion Risk Management Ltd
3 All rights reserved.
4
5 This file is part of ORE, a free-software/open-source library
6 for transparent pricing and risk analysis - http://opensourcerisk.org
7
8 ORE is free software: you can redistribute it and/or modify it
9 under the terms of the Modified BSD License. You should have received a
10 copy of the license along with this program.
11 The license is also available online at <http://opensourcerisk.org>
12
13 This program is distributed on the basis that it will form a useful
14 contribution to risk analytics and model standardisation, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
17*/
18
20
21#include <ql/cashflows/cashflows.hpp>
22#include <ql/cashflows/cashflowvectors.hpp>
23#include <ql/cashflows/coupon.hpp>
24#include <ql/cashflows/iborcoupon.hpp>
25#include <ql/cashflows/simplecashflow.hpp>
26#include <ql/termstructures/yieldtermstructure.hpp>
27
28using namespace QuantLib;
29
30namespace QuantExt {
31
33 legs_.resize(nLegs);
34 payer_.resize(nLegs);
35 currency_.resize(nLegs);
36 legNPV_.resize(nLegs);
37 inCcyLegNPV_.resize(nLegs);
38 legBPS_.resize(nLegs);
39 inCcyLegBPS_.resize(nLegs);
40 startDiscounts_.resize(nLegs);
41 endDiscounts_.resize(nLegs);
42}
43
44CurrencySwap::CurrencySwap(const std::vector<Leg>& legs, const std::vector<bool>& payer,
45 const std::vector<Currency>& currency, const bool isPhysicallySettled,
46 const bool isResettable)
47 : legs_(legs), payer_(legs.size(), 1.0), currency_(currency), isPhysicallySettled_(isPhysicallySettled),
48 isResettable_(isResettable), legNPV_(legs.size(), 0.0), inCcyLegNPV_(legs.size(), 0.0), legBPS_(legs.size(), 0.0),
49 inCcyLegBPS_(legs.size(), 0.0), startDiscounts_(legs.size(), 0.0), endDiscounts_(legs.size(), 0.0),
50 npvDateDiscount_(0.0) {
51 QL_REQUIRE(payer.size() == legs_.size(),
52 "size mismatch between payer (" << payer.size() << ") and legs (" << legs_.size() << ")");
53 QL_REQUIRE(currency.size() == legs_.size(),
54 "size mismatch between currency (" << currency.size() << ") and legs (" << legs_.size() << ")");
55 for (Size j = 0; j < legs_.size(); ++j) {
56 if (payer[j])
57 payer_[j] = -1.0;
58 for (Leg::iterator i = legs_[j].begin(); i != legs_[j].end(); ++i)
59 registerWith(*i);
60 }
61}
62
64 for (Size j = 0; j < legs_.size(); ++j) {
65 Leg::const_iterator i;
66 for (i = legs_[j].begin(); i != legs_[j].end(); ++i)
67 if (!(*i)->hasOccurred())
68 return false;
69 }
70 return true;
71}
72
74 Instrument::setupExpired();
75 std::fill(legBPS_.begin(), legBPS_.end(), 0.0);
76 std::fill(legNPV_.begin(), legNPV_.end(), 0.0);
77 std::fill(inCcyLegBPS_.begin(), inCcyLegBPS_.end(), 0.0);
78 std::fill(inCcyLegNPV_.begin(), inCcyLegNPV_.end(), 0.0);
79 std::fill(startDiscounts_.begin(), startDiscounts_.end(), 0.0);
80 std::fill(endDiscounts_.begin(), endDiscounts_.end(), 0.0);
81 npvDateDiscount_ = 0.0;
82}
83
86 QL_REQUIRE(arguments != 0, "wrong argument type");
87
93}
94
95void CurrencySwap::fetchResults(const PricingEngine::results* r) const {
96 Instrument::fetchResults(r);
97
98 const CurrencySwap::results* results = dynamic_cast<const CurrencySwap::results*>(r);
99 QL_REQUIRE(results != 0, "wrong result type");
100
101 if (!results->legNPV.empty()) {
102 QL_REQUIRE(results->legNPV.size() == legNPV_.size(), "wrong number of leg NPV returned");
104 } else {
105 std::fill(legNPV_.begin(), legNPV_.end(), Null<Real>());
106 }
107
108 if (!results->legBPS.empty()) {
109 QL_REQUIRE(results->legBPS.size() == legBPS_.size(), "wrong number of leg BPS returned");
111 } else {
112 std::fill(legBPS_.begin(), legBPS_.end(), Null<Real>());
113 }
114
115 if (!results->inCcyLegNPV.empty()) {
116 QL_REQUIRE(results->inCcyLegNPV.size() == inCcyLegNPV_.size(), "wrong number of leg NPV returned");
118 } else {
119 std::fill(inCcyLegNPV_.begin(), inCcyLegNPV_.end(), Null<Real>());
120 }
121
122 if (!results->inCcyLegBPS.empty()) {
123 QL_REQUIRE(results->inCcyLegBPS.size() == inCcyLegBPS_.size(), "wrong number of leg BPS returned");
125 } else {
126 std::fill(inCcyLegBPS_.begin(), inCcyLegBPS_.end(), Null<Real>());
127 }
128
129 if (!results->startDiscounts.empty()) {
130 QL_REQUIRE(results->startDiscounts.size() == startDiscounts_.size(),
131 "wrong number of leg start discounts returned");
133 } else {
134 std::fill(startDiscounts_.begin(), startDiscounts_.end(), Null<DiscountFactor>());
135 }
136
137 if (!results->endDiscounts.empty()) {
138 QL_REQUIRE(results->endDiscounts.size() == endDiscounts_.size(), "wrong number of leg end discounts returned");
140 } else {
141 std::fill(endDiscounts_.begin(), endDiscounts_.end(), Null<DiscountFactor>());
142 }
143
144 if (results->npvDateDiscount != Null<DiscountFactor>()) {
146 } else {
147 npvDateDiscount_ = Null<DiscountFactor>();
148 }
149}
150
152 QL_REQUIRE(!legs_.empty(), "no legs given");
153 Date d = CashFlows::startDate(legs_[0]);
154 for (Size j = 1; j < legs_.size(); ++j)
155 d = std::min(d, CashFlows::startDate(legs_[j]));
156 return d;
157}
158
160 QL_REQUIRE(!legs_.empty(), "no legs given");
161 Date d = CashFlows::maturityDate(legs_[0]);
162 for (Size j = 1; j < legs_.size(); ++j)
163 d = std::max(d, CashFlows::maturityDate(legs_[j]));
164 return d;
165}
166
168 QL_REQUIRE(legs.size() == payer.size(), "number of legs and multipliers differ");
169 QL_REQUIRE(currency.size() == legs.size(), "number of legs and currencies differ");
170}
171
173 Instrument::results::reset();
174 legNPV.clear();
175 legBPS.clear();
176 inCcyLegNPV.clear();
177 inCcyLegBPS.clear();
178 startDiscounts.clear();
179 endDiscounts.clear();
180 npvDateDiscount = Null<DiscountFactor>();
181}
182
184 for (auto& leg : legs_) {
185 for (auto& k : leg) {
186 if (auto lazy = ext::dynamic_pointer_cast<LazyObject>(k))
187 lazy->deepUpdate();
188 }
189 }
190 update();
191}
192
194 for (auto& leg : legs_) {
195 for (auto& k : leg) {
196 if (auto lazy = ext::dynamic_pointer_cast<LazyObject>(k))
197 lazy->alwaysForwardNotifications();
198 }
199 }
200 LazyObject::alwaysForwardNotifications();
201}
202
203//=========================================================================
204// Constructors for specialised currency swaps
205//=========================================================================
206
207VanillaCrossCurrencySwap::VanillaCrossCurrencySwap(bool payFixed, Currency fixedCcy, Real fixedNominal,
208 const Schedule& fixedSchedule, Rate fixedRate,
209 const DayCounter& fixedDayCount, Currency floatCcy,
210 Real floatNominal, const Schedule& floatSchedule,
211 const QuantLib::ext::shared_ptr<IborIndex>& iborIndex, Rate floatSpread,
212 boost::optional<BusinessDayConvention> paymentConvention,
213 const bool isPhysicallySettled, const bool isResettable)
214 : CurrencySwap(4) {
215
216 isPhysicallySettled_ = isPhysicallySettled;
217 isResettable_ = isResettable;
218
219 BusinessDayConvention convention;
220 if (paymentConvention)
221 convention = *paymentConvention;
222 else
223 convention = floatSchedule.businessDayConvention();
224
225 // fixed leg
226 currency_[0] = fixedCcy;
227 payer_[0] = (payFixed ? -1 : +1);
228 legs_[0] = FixedRateLeg(fixedSchedule)
229 .withNotionals(fixedNominal)
230 .withCouponRates(fixedRate, fixedDayCount)
231 .withPaymentAdjustment(convention);
232
233 // add initial and final notional exchange
234 currency_[1] = fixedCcy;
235 payer_[1] = payer_[0];
236 legs_[1].push_back(QuantLib::ext::shared_ptr<CashFlow>(
237 new SimpleCashFlow(-fixedNominal, fixedSchedule.calendar().adjust(fixedSchedule.dates().front(), convention))));
238 legs_[1].push_back(QuantLib::ext::shared_ptr<CashFlow>(
239 new SimpleCashFlow(fixedNominal, fixedSchedule.calendar().adjust(fixedSchedule.dates().back(), convention))));
240
241 // floating leg
242 currency_[2] = floatCcy;
243 payer_[2] = (payFixed ? +1 : -1);
244 legs_[2] = IborLeg(floatSchedule, iborIndex)
245 .withNotionals(floatNominal)
246 .withPaymentDayCounter(iborIndex->dayCounter())
247 .withPaymentAdjustment(convention)
248 .withSpreads(floatSpread);
249 for (Leg::const_iterator i = legs_[2].begin(); i < legs_[2].end(); ++i)
250 registerWith(*i);
251
252 // add initial and final notional exchange
253 currency_[3] = floatCcy;
254 payer_[3] = payer_[2];
255 legs_[3].push_back(QuantLib::ext::shared_ptr<CashFlow>(
256 new SimpleCashFlow(-floatNominal, floatSchedule.calendar().adjust(floatSchedule.dates().front(), convention))));
257 legs_[3].push_back(QuantLib::ext::shared_ptr<CashFlow>(
258 new SimpleCashFlow(floatNominal, floatSchedule.calendar().adjust(floatSchedule.dates().back(), convention))));
259}
260
261//-------------------------------------------------------------------------
262CrossCurrencySwap::CrossCurrencySwap(bool payFixed, Currency fixedCcy, std::vector<Real> fixedNominals,
263 const Schedule& fixedSchedule, std::vector<Rate> fixedRates,
264 const DayCounter& fixedDayCount, Currency floatCcy,
265 std::vector<Real> floatNominals, const Schedule& floatSchedule,
266 const QuantLib::ext::shared_ptr<IborIndex>& iborIndex, std::vector<Rate> floatSpreads,
267 boost::optional<BusinessDayConvention> paymentConvention,
268 const bool isPhysicallySettled, const bool isResettable)
269 : CurrencySwap(4) {
270
271 isPhysicallySettled_ = isPhysicallySettled;
272 isResettable_ = isResettable;
273
274 BusinessDayConvention convention;
275 if (paymentConvention)
276 convention = *paymentConvention;
277 else
278 convention = floatSchedule.businessDayConvention();
279
280 // fixed leg
281 currency_[0] = fixedCcy;
282 payer_[0] = (payFixed ? -1 : +1);
283 legs_[0] = FixedRateLeg(fixedSchedule)
284 .withNotionals(fixedNominals)
285 .withCouponRates(fixedRates, fixedDayCount)
286 .withPaymentAdjustment(convention);
287
288 // add initial, interim and final notional flows
289 currency_[1] = fixedCcy;
290 payer_[1] = payer_[0];
291 legs_[1].push_back(
292 QuantLib::ext::shared_ptr<CashFlow>(new SimpleCashFlow(-fixedNominals[0], fixedSchedule.dates().front())));
293 QL_REQUIRE(fixedNominals.size() < fixedSchedule.size(), "too many fixed nominals provided");
294 for (Size i = 1; i < fixedNominals.size(); i++) {
295 Real flow = fixedNominals[i - 1] - fixedNominals[i];
296 legs_[1].push_back(QuantLib::ext::shared_ptr<CashFlow>(
297 new SimpleCashFlow(flow, fixedSchedule.calendar().adjust(fixedSchedule[i], convention))));
298 }
299 if (fixedNominals.back() > 0)
300 legs_[1].push_back(QuantLib::ext::shared_ptr<CashFlow>(new SimpleCashFlow(
301 fixedNominals.back(), fixedSchedule.calendar().adjust(fixedSchedule.dates().back(), convention))));
302
303 // floating leg
304 currency_[2] = floatCcy;
305 payer_[2] = (payFixed ? +1 : -1);
306 legs_[2] = IborLeg(floatSchedule, iborIndex)
307 .withNotionals(floatNominals)
308 .withPaymentDayCounter(iborIndex->dayCounter())
309 .withPaymentAdjustment(convention)
310 .withSpreads(floatSpreads);
311 for (Leg::const_iterator i = legs_[2].begin(); i < legs_[2].end(); ++i)
312 registerWith(*i);
313
314 // add initial, interim and final notional flows
315 currency_[3] = floatCcy;
316 payer_[3] = payer_[2];
317 legs_[3].push_back(
318 QuantLib::ext::shared_ptr<CashFlow>(new SimpleCashFlow(-floatNominals[0], floatSchedule.dates().front())));
319 QL_REQUIRE(floatNominals.size() < floatSchedule.size(), "too many float nominals provided");
320 for (Size i = 1; i < floatNominals.size(); i++) {
321 Real flow = floatNominals[i - 1] - floatNominals[i];
322 legs_[3].push_back(QuantLib::ext::shared_ptr<CashFlow>(
323 new SimpleCashFlow(flow, floatSchedule.calendar().adjust(floatSchedule[i], convention))));
324 }
325 if (floatNominals.back() > 0)
326 legs_[3].push_back(QuantLib::ext::shared_ptr<CashFlow>(new SimpleCashFlow(
327 floatNominals.back(), floatSchedule.calendar().adjust(floatSchedule.dates().back(), convention))));
328}
329
330//-------------------------------------------------------------------------
331CrossCurrencySwap::CrossCurrencySwap(bool pay1, Currency ccy1, std::vector<Real> nominals1, const Schedule& schedule1,
332 std::vector<Rate> rates1, const DayCounter& dayCount1, Currency ccy2,
333 std::vector<Real> nominals2, const Schedule& schedule2, std::vector<Rate> rates2,
334 const DayCounter& dayCount2,
335 boost::optional<BusinessDayConvention> paymentConvention,
336 const bool isPhysicallySettled, const bool isResettable)
337 : CurrencySwap(4) {
338
339 isPhysicallySettled_ = isPhysicallySettled;
340 isResettable_ = isResettable;
341
342 BusinessDayConvention convention;
343 if (paymentConvention)
344 convention = *paymentConvention;
345 else
346 convention = schedule1.businessDayConvention();
347
348 // fixed leg 1
349 currency_[0] = ccy1;
350 payer_[0] = (pay1 ? -1 : +1);
351 legs_[0] = FixedRateLeg(schedule1)
352 .withNotionals(nominals1)
353 .withCouponRates(rates1, dayCount1)
354 .withPaymentAdjustment(convention);
355
356 // add initial, interim and final notional flows
357 currency_[1] = ccy1;
358 payer_[1] = payer_[0];
359 legs_[1].push_back(QuantLib::ext::shared_ptr<CashFlow>(
360 new SimpleCashFlow(-nominals1[0], schedule1.calendar().adjust(schedule1.dates().front(), convention))));
361 QL_REQUIRE(nominals1.size() < schedule1.size(), "too many fixed nominals provided, leg 1");
362 for (Size i = 1; i < nominals1.size(); i++) {
363 Real flow = nominals1[i - 1] - nominals1[i];
364 legs_[1].push_back(QuantLib::ext::shared_ptr<CashFlow>(
365 new SimpleCashFlow(flow, schedule1.calendar().adjust(schedule1[i], convention))));
366 }
367 if (nominals1.back() > 0)
368 legs_[1].push_back(QuantLib::ext::shared_ptr<CashFlow>(
369 new SimpleCashFlow(nominals1.back(), schedule1.calendar().adjust(schedule1.dates().back(), convention))));
370
371 // fixed leg 2
372 currency_[2] = ccy2;
373 payer_[2] = (pay1 ? +1 : -1);
374 legs_[2] = FixedRateLeg(schedule2)
375 .withNotionals(nominals2)
376 .withCouponRates(rates2, dayCount2)
377 .withPaymentAdjustment(convention);
378
379 // add initial, interim and final notional flows
380 currency_[3] = ccy2;
381 payer_[3] = payer_[2];
382 legs_[3].push_back(QuantLib::ext::shared_ptr<CashFlow>(
383 new SimpleCashFlow(-nominals2[0], schedule2.calendar().adjust(schedule2.dates().front(), convention))));
384 QL_REQUIRE(nominals2.size() < schedule2.size(), "too many fixed nominals provided, leg 2");
385 for (Size i = 1; i < nominals2.size(); i++) {
386 Real flow = nominals2[i - 1] - nominals2[i];
387 legs_[3].push_back(QuantLib::ext::shared_ptr<CashFlow>(
388 new SimpleCashFlow(flow, schedule2.calendar().adjust(schedule2[i], convention))));
389 }
390 if (nominals2.back() > 0)
391 legs_[3].push_back(QuantLib::ext::shared_ptr<CashFlow>(
392 new SimpleCashFlow(nominals2.back(), schedule2.calendar().adjust(schedule2.dates().back(), convention))));
393}
394
395//-------------------------------------------------------------------------
396CrossCurrencySwap::CrossCurrencySwap(bool pay1, Currency ccy1, std::vector<Real> nominals1, const Schedule& schedule1,
397 const QuantLib::ext::shared_ptr<IborIndex>& iborIndex1, std::vector<Rate> spreads1,
398 Currency ccy2, std::vector<Real> nominals2, const Schedule& schedule2,
399 const QuantLib::ext::shared_ptr<IborIndex>& iborIndex2, std::vector<Rate> spreads2,
400 boost::optional<BusinessDayConvention> paymentConvention,
401 const bool isPhysicallySettled, const bool isResettable)
402 : CurrencySwap(4) {
403
404 isPhysicallySettled_ = isPhysicallySettled;
405 isResettable_ = isResettable;
406
407 BusinessDayConvention convention;
408 if (paymentConvention)
409 convention = *paymentConvention;
410 else
411 convention = schedule1.businessDayConvention();
412
413 // floating leg 1
414 currency_[0] = ccy1;
415 payer_[0] = (pay1 ? -1 : +1);
416 legs_[0] = IborLeg(schedule1, iborIndex1)
417 .withNotionals(nominals1)
418 .withPaymentDayCounter(iborIndex1->dayCounter())
419 .withPaymentAdjustment(convention)
420 .withSpreads(spreads1);
421 for (Leg::const_iterator i = legs_[0].begin(); i < legs_[0].end(); ++i)
422 registerWith(*i);
423
424 // add initial, interim and final notional flows
425 currency_[1] = ccy1;
426 payer_[1] = payer_[0];
427 legs_[1].push_back(QuantLib::ext::shared_ptr<CashFlow>(
428 new SimpleCashFlow(-nominals1[0], schedule1.calendar().adjust(schedule1.dates().front(), convention))));
429 QL_REQUIRE(nominals1.size() < schedule1.size(), "too many float nominals provided");
430 for (Size i = 1; i < nominals1.size(); i++) {
431 Real flow = nominals1[i - 1] - nominals1[i];
432 legs_[1].push_back(QuantLib::ext::shared_ptr<CashFlow>(
433 new SimpleCashFlow(flow, schedule1.calendar().adjust(schedule1[i], convention))));
434 }
435 if (nominals1.back() > 0)
436 legs_[1].push_back(QuantLib::ext::shared_ptr<CashFlow>(
437 new SimpleCashFlow(nominals1.back(), schedule1.calendar().adjust(schedule1.dates().back(), convention))));
438
439 // floating leg 2
440 currency_[2] = ccy2;
441 payer_[2] = (pay1 ? +1 : -1);
442 legs_[2] = IborLeg(schedule2, iborIndex2)
443 .withNotionals(nominals2)
444 .withPaymentDayCounter(iborIndex2->dayCounter())
445 .withPaymentAdjustment(convention)
446 .withSpreads(spreads2);
447 for (Leg::const_iterator i = legs_[2].begin(); i < legs_[2].end(); ++i)
448 registerWith(*i);
449
450 // add initial, interim and final notional flows
451 currency_[3] = ccy2;
452 payer_[3] = payer_[2];
453 legs_[3].push_back(QuantLib::ext::shared_ptr<CashFlow>(
454 new SimpleCashFlow(-nominals2[0], schedule2.calendar().adjust(schedule2.dates().front(), convention))));
455 QL_REQUIRE(nominals2.size() < schedule2.size(), "too many float nominals provided");
456 for (Size i = 1; i < nominals2.size(); i++) {
457 Real flow = nominals2[i - 1] - nominals2[i];
458 legs_[3].push_back(QuantLib::ext::shared_ptr<CashFlow>(
459 new SimpleCashFlow(flow, schedule2.calendar().adjust(schedule2[i], convention))));
460 }
461 if (nominals2.back() > 0)
462 legs_[3].push_back(QuantLib::ext::shared_ptr<CashFlow>(
463 new SimpleCashFlow(nominals2.back(), schedule2.calendar().adjust(schedule2.dates().back(), convention))));
464}
465} // namespace QuantExt
CrossCurrencySwap(bool payFixed, Currency fixedCcy, std::vector< Real > fixedNominals, const Schedule &fixedSchedule, std::vector< Rate > fixedRates, const DayCounter &fixedDayCount, Currency floatCcy, std::vector< Real > floatNominals, const Schedule &floatSchedule, const QuantLib::ext::shared_ptr< IborIndex > &iborIndex, std::vector< Rate > floatSpreads, boost::optional< BusinessDayConvention > paymentConvention=boost::none, const bool isPhysicallySettled=true, const bool isResettable=false)
std::vector< Currency > currency
void validate() const override
std::vector< DiscountFactor > endDiscounts
std::vector< DiscountFactor > startDiscounts
std::vector< Real > inCcyLegNPV
std::vector< Real > inCcyLegBPS
Currency Interest Rate Swap
DiscountFactor endDiscounts(Size j) const
DiscountFactor npvDateDiscount_
std::vector< Real > inCcyLegNPV_
std::vector< Currency > currency_
void setupArguments(PricingEngine::arguments *) const override
CurrencySwap(const std::vector< Leg > &legs, const std::vector< bool > &payer, const std::vector< Currency > &currency, const bool isPhysicallySettled=true, const bool isResettable=false)
bool isExpired() const override
Real inCcyLegNPV(Size j) const
Real legBPS(Size j) const
void deepUpdate() override
std::vector< Leg > legs_
std::vector< Real > inCcyLegBPS_
std::vector< Real > legNPV_
const Leg & leg(Size j) const
DiscountFactor npvDateDiscount() const
std::vector< Real > legBPS_
std::vector< DiscountFactor > startDiscounts_
Real legNPV(Size j) const
Real inCcyLegBPS(Size j) const
void setupExpired() const override
DiscountFactor startDiscounts(Size j) const
void fetchResults(const PricingEngine::results *) const override
std::vector< DiscountFactor > endDiscounts_
void alwaysForwardNotifications() override
std::vector< Real > payer_
VanillaCrossCurrencySwap(bool payFixed, Currency fixedCcy, Real fixedNominal, const Schedule &fixedSchedule, Rate fixedRate, const DayCounter &fixedDayCount, Currency floatCcy, Real floatNominal, const Schedule &floatSchedule, const QuantLib::ext::shared_ptr< IborIndex > &iborIndex, Rate floatSpread, boost::optional< BusinessDayConvention > paymentConvention=boost::none, const bool isPhysicallySettled=true, const bool isResettable=false)
Interest rate swap with extended interface.