docs/how_to/configure_live_trading.md
Set up a TradingNode for live market connectivity. For background on live
trading architecture and reconciliation, see the
Live trading concept guide.
:::danger[Jupyter notebooks not recommended for live trading] Do not run live trading nodes in Jupyter notebooks. Event loop conflicts and operational risks make them unsuitable:
TradingNode's event loop.nest_asyncio are not production-grade.Use Jupyter for backtesting, analysis, and experimentation. For live trading, run nodes as standalone Python scripts or services. :::
:::warning[One TradingNode per process]
Running multiple TradingNode instances concurrently in the same process is not supported due to global singleton state.
Add multiple strategies to a single node, or run additional nodes in separate processes for parallel execution.
See Processes and threads for details. :::
:::warning[Do not block the event loop]
User code on the event loop thread (strategy callbacks, actor handlers, on_event methods)
must return quickly. This applies to both Python and Rust. Blocking operations like model
inference, heavy calculations, or synchronous I/O cause missed fills, stale data, and
delayed order submissions. Offload long-running work to an executor or a separate thread/process.
:::
:::info[Platform differences] Windows signal handling differs from Unix-like systems. If you are running on Windows, please read the note on Windows signal handling for guidance on graceful shutdown behavior and Ctrl+C (SIGINT) support. :::
TradingNodeConfig inherits from NautilusKernelConfig and adds live-specific options.
For background on how config structs handle defaults and Option<T> semantics, see
the Configuration concept guide.
from nautilus_trader.config import TradingNodeConfig
config = TradingNodeConfig(
trader_id="MyTrader-001",
# Component configurations
cache=CacheConfig(),
message_bus=MessageBusConfig(),
data_engine=LiveDataEngineConfig(),
risk_engine=LiveRiskEngineConfig(),
exec_engine=LiveExecEngineConfig(),
portfolio=PortfolioConfig(),
# Client configurations
data_clients={
"BINANCE": BinanceDataClientConfig(),
},
exec_clients={
"BINANCE": BinanceExecClientConfig(),
},
)
| Setting | Default | Description |
|---|---|---|
trader_id | "TRADER-001" | Unique trader identifier (name‑tag format). |
instance_id | None | Optional unique instance identifier. |
timeout_connection | 60.0 | Connection timeout in seconds. |
timeout_reconciliation | 10.0 | Reconciliation timeout in seconds. |
timeout_portfolio | 10.0 | Portfolio initialization timeout. |
timeout_disconnection | 10.0 | Disconnection timeout. |
timeout_post_stop | 5.0 | Post‑stop cleanup timeout. |
Rust-native live systems keep cache behavior in CacheConfig and Redis connection settings in
RedisCacheConfig.
use nautilus_common::{
cache::{CacheConfig, database::CacheDatabaseFactory},
enums::SerializationEncoding,
};
use nautilus_infrastructure::redis::cache::RedisCacheConfig;
let config = CacheConfig {
encoding: SerializationEncoding::MsgPack,
timestamps_as_iso8601: true,
buffer_interval_ms: Some(100),
flush_on_start: false,
..Default::default()
};
let database = RedisCacheConfig {
host: Some("localhost".to_string()),
port: Some(6379),
username: Some("nautilus".to_string()),
password: Some("pass".to_string()),
connection_timeout: 2,
response_timeout: 2,
..Default::default()
};
let cache_database = database
.create(trader_id, instance_id, config.clone())
.await?;
Message bus behavior stays in MessageBusConfig. Redis connection settings live in
RedisMessageBusConfig, which constructs the backing through MessageBusBackingFactory.
use nautilus_common::{
enums::SerializationEncoding,
msgbus::{backing::MessageBusBackingFactory, config::MessageBusConfig},
};
use nautilus_infrastructure::redis::msgbus::RedisMessageBusConfig;
let config = MessageBusConfig {
encoding: SerializationEncoding::Json,
timestamps_as_iso8601: true,
use_instance_id: false,
types_filter: Some(vec!["QuoteTick".to_string(), "TradeTick".to_string()]),
stream_per_topic: false,
autotrim_mins: Some(30),
heartbeat_interval_secs: Some(1),
..Default::default()
};
let backing = RedisMessageBusConfig {
connection_timeout: 2,
response_timeout: 2,
..Default::default()
};
let message_bus_backing = backing.create(
trader_id,
instance_id,
config.clone(),
)?;
A node can connect to multiple venues. This example configures both spot and futures markets for Binance:
config = TradingNodeConfig(
trader_id="MultiVenue-001",
# Multiple data clients for different market types
data_clients={
"BINANCE_SPOT": BinanceDataClientConfig(
account_type=BinanceAccountType.SPOT,
environment=BinanceEnvironment.LIVE,
),
"BINANCE_FUTURES": BinanceDataClientConfig(
account_type=BinanceAccountType.USDT_FUTURES,
environment=BinanceEnvironment.LIVE,
),
},
# Corresponding execution clients
exec_clients={
"BINANCE_SPOT": BinanceExecClientConfig(
account_type=BinanceAccountType.SPOT,
environment=BinanceEnvironment.LIVE,
),
"BINANCE_FUTURES": BinanceExecClientConfig(
account_type=BinanceAccountType.USDT_FUTURES,
environment=BinanceEnvironment.LIVE,
),
},
)
LiveExecEngineConfig controls order processing, execution events, and
venue reconciliation. For full details see the
API Reference.
Recovers missed order and position events to keep system state consistent with the venue.
| Setting | Default | Description |
|---|---|---|
reconciliation | True | Activate reconciliation at startup to align internal state with the venue. |
reconciliation_lookback_mins | None | How far back (minutes) to request past events for reconciling uncached state. |
reconciliation_instrument_ids | None | Include list of instrument IDs to reconcile. |
filtered_client_order_ids | None | Client order IDs to skip during reconciliation (for venue‑side duplicates). |
See Execution reconciliation for details.
Controls which order events and reports the system processes, preventing conflicts across trading nodes.
| Setting | Default | Description |
|---|---|---|
filter_unclaimed_external_orders | False | Drop unclaimed external orders so they do not affect the strategy. |
filter_position_reports | False | Drop position status reports. Useful when multiple nodes trade one account. |
:::note[Order tagging behavior] Reconciliation tags orders by origin:
VENUE tag: external orders discovered at the venue (placed outside this system).RECONCILIATION tag: synthetic orders generated to align position discrepancies.When filter_unclaimed_external_orders is enabled, only VENUE-tagged orders are filtered.
RECONCILIATION-tagged orders are never filtered, so position alignment always succeeds.
:::
Continuous reconciliation keeps runtime execution state aligned after startup by checking in-flight orders, polling open orders, checking position status, and auditing own order books. Configure the loop with these settings. For runtime state-transition rules, retry coordination, and caveats, see Runtime checks.
| Setting | Default | Description |
|---|---|---|
inflight_check_interval_ms | 2,000 ms | How often to check in‑flight order status. Set to 0 to disable. |
inflight_check_threshold_ms | 5,000 ms | Time before an in‑flight order triggers a venue status check. Lower if colocated. |
inflight_check_retries | 5 retries | Retry attempts to verify an in‑flight order with the venue. |
open_check_interval_secs | None | How often (seconds) to check open orders at the venue. None or 0.0 disables. Recommended: 5-10s. |
open_check_open_only | True | When true, query only open orders; when false, fetch full history (resource‑intensive). |
open_check_lookback_mins | 60 min | Lookback window (minutes) for order status polling. Only orders modified within this window. |
open_check_threshold_ms | 5,000 ms | Minimum time since last cached event before acting on venue discrepancies. |
open_check_missing_retries | 5 retries | Max retries before targeted not‑found resolution for eligible orders. |
max_single_order_queries_per_cycle | 10 | Cap on single‑order queries per cycle. Prevents rate‑limit exhaustion. |
single_order_query_delay_ms | 100 ms | Delay (ms) between single‑order queries to avoid rate limits. |
reconciliation_startup_delay_secs | 10.0 s | Delay (seconds) after startup reconciliation before continuous checks begin. |
own_books_audit_interval_secs | None | Interval (seconds) between auditing own order books against public books. |
position_check_interval_secs | None | Interval (seconds) between position consistency checks. On discrepancy, queries for missing fills. None disables. Recommended: 30-60s. |
position_check_lookback_mins | 60 min | Lookback window (minutes) for querying fill reports on position discrepancy. |
position_check_threshold_ms | 5,000 ms | Minimum time since last local activity before acting on position discrepancies. |
position_check_retries | 3 retries | Max attempts per instrument/account before the engine stops retrying that discrepancy. Once exceeded, an error is logged and the discrepancy is no longer actively reconciled until it clears. |
:::warning
open_check_lookback_mins: do not reduce below 60 minutes. A short window
triggers false "missing order" resolutions because orders fall outside the query range.open_check_threshold_ms: increase if venue timestamps lag the local clock, so
recently updated orders are not marked missing prematurely.reconciliation_startup_delay_secs: do not reduce below 10 seconds in production.
The delay lets the system stabilize after startup reconciliation before continuous
checks begin.:::
| Setting | Default | Description |
|---|---|---|
allow_overfills | False | Allow fills exceeding order quantity (logs warning). Useful when reconciliation races fills. |
generate_missing_orders | True | Generate LIMIT orders during reconciliation to align position discrepancies (strategy EXTERNAL, tag RECONCILIATION). |
snapshot_orders | False | Take order snapshots on order events. |
snapshot_positions | False | Take position snapshots on position events. |
snapshot_positions_interval_secs | None | Interval (seconds) between position snapshots. |
debug | False | Enable debug logging for execution. |
Periodically purges closed orders, closed positions, and account events from the in-memory cache, keeping memory bounded during long-running or HFT sessions.
| Setting | Default | Description |
|---|---|---|
purge_closed_orders_interval_mins | None | How often (minutes) to purge closed orders from memory. Recommended: 10-15 min. |
purge_closed_orders_buffer_mins | None | How long (minutes) an order must be closed before purging. Recommended: 60 min. |
purge_closed_positions_interval_mins | None | How often (minutes) to purge closed positions from memory. Recommended: 10-15 min. |
purge_closed_positions_buffer_mins | None | How long (minutes) a position must be closed before purging. Recommended: 60 min. |
purge_account_events_interval_mins | None | How often (minutes) to purge account events from memory. Recommended: 10-15 min. |
purge_account_events_lookback_mins | None | How old (minutes) an account event must be before purging. Recommended: 60 min. |
purge_from_database | False | Also delete from the backing database (Redis/PostgreSQL). Use with caution. |
Setting an interval enables the purge loop; leaving it unset disables scheduling and
deletion. Database records are unaffected unless purge_from_database is true. Each
loop delegates to the cache APIs described in
Cache.
| Setting | Default | Description |
|---|---|---|
qsize | 100,000 | Size of internal queue buffers. |
graceful_shutdown_on_exception | False | Gracefully shut down on unexpected queue processing exceptions (not user code). |
For a complete parameter list see the StrategyConfig
API Reference.
| Setting | Default | Description |
|---|---|---|
strategy_id | None | Unique strategy identifier. |
order_id_tag | None | Unique tag appended to this strategy's order IDs. |
| Setting | Default | Description |
|---|---|---|
oms_type | None | OMS type for position ID and order processing. |
use_uuid_client_order_ids | False | Use UUID4 values for client order IDs. |
external_order_claims | None | Instrument IDs whose external orders and reconciliation activity this strategy claims. |
manage_contingent_orders | False | Automatically manage OTO, OCO, and OUO contingent orders. |
manage_gtd_expiry | False | Manage GTD expirations for orders. |
:::warning
Windows: asyncio event loops do not implement loop.add_signal_handler. As a result,
TradingNode does not receive OS signals via asyncio on Windows. Use Ctrl+C (SIGINT) handling or
programmatic shutdown; SIGTERM parity is not expected on Windows.
:::
On Windows, asyncio event loops do not implement loop.add_signal_handler, so Unix-style
signal integration is unavailable. TradingNode does not receive OS signals via asyncio
on Windows and will not stop gracefully unless you intervene.
Recommended approaches:
run with try/except KeyboardInterrupt and call node.stop() then node.dispose().
Ctrl+C raises KeyboardInterrupt in the main thread, giving you a clean teardown path.ShutdownSystem command programmatically (or call shutdown_system(...) from
an actor/component) to trigger the same shutdown path.The "inflight check loop task still pending" message appears because the normal graceful shutdown path is not triggered. This is tracked as #2785.
The v2 LiveNode already handles Ctrl+C via tokio::signal::ctrl_c() and a Python SIGINT
bridge, so runner and tasks shut down cleanly.
Example pattern for Windows:
try:
node.run()
except KeyboardInterrupt:
pass
finally:
try:
node.stop()
finally:
node.dispose()