nautechsystems/nautilus_trader · error

Portfolio message has empty currency code

Error message

Portfolio message has empty currency code

What it means

parse_portfolio_to_account_state converts a Deribit portfolio update into an AccountState keyed by currency. A portfolio message with a blank currency string cannot identify the asset, so it is rejected.

Source

Thrown at crates/adapters/deribit/src/common/parse.rs:697

/// subscription channel into Nautilus account state events.
///
/// # Returns
///
/// An `AccountState` containing balances and margin information.
///
/// # Errors
///
/// Returns an error if Money conversion fails for any balance field.
pub fn parse_portfolio_to_account_state(
    portfolio: &DeribitPortfolioMsg,
    account_id: AccountId,
    ts_init: UnixNanos,
) -> anyhow::Result<AccountState> {
    let ccy_str = portfolio.currency.trim();

    // Skip empty currency codes
    if ccy_str.is_empty() {
        anyhow::bail!("Portfolio message has empty currency code");
    }

    let currency = Currency::get_or_create_crypto_with_context(
        ccy_str,
        Some("DERIBIT - Parsing portfolio update"),
    );

    // See `parse_account_state` for the rationale: cross-margin uses equity and
    // `available_withdrawal_funds` (per-currency consistency, conservative free balance);
    // segregated uses `margin_balance` and `available_funds` (per-currency scoped).
    let is_cross_margin = portfolio.cross_collateral_enabled.unwrap_or(false);
    let (total, free) = if is_cross_margin {
        (
            portfolio.equity,
            portfolio
                .available_withdrawal_funds
                .unwrap_or(Decimal::ZERO),
        )

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Skip or log the message instead of hard-failing the stream
  2. Validate the Deribit payload schema; update the client model if Deribit changed it
  3. Check for upstream Deribit issues affecting portfolio channel

Example fix

// before
if ccy_str.is_empty() {
    anyhow::bail!("Portfolio message has empty currency code");
}
// after
if ccy_str.is_empty() {
    log::debug!("Skipping portfolio message with empty currency");
    return Ok(None); // or continue
}
Defensive patterns

Strategy: try-catch

Validate before calling

if portfolio.currency.trim().is_empty() {
    // skip message before parsing
    return Ok(());
}

Type guard

fn has_currency(p: &DeribitPortfolio) -> bool { !p.currency.trim().is_empty() }

Try / catch

match parse_portfolio_to_account_state(&portfolio, ts_init) {
    Err(e) if e.to_string().contains("empty currency code") => {
        debug!("ignoring empty portfolio update");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Deribit sends a portfolio message whose currency field is empty or whitespace-only after trimming.

Common situations: Malformed/empty Deribit websocket payloads, API schema changes, upstream bugs in portfolio snapshot construction.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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