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
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
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
| Metric | 📊 Backtest | 🏖️ Sandbox (Paper Trading) | 🔴 Live Production |
|---|---|---|---|
| Data Intake | Historical Data Files | Live Market Data | Live Market Data |
| Trade Execution | Built-in Simulated Exchange | Built-in Simulated Exchange | Connected Live Exchanges |
| Risk Profile | Zero Risk | Zero Risk | Real Capital Risk |
| Execution Speed | Accelerated Playback | Real-time | Real-time |
| Primary Purpose | Validate Strategy Logic | Validate Real-time Performance | Live Trading |
| Strategy Code | Exactly 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
4.2 Internal Rust Crates Dependency Graph
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
| Category | Contained Crates | Purpose |
|---|---|---|
| Foundation Layer | core, model, common, system, trading | Primitive types, domain models, kernel, and strategy base classes. |
| Engine Layer | data, execution, portfolio, risk | Core trading engine components. |
| Infrastructure Layer | serialization, network, cryptography, persistence | Serialization, networking, cryptography, and data storage. |
| Runtime Layer | live, backtest | Specific environment node implementations (live, backtest). |
| External Adapters | adapters/* | Exchange and data provider adapters. |
| Python Bindings | pyo3 | Exposing 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
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
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:
| Module | Purpose | Required 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
9. Interactive Exercises
Match the following functions to their correct corresponding components:
| Function | Your 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. | ___________ |
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.
10. Module Key Takeaways
- Six Core Components: NautilusKernel, MessageBus, Cache, DataEngine, ExecutionEngine, and RiskEngine.
- The MessageBus is the asynchronous routing layer that lets components communicate without tight coupling.
- The exact same core operates across three distinct environments (Backtest, Sandbox, Live) with strategy logic remaining entirely immutable.
- Market Data Flow: Exchange → Adapter → DataEngine → MessageBus → Strategy.
- Order Execution Flow: Strategy → MessageBus → RiskEngine → ExecutionEngine → Adapter → Exchange.
- Strategies stay behind adapter boundaries. This structural decoupling isolates trading logic from venue-specific infrastructure.