1

I built a zero-coupon curve out of a generic par swap rate curve (Step 1) and I am trying to recover the swap curve back from the zero-coupon curve (Step 2).

Step 1 works but not Step 2. I get close quotes but they do not exactly match. Anyone has any idea what's wrong in my Step 2?

My guess is that it does not come from calendar issues, as I am using theoretical calendar, daycounter, and index, with no adjustment.

Here's my code:

STEP 1:

# define constants
face_amount = 100
settlementDays = 0
calendar = ql.NullCalendar()
fixedLegAdjustment = ql.Unadjusted
floatingLegAdjustment = ql.Unadjusted
fixedLegDayCounter = ql.SimpleDayCounter()
floatingLegDayCounter = ql.SimpleDayCounter()
fixedLegFrequency = ql.Semiannual
end_of_month = False
floating_rate = ql.IborIndex("MyIndex", ql.Period(3, ql.Months), settlementDays, ql.USDCurrency(), calendar, floatingLegAdjustment, end_of_month, floatingLegDayCounter)

# irs is a DataFrame with one line and the column as maturities (from 3M to 120M)
deposits = [irs.columns[0]]
swaps = irs.columns[1:]

# curve dates
zero_rates = {}
curve_date = ql.DateParser.parseFormatted(str("2017-01-01"), "%Y-%m-%d")
ql.Settings.instance().evaluationDate = curve_date
spot_date = calendar.advance(curve_date, settlementDays, ql.Days)

# deposit helper
deposit_helpers_mat = []
for tenor in deposits:
    deposit_helpers_mat.append([ql.Period(int(tenor), ql.Months), ql.QuoteHandle(ql.SimpleQuote(irs[int(tenor)] / 100))])

deposit_helper = [ql.DepositRateHelper(tenors_deposit, settlementDays, calendar, fixedLegAdjustment, end_of_month, fixedLegDayCounter) for tenors_deposit, deposit_rates in deposit_helpers_mat]

# swap helper
swap_helpers_mat = []
for tenor in swaps:
    swap_helpers_mat.append([ql.Period(int(tenor), ql.Months), ql.QuoteHandle(ql.SimpleQuote(irs[int(tenor)] / 100))])

swap_helper = [ql.SwapRateHelper(swap_rates, tenors_swap, calendar, fixedLegFrequency, fixedLegAdjustment, fixedLegDayCounter, floating_rate) for tenors_swap, swap_rates in swap_helpers_mat]

# aggregate helpers
helper = deposit_helper + swap_helper

# build curve
zc_curve = ql.PiecewiseCubicZero(curve_date, helper, ql.SimpleDayCounter())
zero_rate = []
tenors = []
# loop over maturities
for tenor in np.arange(3, 120 + 1, 3):
    maturity_date = calendar.advance(spot_date, ql.Period(int(tenor), ql.Months))
    zero_rate_curve = (zc_curve.zeroRate(maturity_date, ql.SimpleDayCounter(), ql.Compounded, ql.Annual).rate()* 100)
    zero_rate.append(zero_rate_curve)
    tenors.append(tenor)

# build the zero curve representation into a DataFrame
zero_rates = pd.DataFrame(np.transpose(list(zip(zero_rate))), columns=list(tenors))

STEP 2:

# constant
fixedRate = 0.02
spread =0
TENORS = np.arange(3, 120 + 1, 3)

# pre-allocate
irs_rates = {}
# calculate dates
curve_date = ql.DateParser.parseFormatted(str("2017-01-01"), "%Y-%m-%d")
ql.Settings.instance().evaluationDate = curve_date
spot_date = calendar.advance(curve_date, settlementDays, ql.Days)

# zero curve
irs_rate = []
tenors = []
maturity_dates = []
zc_rates = []
# loop over maturities
for tenor in TENORS:
    # maturity date
    maturity_date = calendar.advance(spot_date, ql.Period(int(tenor), ql.Months))
    # gather maturity dates
    maturity_dates.append(maturity_date)
    # gather zc rates
    zc_rates.append(zero_rates[int(tenor)] / 100)


# build zero coupon curve object
zero_curve = ql.YieldTermStructureHandle(ql.CubicZeroCurve(maturity_dates, zc_rates, fixedLegDayCounter, calendar))
# libor curve
libor_curve = ql.YieldTermStructureHandle(ql.CubicZeroCurve(maturity_dates, zc_rates, floatingLegDayCounter, calendar))
# floating rate
floating_rate = ql.IborIndex("MyIndex", ql.Period(3, ql.Months), settlementDays, ql.USDCurrency(), calendar, floatingLegAdjustment, end_of_month, floatingLegDayCounter, libor_curve)

# build swap curve
# loop over maturities
j = 0
for maturity in maturity_dates:
    # fixed leg tenor
    fixedLegTenor = ql.Period(3, ql.Months)
    # fixed leg coupon schedule
    fixedLegSchedule = ql.Schedule(spot_date, maturity, fixedLegTenor, calendar, fixedLegAdjustment, fixedLegAdjustment, ql.DateGeneration.Forward, end_of_month)

    # floating leg tenor
    floatingLegTenor = ql.Period(3, ql.Months)
    # floating leg coupon schedule
    floatingLegSchedule = ql.Schedule(spot_date, maturity, floatingLegTenor, calendar, floatingLegAdjustment, floatingLegAdjustment, ql.DateGeneration.Forward, end_of_month)

    # build swap pricer
    swap_rate = ql.VanillaSwap(ql.VanillaSwap.Payer, face_amount, fixedLegSchedule, fixedRate, fixedLegDayCounter, floatingLegSchedule, floating_rate, spread, floatingLegDayCounter)

    # build swap curve
    swap_curve = ql.DiscountingSwapEngine(zero_curve)
    # get swap rate
    swap_rate.setPricingEngine(swap_curve)

    # gather par irs rate
    irs_rate.append(swap_rate.fairRate() * 100)
    # gather irs tenor
    tenor = int(TENORS[j])
    j = j + 1
    tenors.append(tenor)
    # build the swap curve representation into a DataFrame
    irs_rates = pd.DataFrame(np.transpose(list(zip(irs_rate))), columns=list(tenors))
Jessica F.
  • 13
  • 1
  • 5
  • EDIT for STEP 2: Is it fine to feed the pricer with the same curve (i.e same zero-coupon rates and maturities), both for the zero_curve and the floating_rate? – Jessica F. Jun 02 '20 at 15:23

0 Answers0