nautechsystems/nautilus_trader · error · anyhow::Error

Unsupported underlying type '{underlying_type}' for TRADIFI_

Error message

Unsupported underlying type '{underlying_type}' for TRADIFI_PERPETUAL symbol '{}'

What it means

Binance TRADIFI_PERPETUAL instruments (perpetuals on tokenized traditional-finance underlyings) get their AssetClass from exchangeInfo's underlyingType. Only EQUITY, KR_EQUITY, HK_EQUITY, PREMARKET and COMMODITY are mapped; parse_tradifi_asset_class bails on any other underlyingType string.

Source

Thrown at crates/adapters/binance/src/common/parse.rs:126

        Err(e) => {
            log::warn!("{e}; using initialization timestamp");
            ts_init
        }
    }
}

fn parse_tradifi_asset_class(symbol: &BinanceFuturesUsdSymbol) -> anyhow::Result<AssetClass> {
    let underlying_type = symbol.underlying_type.as_deref().with_context(|| {
        format!(
            "Missing underlying type for TRADIFI_PERPETUAL symbol '{}'",
            symbol.symbol
        )
    })?;

    match underlying_type {
        "EQUITY" | "KR_EQUITY" | "HK_EQUITY" | "PREMARKET" => Ok(AssetClass::Equity),
        "COMMODITY" => Ok(AssetClass::Commodity),
        _ => anyhow::bail!(
            "Unsupported underlying type '{underlying_type}' for TRADIFI_PERPETUAL symbol '{}'",
            symbol.symbol
        ),
    }
}

/// Returns a currency from the internal map or creates a new crypto currency.
pub fn get_currency(code: &str) -> Currency {
    Currency::get_or_create_crypto(code)
}

/// Extracts filter values from Binance symbol filters array.
fn get_filter<'a>(filters: &'a [Value], filter_type: &str) -> Option<&'a Value> {
    filters.iter().find(|f| {
        f.get("filterType")
            .and_then(|v| v.as_str())
            .is_some_and(|t| t == filter_type)
    })

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Update the Binance adapter to a release that maps the new underlying type
  2. Until updated, exclude the symbol from instrument loading via the filter config
  3. Contribute a mapping for the new underlyingType in parse_tradifi_asset_class (crates/adapters/binance/src/common/parse.rs)

Example fix

// before
match underlying_type {
    "EQUITY" | "KR_EQUITY" | "HK_EQUITY" | "PREMARKET" => Ok(AssetClass::Equity),
    "COMMODITY" => Ok(AssetClass::Commodity),
    _ => anyhow::bail!(...),
}

// after: map the newly listed class
    "EQUITY" | "KR_EQUITY" | "HK_EQUITY" | "PREMARKET" => Ok(AssetClass::Equity),
    "COMMODITY" | "METAL" => Ok(AssetClass::Commodity),
Defensive patterns

Strategy: try-catch

Validate before calling

const MAPPED_UNDERLYING: &[&str] = &["EQUITY", "KR_EQUITY", "HK_EQUITY", "PREMARKET", "COMMODITY"];
fn underlying_type_supported(t: &str) -> bool {
    MAPPED_UNDERLYING.contains(&t)
}

Type guard

fn is_supported_tradifi_underlying(underlying_type: &str) -> bool {
    matches!(underlying_type, "EQUITY" | "KR_EQUITY" | "HK_EQUITY" | "PREMARKET" | "COMMODITY")
}

Try / catch

for symbol in symbols {
    if let Err(e) = parse_symbol(&symbol) {
        if e.to_string().contains("Unsupported underlying type") {
            log::warn!("skipping unmapped TradFi symbol {}: {e}", symbol.symbol);
            continue;
        }
        return Err(e);
    }
}

Prevention

When it happens

Trigger: exchangeInfo returns a TRADIFI_PERPETUAL symbol with an underlyingType outside the mapped set - e.g. Binance launches a new class (FX, rates, metals) after your adapter version was cut.

Common situations: Running an older adapter against a live exchange that has listed new TradFi product types; instrument loading suddenly failing for one new symbol after an exchange rollout.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16). Data as JSON: /api/errors/818b141b0365a4fb. Report an issue: GitHub.