# How to Use Actors and Signals for Messaging Between Components in NautilusTrader

> Learn how to send messages between components in NautilusTrader using actors and signals. Publish and receive signals asynchronously with the MessageBus for decoupled communication.

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

---

**NautilusTrader enables decoupled communication between trading components through an actor-based messaging system where actors publish lightweight signals via `publish_signal()` and receive them asynchronously through `on_signal()` callbacks routed by the central MessageBus.**

NautilusTrader uses actors and signals for messaging between components to eliminate tight coupling between strategies, execution algorithms, and custom modules. This architecture allows any component inheriting from the `Actor` base class to emit typed notifications that other registered actors consume through the `MessageBus`. Understanding this publish-subscribe pattern is essential for building modular, maintainable trading systems that function identically in backtesting and live trading environments.

## Core Architecture of Actor Messaging

The messaging system relies on five key components working together to route signals between actors:

| Component | Role | Key Implementation |
|-----------|------|-------------------|
| **Actor** | Base class for messaging-enabled components; handles registration, topic caching, and signal publishing/subscribing | `nautilus_trader/common/actor.pyx` (lines 25-140) |
| **Signal Generation** | Dynamically creates concrete `Data` subclasses for signal names and value types, handling Arrow and message-bus serialization | [`nautilus_trader/common/signal.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/nautilus_trader/common/signal.py) (`generate_signal_class`) |
| **MessageBus** | Central hub storing topic-handler mappings and forwarding published messages to subscribers | `nautilus_trader/common/component.pyx` (lines 140-210) |
| **ActorExecutor** | Queues and runs async actor callbacks on the event loop with cancellation support via `TaskId` | [`nautilus_trader/common/executor.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/nautilus_trader/common/executor.py) (lines 60-115) |
| **Trader** | Orchestrates actor registration, creates the `MessageBus`, and starts the executor | [`nautilus_trader/trading/trader.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/nautilus_trader/trading/trader.py) (lines 33-45) |

### The Actor Base Class

The `Actor` class in `nautilus_trader/common/actor.pyx` provides the foundation for all messaging components. It maintains internal topic caches and exposes methods for publishing and subscribing to signals. Strategies inherit from this class automatically, but custom components like risk managers or execution analyzers can also subclass `Actor` to participate in the messaging ecosystem.

### Dynamic Signal Generation

Signals are not predefined classes. Instead, [`nautilus_trader/common/signal.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/nautilus_trader/common/signal.py) provides `generate_signal_class()`, which creates a concrete `Data` subclass on demand for a given signal name and value type (`int`, `float`, or `str`). This dynamic approach ensures type safety while keeping the system lightweight, as each signal carries only a single primitive value plus timestamps.

## Publishing Signals Between Components

Actors emit signals using the `publish_signal()` method, implemented in `nautilus_trader/common/actor.pyx` (lines 13-48). This method validates inputs, generates the appropriate signal class, timestamps the data, and forwards it to the `MessageBus`.

```python
self.publish_signal(name="MySignal", value=42, ts_event=timestamp_ns)

```

The implementation flow (lines 35-47 of `actor.pyx`) follows these steps:

1. **Validation** → Checks name and value type (lines 35-40)
2. **Class Generation** → Calls `generate_signal_class` to create `SignalMySignal` (lines 35-40)
3. **Timestamping** → Uses `self.clock.timestamp_ns()` if not provided (lines 41-46)
4. **Publication** → Forwards to `self.publish_data()` which routes to the `MessageBus` (line 47)

## Subscribing to Actor Signals

To receive signals, actors must explicitly subscribe to specific signal names or to all signals globally. The `subscribe_signal()` method in `nautilus_trader/common/actor.pyx` (lines 65-71) registers the actor's handler with the `MessageBus`.

```python

# Subscribe to a specific signal

self.subscribe_signal(name="MySignal")

# Subscribe to all signals (wildcard)

self.subscribe_signal()

```

When subscribing to a specific name, the `MessageBus` maps the topic `signal:MySignal` to the actor's `handle_signal` method. A wildcard subscription maps to `signal:*`, capturing all signal publications.

## Handling Incoming Signals

When a matching signal arrives, the `MessageBus` invokes `handle_signal()` on the subscribing actor. This method, located at line 4580 of `nautilus_trader/common/actor.pyx`, performs two critical checks before processing:

1. **State Validation** → Verifies the actor is in the `RUNNING` state
2. **Callback Dispatch** → Invokes the actor's `on_signal()` method, which developers override to implement custom logic

The `ActorExecutor` in [`nautilus_trader/common/executor.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/nautilus_trader/common/executor.py) schedules these callbacks asynchronously on the trader's event loop, ensuring non-blocking execution while maintaining sequential ordering per actor.

## Practical Example: Cross-Component Signal Strategy

The following complete example demonstrates a strategy that publishes threshold-crossing signals and reacts to them. This pattern applies identically to custom actors, risk managers, or execution algorithms.

```python
from nautilus_trader.trading.strategy import Strategy
from nautilus_trader.common.enums import LogColor
from nautilus_trader.core.datetime import unix_nanos_to_dt

class ThresholdStrategy(Strategy):
    """
    Publish a signal when price crosses a configurable threshold.
    Demonstrates actor-based messaging between components.
    """

    def on_start(self):
        # Request market data

        self.subscribe_bars(self.config.bar_type)
        
        # Subscribe to our own signal to receive it when published

        self.subscribe_signal("PriceAboveThreshold")
        self.log.info("Subscribed to bars and signal", color=LogColor.YELLOW)

    def on_bar(self, bar):
        # Simple threshold logic

        if bar.close > self.config.threshold:
            # Publish signal with the price value

            self.publish_signal(
                name="PriceAboveThreshold",
                value=bar.close,
                ts_event=bar.ts_event,
            )
            self.log.info(f"Published PriceAboveThreshold: {bar.close}")

    def on_signal(self, signal):
        """
        Handle incoming signals from the MessageBus.
        Automatically called by the ActorExecutor when signals arrive.
        """
        self.log.info(
            f"Threshold crossed! Signal value={signal.value} "
            f"at {unix_nanos_to_dt(signal.ts_event)}",
            color=LogColor.GREEN,
        )

```

This example illustrates the complete lifecycle of actors and signals for messaging between components:
- **Subscription** → `subscribe_signal("PriceAboveThreshold")` registers the handler with the `MessageBus`
- **Publication** → `publish_signal()` creates a dynamic `SignalPriceAboveThreshold` class and routes it via `publish_data()`
- **Consumption** → `on_signal()` receives the value through the `ActorExecutor` scheduling mechanism

The full working example is available in the repository at [`examples/backtest/example_11_messaging_with_actor_signals/strategy.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/examples/backtest/example_11_messaging_with_actor_signals/strategy.py).

## Summary

- **Decoupled architecture** – Actors communicate exclusively through the `MessageBus` using signals, eliminating direct dependencies between strategies, risk managers, and execution components.
- **Dynamic signal classes** – The `generate_signal_class` function in [`nautilus_trader/common/signal.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/nautilus_trader/common/signal.py) creates type-safe `Data` subclasses on demand for `int`, `float`, or `str` values.
- **Simple API** – Use `publish_signal(name, value, ts_event)` to emit events and `subscribe_signal(name)` to receive them, with automatic routing through `nautilus_trader/common/actor.pyx`.
- **Async execution** – The `ActorExecutor` in [`nautilus_trader/common/executor.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/nautilus_trader/common/executor.py) schedules all `on_signal` callbacks on the trader's event loop, ensuring thread-safe, non-blocking processing.
- **Environment parity** – The messaging system works identically in backtesting and live trading, allowing seamless strategy portability across execution modes.

## Frequently Asked Questions

### What is the difference between actors and strategies in NautilusTrader?

Strategies are specialized actors that inherit from the `Actor` base class defined in `nautilus_trader/common/actor.pyx`. While all strategies are actors, not all actors are strategies—custom risk managers, execution analyzers, or data processors can subclass `Actor` directly to gain access to `publish_signal()` and `subscribe_signal()` without implementing strategy-specific logic like order management. This distinction allows you to build utility components that participate in the messaging ecosystem without being full trading strategies.

### Can signals carry complex data types or only primitives?

Signals are restricted to single primitive values—specifically `int`, `float`, or `str`—as implemented in [`nautilus_trader/common/signal.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/nautilus_trader/common/signal.py). The `generate_signal_class()` function dynamically creates a `Data` subclass specifically for that signal name and value type, ensuring minimal serialization overhead. For complex data structures, components should use the standard `publish_data()` method with custom `Data` subclasses rather than the signal system, as signals are optimized for lightweight notifications like threshold breaches or status flags.

### How does the MessageBus handle concurrent signal processing?

The `MessageBus` in `nautilus_trader/common/component.pyx` routes signals synchronously to subscribers, but the `ActorExecutor` in [`nautilus_trader/common/executor.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/nautilus_trader/common/executor.py) schedules the actual `on_signal` callbacks asynchronously on the trader's main asyncio event loop. This architecture prevents race conditions by ensuring that signal handlers run sequentially per actor while allowing concurrent execution across different actors. The executor also provides cancellation support via `TaskId`, allowing the system to cleanly shut down pending signal processing when stopping the trader.

### Where can I find working examples of actor messaging?

The repository includes a complete working demonstration at [`examples/backtest/example_11_messaging_with_actor_signals/strategy.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/examples/backtest/example_11_messaging_with_actor_signals/strategy.py), which shows a strategy publishing threshold-crossing signals and subscribing to them. Additionally, unit tests verifying `publish_signal`, `subscribe_signal`, and `on_signal` behavior are available in [`tests/unit_tests/common/test_actor.py`](https://github.com/nautechsystems/nautilus_trader/blob/main/tests/unit_tests/common/test_actor.py), providing reference implementations for custom actor development. These examples demonstrate the same patterns used by internal NautilusTrader components, ensuring best practices for production systems.