nautechsystems/nautilus_trader · error · anyhow::Error

AX initial_margin_pct must be positive, was {initial_margin_

Error message

AX initial_margin_pct must be positive, was {initial_margin_pct}

What it means

parse_margin_rates requires initial_margin_pct to be strictly greater than zero before converting percent to a rate for the instrument's margin fields. A zero or negative value — typically an unset field defaulting to 0 — fails instrument parsing immediately.

Source

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

        None,
        Some(margin_init),
        Some(margin_maint),
        Some(maker_fee),
        Some(taker_fee),
        None,
        Some(info),
        ts_event,
        ts_init,
    );

    Ok(InstrumentAny::PerpetualContract(instrument))
}

fn parse_margin_rates(
    initial_margin_pct: Decimal,
    maintenance_margin_pct: Decimal,
) -> anyhow::Result<(Decimal, Decimal)> {
    anyhow::ensure!(
        initial_margin_pct > Decimal::ZERO,
        "AX initial_margin_pct must be positive, was {initial_margin_pct}"
    );
    anyhow::ensure!(
        maintenance_margin_pct > Decimal::ZERO,
        "AX maintenance_margin_pct must be positive, was {maintenance_margin_pct}"
    );
    anyhow::ensure!(
        maintenance_margin_pct <= initial_margin_pct,
        "AX maintenance_margin_pct {maintenance_margin_pct} exceeds initial_margin_pct {initial_margin_pct}"
    );

    Ok((
        margin_percent_to_rate(initial_margin_pct, "initial_margin_pct")?,
        margin_percent_to_rate(maintenance_margin_pct, "maintenance_margin_pct")?,
    ))
}

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Check the symbol's margin fields in AX venue config and set a positive initial margin percent
  2. Verify the field name/type against the AX API docs for your version — a renamed field silently deserializes to 0
  3. If a symbol genuinely has no margin yet (spot-like listing), configure a nominal positive value or exclude it from loading
  4. Retry after config changes; no client-side workaround exists

Example fix

// before (AX contract metadata)
"initial_margin_pct": "0"
// after
"initial_margin_pct": "1.5"
Defensive patterns

Strategy: try-catch

Validate before calling

// Vet margin fields before definitions reach the parser
fn initial_margin_ok(v: Decimal) -> bool { v > Decimal::ZERO }

if !initial_margin_ok(definition.initial_margin_pct) {
    anyhow::bail!("symbol {} has non-positive initial_margin_pct {}}", definition.symbol, definition.initial_margin_pct);
}

Try / catch

match parse_instrument(&definition, ts_event, ts_init) {
    Ok(instrument) => Ok(instrument),
    Err(e) if e.to_string().contains("initial_margin_pct must be positive") => {
        log::error!("symbol {} margin config incomplete (initial_margin_pct <= 0)", definition.symbol);
        Err(e)
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: An instrument definition carries initial_margin_pct of 0 (field never configured for a new listing) or a negative value; any instrument load or /instrument fetch for that symbol then errors during parse_margin_rates.

Common situations: Newly listed symbols whose margin configuration has not been populated in AX yet; an API change renaming the field so deserialization falls back to a zero default; contract templates copied without margin values.

Related errors


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