Use when creating a new trading strategy, modifying strategy logic, adding indicators, or working on position sizing. Covers the 4-file registration pattern, on_bar() flow, and Nautilus indicator gotchas.
Installation
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Use when creating a new trading strategy, modifying strategy logic, adding indicators, or working on position sizing. Covers the 4-file registration pattern, on_bar() flow, and Nautilus indicator gotchas.
Strategy Development Guide
See also: docs/agent/nautilus.md (engine lifecycle, LogGuard, strategy config pattern)
The 4-File Registration Pattern
Every strategy requires exactly 4 components registered in a specific order:
File 1: Strategy Class (src/core/strategies/<name>.py)
from nautilus_trader.trading.strategy import Strategy, StrategyConfig
from src.core.strategy_registry import StrategyRegistry, register_strategy
from src.models.strategy import MyParameters # File 3classMyConfig(StrategyConfig):
"""Config class — all strategy parameters live here."""
instrument_id: InstrumentId
bar_type: BarType
# ... strategy-specific params with defaults@register_strategy(
name="my_strategy",
description="What this strategy does",
aliases=["mystrat", "ms"],
)classMyStrategy(Strategy):
def__init__(self, config: MyConfig) -> None:
super().__init__(config)
# Store config values as instance attrs# Initialize indicators# Initialize state tracking variablesdefon_start(self) -> None:
self.subscribe_bars(self.bar_type)
defon_stop(self) -> None:
self.close_all_positions(self.instrument_id)
self.unsubscribe_bars(self.bar_type)
defon_bar(self, bar: Bar) -> None:
# See on_bar() flow below
...
defon_dispose(self) -> None:
pass
File 2: Config class (same file as Strategy, defined above the class)
The StrategyConfig subclass holds all parameters. Nautilus requires instrument_id and bar_type at minimum.
Every on_bar method must follow this exact sequence:
defon_bar(self, bar: Bar) -> None:
# 1. Store current bar (needed for position sizing)self._current_bar = bar
# 2. Update ALL indicators with new barself.indicator.handle_bar(bar)
# 3. Check if indicators are initialized (have enough data)ifnotself.indicator.initialized:
return# 4. Read indicator values
value = self.indicator.value
# 5. Check for signals (only if previous values exist for crossover detection)ifself._prev_value isnotNone:
self._check_signals(value)
# 6. Store current values for next iterationself._prev_value = value
Nautilus RSI returns 0-1 range, NOT 0-100 — buy threshold of 10 in traditional terms = 0.10 in Nautilus
@register_strategy decorator must appear directly above the class, BEFORE the 3 StrategyRegistry.set_*() calls at module bottom
instrument_id and bar_type are REQUIRED in every StrategyConfig — the engine won't start without them
Bar type format: {SYMBOL}.{VENUE}-{STEP}-{STEP_TYPE}-{PRICE_TYPE}-EXTERNAL for catalog data, -INTERNAL for mock data
Use self.cache.positions() to check open positions — always up-to-date from the engine cache
Close positions before opening opposite — check has_long/has_short before submitting new orders
Order Submission Pattern
# Market order
order = self.order_factory.market(
instrument_id=self.instrument_id,
order_side=OrderSide.BUY, # or OrderSide.SELL
quantity=self._calculate_position_size(),
)
self.submit_order(order)
# Close existing positionself.close_position(position)
Strategy Creation Checklist
Create strategy file in src/core/strategies/<name>.py
Define <Name>Config(StrategyConfig) with all parameters
Apply @register_strategy() decorator with name, description, aliases