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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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