4
$\begingroup$

I'm currently working on building a yield curve using OIS swaps. However, I'm encountering an issue with the cash flow start date that I'm struggling to understand. Here's a simplified version of my code:

Reproducible example

import QuantLib as ql

calculation_date = ql.Date().todaysDate() #When I posted this on Quant exchange the date was 25/5/2023

ql.Settings.instance().evaluationDate = calculation_date

index = ql.OvernightIndex("USD Overnight Index", 2, ql.USDCurrency(), ql.UnitedStates(ql.UnitedStates.Settlement), ql.Actual360())

swaps = {
    ql.Period("1W"): 0.05064,
    ql.Period("2W"): 0.05067,
    ql.Period("3W"): 0.05072,
    ql.Period("1M"): 0.051021000000000004,
    ql.Period("2M"): 0.051391,
    ql.Period("3M"): 0.051745,
    ql.Period("4M"): 0.05194,
    ql.Period("5M"): 0.051980000000000005,
    ql.Period("6M"): 0.051820000000000005,
    ql.Period("7M"): 0.051584000000000005,
    ql.Period("8M"): 0.05131,
    ql.Period("9M"): 0.050924,
    ql.Period("10M"): 0.050603999999999996,
    ql.Period("11M"): 0.050121,
    ql.Period("12M"): 0.049550000000000004,
    ql.Period("18M"): 0.04558500000000001,
    ql.Period("2Y"): 0.042630999999999995,
    ql.Period("3Y"): 0.038952,
    ql.Period("4Y"): 0.036976,
    ql.Period("5Y"): 0.035919,
    ql.Period("6Y"): 0.03535,
    ql.Period("7Y"): 0.034998,
    ql.Period("8Y"): 0.034808,
    ql.Period("9Y"): 0.034738000000000005,
    ql.Period("10Y"): 0.034712,
    ql.Period("12Y"): 0.034801,
    ql.Period("15Y"): 0.034923,
    ql.Period("20Y"): 0.034662,
    ql.Period("25Y"): 0.03375,
    ql.Period("30Y"): 0.032826,
    ql.Period("40Y"): 0.030834999999999998,
    ql.Period("50Y"): 0.02896
}


rate_helpers = []

for tenor, rate in swaps.items():
    helper = ql.OISRateHelper(2, tenor, ql.QuoteHandle(ql.SimpleQuote(rate)), index)
    rate_helpers.append(helper)

yts = ql.RelinkableYieldTermStructureHandle()
curve = ql.PiecewiseSplineCubicDiscount(calculation_date, rate_helpers, ql.Actual360())
yts.linkTo(curve)

index = index.clone(yts)

engine = ql.DiscountingSwapEngine(yts)

print("maturity |  market  |   model")
for tenor, rate in swaps.items():
    schedule = ql.Schedule(calculation_date, 
                        calculation_date + tenor, 
                        ql.Period('1D'), 
                        ql.UnitedStates(ql.UnitedStates.GovernmentBond), 
                        ql.ModifiedFollowing, 
                        ql.ModifiedFollowing, 
                        ql.DateGeneration.Forward, 
                        False)
    swap = ql.OvernightIndexedSwap(ql.OvernightIndexedSwap.Payer, 
                                1.0, 
                                schedule, 
                                0.01, 
                                ql.Actual360(), 
                                index)

    swap.setPricingEngine(engine)

    print(f"   {tenor}    | {rate:.6f} | {swap.fairRate():.6f}")

The issue I'm facing is that the cash flow start date for the swaps is appearing two days before the evaluation date (May 23rd, 2023 and Evaluation date May 25rd, 2023), which doesn't align with my expectations. I have set the evaluation date as the current date in my code, so the cash flow start date should be either on the evaluation date or after it.

  File "AAA", line 173, in build_curve_v2
    print(f"   {tenor}    | {rate:.6f} | {swap.fairRate():.6f}")
                                          ^^^^^^^^^^^^^^^
  File "AAA", line 12, in <module>
    bootstrap.Curve_Build()
RuntimeError: 2nd leg: Missing USD Overnight IndexSN Actual/360 fixing for May 23rd, 2023

I suspect there might be something wrong with my bootstrapping method or the way I'm constructing the yield curve. Any guidance or suggestions on how to resolve this issue would be greatly appreciated.


A Possible explanation : When an OvernightIndexedSwap is created, the floating leg's schedule is generated with its first date being T+2 days before the start of the swap, where T is the start of the swap. In my case, if the swap starts on the evaluation date of May 25th, 2023, the floating leg's schedule will begin on May 23rd, 2023. As a result, a fixing for the overnight rate on May 23rd, 2023, is required.

$\endgroup$
1
  • $\begingroup$ Where did you get these swap rates from? $\endgroup$
    – SuavestArt
    May 26, 2023 at 19:40

1 Answer 1

4
$\begingroup$

There are a few consistency problems.

One is that you're passing 2 fixing days to the ql.OvernightIndex constructor. This way the schedule starts correctly on the calculation date, but the index will look for fixings two days before that. I'd use 0 days instead.

Another is that, when you're creating the helpers, you're passing 2 settlement days. This means that their schedule will start 2 days after the calculation date. For consistency, since you want to reproduce these rates, you need 0 here as well (or you might keep the 2, but in this case the swap schedules will need to start two business days after the calculation date too.)

Finally, when you create the swaps at the end, you're passing a tenor of 1D to the schedule. That would mean payments each day. Instead, the rate is compounded each day but the payments are annual so you need to pass 1Y instead.

With these changes, I'm getting the same rates.

$\endgroup$
3
  • $\begingroup$ I've put a reproducible example, please let me know if you now can run the code. Thank you very much ! $\endgroup$
    – TourEiffel
    May 25, 2023 at 10:24
  • 1
    $\begingroup$ I do. I updated the answer. $\endgroup$ May 25, 2023 at 10:46
  • $\begingroup$ Thank you so much, you are amazing ! $\endgroup$
    – TourEiffel
    May 25, 2023 at 11:58

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.