Phase 1: Global Understanding

Module 02: Architecture OverviewGet the big picture before diving into the implementation details

1. The System in One View

At a high level, NautilusTrader is an event-driven trading system. The diagram below shows the core flow from incoming market data to order execution:

flowchart LR
            subgraph outer_world["🌍 External World"]
            exchange["Exchanges<br />Binance / IB / OKX..."]
            data_provider["Data Providers<br />Databento / Tardis..."]
            end

            subgraph nautilus["🐚 NautilusTrader"]
            direction TB
            adapter_in["Data Adapters<br />DataClient"]
            adapter_out["Execution Adapters<br />ExecutionClient"]
            data_engine["Data Engine<br />DataEngine"]
            exec_engine["Execution Engine<br />ExecutionEngine"]
            risk_engine["Risk Engine<br />RiskEngine"]
            cache["Cache<br />System Cache"]
            msgbus["Message Bus<br />MessageBus"]
            strategy["Your Strategy<br />Strategy"]
            portfolio["Portfolio<br />Portfolio"]
            end

            data_provider -->|"Market Data"| adapter_in
            exchange -->|"Market Data"| adapter_in
            adapter_in --> data_engine
            data_engine --> msgbus
            msgbus --> strategy
            strategy -->|"Order Commands"| msgbus
            msgbus --> risk_engine
            risk_engine --> exec_engine
            exec_engine --> adapter_out
            adapter_out -->|"Order Submit/Modify/Cancel"| exchange
            exchange -->|"Execution Reports"| adapter_out
            adapter_out --> exec_engine
            exec_engine --> portfolio
            data_engine --> cache
            exec_engine --> cache
            strategy -.->|"Query"| cache
Figure 1: Overall NautilusTrader Data Flow — The end-to-end path from incoming market data to order execution.
💡 The Trader’s Perspective

You can think of the framework like a small trading firm: the DataEngine brings in market data, the Strategy makes decisions, the RiskEngine checks orders before they go out, the ExecutionEngine manages the order lifecycle, the Cache holds shared state, and the MessageBus ties everything together.

2. The Six Core Components

The core of the platform is built around six main components. Each has a clear job, and they coordinate through the MessageBus:

flowchart LR
            subgraph kernel["🧠 NautilusKernel — System Core Brain"]
            direction TB
            msgbus["📡 MessageBus<br />Message Bus"]
            cache["💾 Cache<br />System Cache"]
            msgbus --- cache
            end

            subgraph engines["⚙️ Core Engines"]
            direction TB
            data["📊 DataEngine<br />Data Engine"]
            exec["⚙️ ExecutionEngine<br />Execution Engine"]
            risk["🛡️ RiskEngine<br />Risk Engine"]
            data --- exec --- risk
            end

            subgraph management["💰 Capital & Strategy"]
            direction TB
            port["💰 Portfolio<br />Portfolio"]
            strat["🧩 Strategy<br />Your Strategy"]
            port --- strat
            end

            kernel ==> engines ==> management

            style kernel fill:#0f1419,stroke:#4fc3f7,stroke-width:2px
            style engines fill:#0f1419,stroke:#ffa726,stroke-width:1px
            style management fill:#0f1419,stroke:#ab47bc,stroke-width:1px
Figure 2: The Six Core Components and their Relationships
🧠

NautilusKernel — System Core

The “brain” of the entire system. Responsible for initializing all components, managing component lifecycles, and configuring the messaging infrastructure. It is the entry point for system startup.

📡

MessageBus — Message Bus

The event-routing layer between components. Market data, commands, events, and requests move through it using publish/subscribe, request/response, and point-to-point messaging patterns.

💾

Cache — Cache System

The shared runtime state layer. It stores instruments, accounts, orders, and positions so components can query current state without coupling directly to each other.

📊

DataEngine — Data Engine

The “market data division”. Ingests, processes, and distributes market data (quotes, trades, bars/candles, order books), dispatching data events to subscribed components.

⚙️

ExecutionEngine — Execution Engine

The order-management layer. It tracks the order lifecycle from submission through fill, rejection, or cancellation, while keeping active order and position state coherent.

🛡️

RiskEngine — Risk Engine

The “compliance manager”. Intervenes before orders reach the Execution Engine to perform strict pre-trade risk checks, ensuring risk limits are not breached.

3. Three Runtime Environments

The same core runtime operates across three environments. In the intended workflow, your strategy code stays the same while the data source and venue behavior change:

flowchart TB
            subgraph common["🔄 Universal Core (NautilusKernel)"]
            kernel["MessageBus + Cache + DataEngine<br />+ ExecutionEngine + RiskEngine + Portfolio"]
            end

            subgraph bt["📊 Backtest Environment"]
            bt_data["Historical Data Files"]
            bt_venue["Built-in Simulated Exchange"]
            end

            subgraph sb["🏖️ Sandbox Environment (Paper Trading)"]
            sb_data["Live Market Data"]
            sb_venue["Built-in Simulated Exchange"]
            end

            subgraph lv["🔴 Live Production Environment"]
            lv_data["Live Market Data"]
            lv_venue["Connected Live Exchange"]
            end

            bt --> common
            sb --> common
            lv --> common
Figure 3: Three runtime environments built on the same core codebase
Metric📊 Backtest🏖️ Sandbox (Paper Trading)🔴 Live Production
Data IntakeHistorical Data FilesLive Market DataLive Market Data
Trade ExecutionBuilt-in Simulated ExchangeBuilt-in Simulated ExchangeConnected Live Exchanges
Risk ProfileZero RiskZero RiskReal Capital Risk
Execution SpeedAccelerated PlaybackReal-timeReal-time
Primary PurposeValidate Strategy LogicValidate Real-time PerformanceLive Trading
Strategy CodeExactly the same — Requires zero modifications

4. Code Architecture — How the Codebase is Organized

The NautilusTrader codebase is organized into two major parts: the Rust core and the Python/Cython layer.

4.1 Three-Layer Separation — Global Perspective

flowchart LR
            %% Python Layer
            subgraph python_pkg["📦 Python / Cython Layer"]
            direction TB
            p_logic["Trading Strategies & Indicators<br />trading/ / indicators/"]
            p_model["Domain Model Wrappers<br />(model/)"]
            p_engine["Engine Interfaces & Config<br />(core/ / data/ / exec/)"]
            p_logic --- p_model --- p_engine
            end

            %% Bridge Layer
            subgraph bridge["🔗 Bridge Layer"]
            direction TB
            ffi["Cython + PyO3<br />C FFI Interface"]
            binding["Python Bindings"]
            ffi <==> binding
            end

            %% Internal Rust Layer
            subgraph rust_pkg["🦀 Rust Core Layer"]
            direction TB
            r_engines["Core Engine Components<br />(data / execution / risk)"]
            r_base["Foundations & Kernel<br />(core / model / common)"]
            r_infra["Low-level Services<br />(network / persistence)"]
            r_engines --- r_base --- r_infra
            end

            python_pkg <==> bridge <==> rust_pkg

            style bridge fill:#1a1f2e,stroke:#ab47bc,stroke-width:2px
            style python_pkg fill:#0f1419,stroke:#4fc3f7,stroke-width:1px
            style rust_pkg fill:#0f1419,stroke:#ef5350,stroke-width:1px
Figure 4: NautilusTrader Three-Layer Architecture — From Python Strategies to the Underlying Rust Core

4.2 Internal Rust Crates Dependency Graph

⚠️ Advanced Material (Optional Reading)

The next section looks at the internal Rust crate layout. If you mainly plan to build strategies in Python, you can safely skim or skip it for now.

The Rust core is split across 23 crates grouped into five functional layers. The diagram below shows the high-level dependency structure:

flowchart LR
            subgraph layer_found["🧱 Foundation Layer"]
            direction TB
            core["core"]
            model["model"]
            common["common"]
            model --> core
            common --> model
            end

            subgraph layer_infra["🏗️ Engine & Infrastructure Layer"]
            direction TB
            serialization["serialization"]
            persistence["persistence"]
            data["data"]
            execution["execution"]
            risk["risk"]
            portfolio["portfolio"]
            serialization --> model
            persistence --> serialization
            data --> common
            execution --> common
            risk --> portfolio
            portfolio --> common
            end

            subgraph layer_runtime["🚀 Runtime Layer"]
            direction TB
            system["system"]
            trading["trading"]
            backtest["backtest"]
            live["live"]
            adapters["adapters"]
            pyo3["pyo3"]
            backtest --> system
            live --> system
            live --> trading
            adapters --> live
            pyo3 --> adapters
            end

            layer_found ==> layer_infra ==> layer_runtime

            style layer_found fill:#0f1419,stroke:#4fc3f7,stroke-width:1px
            style layer_infra fill:#0f1419,stroke:#ffa726,stroke-width:1px
            style layer_runtime fill:#0f1419,stroke:#ef5350,stroke-width:1px
Figure 5: Internal Rust Crate Dependency Graph — Arrows denote “Depends On”
CategoryContained CratesPurpose
Foundation Layercore, model, common, system, tradingPrimitive types, domain models, kernel, and strategy base classes.
Engine Layerdata, execution, portfolio, riskCore trading engine components.
Infrastructure Layerserialization, network, cryptography, persistenceSerialization, networking, cryptography, and data storage.
Runtime Layerlive, backtestSpecific environment node implementations (live, backtest).
External Adaptersadapters/*Exchange and data provider adapters.
Python Bindingspyo3Exposing Rust functionalities to Python.

5. End-to-end Market Data Streaming Path

Let’s trace how a market quote (e.g., the latest price of BTC/USDT) flows through the system:

sequenceDiagram
            participant Exchange as 🏦 Exchange
            participant DC as 📡 DataClient<br />(Adapter)
            participant DE as 📊 DataEngine
            participant Cache as 💾 Cache
            participant MB as 📡 MessageBus
            participant Strategy as 🎯 Your Strategy

            Exchange->>DC: WebSocket pushes raw quote
            DC->>DC: Convert to standard format (QuoteTick)
            DC->>DE: Pass standardized data
            DE->>Cache: Write to Cache
            DE->>MB: Publish Data Event
            MB->>Strategy: Push to strategies subscribed to the instrument
            Strategy->>Strategy: Execute strategy logic
            Note over Strategy: Determine whether to submit an order
Figure 6: End-to-end Market Data Flow — From Exchange to Strategy.

6. End-to-end Order Execution Sequence

When your strategy decides to trade, how does the order route from the strategy to the exchange?

sequenceDiagram
            participant Strategy as 🎯 Your Strategy
            participant MB as 📡 MessageBus
            participant Risk as 🛡️ RiskEngine
            participant EE as ⚙️ ExecutionEngine
            participant EC as 🔌 ExecutionClient<br />(Adapter)
            participant Exchange as 🏦 Exchange
            participant Portfolio as 💰 Portfolio
            participant Cache as 💾 Cache

            Strategy->>MB: Send Order Command (SubmitOrder)
            MB->>Risk: Risk Check
            Risk->>Risk: Validate risk limits
            alt Risk Check Passed
            Risk->>EE: Forward Command
            EE->>EC: Route to corresponding exchange
            EC->>Exchange: Submit Order (REST/WS)
            Exchange-->>EC: Order Confirmation
            EC->>EE: Report OrderAccepted event
            EE->>Cache: Update Order State
            EE->>MB: Publish event
            Exchange-->>EC: Execution Fill Report
            EC->>EE: Report OrderFilled event
            EE->>Portfolio: Update Positions and Mark-to-Market PnL
            EE->>Cache: Update State
            EE->>MB: Publish event
            MB->>Strategy: Notify Fill
            else Risk Check Failed (Rejected)
            Risk->>MB: Publish OrderDenied Event
            MB->>Strategy: Notify Strategy of Rejection
            end
Figure 7: End-to-end Execution Flow — From Order Generation to Exchange Fill.
✅ Key Insight

Notice that the Strategy code does not communicate directly with the exchange. All activity flows through the MessageBus and engine layers. That is the essence of the platform’s hexagonal architecture: strategies focus on trading logic, while exchange-specific behavior lives outside the core strategy code.

7. Python Module Map

Here are the Python modules you will interact with most frequently as a quantitative developer:

ModulePurposeRequired Proficiency
trading/Strategy and Actor base classes — Where you write strategy code⭐⭐⭐ Must Master
model/Financial domain objects (Orders, Positions, Instruments)⭐⭐⭐ Must be familiar
indicators/Technical Indicators (EMA, SMA, RSI, etc.)⭐⭐⭐ Frequently used
config/System and strategy configuration⭐⭐ Need to know
backtest/Backtesting engine and configuration⭐⭐ Need to know
live/Live node configuration⭐⭐ Needed for deployment
adapters/Exchange connectors⭐ Rarely modified
data/Data Engine internals⭐ Conceptual understanding is sufficient
execution/Execution Engine internals⭐ Conceptual understanding is sufficient
cache/Cache System⭐ Know how to query
core/Low-level utilities and constants⭐ Rarely used directly
risk/Risk Engine⭐ Just understand the configuration

8. An End-to-end Trading Cycle

Stitching all these components together, an end-to-end trading lifecycle looks like this:

flowchart TB
            A["1️⃣ System Boot<br />NautilusKernel initializes all components"] --> B["2️⃣ Connect Data Source<br />DataClient connects to Exchange WebSockets"]
            B --> C["3️⃣ Subscribe to Data<br />Strategy informs DataEngine of required data"]
            C --> D["4️⃣ Ingest Market Data<br />Data arrives at Strategy's `on_quote_tick` / `on_bar` callbacks"]
            D --> E["5️⃣ Strategy Decision<br />Strategy analyzes data, calculates indicators, generates signals"]
            E --> F{"6️⃣ Submit Order?"}
            F -->|"Yes"| G["7️⃣ Risk Check<br />RiskEngine validates the order"]
            F -->|"No"| D
            G --> H["8️⃣ Execute Order<br />ExecutionEngine routes order via adapters"]
            H --> I["9️⃣ Update State<br />Cache & Portfolio updated"]
            I --> D
Figure 8: An End-to-end Trading Lifecycle.

9. Interactive Exercises

🧠 Drill 1: Component Matching Matrix

Match the following functions to their correct corresponding components:

FunctionYour Answer
A. Stores order and position states.___________
B. Receives real-time quotes from the exchange.___________
C. Intervenes to ensure a single order does not breach risk limits.___________
D. Dispatches execution fill events back to the strategy.___________
E. Manages the full lifecycle of order submission and cancellation.___________
A → Cache B → DataClient / DataEngine C → RiskEngine D → MessageBus E → ExecutionEngine
🧠 Drill 2: Precise Telemetry Tracking Sequences

Suppose your strategy subscribes to 1-minute Bars for BTC/USDT. Please describe the exact path a newly formed Bar takes from inception until it is received by your strategy.

1. The exchange’s matching engine executes trades, pushing tick-by-tick data outward via WebSockets. 2. The DataClient ingests the raw payload and normalizes it into NautilusTrader’s standardized `TradeTick` format. 3. The DataEngine aggregates these `TradeTick`s internally into a 1-minute Bar. 4. When the 1-minute interval completes, the Bar is finalized. 5. The DataEngine writes the finalized Bar into the Cache. 6. The DataEngine publishes the Bar event across the MessageBus. 7. The MessageBus routes the event only to components subscribed to that specific Bar. 8. Your strategy’s `on_bar()` callback is triggered, receiving the finalized Bar object.

10. Module Key Takeaways

📋 Foundational System Absolutes
  1. Six Core Components: NautilusKernel, MessageBus, Cache, DataEngine, ExecutionEngine, and RiskEngine.
  2. The MessageBus is the asynchronous routing layer that lets components communicate without tight coupling.
  3. The exact same core operates across three distinct environments (Backtest, Sandbox, Live) with strategy logic remaining entirely immutable.
  4. Market Data Flow: Exchange → Adapter → DataEngine → MessageBus → Strategy.
  5. Order Execution Flow: Strategy → MessageBus → RiskEngine → ExecutionEngine → Adapter → Exchange.
  6. Strategies stay behind adapter boundaries. This structural decoupling isolates trading logic from venue-specific infrastructure.