Fixings#

Published financial fixings form a key part of the valuation of Instruments and for those used in Curve or Surface calibration. Rateslib cannot be distributed with financial data, therefore data management must be handled externally by the user.

But, some easy-to-use classes exist to provide the bridge between external data and the objects constructed in rateslib. The global fixings object coordinates this interaction and is a singleton-instance of the Fixings class.

The key attribute of this class is its loader which is, by default, an instance of the DefaultFixingsLoader, but a user can replace this with their own implementation to interact with their own data sources, if required.

DefaultFixingsLoader#

The DefaultFixingsLoader provides a way for a user to load fixings directly into rateslib. Data can be populated in one of two ways. Either,

  1. Manually, with Python objects, using the add() method. This is often used for simple examples.

  2. From a datafile stored as a CSV. This must have a particular template format.

Let’s demonstrate the CSV method. Create a CSV in the required date-string format in the current working directory.

In [1]: df = DataFrame(
   ...:     data=[1.21, 2.75],
   ...:     index=Index(["02-01-2023", "03-01-2023"], name="reference_date"),
   ...:     columns=["rate"]
   ...: )
   ...: 

In [2]: print(df)
                rate
reference_date      
02-01-2023      1.21
03-01-2023      2.75

In [3]: df.to_csv("SEK_IBOR_3M.csv")  #  <--- Save a CSV file to disk

Now we set the directory of the DefaultFixingsLoader to point to this folder and load the fixings directly.

In [4]: fixings.loader.directory = os.getcwd()

In [5]: fixings["SEK_IBOR_3M"]
Out[5]: 
(-2254238477608731968,
 reference_date
 2023-01-02   1.21
 2023-01-03   2.75
 Name: rate, dtype: float64,
 (Timestamp('2023-01-02 00:00:00'), Timestamp('2023-01-03 00:00:00')))

Note this __getitem__ mechanism loads; a data state id, the timeseries itself, and the range of the index. When an attempt is made for an unavailable dataset a ValueError is raised.

In [6]: try:
   ...:     fixings["unavailable_data"]
   ...: except ValueError as e:
   ...:     print(e)
   ...: 
Fixing data for the index 'unavailable_data' has been attempted, but there is no file:
'unavailable_data.csv' located in the search directory.
For further info see the documentation section regarding `Fixings`.

It is also possible to add a dataseries created in Python directly to the fixings object. If the state is not expressly given then a random state is set upon this insertion.

In [7]: ts = Series(index=[dt(2000, 1, 1), dt(2000, 2, 1)], data=[666., 667.])

In [8]: fixings.add("my_series", ts)

In [9]: fixings["MY_SERIES"]
Out[9]: 
(-8929434576318580492,
 reference_date
 2000-01-01   666.00
 2000-02-01   667.00
 Name: rate, dtype: float64,
 (Timestamp('2000-01-01 00:00:00'), Timestamp('2000-02-01 00:00:00')))

Any data, regardless of the loading method, can be directly removed:

In [10]: fixings.pop("My_Series")
Out[10]: 
reference_date
2000-01-01   666.00
2000-02-01   667.00
Name: rate, dtype: float64

Lowercase and uppercase are ignored.

Relevant dates and objects#

To date, rateslib makes use of 3 classifications of fixing;

The use case for an IndexFixing is for inflation related Instruments.

The relevant date for an IndexFixing is its reference value date. Determining the value of an IndexFixing depends upon other information such as the index_lag, and index_method.

Important

Inflation data, and other monthly data, must be indexed in the Series according to the start of the month relating to the data publication.

As an example the following data in 2025 for the UK RPI series was released:

Publication

Month

Value

18th June

May

402.9

16th July

June

404.5

20th Aug

July

406.2

This must be entered into rateslib as the following dates:

In [11]: uk_rpi = Series(
   ....:     index=[dt(2025, 5, 1), dt(2025, 6, 1), dt(2025, 7, 1)],
   ....:     data=[402.9, 404.5, 406.2]
   ....: )
   ....: 

In [12]: fixings.add("UK_RPI", uk_rpi)

This data is then sufficient to populate some IndexFixing values. The below fixing has a reference value date of 12-Sep, with a 3-month lag and therefore is linearly interpolated between the Jun and July values and should be approximately 405.

In [13]: index_fixing = IndexFixing(
   ....:     index_lag=3,
   ....:     index_method="daily",
   ....:     date=dt(2025, 9, 12),
   ....:     identifier="UK_RPI"
   ....: )
   ....: 

In [14]: index_fixing.value
Out[14]: np.float64(405.12333333333333)

Custom Data Loading#

The _BaseFixingsLoader is the necessary subclass with the required abstract base classes to design a customised user data loader.

However, the easiest way to do this is to inherit the methods of the DefaultFixingsLoader and simply overload them.

Suppose you have an SQL database with fixings data and you want to fetch timeseries from there:

# this represents the SQL database
In [41]: MY_DATABASE = {
   ....:     "SELECT S1 FROM DB": Series(index=[dt(2000, 1, 1)], data=[100.0]),
   ....:     "SELECT S2 FROM DB": Series(index=[dt(2000, 1, 1)], data=[200.0]),
   ....: }
   ....: 

Then you can ignore the CSV file loading system (i.e. have a folder with no CSV files in it) and implement your own class. Make sure to set it as the loader.

In [42]: from rateslib.data.loader import DefaultFixingsLoader

In [43]: class MyFixingsLoader(DefaultFixingsLoader):
   ....:     def __getitem__(self, query: str):
   ....:         try:
   ....:             return super().__getitem__(query)  # <- let DFL handle data caching
   ....:         except ValueError:
   ....:             if query not in MY_DATABASE:
   ....:                 raise ValueError(f"'{query}' is not in MY_DATABASE")
   ....:             result = MY_DATABASE[query]
   ....:             data = (hash(os.urandom(8)), result, (result.index[0], result.index[-1]))
   ....:             self.loaded[query.upper()] = data
   ....:             return data
   ....: 

In [44]: fixings.loader = MyFixingsLoader()

In [45]: fixings["SELECT S1 FROM DB"]
Out[45]: 
(-4819701883534548653,
 2000-01-01   100.00
 dtype: float64,
 (Timestamp('2000-01-01 00:00:00'), Timestamp('2000-01-01 00:00:00')))

This loaded state will persist until it is cleared and reloaded.

In [46]: fixings.loader._loaded = {}

In [47]: fixings["SELECT S1 FROM DB"]
Out[47]: 
(-794224826908797044,
 2000-01-01   100.00
 dtype: float64,
 (Timestamp('2000-01-01 00:00:00'), Timestamp('2000-01-01 00:00:00')))