Slow and Fast Approach#

This page enhances the basic server application, and therefore assumes familiarity with the code and architecture presented there. This page will:

  • outline the mathematics we will implement.

  • make minimal modifications to the code to integrate a fast update.

Chapter 19 of Pricing and Trading Interest Rate Derivatives discusses and outlines the use of ‘slow and fast’ methods for Curve building in market-making.

The Fast Update#

The basic server application is the slow update. It calibrates a Curve with market data through multiple iterations, and it does so every 10 seconds.

When calculating the rate, $r$, of an IRS from a Curve calibrated by a Solver, it is measured as \(r(\mathbf{v})\). The Curve parameters \(\mathbf{v}\) are determined as of the market data set that was used to calibrate the Curve, call it, \(\mathbf{s}\). This, however, may be old and potentially out of date (we are talking in terms of seconds).

Therefore we seek an adjustment to \(r(\mathbf{v})\) which accounts for market data movement since that time to the streaming, real-time data, which we call \(\mathbf{\hat{s}}\). A linear approximation is assumed. I.e. we seek:

\[\hat{r}(\mathbf{v}) = r(\mathbf{v}) + \nabla_{\mathbf{s}} r(\mathbf{v}) \cdot \Delta \mathbf{s}, \qquad \text{where} \quad \Delta \mathbf{s} = \mathbf{\hat{s}} - \mathbf{s}\]

Using the chain rule we have that,

\[\hat{r}(\mathbf{v}) = r(\mathbf{v}) + \nabla_{\mathbf{s}} \mathbf{v^T} \nabla_{\mathbf{v}} r(\mathbf{v}) \cdot (\mathbf{\hat{s}} - \mathbf{s})\]

It just so happens that the Solver generates all of these quantities directly:

  • \(\nabla_{\mathbf{s}} \mathbf{v^T}\) is available as solver.grad_s_vT

  • \(\nabla_{\mathbf{v}} r(\mathbf{v})\) is available as gradient(rate, solver.variables)

So all that the new code needs to capture is the difference between the live market data and that used by the Solver in its calibration and perform the linear algebra.

Code Changes#

The code changes to the main.py are only to the client API USD swap rate request.

# main.py
from rateslib import IRS, FixedRateBond, gradient
import numpy as np

# ...previous code...

@app.route('/api/usd_irs', methods=['GET'])
def usd_irs():
    bar = request.args.to_dict()
    bar["effective"] = datetime.strptime(bar["effective"], "%Y%m%d")
    bar["termination"] = datetime.strptime(bar["termination"], "%Y%m%d")
    irs = IRS(**bar, spec="usd_irs", curves=["sofr"])
    rate = irs.rate(solver=GLOBAL["USD_SOLVER"])
    dr_dv = gradient(rate, GLOBAL["USD_SOLVER"].variables)
    ds = np.array(update_usd_data()) - GLOBAL["USD_SOLVER"].s
    rate_hat = rate + np.inner(np.matmul(GLOBAL["USD_SOLVER"].grad_s_vT, dr_dv[:, None])[:, 0], ds)
    return {
        "solver_rate": float(rate),
        "rate": float(rate_hat),
    }

Now when the server and the scheduler are both restarted and we visit the API endpoint: /api/usd_irs?effective=20000615&termination=20011215&frequency=A we now get a JSON return that gives the previous rate, as calculated directly from the Solver, and a rate that has the linear adjustment made to account for updated market data. In this example that rate will change every request since the market data has random updates.

# http://127.0.0.1:5000/api/usd_irs?effective=20000615&termination=20011215&frequency=A
{
  "rate": 3.340708058569,
  "solver_rate": 3.36401626295668
}

And that’s it. A framework that calculates and returns a mid-market swap rate in micro seconds like a large investment bank.