一键导入
nt-backtest
Use when working with backtesting engine, fill models, matching engine, simulated exchange, or backtest configuration in NautilusTrader.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when working with backtesting engine, fill models, matching engine, simulated exchange, or backtest configuration in NautilusTrader.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Use when working with strategy logic, order execution, risk management, position/portfolio tracking, or exec algorithms in NautilusTrader.
Use when working with market data pipelines, data storage, ParquetDataCatalog, serialization, or cache operations in NautilusTrader.
Use when working with live trading nodes, system boot, NautilusKernel, engine configuration, component lifecycle, or deployment in NautilusTrader.
Use when working with exchange or data provider adapters, HTTP/WebSocket clients, instrument providers, or venue integration in NautilusTrader.
Use when learning NautilusTrader from scratch or deepening understanding. Provides a structured curriculum from installation to building custom NT components in Python and Rust.
Use when working with domain model types, instruments, identifiers, value types, enums, or currencies in NautilusTrader.
| name | nt-backtest |
| description | Use when working with backtesting engine, fill models, matching engine, simulated exchange, or backtest configuration in NautilusTrader. |
NautilusTrader backtesting domain — backtest engine, simulated exchange, fill models, and matching logic.
Python modules: backtest/, backtest/models/, execution/matching_core (simulated exchange context)
Rust crates: nautilus_backtest, nautilus_execution (matching subset)
BacktestNode, BacktestEngine)BacktestRunConfig, BacktestVenueConfig)nt-tradingnt-datant-livent-signalsBacktestNode is the high-level API for running backtests with configuration:
from nautilus_trader.backtest.node import BacktestNode
from nautilus_trader.config import (
BacktestRunConfig,
BacktestDataConfig,
BacktestVenueConfig,
BacktestEngineConfig,
)
config = BacktestRunConfig(
engine=BacktestEngineConfig(
strategies=[
ImportableStrategyConfig(
strategy_path="my_module:MyStrategy",
config_path="my_module:MyStrategyConfig",
config={"instrument_id": "ETHUSDT-PERP.BINANCE", ...},
),
],
),
data=[
BacktestDataConfig(
catalog_path="/path/to/data",
data_cls="nautilus_trader.model.data:Bar",
instrument_id="ETHUSDT-PERP.BINANCE",
bar_type="ETHUSDT-PERP.BINANCE-1-MINUTE-LAST-EXTERNAL",
),
],
venues=[
BacktestVenueConfig(
name="BINANCE",
oms_type="NETTING",
account_type="MARGIN",
base_currency=None,
starting_balances=["10_000 USDT"],
),
],
)
node = BacktestNode(configs=[config])
results = node.run()
BacktestEngine provides lower-level control, useful for strategy testing:
from nautilus_trader.backtest.engine import BacktestEngine
from nautilus_trader.config import BacktestEngineConfig
engine = BacktestEngine(config=BacktestEngineConfig())
# Add venue
engine.add_venue(
venue=Venue("SIM"),
oms_type=OmsType.HEDGING,
account_type=AccountType.MARGIN,
base_currency=USD,
starting_balances=[Money(1_000_000, USD)],
)
# Add data
engine.add_data(bars)
engine.add_instrument(instrument)
# Add strategy
engine.add_strategy(strategy)
# Run
engine.run()
# Get results
engine.trader.generate_order_fills_report()
engine.trader.generate_positions_report()
BacktestVenueConfig(
name="SIM",
oms_type="HEDGING", # HEDGING or NETTING
account_type="MARGIN", # CASH or MARGIN
base_currency="USD",
starting_balances=["1_000_000 USD"],
fill_model=FillModel(), # Optional custom fill model
# latency_model=LatencyModel(), # Optional latency simulation
)
from nautilus_trader.backtest.models import FillModel
class MyFillModel(FillModel):
def __init__(self, ...):
super().__init__()
# TODO: Initialize model parameters
# Override fill probability/slippage methods as needed
See templates/fill_model.py for full template.
Configure fee structures per venue:
from nautilus_trader.model.objects import Money
# Via BacktestVenueConfig
BacktestVenueConfig(
...,
fee_model=MakerTakerFeeModel(
maker_fee=Decimal("0.0002"),
taker_fee=Decimal("0.0004"),
),
)
NautilusTrader provides two Rust APIs for backtesting: BacktestEngine (low-level) and BacktestNode (high-level with catalog streaming). Both run without Python.
Add to your Cargo.toml:
[dependencies]
nautilus-backtest = { version = "0.55", features = ["streaming"] }
nautilus-execution = "0.55"
nautilus-model = { version = "0.55", features = ["stubs"] }
nautilus-persistence = "0.55"
nautilus-trading = { version = "0.55", features = ["examples"] }
ahash = "0.8"
anyhow = "1"
tempfile = "3"
ustr = "1"
Drop streaming, nautilus-persistence, tempfile, ustr if only using the low-level BacktestEngine.
Feature flags:
| Flag | Crate | Effect |
|---|---|---|
high-precision | nautilus-model | 16-digit fixed precision (default 9). Required for crypto. |
stubs | nautilus-model | Test instrument stubs (audusd_sim, etc.) |
examples | nautilus-trading | Example strategies (EmaCross, GridMarketMaker) |
streaming | nautilus-backtest | Catalog-based data streaming via BacktestNode |
Direct control: build engine, add venues/instruments, load data in memory, register strategies, run.
use ahash::AHashMap;
use nautilus_backtest::{config::BacktestEngineConfig, engine::BacktestEngine};
use nautilus_execution::models::{fee::FeeModelAny, fill::FillModelAny};
use nautilus_model::{
enums::{AccountType, BookType, OmsType},
identifiers::Venue,
instruments::{Instrument, InstrumentAny, stubs::audusd_sim},
types::{Money, Quantity},
};
use nautilus_trading::examples::strategies::EmaCross;
// 1. Create engine
let mut engine = BacktestEngine::new(BacktestEngineConfig::default())?;
// 2. Add venue (31 args — most are Option with None defaults)
engine.add_venue(
Venue::from("SIM"),
OmsType::Hedging,
AccountType::Margin,
BookType::L1_MBP,
vec![Money::from("1_000_000 USD")],
None, // base_currency
None, // default_leverage
AHashMap::new(), // per-instrument leverages
None, // margin_model
vec![], // simulation modules
FillModelAny::default(),
FeeModelAny::default(),
None, // latency_model
None, // routing
None, // reject_stop_orders
None, // support_gtd_orders
None, // support_contingent_orders
None, // use_position_ids
None, // use_random_ids
None, // use_reduce_only
None, // use_message_queue
None, // use_market_order_acks
None, // bar_execution
None, // bar_adaptive_high_low_ordering
None, // trade_execution
None, // liquidity_consumption
None, // allow_cash_borrowing
None, // frozen_account
None, // queue_position
None, // oto_full_trigger
None, // price_protection_points
)?;
// 3. Add instrument and data
let instrument = InstrumentAny::CurrencyPair(audusd_sim());
let instrument_id = instrument.id();
engine.add_instrument(&instrument)?;
let quotes = generate_quotes(instrument_id);
engine.add_data(quotes, None, true, true);
// 4. Register strategy and run
let strategy = EmaCross::new(instrument_id, Quantity::from("100000"), 10, 20);
engine.add_strategy(strategy)?;
engine.run(None, None, None, false)?;
Run the example: cargo run -p nautilus-backtest --features examples --example engine-ema-cross
Loads data from ParquetDataCatalog and streams in configurable chunks. Requires streaming feature.
use nautilus_backtest::{
config::{BacktestDataConfig, BacktestEngineConfig, BacktestRunConfig, BacktestVenueConfig, NautilusDataType},
node::BacktestNode,
};
use nautilus_model::enums::{AccountType, BookType, OmsType};
use nautilus_persistence::backend::catalog::ParquetDataCatalog;
use ustr::Ustr;
// 1. Write data to catalog
let catalog = ParquetDataCatalog::new(catalog_path, None, None, None, None);
catalog.write_instruments(vec![instrument])?;
catalog.write_to_parquet(quotes, None, None, None)?;
// 2. Configure the run (configs use builder pattern)
let venue_config = BacktestVenueConfig::builder()
.name(Ustr::from("SIM"))
.oms_type(OmsType::Hedging)
.account_type(AccountType::Margin)
.book_type(BookType::L1_MBP)
.starting_balances(vec!["1_000_000 USD".to_string()])
.build();
let data_config = BacktestDataConfig::builder()
.data_type(NautilusDataType::QuoteTick)
.catalog_path(catalog_path.to_string())
.instrument_id(instrument_id)
.build();
let run_config = BacktestRunConfig::builder()
.venues(vec![venue_config])
.data(vec![data_config])
.maybe_chunk_size(Some(100))
.build();
// 3. Build, add strategies, run
let mut node = BacktestNode::new(vec![run_config])?;
node.build()?;
let engine = node.get_engine_mut("ema-cross-run").context("engine not found")?;
engine.add_strategy(EmaCross::new(instrument_id, Quantity::from("100000"), 10, 20))?;
node.run()?;
Run the example: cargo run -p nautilus-backtest --features examples,streaming --example node-ema-cross
Actors register with add_actor (same pattern as strategies):
let actor = SpreadMonitor::new(instrument_id);
engine.add_actor(actor)?;
Strategies that need historical data on on_start (indicator seeding,
parameter calibration) request a window that ends at the run start. If
your run starts at the catalog's earliest data, the request returns
nothing.
Convention: ingest N+1 days, but set --date-from to the second
day. The first ingested day becomes the 24h warmup window:
# Ingest 7 days
as-mm-ingest --date-from 2026-04-06 --date-to 2026-04-12 ...
# Backtest only 6 days — first day is warmup buffer
as-mm-backtest --date-from 2026-04-07 --date-to 2026-04-12 ...
request_quotes / request_trades issued at run start now have data to
deliver. See nt-trading/references/guides/rust_patterns.md §2 for the
warmup pattern itself.
Backtest data cannot be loaded for an instrument the engine doesn't know about. Always:
// 1. Read instruments from catalog
let instruments = catalog.query_instruments(Some(vec![instrument_id]))?;
for inst in &instruments { engine.add_instrument(inst)?; }
// 2. Then load data
let quotes = catalog.query_quote_ticks(Some(vec![instrument_id]), None, None)?;
engine.add_data(quotes, None, true, true);
Same order in BacktestNode: instruments are loaded from catalog at
build time; if your catalog lacks them, the run aborts before the
strategy starts.
The default FillModelAny::default() is aggressor-style: every
limit order is filled at the touch as soon as the market reaches it,
ignoring queue priority. For market-making strategies this overstates
fill rates by 2–10× vs production.
Implication: backtest PnL is not predictive of live PnL until you either (a) implement a queue-aware fill model, or (b) externally validate fill rates with shadow-mode live runs.
Document this loudly in your README — anyone reading backtest reports should know they're optimistic by construction.
For real strategies, separate concerns into three binaries sharing one strategy:
strategies/my-strat/
├── src/strategy.rs ← used by both backtest and live
├── bin/ingest.rs ← venue archive → ParquetDataCatalog
├── bin/backtest.rs ← BacktestEngine + strategy
└── bin/live.rs ← LiveNode + strategy
Strategy code is identical between backtest and live — no #[cfg]
branches. The only difference is which engine hosts it. See
nt-trading/references/guides/rust_patterns.md §6 for the full pattern.
Rust fill models for complex matching logic (order book simulation, market impact):
use nautilus_model::types::Price;
pub struct MyFillModel {
prob_fill_on_limit: f64,
prob_slippage: f64,
}
impl MyFillModel {
pub fn is_limit_filled(&self) -> bool { /* ... */ }
pub fn is_slipped(&self) -> bool { /* ... */ }
}
The matching engine core lives in crates/execution/src/matching_core/. Extend it for custom matching behavior.
#[pyclass] and #[pymethods] for Python-visible typescrates/pyo3/src/lib.rsabort_on_panic(|| { ... })serde = { workspace = true })BacktestEngine timing for consistent benchmarkspy-spy or cProfile for hotspot identificationreferences/guides/benchmarking.md for detailed practicesrandom_seed in fill model for stochastic fillsts_event on data determines processing orderreferences/concepts/ — backtesting, order bookreferences/api/ — backtest APIreferences/guides/ — benchmarking practices, benchmarking review checklist, run_rust_backtestreferences/examples/ — clock timer, portfolio, cache usage, Rust backtests (engine_ema_cross, node_ema_cross), model configstemplates/ — fill_model.pynt-trading/references/guides/rust_patterns.md for strategy-side patterns (warmup, atomic state, three-binary), nt-trading/references/guides/troubleshooting_rust.md for debug workflow.