{"record":{"id":"d171c7ee346fd758","repo":"nautechsystems/nautilus_trader","slug":"cannot-add-a-multi-currency-spot-instrument-instr","errorCode":null,"errorMessage":"Cannot add a multi-currency spot instrument {instrument_id} for a venue with a single-currency CASH account","messagePattern":"Cannot add a multi-currency spot instrument (.+?) for a venue with a single-currency CASH account","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/backtest/src/engine.rs","lineNumber":355,"sourceCode":"    /// Adds an instrument to the backtest engine for the specified venue.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if:\n    /// - The instrument's associated venue has not been added via `add_venue`.\n    /// - Attempting to add a `CurrencyPair` instrument for a single-currency CASH account.\n    pub fn add_instrument(&mut self, instrument: &InstrumentAny) -> anyhow::Result<()> {\n        let instrument_id = instrument.id();\n        if let Some(exchange) = self.venues.get(&instrument.id().venue) {\n            let previous_expiration_ns = exchange.borrow().instrument_expiration(instrument_id);\n\n            if matches!(\n                instrument,\n                InstrumentAny::CurrencyPair(_) | InstrumentAny::TokenizedAsset(_)\n            ) && exchange.borrow().account_type != AccountType::Margin\n                && exchange.borrow().base_currency.is_some()\n            {\n                anyhow::bail!(\n                    \"Cannot add a multi-currency spot instrument {instrument_id} for a venue with a single-currency CASH account\"\n                )\n            }\n            exchange.borrow_mut().add_instrument(instrument.clone())?;\n            if let Some(expiration_ns) = instrument.expiration_ns() {\n                self.set_instrument_expiration_timer(exchange, instrument_id, expiration_ns)?;\n            }\n\n            if let Some(previous_expiration_ns) = previous_expiration_ns\n                && instrument.expiration_ns() != Some(previous_expiration_ns)\n                && !exchange\n                    .borrow()\n                    .has_unprocessed_instrument_expiration(previous_expiration_ns)\n            {\n                let timer_name = Self::instrument_expiration_timer_name(\n                    instrument_id.venue,\n                    previous_expiration_ns,\n                );","sourceCodeStart":337,"sourceCodeEnd":373,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/backtest/src/engine.rs#L337-L373","documentation":"A CASH account with a base currency is single-currency: it cannot settle instruments quoted/settled in multiple currencies. When adding an instrument to a simulated exchange, the engine rejects multi-currency spot instruments (CurrencyPair or TokenizedAsset) if the venue's account is single-currency CASH, preventing account/SETTLEMENT inconsistencies during the backtest.","triggerScenarios":"Calling add_instrument with an InstrumentAny::CurrencyPair or TokenizedAsset (e.g. BTC/USDT) on a venue configured with a CASH account that has Some(base_currency) — only Margin accounts (or single-currency quote) accept it.","commonSituations":"Configuring the venue account as CASH with base_currency=USD but loading crypto spot pairs (multi-currency by nature); reusing a single-currency FX venue config for crypto instruments; forgetting to set account_type=Margin for crypto spots.","solutions":["Set the venue's account to Margin (account_type: AccountType::Margin) for multi-currency crypto spot instruments","Remove base_currency from the venue config so the CASH account is not single-currency, if multi-currency CASH is intended","Add only single-currency instruments matching the venue's base_currency","Split venues: one single-currency CASH venue per currency pair group"],"exampleFix":"// before\nlet account = AccountConfig::cash(base_currency: Some(USD));  // single-currency\nengine.add_instrument(venue, currency_pair_btcusdt)?;\n// after\nlet account = AccountConfig::margin();  // multi-currency capable\nengine.add_venue(SimulatedVenueConfig{ account, .. });\nengine.add_instrument(venue, currency_pair_btcusdt)?;","handlingStrategy":"validation","validationCode":"fn can_accept(instrument: &InstrumentAny, account_type: AccountType, base_currency: Option<Currency>) -> bool {\n    let multi_ccy = matches!(instrument, InstrumentAny::CurrencyPair(_) | InstrumentAny::TokenizedAsset(_));\n    !(multi_ccy && account_type == AccountType::Cash && base_currency.is_some())\n}\n// call before engine.add_instrument(...)","typeGuard":null,"tryCatchPattern":"if let Err(e) = engine.add_instrument(venue, instrument) {\n    if e.to_string().contains(\"single-currency CASH account\") {\n        anyhow::bail!(\"Set the venue account to Margin (or remove base_currency) for {}\", instrument.id());\n    }\n    return Err(e);\n}","preventionTips":["Use Margin accounts for multi-currency crypto spot venues","Only assign base_currency to single-currency FX-style venues","Validate instrument currency-set against account type in config loading"],"tags":["backtest","account","instrument","validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}