Monday, February 3, 2025

Plotting Yield Curves Using QuantLib

  


M 917 536 3378

maksim_kozyarchuk@yahoo.com








     

Introduction

Visualizing the yield curve structure is a fundamental component of any OTC valuation platform. A well-designed yield curve plotting tool allows analysts and risk managers to assess market conditions, track yield curve evolution over time, and analyze swap valuation and risk metrics like DV01. By plotting key yield curve metrics, practitioners can gain deeper insights into rate movements and pricing behavior.

Key values to plot and their use cases include:

  • Fair Rate – Provides insights into market conditions and OIS swap pricing.

  • Forward Rate – Helps assess interest rate momentum and pricing of forward-starting products.

  • Zero Rate & Discount Factor – Essential for valuing interest rate swaps and computing risk metrics like DV01.

Beyond analyzing these metrics in isolation, visualizing them together helps one better understand relationships between different rates. Tracking the evolution of yield curves across multiple dates also allows for historical analysis and trend identification. The ability to export plotting data as time series enables further quantitative analysis and backtesting.

In the following sections, I will explore the design and implementation challenges of building a yield curve visualization solution using QuantLib, with a specific focus on its integration within the Kupala-Nich platform




Key features of Yield Curve Plotting Tool in Kupala-Nich

The following requirements and practical considerations were addressed when developing the yield curve plotting tool.


  1. Optimizing Data Granularity – There is a tradeoff between performance and granularity when selecting the number of data points for plotting the curve. In Kupala-Nich, I found that using 100 points distributed according to the following function provides a good balance for a 30-year plot, yielding higher density in the short end of the curve, where rates change more frequently, while keeping long-end computations efficient.

 [(i / (num_points - 1)) ** 3 for i in range(num_points)]

  1. Anchoring Curves to a Start Date–  To enable accurate comparisons of curves across different dates, all curve points are expressed in relative days from the start date. This ensures that multiple yield curves can be overlaid without misaligning relative maturities, preserving benchmark consistency

  2. Overlaying Multiple Curves –The tool supports plotting multiple curves on the same chart while maintaining clarity through the following features:

  • Dynamic Y-Axis Scaling – The Y-axis adjusts automatically based on the selected series.

  • Customizable Curve Management – Users can add a new curve to an existing plot or clear the plot and start from scratch.

  • Custom Curve Naming – Each curve can be labeled with a user-defined name.

  • Distinct Color Coding – Each curve is assigned a unique color for better visualizatio

  • Time Series Data Extraction – To facilitate further quantitative analysis, users can export plotted yield curve data as a time series. This allows for historical comparisons, risk analysis, and backtesting strategies



Using QuantLib to Plot Yield Curves: Performance Considerations

Below code snippet demonstrates a high level algorithm for building plot data for yield curves using QuantLib.  Beyond ensuring the correctness of the data, in this section, I’ll discuss some performance considerations when using QuantLib for plotting yield curves.



        for end_date,tenor_str in self.get_date_tenors(points):

            point = { "end_date": end_date.ISO(), "tenor": tenor_str }

            if to_calculate in ('all', 'zero_rate'):

                point["zero_rate"] = self.get_zero_rate(...)

            if to_calculate in ('all', 'forward_rate'):

                point["forward_rate"] = self.get_forward_rate(...)

            if to_calculate in ('all', 'discount'):

                point["discount"] = self.curve.discount(end_date)

            if to_calculate in ('all', 'pv_1pct', 'fair_rate'):

                swap = self.create_swap( tenor_str, end_date )

                if to_calculate in ('all', 'pv_1pct'):

                    point["pv_1pct"] =  swap.NPV()

                if to_calculate in ('all', 'fair_rate'):

                    point["fair_rate"] = swap.fairRate()

            res.append(point)


QuantLib Consideration

From profiling the code above, several key performance insights emerged:

  • Some rates are fast: Computing Zero Rates, Forward Rates, and Discount Factors is relatively efficient. These calculations take approximately 100ms for 100 points, making them negligible compared to performance overhead of Fair Rate and PV calculations.  Construction of on fly OIS swaps needed for Fair Rate is the most expensive part of the above loop.

  • MakeOIS vs. OvernightIndexedSwap:   QuantLib provides two helper functions for OIS swaps:

  • Other considerations with respect to performance of OIS Swap creation.

    • Construction time of the OIS swap is directly proportional to the tenor of a swap.  Constructing 100 1 year swaps takes ~70ms while constructing 100 30 year swaps takes 2.1 seconds.  Hence reducing the resolution of the curve towards the end improves performance.

    • Larger rolling periods are slow, typical OIS swaps have a rolling period of 1 year, which is quite expensive, reducing that to 3 or even 1 month significantly improves calculation speed.

    • Calendar operations in QuantLib can be slow, especially when tenors are expressed in days. For example, performing calendar.advance() 1000 times for 30y tenor takes 30 ms, while doing equivalent in days terms( 11000d ) takes 13 seconds.

    • QuantLib calendars are programmatic and might not reflect unexpected market closures (e.g., emergency bank holidays).


Conclusion

I encourage you to try out the yield curve plotting functionality on Kupala-Nich.com and share your feedback and suggestions. Your insights will help refine and enhance the tool to better serve the financial and quantitative analysis community.









No comments: