nautechsystems/nautilus_trader · error

Strategy {strategy_id} is already registered

Error message

Strategy {strategy_id} is already registered

What it means

Thrown by `prepare_python_strategy_instance` when the StrategyId derived from the strategy's class name and order_id_tag is already in the trader's `strategy_ids` set. Each strategy on a trader needs a unique ID because order IDs and client routing are keyed by it. Registration is aborted before the strategy is bound.

Source

Thrown at crates/system/src/python/registration.rs:235

                configure_py_strategy(&mut py_strategy_ref, config_obj)?;
            }

            // Mirrors the native path: a configured ID is kept, otherwise the runtime class name
            // takes the configured order ID tag, or the next positional tag
            let runtime_order_id_tag = py_strategy_ref.order_id_tag();
            let strategy_id = if let Some(strategy_id) = py_strategy_ref.configured_strategy_id() {
                strategy_id
            } else {
                let order_id_tag = normalize_order_id_tag(runtime_order_id_tag.as_deref())
                    .map_or_else(
                        || format!("{:03}", existing_order_id_tags.len()),
                        str::to_string,
                    );
                StrategyId::new_checked(format!("{class_name}-{order_id_tag}"))?
            };

            if self.strategy_ids.contains(&strategy_id) {
                anyhow::bail!("Strategy {strategy_id} is already registered");
            }
            ensure_unique_order_id_tag(&existing_order_id_tags, strategy_id.get_tag())?;

            py_strategy_ref.set_strategy_id(strategy_id)?;
            py_strategy_ref.set_python_instance(bound)?;

            Ok(py_strategy_ref.strategy_id())
        })?;

        // Rejected here as well as on commit so a caller which acts between the two phases, such
        // as registering external order claims, does not act on a doomed registration
        self.ensure_component_id_available(ComponentId::from(strategy_id))?;

        Ok(strategy_id)
    }

    /// Commits a previously prepared Python strategy instance.
    ///

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Set a distinct `order_id_tag` on each strategy instance of the same class
  2. Remove the duplicate strategy from your config or add-loop
  3. Check `trader.strategy_ids` before adding and skip already-registered strategies
  4. Use unique strategy config names if your builder derives the tag from config

Example fix

// before
strategy = MyStrategy(config)  # order_id_tag defaults to '001'
trader.add_strategy(strategy)
strategy2 = MyStrategy(config2)  # same tag -> duplicate id
trader.add_strategy(strategy2)
// after
strategy2 = MyStrategy(config2 with order_id_tag='002')
Defensive patterns

Strategy: validation

Validate before calling

used_tags = set()
for strat_cfg in strategy_configs:
    tag = strat_cfg.order_id_tag
    assert tag not in used_tags, f"duplicate order_id_tag {tag}"
    used_tags.add(tag)

Type guard

def has_unique_tag(strategies: list) -> bool:
    tags = [s.order_id_tag for s in strategies]
    return len(tags) == len(set(tags))

Try / catch

try:
    trader.add_strategy(strategy)
except ValueError as e:
    if "Strategy" in str(e) and "already registered" in str(e):
        strategy.set_order_id_tag(next_free_tag())
        trader.add_strategy(strategy)
    else:
        raise

Prevention

When it happens

Trigger: Adding a second instance of the same strategy class without varying its `order_id_tag` (StrategyId defaults to `{class_name}-{order_id_tag}`); explicitly passing an order_id_tag that another strategy already uses.

Common situations: Running multiple instances of the same strategy class in one trader config but forgetting to set distinct `order_id_tag` values (e.g. two EmaCross strategies both tagged 001); programmatic loops that add the same configured strategy twice.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/cddd63c78e865cf4. Report an issue: GitHub.