# How to Create Custom Data Types for Proprietary Feeds in Nautilus Trader

> Learn to create custom data types for proprietary feeds in Nautilus Trader. Define frozen dataclasses, wrap data with CustomData, and emit via LiveMarketDataClient for strategy subscription.

- Repository: [Nautech Systems/nautilus_trader](https://github.com/nautechsystems/nautilus_trader)
- Tags: how-to-guide
- Published: 2026-02-19

---

**You can create custom data types for proprietary feeds by defining a frozen dataclass that inherits from `Data`, wrapping instances in `CustomData` with a `DataType` metadata, and emitting them through a `LiveMarketDataClient` implementation where strategies can subscribe via the standard `subscribe()` method.**

Nautilus Trader treats every market information object as a **Data** instance, but proprietary feeds often transmit non-standard fields that don't map to built-in types like `QuoteTick` or `Bar`. When you need to create custom data types for proprietary feeds, the framework provides a unified pipeline through the `CustomData` wrapper and `DataType` metadata system. This guide walks through the implementation using actual source paths from the `nautechsystems/nautilus_trader` repository.

## Define a Custom Data Model for Proprietary Feeds

Create a frozen dataclass that inherits from `nautilus_trader.model.data.Data`. This class should model the exact fields coming from your proprietary feed using serializable primitive types.

```python

# src/nautilus_trader/model/custom.py

from dataclasses import dataclass
from nautilus_trader.model.data import Data
from nautilus_trader.model.identifiers import InstrumentId

@dataclass(frozen=True, slots=True)
class MyFeedTick(Data):
    """A tick coming from a proprietary feed."""
    instrument_id: InstrumentId
    price: float
    size: float
    timestamp: int                # epoch‑ns

```

The `Data` base class is defined in [`nautilus_trader/model/data.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/nautilus_trader/model/data.py) and provides the foundation for all data objects in the system.

## Wrap Custom Data in the CustomData Container

The framework uses `CustomData` to associate a **`DataType`** (the class plus optional metadata) with your raw payload. This wrapper is required for the message bus and persistence layer to route your data correctly.

```python
from nautilus_trader.model.data import CustomData, DataType
from .custom import MyFeedTick

def make_custom_tick(tick: MyFeedTick) -> CustomData:
    data_type = DataType(MyFeedTick, metadata={"source": "my_proprietary_feed"})
    return CustomData(data_type=data_type, data=tick)

```

Both `CustomData` and `DataType` are defined in [`nautilus_trader/model/data.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/nautilus_trader/model/data.py). The `DataType` metadata dictionary can include any contextual information your strategy needs to distinguish between different proprietary sources.

## Emit Custom Data from a Live Market Data Client

Implement a `LiveMarketDataClient` that connects to your proprietary API, parses incoming messages, and publishes `CustomData` instances to the message bus.

```python

# src/nautilus_trader/adapters/my_feed/data.py

from nautilus_trader.live.data_client import LiveMarketDataClient
from nautilus_trader.model.identifiers import Venue
from ..model.custom import MyFeedTick, make_custom_tick

class MyFeedDataClient(LiveMarketDataClient):
    def __init__(self, config):
        super().__init__(config)
        self._venue = Venue("MYFEED")

    async def _connect(self) -> None:
        # Open websocket or TCP connection to your feed

        pass

    async def _handle_message(self, raw_msg: dict) -> None:
        # Parse the proprietary message format

        tick = MyFeedTick(
            instrument_id=self._parse_instrument(raw_msg["symbol"]),
            price=raw_msg["p"],
            size=raw_msg["s"],
            timestamp=raw_msg["t"],
        )
        custom = make_custom_tick(tick)
        await self._msgbus.publish(custom)   # Same path as native data

```

The base class `LiveMarketDataClient` is defined in [`nautilus_trader/live/data_client.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/nautilus_trader/live/data_client.py). For a complete template, see [`adapters/_template/data.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/adapters/_template/data.py) in the repository. The Betfair adapter demonstrates a production implementation of this pattern in [`adapters/betfair/parsing/streaming.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/adapters/betfair/parsing/streaming.py).

## Subscribe to Custom Data Types in Trading Strategies

Strategies consume custom data through the standard subscription mechanism and process it in the `on_data` callback.

```python

# src/nautilus_trader/examples/strategies/my_feed_strategy.py

from nautilus_trader.trading.strategy import Strategy
from ..model.custom import MyFeedTick

class MyFeedStrategy(Strategy):
    def __init__(self, config):
        super().__init__(config)
        self.subscribe(MyFeedTick)               # Subscribe to custom type

    def on_data(self, data):
        if isinstance(data, MyFeedTick):
            self._process_tick(data)

    def _process_tick(self, tick: MyFeedTick) -> None:
        self.logger.info(f"Received custom tick: {tick.instrument_id} {tick.price}/{tick.size}")

```

A real-world example of subscribing to custom data appears in [`examples/strategies/orderbook_imbalance.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/examples/strategies/orderbook_imbalance.py), where the strategy subscribes to Betfair-specific custom data types (lines 125-153).

## Persist Custom Data to the Catalog

The persistence layer automatically handles `CustomData` without additional configuration. When you write custom data to the `DataCatalog`, the `DataWriter` in [`nautilus_trader/persistence/writer.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/nautilus_trader/persistence/writer.py) checks for `isinstance(obj, CustomData)` and serializes it using the same Parquet-based machinery as native data types. The `DataType` metadata ensures the data can be correctly queried and reconstructed during backtests.

## Summary

- **Define** a frozen dataclass inheriting from `Data` in [`nautilus_trader/model/data.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/nautilus_trader/model/data.py) to model your proprietary feed's schema.
- **Wrap** instances in `CustomData` with a `DataType` metadata object to enable routing through the message bus.
- **Emit** custom data from a `LiveMarketDataClient` implementation by publishing to the message bus via `_msgbus.publish()`.
- **Subscribe** to your custom type in strategies using `self.subscribe(MyFeedTick)` and handle it in `on_data()`.
- **Persist** custom data automatically through the built-in `DataWriter` without additional configuration.

## Frequently Asked Questions

### How does Nautilus Trader serialize custom data types for storage?

The framework uses the `DataWriter` class in [`nautilus_trader/persistence/writer.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/nautilus_trader/persistence/writer.py) to serialize all data objects. When it encounters a `CustomData` instance (verified via `isinstance(obj, CustomData)`), it extracts the underlying data class and converts it to Parquet format using the same schema inference engine used for built-in types. The `DataType` metadata is stored alongside the payload to ensure correct deserialization during backtesting.

### Can I use custom data types in backtesting, or are they only for live trading?

Custom data types work identically in both live trading and backtesting. When you write custom data to the `DataCatalog`, the persistence layer stores it with the same timestamp-indexed partitioning as native data. During backtests, the `BacktestEngine` replays `CustomData` objects through the same `on_data` callback in your strategy, allowing you to test proprietary signals alongside standard market data.

### What performance considerations apply when creating high-frequency custom data types?

For high-frequency proprietary feeds, use `slots=True` in your dataclass definition to reduce memory overhead, and ensure all fields use primitive types (int, float, str) rather than nested objects. The `CustomData` wrapper adds minimal overhead because it simply references your data object and a lightweight `DataType` tuple. According to the implementation in [`nautilus_trader/model/data.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/nautilus_trader/model/data.py), the framework handles millions of custom data events per second when using the Cython-accelerated message bus.

### How do I distinguish between multiple proprietary feeds using the same data class?

Use the `metadata` parameter of the `DataType` constructor to tag instances with source-specific identifiers. For example, when wrapping your data class, pass `DataType(MyFeedTick, metadata={"venue": "PROP_FEED_A", "channel": "ticks"})`. Your strategy can then filter incoming data in `on_data` by checking `data.data_type.metadata`, or subscribe to specific metadata filters if your data client supports selective publication. This pattern is demonstrated in the Betfair adapter where different market data streams carry distinct metadata tags.