nautechsystems/nautilus_trader · error · anyhow::Error

AX final-settlement fill must also be classified as a block

Error message

AX final-settlement fill must also be classified as a block trade

What it means

parse_fill_report models final settlements as synthetic block fills (venue order id "AX-FILL-{trade_id}"), so it requires any fill with is_final_settlement = true to also carry is_block_trade = true. A fill flagged as final settlement but explicitly not a block trade is ambiguous under that model and is rejected instead of mis-linked to an order.

Source

Thrown at crates/adapters/architect_ax/src/http/parse.rs:659

/// # Errors
///
/// Returns an error if:
/// - Price or quantity fields cannot be parsed.
/// - Fee parsing fails.
/// - Fill classification is inconsistent.
/// - A fill is neither explicitly special nor linked to a valid order ID.
pub fn parse_fill_report(
    fill: &AxFill,
    account_id: AccountId,
    instrument: &InstrumentAny,
    ts_init: UnixNanos,
) -> anyhow::Result<FillReport> {
    let instrument_id = instrument.id();

    let trade_id = TradeId::new_checked(&fill.trade_id).context("Invalid trade_id in Ax fill")?;
    let is_block_trade = fill.is_block_trade;
    let is_final_settlement = fill.is_final_settlement;
    anyhow::ensure!(
        !(is_final_settlement == Some(true) && is_block_trade == Some(false)),
        "AX final-settlement fill must also be classified as a block trade"
    );

    let is_special_fill = is_block_trade == Some(true) || is_final_settlement == Some(true);
    let venue_order_id = if is_special_fill {
        VenueOrderId::new_checked(format!("AX-FILL-{}", fill.trade_id))
            .context("Invalid synthetic venue order ID for AX fill")?
    } else {
        let order_id = fill
            .order_id
            .as_deref()
            .context("AX fill is missing order_id and explicit special-fill classification")?;
        anyhow::ensure!(
            !order_id.is_empty(),
            "AX regular fill has an empty order_id"
        );
        VenueOrderId::new_checked(order_id).context("Invalid order_id in AX fill")?

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Fetch the raw fill by the trade_id from /fills and inspect both flags to confirm the mismatch
  2. If the fill really is a settlement, correct the classification upstream so is_block_trade is also true (venue config or the integration writing fills)
  3. If AX semantics genuinely allow settlement-without-block, relax the invariant in the adapter via a PR with tests covering both flags
  4. Until fixed, expect the fills request containing this fill to fail and reconcile the affected trades manually

Example fix

// before (AX fill payload)
{"trade_id": "T1", "is_final_settlement": true, "is_block_trade": false}
// after
{"trade_id": "T1", "is_final_settlement": true, "is_block_trade": true}
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check fill classification flags before parsing (parse_fill_report is pub)
fn fill_flags_consistent(fill: &AxFill) -> bool {
    !(fill.is_final_settlement == Some(true) && fill.is_block_trade == Some(false))
}

for fill in &fills {
    if !fill_flags_consistent(fill) {
        log::error!("fill {} has inconsistent settlement/block flags; quarantining", fill.trade_id);
        continue;
    }
    reports.push(parse_fill_report(fill, account_id, &instrument, ts_init)?);
}

Try / catch

match parse_fill_report(fill, account_id, &instrument, ts_init) {
    Ok(report) => reports.push(report),
    Err(e) if e.to_string().contains("must also be classified as a block trade") => {
        log::error!("quarantining fill {}: settlement/block flag mismatch: {e}", fill.trade_id);
        quarantined.push(fill.trade_id.clone()); // reconcile manually, do not double-count
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: The /fills stream returns a fill with is_final_settlement: true and is_block_trade: false — e.g. the venue starts emitting settlement fills without the block flag, or a custom integration writes fills with incomplete classification flags.

Common situations: AX changes fill classification semantics between versions; a perp contract expires and its settlement fill arrives with flags the adapter's invariant does not allow; hand-crafted fills in test fixtures with only one flag set.

Related errors


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