Skip to content

Commit 2ee4904

Browse files
pcaspersjenkins
authored andcommitted
QPR-11786 support for "TBS" OIS vs OIS par conversion
1 parent 71a4af9 commit 2ee4904

4 files changed

Lines changed: 84 additions & 32 deletions

File tree

OREAnalytics/orea/engine/parsensitivityanalysis.cpp

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include <qle/instruments/oibasisswap.hpp>
4141
#include <qle/instruments/subperiodsswap.hpp>
4242
#include <qle/instruments/tenorbasisswap.hpp>
43+
#include <qle/instruments/doubleoibasisswap.hpp>
4344
#include <qle/math/blockmatrixinverse.hpp>
4445
#include <qle/pricingengines/crossccyswapengine.hpp>
4546
#include <qle/pricingengines/depositengine.hpp>
@@ -119,6 +120,12 @@ Real impliedQuote(const boost::shared_ptr<Instrument>& i) {
119120
else
120121
return boost::dynamic_pointer_cast<OvernightIndexedBasisSwap>(i)->fairIborSpread();
121122
}
123+
if (boost::dynamic_pointer_cast<DoubleOvernightIndexedBasisSwap>(i)) {
124+
if (boost::dynamic_pointer_cast<DoubleOvernightIndexedBasisSwap>(i)->spreadOnShort())
125+
return boost::dynamic_pointer_cast<DoubleOvernightIndexedBasisSwap>(i)->fairPaySpread();
126+
else
127+
return boost::dynamic_pointer_cast<DoubleOvernightIndexedBasisSwap>(i)->fairRecSpread();
128+
}
122129
if (boost::dynamic_pointer_cast<FixedBMASwap>(i))
123130
return boost::dynamic_pointer_cast<FixedBMASwap>(i)->fairRate();
124131
if (boost::dynamic_pointer_cast<SubPeriodsSwap>(i))
@@ -1497,6 +1504,7 @@ std::pair<boost::shared_ptr<QuantLib::Instrument>, Date> ParSensitivityAnalysis:
14971504
boost::shared_ptr<IborIndex> longIndex = parseIborIndex(conv->longIndexName());
14981505
boost::shared_ptr<IborIndex> shortIndex = parseIborIndex(conv->shortIndexName());
14991506
boost::shared_ptr<OvernightIndex> shortIndexOn = boost::dynamic_pointer_cast<OvernightIndex>(shortIndex);
1507+
boost::shared_ptr<OvernightIndex> longIndexOn = boost::dynamic_pointer_cast<OvernightIndex>(longIndex);
15001508

15011509
if (market != nullptr) {
15021510
if (!expDiscountCurve.empty()) {
@@ -1526,6 +1534,8 @@ std::pair<boost::shared_ptr<QuantLib::Instrument>, Date> ParSensitivityAnalysis:
15261534
shortIndex = shortIndex->clone(shortIndexCurve);
15271535
if (shortIndexOn)
15281536
shortIndexOn = boost::static_pointer_cast<OvernightIndex>(shortIndexOn->clone(shortIndexCurve));
1537+
if (longIndexOn)
1538+
longIndexOn = boost::static_pointer_cast<OvernightIndex>(longIndexOn->clone(longIndexCurve));
15291539
boost::shared_ptr<Swap> helper;
15301540
Date latestRelevantDate;
15311541
boost::shared_ptr<Libor> longIndexAsLibor = boost::dynamic_pointer_cast<Libor>(longIndex);
@@ -1536,10 +1546,15 @@ std::pair<boost::shared_ptr<QuantLib::Instrument>, Date> ParSensitivityAnalysis:
15361546
shortIndexAsLibor != nullptr ? shortIndexAsLibor->jointCalendar() : shortIndex->fixingCalendar();
15371547
removeTodaysFixingIndices_.insert(shortIndex->name());
15381548
removeTodaysFixingIndices_.insert(longIndex->name());
1539-
if (shortIndexOn) {
1549+
1550+
if(!shortIndexOn && longIndexOn)
1551+
QL_FAIL("This is unexpected: long index is overnight, short index is longer");
1552+
1553+
Date settlementDate = longIndexCalendar.advance(
1554+
longIndexCalendar.adjust(asof_), longIndex->fixingDays() * Days);
1555+
1556+
if (shortIndexOn && !longIndexOn) {
15401557
// OIS vs Libor
1541-
Date settlementDate =
1542-
longIndexCalendar.advance(longIndexCalendar.adjust(asof_), longIndex->fixingDays() * Days);
15431558
Schedule oisSchedule = MakeSchedule()
15441559
.from(settlementDate)
15451560
.to(settlementDate + term)
@@ -1562,17 +1577,48 @@ std::pair<boost::shared_ptr<QuantLib::Instrument>, Date> ParSensitivityAnalysis:
15621577
boost::shared_ptr<QuantLib::OvernightIndexedCoupon> lastCoupon2 =
15631578
boost::dynamic_pointer_cast<QuantLib::OvernightIndexedCoupon>(
15641579
boost::static_pointer_cast<OvernightIndexedBasisSwap>(helper)->overnightLeg().back());
1580+
latestRelevantDate = std::max(helper->maturityDate(),
1581+
std::max(lastCoupon1->fixingEndDate(),
1582+
shortIndexOn->fixingCalendar().advance(lastCoupon2->valueDates().back(), 1 * Days)));
1583+
} else if (shortIndexOn && longIndexOn) {
1584+
// OIS vs OIS
1585+
// from userguide: long index ... should be interpreted as the index of the received leg.
1586+
Schedule shortSchedule = MakeSchedule()
1587+
.from(settlementDate)
1588+
.to(settlementDate + term)
1589+
.withTenor(conv->shortPayTenor())
1590+
.withCalendar(shortIndexCalendar)
1591+
.withConvention(shortIndex->businessDayConvention())
1592+
.forwards();
1593+
Schedule longSchedule = MakeSchedule()
1594+
.from(settlementDate)
1595+
.to(settlementDate + term)
1596+
.withTenor(longIndex->tenor())
1597+
.withCalendar(longIndexCalendar)
1598+
.withConvention(longIndex->businessDayConvention())
1599+
.forwards();
1600+
1601+
helper = boost::make_shared<DoubleOvernightIndexedBasisSwap>(
1602+
100.0, shortSchedule, shortIndexOn, longSchedule, longIndexOn, 0.0, 0.0, conv->spreadOnShort(), false);
1603+
1604+
boost::shared_ptr<QuantLib::OvernightIndexedCoupon> lastCouponShort =
1605+
boost::dynamic_pointer_cast<QuantLib::OvernightIndexedCoupon>(
1606+
boost::static_pointer_cast<DoubleOvernightIndexedBasisSwap>(helper)->payLeg().back());
1607+
1608+
boost::shared_ptr<QuantLib::OvernightIndexedCoupon> lastCouponLong =
1609+
boost::dynamic_pointer_cast<QuantLib::OvernightIndexedCoupon>(
1610+
boost::static_pointer_cast<DoubleOvernightIndexedBasisSwap>(helper)->recLeg().back());
1611+
15651612
latestRelevantDate =
15661613
std::max(helper->maturityDate(),
1567-
std::max(lastCoupon1->fixingEndDate(),
1568-
shortIndexOn->fixingCalendar().advance(lastCoupon2->valueDates().back(), 1 * Days)));
1614+
std::max(shortIndexOn->fixingCalendar().advance(lastCouponShort->valueDates().back(), 1 * Days),
1615+
longIndexOn->fixingCalendar().advance(lastCouponLong->valueDates().back(), 1 * Days)));
1616+
15691617
} else {
15701618
// Libor vs Libor
1571-
Date settlementDate =
1572-
longIndexCalendar.advance(longIndexCalendar.adjust(asof_), longIndex->fixingDays() * Days);
1573-
helper = boost::make_shared<TenorBasisSwap>(
1574-
settlementDate, 1.0, term, true, longIndex, 0.0, shortIndex, 0.0, conv->shortPayTenor(),
1575-
DateGeneration::Backward, conv->includeSpread(), conv->spreadOnShort(), conv->subPeriodsCouponType());
1619+
helper = boost::make_shared<TenorBasisSwap>(settlementDate, 1.0, term, true, longIndex, 0.0, shortIndex, 0.0,
1620+
conv->shortPayTenor(), DateGeneration::Backward,
1621+
conv->includeSpread(), conv->spreadOnShort(), conv->subPeriodsCouponType());
15761622
boost::shared_ptr<IborCoupon> lastCoupon1 = boost::dynamic_pointer_cast<IborCoupon>(
15771623
boost::static_pointer_cast<TenorBasisSwap>(helper)->longLeg().back());
15781624
Date maxDate2;

QuantExt/qle/instruments/doubleoibasisswap.cpp

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,24 @@ using namespace QuantLib;
2323

2424
namespace QuantExt {
2525

26-
DoubleOvernightIndexedBasisSwap::DoubleOvernightIndexedBasisSwap(Real nominal,
27-
const Schedule& paySchedule, const boost::shared_ptr<OvernightIndex>& payIndex,
28-
const Schedule& recSchedule, const boost::shared_ptr<OvernightIndex>& recIndex,
29-
Spread paySpread, Spread recSpread, const bool telescopicValueDates)
30-
: Swap(2), nominals_(std::vector<Real>(1, nominal)), paySchedule_(paySchedule),
31-
payIndex_(payIndex), recSchedule_(recSchedule), recIndex_(recIndex),
32-
paySpread_(paySpread), recSpread_(recSpread), telescopicValueDates_(telescopicValueDates) {
26+
DoubleOvernightIndexedBasisSwap::DoubleOvernightIndexedBasisSwap(
27+
Real nominal, const Schedule& paySchedule, const boost::shared_ptr<OvernightIndex>& payIndex,
28+
const Schedule& recSchedule, const boost::shared_ptr<OvernightIndex>& recIndex, Spread paySpread, Spread recSpread,
29+
const bool spreadOnShort, const bool telescopicValueDates)
30+
: Swap(2), nominals_(std::vector<Real>(1, nominal)), paySchedule_(paySchedule), payIndex_(payIndex),
31+
recSchedule_(recSchedule), recIndex_(recIndex), paySpread_(paySpread), recSpread_(recSpread),
32+
spreadOnShort_(spreadOnShort), telescopicValueDates_(telescopicValueDates) {
3333

3434
initialize();
3535
}
3636

37-
DoubleOvernightIndexedBasisSwap::DoubleOvernightIndexedBasisSwap(std::vector<Real> nominals,
38-
const Schedule& paySchedule, const boost::shared_ptr<OvernightIndex>& payIndex,
39-
const Schedule& recSchedule, const boost::shared_ptr<OvernightIndex>& recIndex,
40-
Spread paySpread, Spread recSpread, const bool telescopicValueDates)
41-
: Swap(2), nominals_(nominals), paySchedule_(paySchedule),
42-
payIndex_(payIndex), recSchedule_(recSchedule), recIndex_(recIndex),
43-
paySpread_(paySpread), recSpread_(recSpread), telescopicValueDates_(telescopicValueDates) {
37+
DoubleOvernightIndexedBasisSwap::DoubleOvernightIndexedBasisSwap(
38+
std::vector<Real> nominals, const Schedule& paySchedule, const boost::shared_ptr<OvernightIndex>& payIndex,
39+
const Schedule& recSchedule, const boost::shared_ptr<OvernightIndex>& recIndex, Spread paySpread, Spread recSpread,
40+
const bool spreadOnShort, const bool telescopicValueDates)
41+
: Swap(2), nominals_(nominals), paySchedule_(paySchedule), payIndex_(payIndex), recSchedule_(recSchedule),
42+
recIndex_(recIndex), paySpread_(paySpread), recSpread_(recSpread), spreadOnShort_(spreadOnShort),
43+
telescopicValueDates_(telescopicValueDates) {
4444

4545
initialize();
4646
}

QuantExt/qle/instruments/doubleoibasisswap.hpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,16 @@ using namespace QuantLib;
3939
*/
4040
class DoubleOvernightIndexedBasisSwap : public Swap {
4141
public:
42-
DoubleOvernightIndexedBasisSwap(Real nominal,
43-
const Schedule& paySchedule, const boost::shared_ptr<OvernightIndex>& payIndex,
44-
const Schedule& recSchedule, const boost::shared_ptr<OvernightIndex>& recIndex,
45-
Spread paySpread = 0.0, Spread recSpread = 0.0, const bool telescopicValueDates = false);
42+
DoubleOvernightIndexedBasisSwap(Real nominal, const Schedule& paySchedule,
43+
const boost::shared_ptr<OvernightIndex>& payIndex, const Schedule& recSchedule,
44+
const boost::shared_ptr<OvernightIndex>& recIndex, Spread paySpread = 0.0,
45+
Spread recSpread = 0.0, const bool spreadOnShort = true,
46+
const bool telescopicValueDates = false);
4647
DoubleOvernightIndexedBasisSwap(std::vector<Real> nominals, const Schedule& paySchedule,
47-
const boost::shared_ptr<OvernightIndex>& payIndex, const Schedule& recSchedule,
48-
const boost::shared_ptr<OvernightIndex>& recIndex, Spread paySpread = 0.0,
49-
Spread secondLegSpread = 0.0, const bool telescopicValueDates = false);
48+
const boost::shared_ptr<OvernightIndex>& payIndex, const Schedule& recSchedule,
49+
const boost::shared_ptr<OvernightIndex>& recIndex, Spread paySpread = 0.0,
50+
Spread secondLegSpread = 0.0, const bool spreadOnShort = true,
51+
const bool telescopicValueDates = false);
5052
//! \name Inspectors
5153
//@{
5254
Type type() const { return type_; }
@@ -64,6 +66,9 @@ class DoubleOvernightIndexedBasisSwap : public Swap {
6466

6567
const Leg& payLeg() const { return legs_[0]; }
6668
const Leg& recLeg() const { return legs_[1]; }
69+
70+
const bool spreadOnShort() const { return spreadOnShort_; }
71+
6772
//@}
6873

6974
//! \name Results
@@ -85,6 +90,7 @@ class DoubleOvernightIndexedBasisSwap : public Swap {
8590
Schedule recSchedule_;
8691
boost::shared_ptr<OvernightIndex> recIndex_;
8792
Spread paySpread_, recSpread_;
93+
bool spreadOnShort_;
8894
bool telescopicValueDates_;
8995
};
9096

QuantExt/qle/termstructures/doubleoibasisswaphelper.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ void DoubleOIBSHelper::initializeDates() {
119119
.forwards();
120120
swap_ = boost::shared_ptr<DoubleOvernightIndexedBasisSwap>(
121121
new DoubleOvernightIndexedBasisSwap(10000.0, // arbitrary
122-
paySchedule, payIndex_, recSchedule, recIndex_, 0.0, 0.0, true));
122+
paySchedule, payIndex_, recSchedule, recIndex_, 0.0, 0.0, true, true));
123123
boost::shared_ptr<PricingEngine> engine(
124124
new DiscountingSwapEngine(discount_.empty() ? discountRelinkableHandle_ : discount_));
125125
swap_->setPricingEngine(engine);

0 commit comments

Comments
 (0)