nautechsystems/nautilus_trader · error

missing fee for spread fill report sprd_id={}; OKX sprd-orde

Error message

missing fee for spread fill report sprd_id={}; OKX sprd-orders updates omit fee

What it means

Fill reports require a commission/fee value, but OKX sprd-orders channel updates do not include a fee field. The parser deliberately bails to signal that a spread fill report cannot be built from this channel — fills with fees must come from another source (e.g. a fills channel or REST).

Source

Thrown at crates/adapters/okx/src/websocket/parse.rs:2044

            if (current_filled - prev_qty).is_zero() {
                log::debug!(
                    "Skipping duplicate spread fill: acc_fill_sz='{}' unchanged from previous={}",
                    msg.acc_fill_sz,
                    prev_qty
                );
                return Ok(None);
            }
        }
    } else {
        anyhow::bail!(
            "Cannot determine spread fill quantity: fill_sz='{}' and acc_fill_sz='{}'",
            msg.fill_sz,
            msg.acc_fill_sz
        );
    }

    anyhow::bail!(
        "missing fee for spread fill report sprd_id={}; OKX sprd-orders updates omit fee",
        msg.sprd_id
    )
}

/// Parses an OKX order message into a Nautilus fill report.
///
/// # Errors
///
/// Returns an error if order quantities, prices, or fees cannot be parsed.
pub fn parse_fill_report(
    msg: &OKXOrderMsg,
    instrument: &InstrumentAny,
    account_id: AccountId,
    previous_fee: Option<Money>,
    previous_filled_qty: Option<Quantity>,
    ts_init: UnixNanos,
) -> anyhow::Result<Option<FillReport>> {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Use the dedicated spread public/executes (fills) channel or REST spread fills endpoint, which includes fee data, to build fill reports
  2. Pre-populate a fee cache keyed by sprd_id if the design supports it (as parse_fill_report does for orders)
  3. Treat sprd-orders updates as order-status only and merge fees from a separate source

Example fix

// before
let fill = parse_spread_order_fill_report(&msg, prev_qty)?; // bails: missing fee
// after
// subscribe to the spread fills channel instead of deriving fills from sprd-orders
ws.subscribe("sprd-public-executes", ...).await?;
Defensive patterns

Strategy: fallback

Try / catch

match parse_spread_order_fill_report(&msg, prev_qty) {
    Err(e) if e.to_string().contains("missing fee for spread fill") => {
        // fetch fee via REST spread fills or the fills channel, then merge
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling parse_spread_order_fill_report (via parse_spread_order_event / parse_spread_order_msg) on any spread fill — the sprd-orders update never carries a fee, so this always triggers once quantity parsing succeeds.

Common situations: Subscribing to sprd-orders expecting complete fill data; building fills from the order channel instead of the dedicated spread fills channel; fee-cache not populated for spread orders.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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