• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lballabio / QuantLib / 20991802759

14 Jan 2026 11:03AM UTC coverage: 74.155% (-0.02%) from 74.172%
20991802759

Pull #2418

github

web-flow
Merge 332e795f4 into b64f97dc2
Pull Request #2418: avoid storing references in gsr and markov functional state processes

55 of 59 new or added lines in 8 files covered. (93.22%)

155 existing lines in 14 files now uncovered.

57332 of 77314 relevant lines covered (74.15%)

8753424.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

57.59
/ql/instruments/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
 <https://www.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/overnightindexedcoupon.hpp>
27
#include <ql/cashflows/simplecashflow.hpp>
28
#include <ql/instruments/assetswap.hpp>
29
#include <ql/pricingengines/swap/discountingswapengine.hpp>
30
#include <utility>
31

32
using std::vector;
33

34
namespace QuantLib {
35

36
    AssetSwap::AssetSwap(bool payBondCoupon,
96✔
37
                         ext::shared_ptr<Bond> bond,
38
                         Real bondCleanPrice,
39
                         const ext::shared_ptr<IborIndex>& iborIndex,
40
                         Spread spread,
41
                         Schedule floatSchedule,
42
                         const DayCounter& floatingDayCounter,
43
                         bool parSwap,
44
                         Real gearing,
45
                         Real nonParRepayment,
46
                         Date dealMaturity)
96✔
47
    : Swap(2), bond_(std::move(bond)), bondCleanPrice_(bondCleanPrice),
96✔
48
      nonParRepayment_(nonParRepayment), spread_(spread), parSwap_(parSwap) {
96✔
49

50
        auto overnight = ext::dynamic_pointer_cast<OvernightIndex>(iborIndex);
96✔
51
        if (overnight) {
96✔
52
            QL_REQUIRE(!floatSchedule.empty(),
6✔
53
                       "floating schedule is needed when using an overnight index");
54
        }
55

56
        Schedule schedule = floatSchedule.empty()
57
            ? Schedule(bond_->settlementDate(),
58
                       bond_->maturityDate(),
90✔
59
                       iborIndex->tenor(),
90✔
60
                       iborIndex->fixingCalendar(),
90✔
61
                       iborIndex->businessDayConvention(),
62
                       iborIndex->businessDayConvention(),
63
                       DateGeneration::Backward,
64
                       false) // endOfMonth
65
            : std::move(floatSchedule);
186✔
66

67
        if (dealMaturity == Date())
96✔
68
            dealMaturity = schedule.back();
96✔
69
        QL_REQUIRE(dealMaturity <= schedule.back(),
96✔
70
                   "deal maturity " << dealMaturity <<
71
                   " cannot be later than (adjusted) bond maturity " <<
72
                   schedule.back());
73
        QL_REQUIRE(dealMaturity > schedule.front(),
96✔
74
                   "deal maturity " << dealMaturity <<
75
                   " must be later than swap start date " <<
76
                   schedule.front());
77

78
        // the following might become an input parameter
79
        BusinessDayConvention paymentAdjustment = Following;
80

81
        Date finalDate = schedule.calendar().adjust(
96✔
82
            dealMaturity, paymentAdjustment);
83
        schedule = schedule.until(finalDate);
96✔
84

85
        // bondCleanPrice must be the (forward) clean price
86
        // at the floating schedule start date
87
        upfrontDate_ = schedule.startDate();
96✔
88
        Real dirtyPrice = bondCleanPrice_ + bond_->accruedAmount(upfrontDate_);
96✔
89

90
        Real notional = bond_->notional(upfrontDate_);
96✔
91
        /* In the market asset swap, the bond is purchased in return for
92
           payment of the full price. The notional of the floating leg is
93
           then scaled by the full price. */
94
        if (!parSwap_)
96✔
95
            notional *= dirtyPrice/100.0;
24✔
96

97
        /******** Bond leg ********/
98

99
        const Leg& bondLeg = bond_->cashflows();
96✔
100
        QL_REQUIRE(!bondLeg.empty(), "no cashflows from bond");
96✔
101

102
        bool includeOnUpfrontDate = false; // a cash flow on the upfront
103
                                           // date must be discarded
104

105
        // add coupons for the time being, not the redemption
106
        Leg::const_iterator i;
107
        for (i = bondLeg.begin(); i < bondLeg.end()-1 && (*i)->date()<=dealMaturity; ++i) {
1,798✔
108
            if (!(*i)->hasOccurred(upfrontDate_, includeOnUpfrontDate))
1,702✔
109
                legs_[0].push_back(*i);
1,490✔
110
        }
111

112
        // if we're skipping a cashflow before the redemption
113
        // and it's a coupon, then add the accrued coupon.
114
        if (i < bondLeg.end()-1) {
96✔
UNCOV
115
            auto c = ext::dynamic_pointer_cast<Coupon>(*i);
×
UNCOV
116
            if (c != nullptr) {
×
UNCOV
117
                Real accruedAmount = c->accruedAmount(dealMaturity);
×
118
                auto accruedCoupon =
UNCOV
119
                    ext::make_shared<SimpleCashFlow>(accruedAmount, finalDate);
×
UNCOV
120
                legs_[0].push_back(accruedCoupon);
×
121
            }
122
        }
123

124
        // add the redemption, or whatever the final payment is
125
        if (nonParRepayment_ == Null<Real>()) {
96✔
126
            auto redemption = bondLeg.back();
127
            auto finalFlow =
128
                ext::make_shared<SimpleCashFlow>(redemption->amount(), finalDate);
96✔
129
            legs_[0].push_back(finalFlow);
×
130
            nonParRepayment_ = 100.0;
96✔
131
        } else {
132
            auto finalFlow =
133
                ext::make_shared<SimpleCashFlow>(nonParRepayment_, finalDate);
×
134
            legs_[0].push_back(finalFlow);
×
135
        }
136

137
        /******** Floating leg ********/
138

139
        if (overnight) {
96✔
140
            legs_[1] =
141
                OvernightLeg(schedule, overnight)
12✔
142
                .withNotionals(notional)
6✔
143
                .withPaymentAdjustment(paymentAdjustment)
6✔
144
                .withGearings(gearing)
6✔
145
                .withSpreads(spread)
6✔
146
                .withPaymentDayCounter(floatingDayCounter);
12✔
147
        } else {
148
            legs_[1] =
149
                IborLeg(std::move(schedule), iborIndex)
180✔
150
                .withNotionals(notional)
90✔
151
                .withPaymentAdjustment(paymentAdjustment)
90✔
152
                .withGearings(gearing)
90✔
153
                .withSpreads(spread)
90✔
154
                .withPaymentDayCounter(floatingDayCounter);
180✔
155
        }
156

157
        if (parSwap_) {
96✔
158
            // upfront
159
            Real upfront = (dirtyPrice-100.0)/100.0 * notional;
72✔
160
            auto upfrontCashFlow =
161
                ext::make_shared<SimpleCashFlow>(upfront, upfrontDate_);
72✔
162
            legs_[1].insert(legs_[1].begin(), upfrontCashFlow);
72✔
163
            // backpayment (accounts for non-par redemption, if any)
164
            Real backPayment = notional;
72✔
165
            auto backPaymentCashFlow =
166
                ext::make_shared<SimpleCashFlow>(backPayment, finalDate);
72✔
167
            legs_[1].push_back(backPaymentCashFlow);
72✔
168
        } else {
169
            // final notional exchange
170
            auto finalCashFlow =
171
                ext::make_shared<SimpleCashFlow>(notional, finalDate);
24✔
172
            legs_[1].push_back(finalCashFlow);
24✔
173
        }
174

175
        /******** registration and sides ********/
176

177
        for (const auto& leg: legs_)
288✔
178
            for (const auto& c: leg)
5,146✔
179
                registerWith(c);
9,908✔
180

181
        if (payBondCoupon) {
96✔
182
            payer_[0]=-1.0;
96✔
183
            payer_[1]=+1.0;
96✔
184
        } else {
UNCOV
185
            payer_[0]=+1.0;
×
UNCOV
186
            payer_[1]=-1.0;
×
187
        }
188
    }
192✔
189

190
    void AssetSwap::setupArguments(PricingEngine::arguments* args) const {
98✔
191

192
        Swap::setupArguments(args);
98✔
193

194
        auto* arguments = dynamic_cast<AssetSwap::arguments*>(args);
98✔
195

196
        if (arguments == nullptr) // it's a swap engine...
98✔
197
            return;
198

199
        const Leg& fixedCoupons = bondLeg();
200

201
        arguments->fixedResetDates = arguments->fixedPayDates =
UNCOV
202
            vector<Date>(fixedCoupons.size());
×
UNCOV
203
        arguments->fixedCoupons = vector<Real>(fixedCoupons.size());
×
204

UNCOV
205
        for (Size i=0; i<fixedCoupons.size(); ++i) {
×
206
            ext::shared_ptr<FixedRateCoupon> coupon =
UNCOV
207
                ext::dynamic_pointer_cast<FixedRateCoupon>(fixedCoupons[i]);
×
208

UNCOV
209
            arguments->fixedPayDates[i] = coupon->date();
×
UNCOV
210
            arguments->fixedResetDates[i] = coupon->accrualStartDate();
×
UNCOV
211
            arguments->fixedCoupons[i] = coupon->amount();
×
212
        }
213

214
        const Leg& floatingCoupons = floatingLeg();
215

216
        arguments->floatingResetDates = arguments->floatingPayDates =
217
            arguments->floatingFixingDates =
UNCOV
218
            vector<Date>(floatingCoupons.size());
×
219
        arguments->floatingAccrualTimes =
UNCOV
220
            vector<Time>(floatingCoupons.size());
×
221
        arguments->floatingSpreads =
UNCOV
222
            vector<Spread>(floatingCoupons.size());
×
223

224
        for (Size i=0; i<floatingCoupons.size(); ++i) {
×
225
            ext::shared_ptr<FloatingRateCoupon> coupon =
UNCOV
226
                ext::dynamic_pointer_cast<FloatingRateCoupon>(floatingCoupons[i]);
×
227

UNCOV
228
            arguments->floatingResetDates[i] = coupon->accrualStartDate();
×
UNCOV
229
            arguments->floatingPayDates[i] = coupon->date();
×
UNCOV
230
            arguments->floatingFixingDates[i] = coupon->fixingDate();
×
UNCOV
231
            arguments->floatingAccrualTimes[i] = coupon->accrualPeriod();
×
232
            arguments->floatingSpreads[i] = coupon->spread();
×
233
        }
234
    }
235

236
    Spread AssetSwap::fairSpread() const {
66✔
237
        static const Spread basisPoint = 1.0e-4;
238
        calculate();
66✔
239
        if (fairSpread_ != Null<Spread>()) {
66✔
240
            return fairSpread_;
241
        } else if (legBPS_.size() > 1 && legBPS_[1] != Null<Spread>()) {
66✔
242
            fairSpread_ = spread_ - NPV_/legBPS_[1]*basisPoint;
66✔
243
            return fairSpread_;
66✔
244
        } else {
245
            QL_FAIL("fair spread not available");
×
246
        }
247
    }
248

UNCOV
249
    Real AssetSwap::floatingLegBPS() const {
×
UNCOV
250
        calculate();
×
UNCOV
251
        QL_REQUIRE(legBPS_.size() > 1 && legBPS_[1] != Null<Real>(),
×
252
                   "floating-leg BPS not available");
UNCOV
253
        return legBPS_[1];
×
254
    }
255

UNCOV
256
    Real AssetSwap::floatingLegNPV() const {
×
UNCOV
257
        calculate();
×
UNCOV
258
        QL_REQUIRE(legNPV_.size() > 1 && legNPV_[1] != Null<Real>(),
×
259
                   "floating-leg NPV not available");
UNCOV
260
        return legNPV_[1];
×
261
    }
262

263
    Real AssetSwap::fairCleanPrice() const {
50✔
264
        calculate();
50✔
265
        if (fairCleanPrice_ != Null<Real>()) {
50✔
266
            return fairCleanPrice_;
267
        } else {
268
            QL_REQUIRE(startDiscounts_[1]!=Null<DiscountFactor>(),
50✔
269
                       "fair clean price not available for seasoned deal");
270
            Real notional = bond_->notional(upfrontDate_);
50✔
271
            if (parSwap_) {
50✔
272
                fairCleanPrice_ = bondCleanPrice_ - payer_[1] *
41✔
273
                    NPV_*npvDateDiscount_/startDiscounts_[1]/(notional/100.0);
41✔
274
            } else {
275
                Real accruedAmount = bond_->accruedAmount(upfrontDate_);
9✔
276
                Real dirtyPrice = bondCleanPrice_ + accruedAmount;
9✔
277
                Real fairDirtyPrice = - legNPV_[0]/legNPV_[1] * dirtyPrice;
9✔
278
                fairCleanPrice_ = fairDirtyPrice - accruedAmount;
9✔
279
            }
280

281
            return fairCleanPrice_;
50✔
282
        }
283
    }
284

UNCOV
285
    Real AssetSwap::fairNonParRepayment() const {
×
UNCOV
286
        calculate();
×
UNCOV
287
        if (fairNonParRepayment_ != Null<Real>()) {
×
288
            return fairNonParRepayment_;
289
        } else {
UNCOV
290
            QL_REQUIRE(endDiscounts_[1]!=Null<DiscountFactor>(),
×
291
                       "fair non par repayment not available for expired leg");
UNCOV
292
            Real notional = bond_->notional(upfrontDate_);
×
UNCOV
293
            fairNonParRepayment_ = nonParRepayment_ - payer_[0] *
×
UNCOV
294
                NPV_*npvDateDiscount_/endDiscounts_[1]/(notional/100.0);
×
UNCOV
295
            return fairNonParRepayment_;
×
296
        }
297
    }
298

299
    void AssetSwap::setupExpired() const {
×
300
        Swap::setupExpired();
×
301
        fairSpread_ = Null<Spread>();
×
UNCOV
302
        fairCleanPrice_ = Null<Real>();
×
UNCOV
303
        fairNonParRepayment_ = Null<Real>();
×
304
    }
×
305

306
    void AssetSwap::fetchResults(const PricingEngine::results* r) const {
98✔
307
        Swap::fetchResults(r);
98✔
308
        const auto* results = dynamic_cast<const AssetSwap::results*>(r);
98✔
309
        if (results != nullptr) {
98✔
UNCOV
310
            fairSpread_ = results->fairSpread;
×
UNCOV
311
            fairCleanPrice_= results->fairCleanPrice;
×
UNCOV
312
            fairNonParRepayment_= results->fairNonParRepayment;
×
313
        } else {
314
            fairSpread_ = Null<Spread>();
98✔
315
            fairCleanPrice_ = Null<Real>();
98✔
316
            fairNonParRepayment_ = Null<Real>();
98✔
317
        }
318
    }
98✔
319

UNCOV
320
    void AssetSwap::arguments::validate() const {
×
UNCOV
321
        QL_REQUIRE(fixedResetDates.size() == fixedPayDates.size(),
×
322
                   "number of fixed start dates different from "
323
                   "number of fixed payment dates");
324
        QL_REQUIRE(fixedPayDates.size() == fixedCoupons.size(),
×
325
                   "number of fixed payment dates different from "
326
                   "number of fixed coupon amounts");
UNCOV
327
        QL_REQUIRE(floatingResetDates.size() == floatingPayDates.size(),
×
328
                   "number of floating start dates different from "
329
                   "number of floating payment dates");
UNCOV
330
        QL_REQUIRE(floatingFixingDates.size() == floatingPayDates.size(),
×
331
                   "number of floating fixing dates different from "
332
                   "number of floating payment dates");
UNCOV
333
        QL_REQUIRE(floatingAccrualTimes.size() == floatingPayDates.size(),
×
334
                   "number of floating accrual times different from "
335
                   "number of floating payment dates");
UNCOV
336
        QL_REQUIRE(floatingSpreads.size() == floatingPayDates.size(),
×
337
                   "number of floating spreads different from "
338
                   "number of floating payment dates");
UNCOV
339
    }
×
340

341
    void AssetSwap::results::reset() {
×
UNCOV
342
        Swap::results::reset();
×
UNCOV
343
        fairSpread = Null<Spread>();
×
344
        fairCleanPrice = Null<Real>();
×
UNCOV
345
        fairNonParRepayment = Null<Real>();
×
UNCOV
346
    }
×
347

348
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc