nautechsystems/nautilus_trader · error

Funding rates only available for perpetuals, not {instrument

Error message

Funding rates only available for perpetuals, not {instrument_id}

What it means

Beyond the product-type check, subscribe_funding_rates verifies that a cached instrument matching the requested ID is actually a CryptoPerpetual. If the instrument is loaded in the client's cache but is another type (spot, option, futures contract), the subscription is rejected because funding rates are a perpetual-only concept in this adapter.

Source

Thrown at crates/adapters/bybit/src/data.rs:1135

        );
        Ok(())
    }

    fn subscribe_funding_rates(&mut self, cmd: SubscribeFundingRates) -> anyhow::Result<()> {
        let instrument_id = cmd.instrument_id;
        let product_type = self
            .get_product_type_for_instrument(instrument_id)
            .unwrap_or(BybitProductType::Linear);

        if product_type == BybitProductType::Spot || product_type == BybitProductType::Option {
            anyhow::bail!("Funding rates not available for {product_type:?} instruments");
        }

        let guard = self.instruments.load();
        if let Some(instrument) = guard.get(&instrument_id)
            && !matches!(instrument, InstrumentAny::CryptoPerpetual(_))
        {
            anyhow::bail!("Funding rates only available for perpetuals, not {instrument_id}");
        }

        let mut should_subscribe = false;
        self.ticker_subs.rcu(|m| {
            let entry = m.entry(instrument_id).or_default();
            should_subscribe = entry.is_empty();
            entry.insert("funding");
        });

        if should_subscribe {
            let ws = self
                .get_ws_client_for_product(product_type)
                .context("no WebSocket client for product type")?
                .clone();

            self.spawn_ws(
                async move {
                    ws.subscribe_ticker(instrument_id)

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Use the exact perpetual instrument ID (CryptoPerpetual) when subscribing to funding rates
  2. Verify what instrument is registered under that ID in the client cache
  3. Load/refresh the correct perpetual instrument before subscribing
  4. Filter non-perpetual instruments out of automated funding-rate subscription loops

Example fix

// before
subscribe_funding_rates("BTCUSDT-27SEP26".into())? // dated future: bails
// after
subscribe_funding_rates("BTCUSDT-PERP".into())? // CryptoPerpetual
Defensive patterns

Strategy: validation

Validate before calling

fn is_perp_cached(client: &BybitDataClient, id: InstrumentId) -> bool {
    client.instruments.load().get(&id)
        .map(|i| matches!(i, InstrumentAny::CryptoPerpetual(_)))
        .unwrap_or(false)
}

Type guard

fn is_perpetual(inst: &InstrumentAny) -> bool {
    matches!(inst, InstrumentAny::CryptoPerpetual(_))
}

Try / catch

if !is_perpetual_cached(&client, id) {
    log::debug!("{id} is not a perpetual; skipping funding subscription");
} else {
    client.subscribe_funding_rates(cmd)?;
}

Prevention

When it happens

Trigger: subscribe_funding_rates called with an instrument_id present in the client's instrument cache whose InstrumentAny variant is not CryptoPerpetual — e.g. an expiring future or an option instrument registered under that ID.

Common situations: Subscribing to funding rates for dated futures or options assuming perpetual-like behavior; instruments cached with a wrong/generic type after a load; mistyped instrument ID strings that resolve to a non-perpetual symbol.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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