2
$\begingroup$

I am trying to fit a EUR curve based on the following instruments:-

  1. EONIA quotes
  2. 1m vs 6m basis quotes
  3. 3m (outright) quotes
  4. 6m (outright) quotes
  5. 6m vs 12m quotes

(1) , (3) & (4) are simply outright quotes and I fit that (using a python optimiser) by modelling them as :-

  1. EONIA is modelled as a Quantlib OvernightIndexedSwap object
  2. 3m & 6m Swaps are modelled as VanillaSwap object

I am having trouble fitting the basis quotes.

1m vs 6m basis quotes

The quotes are as follows:-[('1Y',12.3),('2Y',12.6),('3Y',13),('4Y',13.1),('5Y',13.1),('10Y',11.6),('20Y',6.5),('30Y',4.4)]

6m vs 12m basis quotes

The quotes are as follows:- [('1Y',7),('2Y',7.2),('3Y',7.4),('4Y',7.6),('5Y',7.7),('10Y',7.5),('20Y',6.3),('30Y',5.4)]

The attached code is what I use to model these basis swaps (basically I model them as 2 VanillaSwaps - long the long term Euribor and short the short term Euribor)

My question is regarding which leg to use a Swap1 vs Swap2.

My understanding is , the spread is added to the libor leg of the short tenor.

So for example, for modelling 1m vs 6m, I create VanillaSwap1 object (for the 1m Euribor leg) and add the spread to the floating leg. I then create VanillaSwap2 for the long tenor (i.e. 6m Euribor)

Similarly, for modelling 6m vs 12m basis,:- I create VanillSwap1 object (for the 6m Euribor leg) and add the spread to the floating leg. I then create VanillaSwap2 for the long tenor (i.e. 12m Euribor).

However, I am getting negative fitted zero rate (after the optimisation process) for the 6m vs 12m. NOTE:I get the correct zero spread rate which matches the market but the sign is -ve

For the 1m vs 6m basis, I correctly fit the zero rate spreads to the market

Am I doing something wrong here?

self.z_leg1_forcurve = curves[self.leg1_forcurve]
self.z_leg2_forcurve = curves[self.leg2_forcurve]
self.z_discurve_1 = curves[self.leg1_discurve]
self.z_discurve_2 = curves[self.leg2_discurve]

# set the base curve IborIndex
self.IBOR_Index1 = ql.IborIndex(
    'IborIndex1',
    ql.Period(self.leg1_frequency),
    self.settle_days,
    self.currency,
    self.calendar,
    ql.ModifiedFollowing,
    False,
    self.day_count,
    self.z_leg1_forcurve.QLZeroCurve # Zero curve for the forecasting curve
)

# set the spread curve IborIndex
self.IBOR_Index2 = ql.IborIndex(
    'IborIndex2',
    ql.Period(self.leg2_frequency),
    self.settle_days,
    self.currency,
    self.calendar,
    ql.ModifiedFollowing,
    False,
    self.day_count,
    self.z_leg2_forcurve.QLZeroCurve # Zero curve for the forecasting curve
)

# create a payment schedule for leg_1
schedule1 = ql.Schedule(
    self.start_date,
    self.end_date,
    ql.Period(self.leg1_frequency),
    self.calendar,
    ql.ModifiedFollowing,
    ql.ModifiedFollowing,
    ql.DateGeneration.Backward,
    False
)

# create a payment schedule for leg_2
schedule2 = ql.Schedule(
    self.start_date,
    self.end_date,
    ql.Period(self.leg2_frequency),
    self.calendar,
    ql.ModifiedFollowing,
    ql.ModifiedFollowing,
    ql.DateGeneration.Backward,
    False
)

# Swap1 is the short tenor (to which we add the spread quote)
self.QL_Swap1 = ql.VanillaSwap(
    ql.VanillaSwap.Payer,
    self.notional,      # nominal
    schedule1,          # fixed leg schedule
    0.0,                # fixed rate
    self.day_count,     # fixed leg day_count
    schedule1,          # float leg schedule
    self.IBOR_Index1,   # libor index
    self.quote,         # spread (added to leg1 ALWAYS)
    self.day_count      # float leg day_count
)

# Swap2 is the long tenor
self.QL_Swap2 = ql.VanillaSwap(
    ql.VanillaSwap.Payer,
    self.notional,      # nominal
    schedule2,          # fixed schedule
    0.0,                # fixedRate
    self.day_count,     # fixed leg DayCount
    schedule2,          # float schedule
    self.IBOR_Index2,   # liborIndex
    0.0,                # spread
    self.day_count      # float leg day_count
)

# discount the base curve with it's own discounting_curve
engine = ql.DiscountingSwapEngine(self.z_discurve_1.QLZeroCurve)
self.QL_Swap1.setPricingEngine(engine)

# discount the spread curve with it's own discounting_curve
engine2 = ql.DiscountingSwapEngine(self.z_discurve_2.QLZeroCurve)
self.QL_Swap2.setPricingEngine(engine2)
```
$\endgroup$

1 Answer 1

1
$\begingroup$

You are probably optimizing the spread on the wrong leg. Spread on the 1M in a 6M vs 1M swap will be positive and spread on the 12M in a 6M vs 12M swap will be negative.

Also, you could try using the FloatFloatSwap class instead of creating two swaps...

start = ql.Date(30,12,2019)
maturity = ql.Date(30,12,2024)

index6m = ql.Euribor6M(fwd_curve_6m)
float6m = ql.Schedule(start,
                      maturity,
                      index6m.tenor(),
                      index6m.fixingCalendar(),
                      index6m.businessDayConvention(),
                      index6m.businessDayConvention(),
                      ql.DateGeneration.Backward,
                      False)

index3m = ql.Euribor3M(fwd_curve_3m)
float3m = ql.Schedule(start,
                      maturity,
                      index3m.tenor(),
                      index3m.fixingCalendar(),
                      index3m.businessDayConvention(),
                      index3m.businessDayConvention(),
                      ql.DateGeneration.Backward,
                      False)

notional = 10e6
basis = ql.FloatFloatSwap(ql.VanillaSwap.Payer,
                  [notional] * (len(float3m)-1),
                  [notional] * (len(float6m)-1),
                  float3m,
                  index3m,
                  ql.Actual360(),
                  float6m,
                  index6m,
                  ql.Actual360(), False, False,
                  [1] * (len(float3m)-1),
                  [0.0007194] * (len(float3m)-1))

swap_engine = ql.DiscountingSwapEngine(discount_curve)
basis.setPricingEngine(swap_engine)

npv = basis.NPV()
print(f"NPV of basis swap is: {npv:,.2f}")
$\endgroup$
1
  • $\begingroup$ Thanks. Will try out the FloatFloat constructor. For the 2 swap methodology, I was adding the positive spread always to the short tenor floating leg. So for example, 1m vs 6m - I was adding the 12 bps spread to the float leg of the 1m Euribor Vanilla Swap. Whereas for the 6m vs 12m basis, I was adding the spread, say 7 bps, to the float leg of the 6m Euribor Vanilla Swap. $\endgroup$
    – sumit_uk1
    Commented Dec 26, 2019 at 17:33

Not the answer you're looking for? Browse other questions tagged or ask your own question.