image1

Interest Rate Swap

[74]:
from rivapy.pricing.interest_rate_swap_pricing import InterestRateSwapPricer
from rivapy.instruments.ir_swap_specification import InterestRateSwapSpecification, IrFixedLegSpecification, IrFloatLegSpecification, InterestRateBasisSwapSpecification
from rivapy.instruments.components import NotionalStructure, ConstNotionalStructure, VariableNotionalStructure, ResettingNotionalStructure
from rivapy.pricing.pricing_data import (
    InterestRateSwapFloatLegPricingData_rivapy,
    InterestRateSwapLegPricingData_rivapy,
    InterestRateSwapPricingData_rivapy,
)
from rivapy.pricing.pricing_request import InterestRateSwapPricingRequest
import datetime as dt
from dateutil.relativedelta import relativedelta
from rivapy.marketdata.curves import DiscountCurve
from rivapy.tools.enums import InterpolationType, ExtrapolationType
import math
from dateutil.relativedelta import relativedelta

%matplotlib inline

Definition of an Interest Rate Swap

The most common type of an interest rate swap is the so called “plain vanilla” interest rate swap. Here, a company agrees to pay cashflows equal to a predetermined fixed interest rate on a notional principal during a predetermined number of years; in return, it receives interest payments on the same notional principal at a floating rate during the same number of years.

Valuation of Interest Rate Swaps

The value of a swap can, for example, be regarded as the difference between two bonds. From the point of view of a fixed-rate payer, the value of the swap can be regarded as a long position in a floating-rate bond and a short position in a fixed-rate bond.

\[V_{swap} = B_{float} - B_{fix}\]

Alternatively, a plain vanilla interest rate swap can also be valued making the assumption that forward interest rates are realized. Here, cashflows of the floating leg are calculated assuming that the floating interest rate equals the forward interest rates. The value of the interest rate swap is then the sum of the present value of the net cashflows. This method will be applied in the following.

For a detailed discussion please refer to Hull, Options, futures, and other derivatives, 8th Edition, 2012, p. 148 ff.

In the following, we will use a slighly modified example taken from Hull, p. 161 to illustrate how a interest rate swap can be valued.

Example:

Suppose that a financial institution has agreed to pay 6-month EURIBOR and receive 8% per annum (with semiannual compounding) on a notional principal of €100 million. The swap has a remaining life of 1.5 years. The EURIBOR rates with continuous compunding for 6-month, 12-month, and 18-month maturities are 10%, 10.5%, and 11%, respectively.

Valuation in terms of bond prices

Fixed Leg

The fixed-rate bond has three payment dates in 6 months, 12 months, and 18 months with a cashflow of €4 million at the first two payment dates and a cashflow of €4 million plus the notional of €100 million at the last payment date. These cashflows have to be discounted using the given EURIBOR rates and subsequently summed up to receive the present value of the fixed leg.

Floating Leg

The floating leg bond is worth the notional immediately after an interest payment because at this time, the bond is a “fair deal” where the borower pays EURIBOR for earch subsequent accrual period. Consequently, immediately before a payment, the bond is worth the notional plus the accrued interest. Also, this cashflow has to be discounted using the given EURIBOR rate.

[ ]:
#Fixed Leg
time_to_maturity = [0.5, 1.0, 1.5]
cf_fix = [4, 4, 104]
libor_rates = [0.1, 0.105, 0.11]

df = [math.exp(-lr * tm) for lr, tm in zip(libor_rates, time_to_maturity)]

pv_cf_fix = [cf * d for cf, d in zip(cf_fix, df)]

B_fix = sum(pv_cf_fix)
print(f'Fixed Leg Value = {B_fix}')


# Floating Leg
floating_rate = 2 * (math.exp(libor_rates[0] / 2) - 1)  # libor rate has to be converted from contiuous compounding to semiannual comp.
cf_float = 100 + (100 * floating_rate * 0.5) # 1/2 of converted libor_rate as semiannual payment
pv_cf_float = cf_float * df[0] # discounted with 3-month libor rate
print(f'Floating Leg Value = {pv_cf_float}')


# Interest Rate Swap Valuation (from a fixed-rate payer's perspective)
value = (pv_cf_float - B_fix)
print(f'Interest Rate Swap Value = {value}')

Fixed Leg Value = 95.58716101349117
Floating Leg Value = 100.00000000000001
Interest Rate Swap Value = 4.412838986508845
[76]:
pv_cf_float/B_fix
[76]:
1.0461656036199884

Valuation in terms of forward rate agreements

As before, the value of the interest rate swap is given as the difference between the present value of the fixed and the floating leg.

Fixed Leg

The present value of the fixed leg is given by:

\[PV_{fixed}(t) = rN\sum_{i=1}^n\tau_iD_i\]

with \(t\) as the valuation date, \(r\) as the interest rate of the fixed leg, \(N\) as the notional principal, \(i\) as the \(i^{th}\) cashflow, \(\tau_i\) as the accrual period for the \(i^{th}\) cashflow and \(D_i\) as the discount factor of the \(i^{th}\) period.

Floating Leg

The present value of the float leg is given by:

\[PV_{floating}(t)=N\sum_{i=1}^n(F_i+s)\tau_iD_i\]

with \(F_i\) as the simply compounded forward rate defined as \(\frac{1}{\tau_i}(\frac{D_{i-1}}{D_i}-1)\), \(s\) as the floating spread and the rest defined as before.

Note that the discount factors used for the forward rate can be different from the discount factor used to discount the cashflows. Also, a different discount factors from the fixed leg are theoretically possible.

The following code manually calculates the value of the interest-rate swap. Thereby, the formula for the floating leg assuming that there is no spread \(s\) is simplified as follows:

\begin{align} PV_{floating}(t) & =N\sum_{i=1}^n\frac{1}{\tau_i}(\frac{D_{i-1}}{D_i}-1)\tau_iD_i \\ & =N\sum_{i=1}^n(\frac{D_{i-1}}{D_i}-1)D_i \\ & =N\sum_{i=1}^n(D_{i-1}-{D_i}) \\ & =N\cdot[(D_0-D_1)+(D_1-D_2)+ ... +(D_{n-2}-D_{n-1})+(D_{n-1}-D_n)] \\ & =N\cdot(D_0-D_n) \end{align}

[77]:
ttm = [0.5, 1, 1.5]
rates = [0.1, 0.105, 0.11]
yf = [0.25, 0.75, 1.25]
r = 0.08
N = 100

df = [math.exp(-r * tm) for r, tm in zip(rates, ttm)]

PV_fix = r * N * ((ttm[0] - 0) * df[0] + (ttm[1] - ttm[0]) * df[1] + (ttm[2] - ttm[1]) * df[2])
print('PV fixed leg =',PV_fix)

PV_fl = N * (1 - df[2])
print(f'PV floating leg = {PV_fl}')

print(f'Interest rate swap value = {PV_fl-PV_fix}')

# PV fixed leg = 10.797790604699582
# PV floating leg = 15.210629591208413
# Interest rate swap value = 4.412838986508831
PV fixed leg = 10.797790604699582
PV floating leg = 15.210629591208413
Interest rate swap value = 4.412838986508831

The following code shows the manual calculation of the example as it is done by Hull:

[78]:
ttm = [0.5, 1.0, 1.5]
rates = [0.1, 0.105, 0.11]
N = 100 # Notional
m = 2 # compounding frequency

ref_date = dt.datetime(2017, 1, 1)

days_to_maturity = [180, 360, 540]
dates = [ref_date + dt.timedelta(days=d) for d in days_to_maturity]

# discount factors from constant rate
df = [math.exp(-r * t) for r, t in zip(rates, ttm)]

# Fixed leg
fix_cf = [4, 4, 4]
pv_fix = [cf * d for cf, d in zip(fix_cf, df)]
print(f'Present value fixed leg = {sum(pv_fix)}')

# Floating leg
floatrates = [m * (math.exp(rates[0] / m) - 1)]

for i in range(1, len(rates)):
    contrate = ((rates[i] * ttm[i] - rates[i-1] * ttm[i-1]) / (ttm[i] - ttm[i-1]))
    floatrates.append(m * (math.exp(contrate / m) - 1))

float_cf = [fr * N / m for fr in floatrates]
float_pv = [cf * d for cf, d in zip(float_cf, df)]
print(f'Present value floating leg = {sum(float_pv)}')

# Net Cash-Flow
net_CF = [fl - fx for fl, fx in zip(float_cf, fix_cf)]
pv_net_cf = [cf * d for cf, d in zip(net_CF, df)]
value = sum(pv_net_cf)
print(f'Interest rate swap value = {value}')

# Present value fixed leg = 10.797790604699582
# Present value floating leg = 15.210629591208427
# Interest rate swap value = 4.412838986508847
Present value fixed leg = 10.797790604699582
Present value floating leg = 15.210629591208429
Interest rate swap value = 4.412838986508847

Valuation using the interest rate swap specification

The pyvacon interest rate swap specification uses the valuation in terms of forward rate agreements. In order to price an interest rate swap, we need to create the necessary market data and setup the specification and pricing data.

Create the necessary market data

As a first step, we need to create a discount curve containing the discount factors to derive the present values of both, the fixed and the floating leg. Here, we use this discount curve to derive the forward rate discount factors as well. Theoretically, we could also use different discount curves for the fixed and floating legs as well as for the forward rates.

[79]:
# Discount curve - we use these discount factors to get the present values of both the fixed and floating leg as well as

object_id = "TEST_DC"
refdatedc = dt.datetime(2017, 1, 1)
days_to_maturity = [180, 360, 540]
dates = [refdatedc + dt.timedelta(days=d) for d in days_to_maturity]
# discount factors from constant rate
rates = [0.10, 0.105, 0.11]
df = [math.exp(-r * d / 360) for r, d in zip(rates, days_to_maturity)]
dc = DiscountCurve(id=object_id, refdate=refdatedc, dates=dates, df=df, interpolation=InterpolationType.LINEAR, extrapolation=ExtrapolationType.LINEAR)
[80]:
# #test market data
# ref_date = dt.datetime(2019, 8, 31)
# dates = [dt.datetime(2019, 8, 31), dt.datetime(2020, 8, 31)]
# df = [1.0, 0.9900633419771339]
# dc = DiscountCurve(id=object_id, refdate=refdatedc, dates=dates, df=df, interpolation=InterpolationType.LINEAR, extrapolation=ExtrapolationType.LINEAR)

Setup the specification

Here, we need to provide the start-, end-, and pay-dates for the different accrual periods. The floating leg additionally needs a reset date vector. Moreover, we need to define a vector of notionals since if needed, each accrual period may have a different notional. However, if the vector if of length 1, the same notional is applied to all periods.

Having defined the relevant information, we are able to create the fixed- and floating leg. The fixed leg needs to be provided with the fixed rate; the float leg requires information about an eventual spread. Afterwards, the interest rate swap specification can be set.

Setting up an interest rate swap

A plain vanilla interest rate swap is a financial contract in which a stream of fixed payments is exchanged for floating payments linked to a reference index. The par rate (r) of a swap is the fixed rate under which the value of the two streams (legs) is equal:

\[r \cdot \sum_{i=1}^n dcf_{i} \cdot P(0,t_{i} ) = \sum_{k=1}^m F_{k} \cdot dcf_{k} \cdot P(0,t_{k})\]

where \(t_{i}\), \(i=1,..,n\) and \(t_{k}\), \(i=1,..,m\) are the payment structures of the fixed and floating legs, and \(P(0,t_{i/k})\) are the corresponding discount factors, \(dcf_{i/k}\) is the day count fraction for the period \([t_{(i/k-1)},t_{i/k}]\), and \(F_{k}\) is the expected value of underlying reference rate for the period \([t_{(k-1)},t_{k}]\).

The standard payment frequency of the fixed leg depends on the currency of the swap as well as the tenor of the underlying. In the EUR market swaps are usually quoted with annual fixed payments.

The payment frequency of the floating leg usually coincides with the tenor of the underlying reference index. In some currencies, however, the floating rate can be compounded and payed out at less frequent intervals (e.g. CAD).

In the context of pyvacon an IRS can be defined using an InterestRateSwapSpecification.

[81]:
# # Create the vectors defining the statdates, enddates, paydates and reset dates
days_to_maturity = [0, 180, 360, 540]
dates = [dt.datetime(2017, 1, 1) + dt.timedelta(d) for d in days_to_maturity]

startdates = dates[:-1]
enddates = dates[1:]
paydates = enddates
resetdates = startdates
refdate = dates[0]


fixed_leg = IrFixedLegSpecification(fixed_rate = 0.08, obj_id = 'dummy_fixed_leg', notional = 100.0,
                                    start_dates=startdates, end_dates=enddates, pay_dates=paydates, currency='EUR', day_count_convention='Act360')

spread = 0.00


ns = ConstNotionalStructure(100.0)

float_leg =IrFloatLegSpecification(obj_id = 'dummy_float_leg', notional = ns, reset_dates=resetdates, start_dates=startdates, end_dates=enddates,
                                   rate_start_dates=startdates, rate_end_dates=enddates, pay_dates=paydates, currency = "EUR",
                                   udl_id="test_udl_id", fixing_id="test_fixing_id", day_count_convention="Act360", spread=spread)

maturity_date = refdate + dt.timedelta(600)

ir_swap = InterestRateSwapSpecification(obj_id="dummy_swap", notional=ns, issue_date=refdate, maturity_date=maturity_date,
                                        pay_leg=fixed_leg, receive_leg=float_leg,currency='EUR', day_count_convention="Act360",
                                        issuer="DBK", securitization_level="COLLATERALIZED"
                                        )

Setup the pricing data

A product may be priced in two different ways: One may either fill the respective pricing data needed for a special pricer (which inherits from th BasePricingData) and use the respective price method where just the pricing data is given. Another possibility is to use the price-method where the storages are given. In this case, the pricer will fill the needed pricing data according to the underlying and other data as specified in the product specification.

Here we show the approach explicitely setting the pricing data.

The pricing data needs the following information:

  • A discount curve for the pay- and receive leg

  • A discount curve for the floating leg (here the receiving leg)

  • A pricer

  • A pricing request

  • A valuation date

  • The created interest rate swap specification

  • An eventual FX-rate for the pay- and receive leg

[ ]:

pay_leg_pricing_data = InterestRateSwapLegPricingData_rivapy( spec=ir_swap.get_pay_leg(), discount_curve=dc, forward_curve=dc, fixing_map = None, fx_rate=1.0, weight=-1.0 ) rec_leg_pricing_data = InterestRateSwapFloatLegPricingData_rivapy( spec=ir_swap.get_receive_leg(), discount_curve=dc, forward_curve=dc, fixing_map = None, fixing_grace_period=0, fixing_curve=dc, fx_rate=1.0, weight=1.0, ) pricing_data_all = {} pricing_data_all['discount_curve_pay_leg'] =dc pricing_data_all['discount_curve_receive_leg']=dc pricing_data_all['fixing_curve_pay_leg']=dc pricing_data_all['fixing_curve_receive_leg']=dc pricing_data_all['fx_fwd_curve_pay_leg']=dc pricing_data_all['fx_fwd_curve_receive_leg']=dc pricing_data_all['pricing_param']={"fixing_grace_period": 0} pricing_data_all['fixing_map']=None pricing_data_all['fx_pay_leg']=1.0 pricing_data_all['fx_receive_leg']=1.0 ir_swap_pr = InterestRateSwapPricingRequest()

[83]:
ir_swap_pricing_data = InterestRateSwapPricingData_rivapy(
    spec=ir_swap,
    val_date=ref_date,
    pricing_request = ir_swap_pr,
    pricer="InterestRateSwapPricer",
    ccy='EUR',
    leg_pricing_data=pricing_data_all # previousl this heald the pricingData objects for each leg...
)

Pricing

[84]:
# tic = dt.datetime.now()
# pr = price(ir_swap_pricing_data)
# print(f'runtime: {dt.datetime.now() - tic}')
# pr.getPrice()


tic = dt.datetime.now()
pr = ir_swap_pricing_data.price()
print(f'runtime: {dt.datetime.now() - tic}')
print(f"Price: {pr}")

# runtime: 0:00:00.000938
# 4.412838984470563
<rivapy.marketdata.curves.DiscountCurve object at 0x00000219816C7680>
runtime: 0:00:00.001991
Price: 4.522463649728445

Further Example

Create the necessary market data

This time, we distinguish between the discount curve used to discount the cashflows to present values and a discount curve for the calculation of the forward interest rates which we use as fixing curve.

[85]:
refdatedc = dt.datetime(2017, 1, 1)
days_to_maturity = [1, 180, 365, 720, 3 * 365, 4 * 365, 10 * 365]
dates = [refdatedc + dt.timedelta(days=d) for d in days_to_maturity]

# Discount curve
object_id = "TEST_DC"
dsc_rate = 0.01
df = [math.exp(-d / 365.0 * dsc_rate) for d in days_to_maturity]
dc = DiscountCurve(id=object_id, refdate=refdatedc, dates=dates, df=df)

# Fixing curves
object_id = "TEST_fwd_"
fwd_rate = {'3m': 0.05, '6m': 0.052}
fwd_df = {t: [math.exp(-d / 365.0 * r) for d in days_to_maturity] for t, r in fwd_rate.items()}
fwd_dc = {t: DiscountCurve(id=object_id + t, refdate=refdatedc, dates=dates, df=df) for t, df in fwd_df.items()}


Setup the specification

In our example we are using one fixed leg and one floating leg (with just two payment and fixing dates) The swap needs to get vectors with start, end, and payment dates (floater additionally need a rest date vector). The i-th accrual period is defined by the i-th entry of start and end vectors. Note that this kind of swap has only floating legs with floating rate period equal to accrual period.

[86]:
# Create the vectors defining the statdates, enddates, paydates and reset dates
days_to_maturity = [0, 180, 360, 540]
dates = [dt.datetime(2017,1,1) + dt.timedelta(days=d) for d in days_to_maturity]

startdates = dates[:-1]
enddates = dates[1:]
paydates = enddates
resetdates = startdates
refdate = dates[0]

# We need a vector of notionals since if needed, each accrual period may have a different notional
# However, if the vector is of length 1 the same notional is applied to all periods

notionals = ConstNotionalStructure(100.0)

fixedleg = IrFixedLegSpecification(0.03, "fixed_leg", notionals, startdates, enddates, paydates,'EUR', 'Act365Fixed')
spread = 0.00
floatleg = IrFloatLegSpecification("float_leg", notionals, resetdates, startdates, enddates,startdates, enddates, paydates,'EUR',
                                   "dummy_udl", "dummy_fixing_id", 'Act365Fixed','Act365Fixed', spread)

ir_swap = InterestRateSwapSpecification('TEST_SWAP', notionals, refdate, paydates[-1], fixedleg, floatleg, "EUR",
                                        day_count_convention="Act365Fixed",
                                        issuer="DBK", securitization_level="COLLATERALIZED"
                                        )

Setup the pricer object

[87]:
ir_pricer = InterestRateSwapPricer(
    val_date=refdate,
    spec=ir_swap,
    discount_curve_pay_leg=dc,
    discount_curve_receive_leg=dc,
    fixing_curve_pay_leg=dc,
    fixing_curve_receive_leg=fwd_dc['6m'],
    fx_fwd_curve_pay_leg=None,
    fx_fwd_curve_receive_leg=None,
    pricing_request=[],
    pricing_param={"fixing_grace_period": 0.0},
)


Pricing

[88]:
tic = dt.datetime.now()
pr = ir_pricer.price()
print(f'runtime: {dt.datetime.now() - tic}')
print(pr)

# runtime: 0:00:00.013420
# 3.3213902674791154
runtime: 0:00:00.007013
3.3213902680066774

Tenor Basis Swap

There are two types of tenor basis swaps:

  • In most currencies, a tenor basis swap consists of two float legs (e.g. 3M-USD-Libor vs 6M-USD-Libor) where the on the leg with the shorter tenor (in this case the 3M leg) there is an additional spread (the “basis”).

  • In EUR, a the “basis” is quoted as the difference of swap rates between two vanilla swaps with different float leg tenor (e.g. [swap rate of 6M-Euribor-Swap] - [swap rate of 3M-Euribor-Swap]). This is equivalent to a swap with three legs:

    • a floating leg which pays 3M-Euribor

    • a floating leg which receives 6M-Euribor

    • a fixed leg which pays the basis (usually annually)

In either case, if the discount curve and one of the forward (projection) curve are known, a strip of tenor basis swap quotes can be used to bootstrap the other forward curve.

[89]:
# Set up curves
# Discount curve
object_id = "TEST_DC"
dsc_rate = 0.01
df = [math.exp(-d / 365.0 * dsc_rate) for d in days_to_maturity]
dc = DiscountCurve(id=object_id, refdate=refdatedc, dates=dates, df=df, interpolation = InterpolationType.LINEAR_LOG, extrapolation=ExtrapolationType.LINEAR_LOG)

# Fixing curves
object_id = "TEST_fwd_"
fwd_rate = {'3m': 0.05, '6m': 0.052}
fwd_df = {t: [math.exp(-d / 365.0 * r) for d in days_to_maturity] for t, r in fwd_rate.items()}
fwd_dc = {t: DiscountCurve(id=object_id + t, refdate=refdatedc, dates=dates, df=df, interpolation = InterpolationType.LINEAR_LOG, extrapolation=ExtrapolationType.LINEAR_LOG) for t, df in fwd_df.items()}
[90]:
# set up two payer swaps (receive 3m/6m-Euribor, pay fixed)
refdate = dt.datetime(2017, 1, 1)
days = 720
tenor_in_days = {'3m': 90, '6m': 180, '12m': 360}
spread = 0.002
swap_rate_3m = .03
swap_rate_6m = swap_rate_3m + spread
swap_rates = {'3m': swap_rate_3m, '6m': swap_rate_6m}
days_to_maturity = {t: list(range(0, days + 1, tenor_days)) for t, tenor_days in tenor_in_days.items()}
notionals = ConstNotionalStructure(100.0)
ccy = 'EUR'
swaps = {}
swap_pricer = {}
t_fix = '12m'
dates_fix = [refdate + dt.timedelta(days=d) for d in days_to_maturity[t_fix]]

for t_fl in ['3m', '6m']:
# set up specification
    tenor_days_fl = tenor_in_days[t_fl]
    dates_fl = [refdate + dt.timedelta(days=d) for d in days_to_maturity[t_fl]]


    fixedleg = IrFixedLegSpecification(swap_rates[t_fl], "fixed_leg", notionals, dates_fix[:-1], dates_fix[1:], dates_fix[1:],ccy, 'Act365Fixed')

    floatleg = IrFloatLegSpecification("float_leg", notionals, dates_fl[:-1], dates_fl[:-1], dates_fl[1:], dates_fl[:-1], dates_fl[1:], dates_fl[1:],ccy,
                                    "dummy_udl", "dummy_fixing_id", 'Act365Fixed','Act365Fixed', spread)

    swaps[t_fl] = InterestRateSwapSpecification('TEST_SWAP', notionals, refdate, paydates[-1], fixedleg, floatleg, "EUR",
                                            day_count_convention="Act365Fixed",
                                            issuer="DBK", securitization_level="COLLATERALIZED"
                                            )


    #set up pricing data
    swap_pricer[t_fl] = ir_pricer = InterestRateSwapPricer(
                val_date=refdate,
                spec=swaps[t_fl],
                discount_curve_pay_leg=dc,
                discount_curve_receive_leg=dc,
                fixing_curve_pay_leg=dc,
                fixing_curve_receive_leg=fwd_dc[t_fl],
                fx_fwd_curve_pay_leg=None,
                fx_fwd_curve_receive_leg=None,
                pricing_request=[],
                pricing_param={"fixing_grace_period": 0.0},
            )





[91]:
# Price the swaps
price_3m = swap_pricer['3m'].price()
price_6m = swap_pricer['6m'].price()

price_diff = price_6m - price_3m
print(f"3m payer swap: {price_3m}")
print(f"6m payer swap: {price_6m}")
print(f"Difference: {price_diff}")

3m payer swap: 4.373913684160208
6m payer swap: 4.433019638823725
Difference: 0.05910595466351687

[92]:
# Set up basis swap

float_leg_3m = swaps['3m'].get_float_leg()
float_leg_6m = swaps['6m'].get_float_leg()
spread_leg = IrFixedLegSpecification(spread, 'spread_leg',notionals, dates_fix[:-1], dates_fix[1:], dates_fix[1:], ccy,
                                   'Act365Fixed')

#basis_swap = InterestRateBasisSwapSpecification('xx', 'dummy_issuer', 'COLLATERALIZED', ccy, dates_fix[-1], float_leg_6m, float_leg_3m, spread_leg)
basis_swap = InterestRateBasisSwapSpecification(
            obj_id="basis_3m_6m",
            notional=notionals,
            issue_date=refdate,
            maturity_date=refdate + relativedelta(years=1),
            pay_leg=float_leg_3m,
            receive_leg=float_leg_6m,
            spread_leg=spread_leg,
            currency=ccy,
            day_count_convention="Act365Fixed",
            issuer="dummy_issuer",
            securitization_level="COLLATERALIZED",
        )


[93]:
fair_bs_spread = InterestRateSwapPricer.compute_basis_spread(
            ref_date=refdate,
            discount_curve=dc,
            payLegFixingCurve=fwd_dc['3m'],
            receiveLegFixingCurve=fwd_dc['6m'],
            pay_leg= basis_swap.get_pay_leg(),
            receive_leg= basis_swap.get_receive_leg(),
            spread_leg= basis_swap.get_spread_leg(),

            pricing_params= {"fixing_grace_period": 0.0, "set_rate": True,
            "desired_rate": 1.0},
    )
[94]:
print(f"3m/6m basis swap fair spread: {fair_bs_spread}")


3m/6m basis swap fair spread: 0.002304096556289179