4
$\begingroup$

I have bootstrapped a curve using several depo and swap rates and am trying to use that curve to get the NPV of a swap over a period of time. The generation of prices iteratively through time is incredibly slow. Given that i'm only pricing over a 4 month period I wouldn't expect it to take 30 minutes. Am I doing something silly here? I saw a previous post commenting on a bug in the SWIG wrapper from python to c++, but according to one of the project maintainers it was patched years ago. See the below:

test_curve.referenceDate() == Date(4,1,2023)

Engine creation

ts = ql.RelinkableYieldTermStructureHandle()
yts.linkTo(test_curve)
engine = ql.DiscountingSwapEngine(yts)

Swap Definition and Creation

swapTenor = ql.Period('1Y')
overnightIndex = ql.Sofr(yts)
fixedRate = 0.01
ois_swap = ql.MakeOIS(swapTenor, overnightIndex, fixedRate, pricingEngine=engine, discountingTermStructure=yts)

NPV Generation

    new_prices = []

instance = ql.Settings.instance()

start_date = ql.Date(1,1,2024)
success_counter = 0

while date < start_date:
    # Update eval date in sim
    instance.evaluationDate = date
    price = ois_swap.NPV()
    new_prices.append(price)

    # Increment date forward
    date += ql.Period('1D')
    new_curve = test_model.get_curve_by_date(date.to_date().strftime('%Y-%m-%d'))

    count = 0

    # Check for new_curve to exist
    while new_curve is None:
        date += ql.Period('1D')
        new_curve = test_model.get_curve_by_date(date.to_date().strftime('%Y-%m-%d'))

        count += 1
        if count == 100:
            break
    yts.linkTo(new_curve)
    engine = ql.DiscountingSwapEngine(yts)
    overnightIndex = ql.Sofr(yts)
    ois_swap = ql.MakeOIS(swapTenor, overnightIndex, fixedRate, pricingEngine=engine, discountingTermStructure=yts, effectiveDate=ql.Date(2,1,2024))

The maturity date on the swap is set to May 2024. Thanks!

$\endgroup$
1
  • $\begingroup$ It's worth noting that I made a separate attempt utilizing a unique curve for each day and created a new swap instrument to generate the price from make_ois. In some isolation testing, it does indeed appear to be the evaluationDate update that is extremely slow. $\endgroup$
    – StormsEdge
    May 10, 2023 at 21:13

1 Answer 1

3
$\begingroup$

After reading the documentation more closely and some scenario testing, I was able to determine that ql.Settings.instance().evaluationDate = date was the culprit. It seems that updating the evaluationdate causes a refresh of ALL instantiated objects within QuantLib that are related to that evaluationDate. I had instantiated a dataframe within my test_model class and pre-built all of the bootstrapped curves I was intending to use, which resulted in the creation of many swap and depo helper objects, all of which would be updated on the evaluationDate change.

For added color: Python destroys objects when their reference counter reaches 0. So, I simply made all of these objects transient within event loop so that I am incrementing, at most, 1 curve and 1 swap object at each step forward. The solution is not lightning fast, since I am bootstrapping a curve at each step, but it's working.

while date < effectiveDate:
    instance.evaluationDate = date

    curve = bootstrap_model.get_curve_by_date(date.to_date().strftime('%Y-%m-%d'), depo=True, swaps=True)

    if curve is None:
        date += ql.Period('1D')
    else:
        yts = ql.RelinkableYieldTermStructureHandle()
        yts.linkTo(curve)
        engine = ql.DiscountingSwapEngine(yts)

        overnightIndex = ql.Sofr(yts)

        ois_swap = ql.MakeOIS(swapTenor, overnightIndex, fixedRate, pricingEngine=engine, discountingTermStructure=yts, effectiveDate=effectiveDate)
        swap_price = ois_swap.NPV()
        new_prices.append(swap_price)
$\endgroup$

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.