.. _server-basic-doc: ********************** Basic Implementation ********************** This page is designed to provide inspiration for a server-side-client-side *rateslib* integration. It will discuss: - Setting up a web-server with the globally managed objects, i.e. :class:`~rateslib.curves.Curve` and :class:`~rateslib.solver.Solver`. - Designing some very crude API endpoints for clients to extract information. - Designing some API endpoints as triggers for data management. - Spinning up a worker to schedule data updates. A Basic Web Server ********************** Personally we like ``flask``, and have used it reliably for commericial projects in the past, so this page adopts that as the core Python web server. Suppose we have a new Python environment, and we install both the ``flask`` and ``rateslib`` dependencies. .. code-block:: (venv) my-server/> pip install flask rateslib This application will be minimalist for demonstration purposes and it will have the directory structure shown below: .. code-block:: my-server/ main.py updater.py data.py usd_config.py Go ahead and create these four files in your directory (they can be empty initially), and then populate ``main.py`` with the following code, which configures the web-app and sets up the main, *index*, *route*: .. code-block:: python # main.py from flask import Flask, request app = Flask(__name__) @app.route('/') def index(): return ( 'Rateslib Server is running!

' 'Try calling /api/ytm?effective=20000615&termination=20011215&spec=de_gb&fixed_rate=1.0&settlement=20000815&price=100.34

' 'Try calling /api/usd_irs?effective=20000615&termination=20011215&frequency=A' ) Now you can start this web server by executing this script from terminal with: .. code-block:: (venv) my-server/> flask --app main run * Running on http://127.0.0.1:5000 You can now visit the local URL stated and it will return the string above to the browser. If you happen to try the additional links as suggested it will return with *404 (NOT FOUND)* because neither route has yet been constructed. **But**, at least, we are still up and running here. Static Routes ******************** The first suggested route at the homepage is a YTM calculation for a :class:`~rateslib.instruments.FixedRateBond` defined by static inputs. This does not require any data configuration or realtime market updates. So this is suitable for our next addition. We will create a single API endpoint that recovers parameters from the query string and returns the YTM. Add the following to ``main.py``, which directly adds one *route*. .. code-block:: python # main.py from datetime import datetime from flask import Flask, request from rateslib import IRS, FixedRateBond # ...previous code... @app.route('/api/ytm', methods=['GET']) def ytm(): input = request.args.to_dict() input["effective"] = datetime.strptime(input["effective"], "%Y%m%d") input["termination"] = datetime.strptime(input["termination"], "%Y%m%d") input["settlement"] = datetime.strptime(input["settlement"], "%Y%m%d") input["price"] = float(input["price"]) input["fixed_rate"] = float(input["fixed_rate"]) kwargs = dict(price=input.pop("price"), settlement=input.pop("settlement")) frb = FixedRateBond(**bar) return {"ytm": frb.ytm(**kwargs)} When the server is restarted and the first hyperlink retried the server now returns the following JSON to the browser: .. code-block:: {"ytm":0.7414146516155802} This is the first example of providing direct access to the *rateslib* API via input conversion from a URL hyperlink. Let's consider how to do this with market updates. Initial Config ******************************** Our server will build and maintain a USD SOFR curve and provide the ability to trigger updates with new market data on request. The first thing we need to do is start the server in some viable state. This is its *configuration*. Add the following code to the ``usd_config.py`` file. This code defines a USD SOFR :class:`~rateslib.curves.Curve` and creates a :class:`~rateslib.solver.Solver` object stored on the server which has the capability of mutating that *Curve*. None of this should be new if you are familiar with any of the *Curve* calibration tutorials. The last line indicates that all this file is really needed for is to define and export the ``usd_solver``. .. code-block:: python # usd_config.py from rateslib import Curve, IRS, Solver, dt usd_curve = Curve( nodes={ dt(2000, 1, 1): 1.0, dt(2001, 1, 1): 0.99, dt(2002, 1, 10): 0.98, }, convention="Act360", calendar="nyc", interpolation="spline", id="sofr" ) usd_solver = Solver( curves=[usd_curve], instruments=[ IRS(dt(2000, 1, 1), "1y", spec="usd_irs", curves=["sofr"]), IRS(dt(2000, 1, 1), "2y", spec="usd_irs", curves=["sofr"]), ], s=[3.25, 3.45], ) __all__ = ["usd_solver"] Our ``main.py`` can now be extended to utilise this configured object and cache it. Make the following addition to the top of the file. .. code-block:: python # main.py from datetime import datetime from flask import Flask, request from rateslib import IRS, FixedRateBond from usd_config import usd_solver app = Flask(__name__) GLOBAL = { "USD_SOLVER": usd_solver, } # ...previous code... OK, that's more plumbing in place but we are missing two items; - A *route* to allow a client to request a swap rate (or any other such IRS data metric) - A *route* to trigger data updates and mutate the *Curve*. Client API Request ************************* Since the :class:`~rateslib.solver.Solver` has calibrated lets first design a minimalist endpoint to get a swap rate. This is now very similar to the *ytm* endpoint created above. Add the following *route* to ``main.py`` .. code-block:: python # main.py # ...previous code... @app.route('/api/usd_irs', methods=['GET']) def usd_irs(): input = request.args.to_dict() input["effective"] = datetime.strptime(bar["effective"], "%Y%m%d") input["termination"] = datetime.strptime(bar["termination"], "%Y%m%d") irs = IRS(**bar, spec="usd_irs", curves=["sofr"]) return {"rate": float(irs.rate(solver=GLOBAL["USD_SOLVER"]))} When the server is restarted and the second hyperlink retried the server returns the following JSON to the browser: .. code-block:: {"rate":3.5179617581852303} Technically this is still a static endpoint currently becuase it depends only on the statically configured *Solver* at start-up. But that is now about to change.. Data Updates ******************************* Now we will build into the server a mechanism for data updates to be triggered. First we need an ability to get new market data. Add the following code to the empty ``data.py`` .. code-block:: python # data.py import random def update_usd_data() -> list[float]: # HERE is where you fetch data in the appropriate format from # your external data source API s = [3.25 + random.randint(0, 10) / 100, 3.35 + random.randint(0, 10) / 100] return s __all__ = ["update_usd_data"] And secondly add a *route* to the server so that when it is visted it will trigger a *Solver* recalibration. .. code-block:: python # main.py # ...previous code... from usd_config import usd_solver from data import update_usd_data # ...previous code... @app.route('/update_usd') def update_usd(): data = update_usd_data() solver = GLOBAL["USD_SOLVER"] solver.s = data solver.iterate() return GLOBAL["USD_SOLVER"].result Now if you visit the the *'/update_usd'* you will get the output from the *Solver* calibration, and then if you visit the link again for the swap rate you will observe that the rate has indeed changed - reacting to the random data update. Scheduling Updates ************************ This server is designed in such a way that at any point it only does two things; - maintain in memory, static objects, i.e. :class:`~rateslib.solver.Solver` (which contains cached :class:`~rateslib.curves.Curve` and risk jacobians) - react to external requests from clients, by providing data from those objects via a calculation, or triggering a data mutation. It is impractical to require a client-user to first trigger a data update and then make their own request. In practice it is simpler to schedule some other process or worker to trigger data updates at regular intervals. We make no comment on what form your scheduler should take. The below is just a single Python scipt that continuously loops and calls one URL request. .. code-block:: python # updater.py import time import urllib.request as ur if __name__ == "__main__": usd_update = "http://localhost:5000/update_usd" while True: time.sleep(10) result = ur.urlopen(usd_update) print(result) To run this application requires two processes initiated from terminal: .. container:: twocol .. container:: leftside50 .. code-block:: (venv) my-server/> flask --app main run * Running on http://127.0.0.1:5000 SUCCESS: `func_tol` reached after 3 iterations (levenberg_marquardt) ... 127.0.0.1 - - [01/Feb/2026 14:13:24] "GET /update_usd HTTP/1.1" 200 - SUCCESS: `func_tol` reached after 3 iterations (levenberg_marquardt) ... 127.0.0.1 - - [01/Feb/2026 14:13:34] "GET /update_usd HTTP/1.1" 200 - SUCCESS: `func_tol` reached after 3 iterations (levenberg_marquardt) ... 127.0.0.1 - - [01/Feb/2026 14:13:44] "GET /update_usd HTTP/1.1" 200 - The server is started in the usual way. After every 10 seconds it gets a trigger update. .. container:: rightside50 .. code-block:: (venv) my-server/> python updater.py When we spin up the worker, in another terminal process, it simply acts as the trigger and does not do anything with the response: although of course you can build in error processing, logging and signaling here. .. raw:: html
This is now automated, and if you continue to retry the IRS rate API endpoint it will update to new data after every 10 seconds.