Rateslib

repository·main·Indexed 43 days ago

https://github.com/attack68/rateslib

A high-performance Python library for professional fixed income analysis, version 2.7.1. It provides tools for interest rate volatility pricing, automatic differentiation using dual numbers, calendar and tenor manipulations, and curve calibration and composition via the Solver and CompositeCurve classes.

Tokens
18K
Snippets
65
Records
70
Agent score
55%

What's inside Rateslib

  1. Overview of Rateslib

    main
    Rateslib is a state-of-the-art fixed income library for Python. It is designed to provide advanced, flexible, and efficient fixed income analysis through a high-level, well-documented API. The library's architecture and object interactions are modeled after the requirements of multi-disciplined fixed income teams in global investment banks.
  2. Understand IR Volatility Time to Expiry

    main

    In rateslib, Interest Rate (IR) volatility pricing objects (inheriting from _BaseIRSmile) calculate a natural measure of time to expiry ($t$) based on the difference between the instrument's expiry and the object's eval_date.

    By default, time to expiry is measured in Act365 calendar days using the formula:

    $$t = \frac{days(expiry - eval\ date)}{365}$$

    When a volatility value is yielded for a specific strike, it is assumed to be associated with this calculated time to expiry. This pair $(t, \sigma)$ is then used directly in pricing models like Black76 or Bachelier.

  3. Use weights to handle time interpolation in Cubes

    main

    Instead of applying time_scalar to individual smiles, Cubes (like IRSabrCube or IRSplineCube) use a weights argument to handle time interpolation between chosen expiries.

    By passing a pandas.Series to the weights argument, you can apportion volatility to specific dates. This is commonly used to adjust for business days vs. calendar days.

    Note: It is assumed that on every given expiry, the time scalar equals one and the Cube is calibrated to market instruments.

    from rateslib import IRSabrCube, calendars
    from pandas import Series
    from datetime import datetime as dt
    
    nyc = calendars.get("nyc")
    # Define weights for specific dates (e.g., setting non-business days to 0.0)
    weights = Series(
        index=[_ for _ in nyc.cal_date_range(dt(2001, 1, 1), dt(2001, 8, 1)) if nyc.is_non_bus_day(_)],
        data=0.0
    )
    
    irsc2 = IRSabrCube(
        eval_date=dt(2001, 1, 1),
        expiries=[dt(2001, 2, 1), dt(2001, 3, 1), dt(2001, 4, 1), dt(2001, 7, 1)],
        tenors=["1y"],
        alpha=0.35,
        beta=0.5,
        rho=-0.05,
        nu=0.45,
        irs_series="usd_irs",
        weights=weights,
    )
  4. Perform operations on CompositeCurves

    main

    Because CompositeCurve is composed of individual curves, it inherits the ability to perform transformation operations. You can call .shift(), .roll(), and .translate() directly on a CompositeCurve instance, and the operation will be applied to the composite structure.

    Supported operations:

    • .shift(n)
    • .roll(duration)
    • .translate(date)
    # Assuming composite_curve is already defined
    # Shift the entire composite structure
    shifted_comp = composite_curve.shift(50)
    
    # Roll the entire composite structure
    rolled_comp = composite_curve.roll("30d")
    
    # Translate the entire composite structure
    translated_comp = composite_curve.translate(dt(2022, 5, 1))
    
    # Get rate from the transformed composite
    rate = translated_comp.rate(dt(2022, 6, 1), "1d")
  5. Combining multiple FXRates in FXForwards

    main

    The FXForwards constructor can accept a list of FXRates objects (fx_rates=[fxr1, fxr2, ...]) to handle different settlement dates.

    Important Constraints:

    • Separable/Acyclic Systems: The system of curves and rates must be solvable. If the dependencies between rates and curves form a cycle that cannot be resolved, the system will fail.
    • Cyclic Systems: If you encounter cyclic dependencies, you may need to restructure the input by splitting the FXRates into more granular objects (e.g., instead of one object with multiple rates, use multiple FXRates objects with single rates) to ensure the dependency graph is acyclic.
    • Unsolvable Systems: If the provided curves and rates do not provide enough information to bridge the required currency pairs, the system will be unsolvable.
    # Combining multiple FXRates with different settlement dates
    fxr1 = FXRates({"eurusd": 1.05}, settlement=dt(2022, 1, 3))
    fxr2 = FXRates({"usdcad": 1.1}, settlement=dt(2022, 1, 2))
    
    fxf = FXForwards(
        fx_rates=[fxr1, fxr2],
        fx_curves={ ... }
    )
    
    # The system resolves the rate for the target date across the provided components
    rate = fxf.rate("eurcad", dt(2022, 2, 1))
  6. Inject turns into spline curves using CompositeCurve

    main

    To add specific 'turns' or local features to a smooth spline curve, use a CompositeCurve. This involves creating a turn_curve (representing the local feature) and a cubic_curve (the base spline), then combining them.

    When using CompositeCurve in a Solver, ensure you include all constituent curves in the curves list and provide parameter guesses for all of them.

    turn_curve = Curve({
        dt(2022, 12, 1): 1.0,
        dt(2022, 12, 31): 1.0,
        dt(2023, 1, 1): 1.0,
        dt(2023, 2, 1): 1.0,
    }, interpolation="log_linear")
    
    cubic_curve = Curve({
        dt(2022, 12, 1): 1.0,
        dt(2022, 12, 21): 1.0,
        dt(2023, 1, 11): 1.0,
        dt(2023, 2, 1): 1.0,
    }, t = [
        dt(2022, 12, 1), dt(2022, 12, 1), dt(2022, 12, 1), dt(2022, 12, 1),
        dt(2022, 12, 21),
        dt(2023, 1, 11),
        dt(2023, 2, 1), dt(2023, 2, 1), dt(2023, 2, 1), dt(2023, 2, 1),
    ])
    
    composite_curve = CompositeCurve([turn_curve, cubic_curve])
  7. Optimize fixings calculation performance

    main

    To improve the performance of fixings_table calls, you can use the approximate=True flag. Additionally, you can control curve caching behavior via defaults.curve_caching.

    • float_period.fixings_table(curve, approximate=True): Uses an approximation method to speed up calculation.
    • defaults.curve_caching = False: Disables the global curve caching mechanism.
    from rateslib import defaults
    
    # Disable caching
    defaults.curve_caching = False
    
    # Use approximation for speed
    float_period.fixings_table(curve, approximate=True)
  8. Retrieve and construct calendars

    main

    You can retrieve calendars using get_calendar() which fetches them from a hash table. You can also construct calendars manually using the Cal class by providing a list of holidays and a week_mask. For multiple calendars, use UnionCal to combine them. To make a specific calendar configuration easily accessible via get_calendar(), you can assign it to defaults.calendars.

    from rateslib import *
    
    # Get a calendar by code
    cal = get_calendar("ldn")
    
    # Construct a Cal directly
    cal = Cal(holidays=holidays_list, week_mask=[5,6])
    
    # Get a parsed NamedCal (e.g., combining London and Target)
    cal = get_calendar("ldn,tgt")
    
    # Combine multiple calendars into a UnionCal
    c1 = Cal(holidays=get_calendar("ldn", named=False).holidays, week_mask=[5,6])
    c2 = Cal(holidays=get_calendar("tgt", named=False).holidays, week_mask=[5,6])
    u = UnionCal([c1, c2])
    
    # Register a custom calendar to the defaults for easy retrieval
    defaults.calendars["ldn,tgt"] = get_calendar("ldn,tgt")
    cal = get_calendar("ldn,tgt")
  9. Calibrate an FX SABR Smile to market option data

    main

    You can calibrate a volatility smile (e.g., FXSabrSmile) using a Solver. The solver takes a list of pre_solvers (to handle underlying curve calibration first), the target curves (the smile), and a list of market instruments such as FXStraddle, FXRiskReversal, and FXBrokerFly.

    Common option_args for calibration include:

    • pair: The FX pair (e.g., "eurusd").
    • expiry: The option expiry date.
    • calendar: Calendar string (e.g., "tgt|fed").
    • delta_type: Type of delta used for strikes (e.g., "spot" or "forward").
    • curves: The list of curves involved in the calculation.
    • vol: The name of the volatility surface/smile to use.
    smile = FXSabrSmile(
        nodes={"alpha": 0.05, "beta": 1.0, "rho": 0.01, "nu": 0.03}, 
        eval_date=dt(2024, 5, 7),
        expiry=dt(2024, 5, 28),
        id="smile",
        pair="eurusd",
    )
    
    option_args = dict(
        pair="eurusd",
        expiry=dt(2024, 5, 28), 
        calendar="tgt|fed", 
        delta_type="spot",
        curves=[None, "eurusd", None, "usd"], 
        vol="smile",
    )
    
    solver = Solver( 
        pre_solvers=[pre_solver], 
        curves=[smile],
        instruments=[
            FXStraddle(strike="atm_delta", **option_args),
            FXRiskReversal(strike=("-25d", "25d"), **option_args),
            FXRiskReversal(strike=("-10d", "10d"), **option_args),
            FXBrokerFly(strike=(("-25d", "25d"), "atm_delta"), **option_args),
            FXBrokerFly(strike=(("-10d", "10d"), "atm_delta"), **option_args),
        ],
        s=[5.493, -0.157, -0.289, 0.071, 0.238],
        fx=fxf,
        id="smile",
    )
  10. Set up an FX Forward market and solve curves to market

    main

    To model an FX market, you must define base currency Curve objects, an FXRates object for spot rates, and an FXForwards object to combine them. You can then use the Solver class to calibrate these curves to market instruments like IRS (Interest Rate Swaps) and FXSwap.

    # Define Curves
    usd = Curve({dt(2024, 5, 7): 1.0, dt(2024, 5, 30): 1.0}, calendar="nyc", id="usd") 
    eur = Curve({dt(2024, 5, 7): 1.0, dt(2024, 5, 30): 1.0}, calendar="tgt", id="eur") 
    eurusd = Curve({dt(2024, 5, 7): 1.0, dt(2024, 5, 30): 1.0}, id="eurusd")
    
    # Create an FX Forward market
    spot = dt(2024, 5, 9)
    fxr = FXRates({"eurusd": 1.0760}, settlement=spot) 
    fxf = FXForwards(
        fx_rates=fxr, 
        fx_curves={"eureur": eur, "usdusd": usd, "eurusd": eurusd},
    )
    
    # Solve the Curves to market
    pre_solver = Solver(
        curves=[eur, eurusd, usd], 
        instruments=[
            IRS(spot, "3W", spec="eur_irs", curves="eur"),
            IRS(spot, "3W", spec="usd_irs", curves="usd"),
            FXSwap(spot, "3W", pair="eurusd", curves=[None, "eurusd", None, "usd"]),
        ],
        s=[3.90, 5.32, 8.85], 
        fx=fxf,
        id="fxf",
    )