nautechsystems/nautilus_trader · error

Funding rates are only available for Derive perpetual instru

Error message

Funding rates are only available for Derive perpetual instruments (got {instrument_id})

What it means

`request_funding_rates` looks up the requested instrument and, via `anyhow::ensure!`, throws when the instrument is not a `CryptoPerpetual`. Derive funding rates are only defined for perpetuals, so requests for spots/options/futures are rejected.

Source

Thrown at crates/adapters/derive/src/data.rs:1241

                params,
            ));

            if let Err(e) = sender.send(DataEvent::Response(response)) {
                log::error!("Failed to send Derive trades response: {e}");
            }
            Ok(())
        });

        Ok(())
    }

    fn request_funding_rates(&self, request: RequestFundingRates) -> anyhow::Result<()> {
        let instrument_id = request.instrument_id;
        let instrument = self
            .instruments
            .get_cloned(&instrument_id)
            .ok_or_else(|| InstrumentLookupError::not_found(instrument_id))?;
        anyhow::ensure!(
            matches!(instrument, InstrumentAny::CryptoPerpetual(_)),
            "Funding rates are only available for Derive perpetual instruments (got {instrument_id})",
        );
        let venue_symbol = format_venue_symbol(&instrument_id)?.to_string();

        let http_client = self.http_client.clone();
        let sender = self.data_sender.clone();
        let clock = self.clock;
        let client_id = request.client_id.unwrap_or(self.client_id);
        let request_id = request.request_id;
        let params = request.params;
        let start = request.start;
        let end = request.end;
        let limit = request.limit.map(NonZeroUsize::get);
        let start_nanos = datetime_to_unix_nanos(start);
        let end_nanos = datetime_to_unix_nanos(end);
        let start_ms = start.map(|dt| dt.as_millisecond());
        let end_ms = end.map(|dt| dt.as_millisecond());

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Use a Derive perpetual instrument ID such as `ETH-PERP.DERIVE`.
  2. Check the instrument type before requesting: `matches!(instrument, InstrumentAny::CryptoPerpetual(_))`.
  3. Filter your instrument list to perpetuals before issuing funding-rate requests.
  4. For non-perpetual instruments, remove funding-rate logic or use a data source that supports them.

Example fix

// before
client.request_funding_rates(RequestFundingRates::new("ETH-20260926-4000-C.DERIVE", ...))?;
// after
client.request_funding_rates(RequestFundingRates::new("ETH-PERP.DERIVE", ...))?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust
if let Some(inst) = cache.instrument(&instrument_id) {
    if !matches!(inst, InstrumentAny::CryptoPerpetual(_)) {
        return Err(anyhow!("funding rates require a perpetual: {instrument_id}"));
    }
}

Type guard

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

Try / catch

if let Err(e) = client.request_funding_rates(req) {
    if e.to_string().contains("perpetual instruments") {
        // skip or switch instrument
    }
}

Prevention

When it happens

Trigger: Calling `request_funding_rates` with an `instrument_id` that resolves to a Derive spot, option, or dated-future instrument instead of a perpetual (e.g. "ETH-20260926-4000-C.DERIVE").

Common situations: Configuring a strategy's funding-rate subscription against an options/dated instrument; automated code that iterates all instruments in the cache and requests funding rates for each; users confusing Derive futures with perpetual swaps.

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/110298919f3e3120. Report an issue: GitHub.