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;
0 commit comments