How to Use Built-In Indicators in NautilusTrader Trading Strategies
NautilusTrader provides a high-performance library of technical indicators implemented as C-extension classes that you instantiate, register with your strategy's actor, and read from within on_bar or on_tick callbacks to generate trading signals.
The nautechsystems/nautilus_trader repository ships with a comprehensive suite of technical indicators—including EMA, SMA, MACD, Bollinger Bands, and RSI—implemented as cdef classes for C-speed calculations. Understanding how to use built-in indicators in trading strategies allows you to leverage these optimized tools without writing custom indicator logic from scratch.
Creating Built-In Indicators
You can instantiate indicators either through the factory pattern for moving averages or by directly importing concrete classes for other indicator types.
Using the MovingAverageFactory
The MovingAverageFactory.create method defined in nautilus_trader/indicators/averages.pyx (lines 898-907) provides a unified interface for creating various moving average types:
from nautilus_trader.indicators import MovingAverageFactory, MovingAverageType
# Create a 10-period Exponential Moving Average
ema10 = MovingAverageFactory.create(10, MovingAverageType.EXPONENTIAL)
MovingAverageType is an Enum exposing supported families including EXPONENTIAL, SIMPLE, WILDERS, and WEIGHTED.
Direct Class Instantiation
For specific implementations or non-moving-average indicators, import directly from the indicators package:
from nautilus_trader.indicators import ExponentialMovingAverage
# Instantiate EMA directly (implementation in averages.pyx lines 182-207)
ema10 = ExponentialMovingAverage(10) # Defaults to PriceType.LAST
The ExponentialMovingAverage class and other indicators are implemented as cdef classes in .pyx files, ensuring the value attribute updates with minimal latency.
Registering Indicators for Automatic Updates
Indicators must be registered with the strategy's actor to receive market data updates automatically. The registration API is defined in nautilus_trader/common/actor.pyx (lines 444-471).
For bar-based strategies, call register_indicator_for_bars inside on_start:
def on_start(self):
# Subscribe to the bar stream
self.subscribe_bars(self.bar_type)
# Register indicator to receive bar updates automatically
self.register_indicator_for_bars(self.bar_type, self.ema10)
When registered, the engine automatically invokes the indicator's handle_bar method (or handle_quote_tick/handle_trade_tick for tick data) before executing your strategy's on_bar callback. This ensures indicator.value reflects the current bar's calculation when you read it.
For tick-based indicators, use register_indicator_for_quotes or register_indicator_for_trade_ticks instead.
Accessing Indicator Values in Strategy Logic
Inside your strategy callbacks (on_bar, on_tick, etc.), read the indicator's value property to access the latest calculated result. Always check the initialized property first to ensure the indicator has received sufficient data (at least period data points):
def on_bar(self, bar: Bar):
self.bars_processed += 1
if self.ema10.initialized:
# Indicator is ready - use the value for signal generation
ema_value = self.ema10.value
self.log.info(f"EMA(10) value: {ema_value:.5f}")
# Example signal logic
if bar.close > ema_value:
# Bullish condition
pass
else:
# Indicator warming up
self.log.info(f"Waiting for EMA initialization... ({self.bars_processed} bars processed)")
The value attribute updates automatically via the indicator's internal handle_bar method, which the engine invoked during the registration phase.
Complete Working Example
The repository provides a full implementation in examples/backtest/example_07_using_indicators/strategy.py (lines 27-73). Below is a condensed version demonstrating the complete workflow:
import datetime as dt
from collections import deque
from nautilus_trader.common.enums import LogColor
from nautilus_trader.indicators import MovingAverageFactory, MovingAverageType
from nautilus_trader.model.data import Bar, BarType
from nautilus_trader.trading.strategy import Strategy
class EmaDemoStrategy(Strategy):
"""
Minimal demo showing how to use built-in indicators in trading strategies
by calculating a 10-period EMA and logging its values.
"""
def __init__(self, bar_type: BarType):
super().__init__()
self.bar_type = bar_type
self.bars_processed = 0
# Step 1: Create the indicator
self.ema10 = MovingAverageFactory.create(10, MovingAverageType.EXPONENTIAL)
self.ema_history: deque[float] = deque(maxlen=100)
def on_start(self):
# Step 2: Subscribe and register
self.subscribe_bars(self.bar_type)
self.register_indicator_for_bars(self.bar_type, self.ema10)
def on_bar(self, bar: Bar):
self.bars_processed += 1
# Step 3: Use the indicator value
if self.ema10.initialized:
self.ema_history.appendleft(self.ema10.value)
self.log.info(
f"Bar #{self.bars_processed} | Close={bar.close} | EMA(10)={self.ema10.value:.5f}",
color=LogColor.YELLOW,
)
else:
self.log.info(
f"Bar #{self.bars_processed} | Close={bar.close} | Waiting for EMA init...",
color=LogColor.RED,
)
When executed through the NautilusTrader backtest engine, this strategy automatically receives bar updates, calculates the EMA in C-speed via the cdef class implementation, and logs values once the 10-period warm-up completes.
Summary
- Instantiation: Create indicators using
MovingAverageFactory.create()for moving averages or direct class imports fromnautilus_trader.indicatorsfor other types like MACD or RSI. - Registration: Call
register_indicator_for_bars()(or tick variants) inon_start()to wire indicators to data streams, as implemented innautilus_trader/common/actor.pyx. - Access: Read
indicator.valueinsideon_bar()oron_tick()after checkingindicator.initializedto ensure warm-up completion. - Performance: Built-in indicators use
cdefC-extension classes defined in.pyxfiles (e.g.,averages.pyx) for high-speed calculations without Python overhead.
Frequently Asked Questions
What indicator types are available in NautilusTrader?
NautilusTrader provides a comprehensive library including moving averages (EMA, SMA, WMA, Wilders), momentum indicators (RSI, MACD), volatility measures (Bollinger Bands, ATR), and volume-based tools. All are implemented as cdef classes in the nautilus_trader.indicators package for C-speed performance.
How do I know when an indicator is ready to use?
Check the initialized boolean property before accessing value. An indicator becomes initialized once it has processed at least period data points. Accessing value before initialization may return None or incomplete calculations depending on the specific indicator implementation.
Can I use indicators with tick data instead of bars?
Yes. Use register_indicator_for_quotes() or register_indicator_for_trade_ticks() instead of register_indicator_for_bars(). The indicator will then receive QuoteTick or TradeTick objects via its handle_quote_tick or handle_trade_tick methods, updating its value property accordingly.
Where can I find example strategies using built-in indicators?
The repository includes a complete working example at examples/backtest/example_07_using_indicators/strategy.py. This file demonstrates creating an EMA indicator via MovingAverageFactory, registering it for bar updates, and accessing values within the on_bar callback to generate trading signals.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →