Due to the modular nature of the Nautilus design, it is possible to set up systems with very flexible data streams, including custom user defined data types. This guide covers some possible use cases for this functionality.

Custom/Generic Data

It’s possible to create custom data types within the Nautilus system. First you will need to define your data by subclassing from Data .

from import Data

class MyDataPoint(Data):

    def __init__(
        label: str,
        x: int,
        y: int,
        z: int,
        ts_event: int,
        ts_init: int,
    ) -> None:
        super().__init__(ts_event, ts_init)

        self.label = label
        self.x = x
        self.y = y
        self.z = z

As you can see, this requires you to call the Data base classes __init__ method, and passing in the UNIX nanosecond timestamps for the event, and object initialization.


Passing these timestamps is what allows Nautilus to correctly order data streams by monotonically increasing ts_init timestamps for backtests.

We can now work with this data type for backtesting and live trading. For instance, we could now create an adapter which is able to parse and create objects of this type - and send them back to the DataEngine for consumption by subscribers.

You can subscribe to these custom data types within your actor/strategy in the following way:

    data_type=DataType(MyDataPoint, metadata={"some_optional_category": 1}),

This will result in your actor/strategy passing these received MyDataPoint objects to your on_data method. You will need to check the type, as this method acts as a flexible handler for all custom/generic data.

def on_data(self, data: Data) -> None:
    # First check the type of data
    if isinstance(data, MyDataPoint):
        # Do something with the data