Phase 0: Getting Started

Module 00: Environment Setup & First RunGet from zero to your first backtest in under 30 minutes.

This module walks you through the required toolchain and your first backtest with a minimal strategy. At this stage, the goal is to confirm that your environment works end to end.

1. Prerequisites and Dependencies

flowchart LR
                        subgraph os["💻 Operating System"]
                        direction TB
                        linux["Linux (Recommended)"]
                        mac["macOS"]
                        wsl["Windows WSL2"]
                        end

                        subgraph tools["🔧 Toolchain"]
                        direction TB
                        python["Python 3.11+"]
                        uv["uv Package Manager"]
                        end

                        subgraph nautilus["🐚 NautilusTrader"]
                        direction TB
                        pip_install["pip install"]
                        source["Compile from Source"]
                        end

                        os ==> tools ==> nautilus
Figure 1: The dependency stack for getting NautilusTrader running
⚠️ Windows Users: Installation Guide

NautilusTrader does not currently support native Windows installs because the build process depends on Unix-based tooling for Rust and Cython components.

On Windows 10 or 11, the recommended path is WSL2 (Windows Subsystem for Linux), which gives you a full Linux environment with near-native performance.

▶ 🪟 Windows (WSL2) Installation Steps
  1. Enable WSL

    Run the following command in PowerShell as Administrator:
    wsl --install
    Then restart your computer.

  2. Install Ubuntu

    After the restart, the Ubuntu installation window will usually pop up. If not, search for “Ubuntu” in the Microsoft Store to install it.
    Launch the application and configure your UNIX credentials.

  3. Install VS Code and Extensions

    Download and install Visual Studio Code on Windows.
    Open VS Code, navigate to Extensions, and install the WSL extension.

  4. Connect to WSL

    In your Ubuntu terminal, execute:
    code .
    This opens VS Code automatically connected to your WSL environment. The bottom-left corner should display WSL: Ubuntu.

  5. Start Installing NautilusTrader

    You can now use the VS Code integrated terminal to run commands (e.g., pip install) identically to a native Linux environment.

2. Step-by-Step Installation

Step 1: Verify Python Version

# Check Python version (requires >= 3.11)
python3 --version
# Expected Output: Python 3.11.x or 3.12.x

# 💡 Why >= 3.11?
# NautilusTrader leverages several Python 3.11 features:
#   - tomllib (for TOML configuration parsing)
#   - Significantly faster startup times (CPython optimizations)
#   - Enriched error messaging

# ❌ What if I use Python 3.9?
#   pip install nautilus_trader
#   → ERROR: Requires-Python >=3.11

Step 2: Install NautilusTrader

# Method 1: Using pip (Simplest)
pip install -U nautilus_trader

# Method 2: Using uv (Much faster, Highly Recommended)
pip install uv
uv pip install nautilus_trader

# 💡 Why recommend uv?
# uv is an ultra-fast Python package manager written in Rust.
# ❌ pip install nautilus_trader → Can take 2-5 minutes
# ✅ uv pip install nautilus_trader → Usually done in 10-30 seconds

Step 3: Installation Verification

# Verify inside the Python interpreter
import nautilus_trader
print(nautilus_trader.__version__)
# Example Output: 1.208.0

# Verify that core C-extension components load correctly
from nautilus_trader.backtest.node import BacktestNode
from nautilus_trader.model.data import QuoteTick, Bar
from nautilus_trader.trading.strategy import Strategy
print("✅ Core components loaded successfully!")
🚨 Common Installation Errors

If you encounter an ImportError: libpython3.11.so, you must install the Python development headers:

# Ubuntu/Debian
sudo apt install python3.11-dev

# If you see Rust-related compiler errors (when compiling from source)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

3. Your First Backtest: Hello NautilusTrader

Below is a minimal backtest example. It places no trades and is only meant to verify that your runtime environment is working correctly.

💡 Why start with a “do nothing” strategy?

You want to verify the full pipeline first: data loading → engine initialization → strategy execution → output generation.
❌ Start with a complex strategy: if something breaks, you will not know whether the problem is your trading logic or your environment.
✅ Start with an empty strategy: if it fails, you know the setup needs attention.

"""
Your first NautilusTrader Backtest — Hello World
Save this code to a file named hello_nautilus.py, and run it using `python hello_nautilus.py`
"""
from nautilus_trader.backtest.engine import BacktestEngine
from nautilus_trader.backtest.engine import BacktestEngineConfig
from nautilus_trader.model.currencies import USD
from nautilus_trader.model.data import QuoteTick
from nautilus_trader.model.enums import AccountType, OmsType
from nautilus_trader.model.identifiers import Venue
from nautilus_trader.model.objects import Money
from nautilus_trader.trading.strategy import Strategy
from nautilus_trader.config import StrategyConfig

# ── 1. Define a Minimalist Strategy ────────────────────────
class DoNothingConfig(StrategyConfig, frozen=True):
    """Minimalist StrategyConfig: no parameters."""
    pass

class DoNothingStrategy(Strategy):
    """
    This strategy executes no operations.
    Its sole purpose is to validate the execution environment.
    """
    def __init__(self, config: DoNothingConfig):
        super().__init__(config)

    def on_start(self):
        self.log.info("🚀 Strategy initialized. Environment validated.")

    def on_stop(self):
        self.log.info("🛑 Strategy terminated. Initial backtest execution successful.")

# ── 2. Configure the Backtest Engine ───────────────────────
config = BacktestEngineConfig(
    logging_level="INFO",        # Output logs to standard output
    logging_level_file=None,     # Disable file logging
)

engine = BacktestEngine(config=config)

# ── 3. Register a Venue (Exchange) ─────────────────────────
SIM = Venue("SIM")  # "SIM" represents a simulated venue
engine.add_venue(
    venue=SIM,
    oms_type=OmsType.NETTING,         # Netting OMS
    account_type=AccountType.MARGIN,  # Margin account type
    base_currency=USD,                # Account base currency
    starting_balances=[Money(100_000, USD)],  # Initial capital: $100,000
)

# ── 4. Register the Strategy ───────────────────────────────
strategy = DoNothingStrategy(config=DoNothingConfig())
engine.add_strategy(strategy)

# ── 5. Execute Backtest ────────────────────────────────────
engine.run()

# ── 6. Output Results ──────────────────────────────────────
print("\n" + "="*60)
print("📊 Backtest Results")
print("="*60)
print(f"Account Balances: {engine.portfolio.balances()}")
print(f"Total Orders: {len(engine.cache.orders())}")
print(f"Total Positions: {len(engine.cache.positions())}")
print("=" * 60)
print("✅ NautilusTrader environment is fully operational.")

# Dispose engine resources
engine.dispose()
✅ Expected Output

If the setup is working, you should see terminal output resembling this:

INFO  DoNothingStrategy: 🚀 Strategy initialized. Environment validated.
INFO  DoNothingStrategy: 🛑 Strategy terminated. Initial backtest execution successful.

============================================================
📊 Backtest Results
============================================================
Account Balances: {USD: Money(100000.00, USD)}
Total Orders: 0
Total Positions: 0
============================================================
✅ NautilusTrader environment is fully operational.

4. Code Walkthrough

Even though the script is minimal, it demonstrates the core execution pattern of NautilusTrader:

flowchart LR
                        subgraph setup["⚙️ Configuration Phase"]
                        direction TB
                        config_engine["1. Configure Engine"]
                        add_venue["2. Register Venue"]
                        add_strategy["3. Register Strategy"]
                        config_engine --> add_venue --> add_strategy
                        end

                        subgraph run["▶️ Execution Phase"]
                        direction TB
                        init["Initialize Core Components"]
                        start["Invoke Strategy on_start()"]
                        process["Process Data Events"]
                        stop["Invoke Strategy on_stop()"]
                        init --> start --> process --> stop
                        end

                        subgraph result["📊 Analysis Phase"]
                        direction TB
                        balance["Query Account Balances"]
                        orders["Query Orders"]
                        tearsheet["Generate Tearsheet"]
                        end

                        setup ==> run ==> result
Figure 2: The Three Phases of a NautilusTrader Backtest
Code SnippetWhat it doesRelevant Architecture ComponentCovered Next In
BacktestEngineInstantiates the backtest engineSystem Core (Kernel)Module 12
add_venue()Registers a simulated venueAdaptersModule 13
OmsType.NETTINGSpecifies Order Management System behaviorExecution EngineModule 08
StrategyBase strategy classStrategy DevelopmentModule 11
Money(100_000, USD)Defines capital (fixed-point arithmetic)Domain ModelModule 04
engine.portfolioQueries portfolio statePortfolioModule 10
engine.cacheQueries in-memory cacheCacheModule 06

5. Python Survival Guide

If you are new to Python, below are the most frequent syntax patterns used in NautilusTrader. You do not need to master Python before starting; understanding these 7 core concepts is sufficient:

1️⃣

Class & Inheritance

Your custom strategy must inherit from the NautilusTrader Strategy base class to inherit system capabilities (e.g., self.cache).

class MyStrategy(Strategy):
    # The strategy inherits from the Strategy base class
    # This automatically grants all base capabilities
2️⃣

__init__ Constructor

This is the class initialization method. Calling super().__init__(config) ensures the parent class is properly initialized before custom variables are defined.

def __init__(self, config):
    super().__init__(config)  # Let parent class process state first
    self.my_var = 0           # Then define custom variables safely
3️⃣

The `self` Keyword (Instance Reference)

Within class methods, instance variables are prefixed with self. to show they belong to the specific strategy instance rather than being local temporary variables.

def on_start(self):
    self.log.info("...")      # Accesses the instance's unique logger
    self.count = 1            # Sets up the instance's unique counter
4️⃣

Type Hints

The : Bar annotation is not an assignment; it is a type hint that explicitly declares the object’s expected type, which is critical for static analysis and IDE autocomplete.

def on_bar(self, bar: Bar):
    # IDEs automatically resolve 'bar' attributes: open, high, low, close
    print(bar.close)
5️⃣

frozen=True (Immutability)

Configuration objects should be immutable. Setting frozen=True locks the configuration after construction, preventing unintended runtime mutations.

class MyConfig(StrategyConfig, frozen=True):
    symbol: str = "BTC-USDT"
    # c = MyConfig(...)
    # c.symbol = "ETH"  # ❌ Error! Configuration instances are immutable.
6️⃣

f-strings (String Formatting)

Prefix strings with f to dynamically interpolate variables using the {variable} syntax.

price = 100
self.log.info(f"The current price is {price}")  # Outputs: The current price is 100
7️⃣

Fixed-Point Arithmetic (`Decimal`)

For exact decimal arithmetic, NautilusTrader often uses Python’s Decimal module instead of standard float primitives. Always initialize Decimals from strings to avoid floating-point artifacts.

from decimal import Decimal
qty = Decimal("0.1")  # ✅ Perfect precision
# qty = Decimal(0.1)  # ❌ Dangerous! Floating-point inaccuracies
💡 Unfamiliar Syntax is Standardized

NautilusTrader follows a consistent architectural pattern: configuration class + strategy class + event handlers (`on_xxx`). In most cases, you place your logic inside event handlers and let the framework hide the lower-level Cython and Rust details.

6. Project Directory Map

Knowing the source tree makes debugging and tracing subsystem behavior much faster.

📂 Repository Root Overview

When reading the source repository, start with the nautilus_trader/ directory which contains the core codebase. Build and dependency files (e.g., .cargo, poetry.lock, Makefile) can be safely ignored at this stage.

nautilus_trader/
├── model/           # 💎 Domain Model (Instruments, Orders, Positions)
|   |-- orders/      #     Order types and order-state logic
│   ├── instruments/ #     Instrument definitions (Equities, Futures, Options...)
│   ├── data.pyx     #     Market data structs (QuoteTick, Bar...)
│   └── position.pyx #     Position accounting
│
├── trading/         # 🎯 Strategy routing and execution
│   ├── strategy.pyx #     Strategy Base Class (inherited by custom strategies)
│   ├── trader.py    #     Main Trader Manager instance
│   └── controller.py#     Trading Controllers
│
├── data/            # 📊 Data Engine
│   ├── engine.pyx   #     Central hub
│   └── aggregation.pyx #  Bar aggregation
│
├── execution/       # ⚙️ Execution Engine
├── risk/            # 🛡️ Risk Engine (Throttlers/Rules)
├── cache/           # 💾 State Cache System
├── common/          # 🔧 Shared Modules (Actor, MessageBus, Logger)
│
├── backtest/        # 🔬 Backtest Engine
│   ├── engine.pyx   #     Backtest Kernel Hub
│   └── models/      #     FillModel, FeeModel, LatencyModel
│
├── live/            # 🚀 Live Execution Engine
│   ├── node.py      #     TradingNode
│   ├── reconciliation.py # Order reconciliation
│   └── retry.py     #     Automated retry routines
│
├── adapters/        # 🔌 Exchange adapters (16+ integrations)
├── indicators/      # 📈 Built-in Cythonized technical indicators
├── accounting/      # 💰 Accounting & Margin calculations
├── analysis/        # 📋 Performance analysis & Tearsheets
├── persistence/     # 💿 Persistence frameworks (Parquet DataCatalog)
├── serialization/   # 📦 Protocol serialization (Arrow/MsgPack)
├── system/          # ⚡ System Core (Kernel)
└── config/          # ⚙️ Configuration data classes
✅ Progressive Module Unpacking

You do not need to memorize this structure. Treat this schematic as an architectural map: when tracing logic or debugging, it guides you to the relevant subsystem.

7. Interactive Exercises

🧠 Exercise 1: Installation Diagnostics

Run the diagnostic script below. If all checks return ✅, your environment is fully verified:

checks = {
    "nautilus_trader": True,
    "BacktestEngine": True,
    "Strategy": True,
    "QuoteTick": True,
}
try:
    import nautilus_trader
    from nautilus_trader.backtest.engine import BacktestEngine
    from nautilus_trader.trading.strategy import Strategy
    from nautilus_trader.model.data import QuoteTick
except ImportError as e:
    print(f"❌ Missing dependency: {e}")
else:
    for name in checks:
        print(f"  ✅ {name}")
    print(f"\nVersion: {nautilus_trader.__version__}")
🧠 Exercise 2: Code Review

The following snippet contains 3 intentional errors. Can you identify them?

from nautilus_trader.backtest.engine import BacktestEngine
from nautilus_trader.model.currencies import USD

engine = BacktestEngine()  # Error 1: missing config
engine.add_venue(
    venue="SIM",             # Error 2: wrong venue type
    oms_type="NETTING",      # Error 3: wrong enum type
    account_type=AccountType.MARGIN,
    base_currency=USD,
    starting_balances=[100000],
)
Error 1: BacktestEngine requires a BacktestEngineConfig instance. → Resolution: engine = BacktestEngine(config=BacktestEngineConfig())Error 2: `venue` requires a strongly-typed Venue object, not a string primitive. → Resolution: venue=Venue(“SIM”)Error 3: `oms_type` requires an OmsType enumeration, not a string primitive. → Resolution: oms_type=OmsType.NETTING💡 Architectural Lesson: NautilusTrader uses strong typing as part of its fail-fast design. It does not allow implicit string conversions that could lead to ambiguous runtime state. If a configuration value is wrong, the framework prefers to fail immediately rather than let the issue surface later in harder-to-debug ways. This design philosophy is covered in Module 03.
🧠 Exercise 3: Architectural Design

If you want to log the full list of all available instruments when initializing DoNothingStrategy, which event handler should this logic reside? Which subsystem must be queried?

The logic should be implemented within the `on_start()` event handler:def on_start(self): instruments = self.cache.instruments() self.log.info(f”📋 Available Instruments ({len(instruments)} total):”) for inst in instruments: self.log.info(f” → {inst.id}”)Why on_start()? – `on_start()` is invoked only after the engine has initialized all core components. – At this stage, the in-memory Cache has fully populated the instrument universe. Querying this within `__init__` is premature as the Cache may remain uninitialized.Why use `self.cache`? – The `Strategy` class inherits from the `Actor` primitive. – Actors possess native memory access to the global Cache subsystem, enabling direct state queries. – The Cache subsystem architecture will be thoroughly explored in Module 06.

8. Module Recap

📋 Key Takeaways
  1. Environment Prerequisites: Python 3.11+ is required. Linux, macOS, or WSL2 are the most reliable environments for setup.
  2. Installation Options: Install with pip install nautilus_trader or use uv for faster dependency resolution.
  3. Backtest Lifecycle: Configuration Phase → Event-Driven Execution Phase → Post-Execution Analytics.
  4. Strategy Architecture: Configuration Class (Config) + Inherited Strategy Base (Strategy) + Event-Driven Handlers (on_xxx).
  5. Strong Typing: NautilusTrader expects proper domain objects and enums for system parameters instead of loose string values.
  6. Modular Subsystems: The repository is split into clear subsystems, and later modules walk through each one in detail.
🎯 Next Step

Your local trading environment is now installed, smoke-tested, and ready for the next layer. The next module asks the strategic question: In an ecosystem saturated with quantitative trading frameworks, why choose NautilusTrader?