nautechsystems/nautilus_trader · error
Unknown position currency '{pos_ccy}' for instrument {instru
Error message
Unknown position currency '{pos_ccy}' for instrument {instrument_id} (base={base_ccy}, quote={quote_ccy}) What it means
parse_position_status_report maps an OKX position report to a Nautilus PositionStatusReport, deriving the position side from the position currency. It only recognizes the instrument's base currency (or quote currency in some inverse/margin cases); any other currency string makes side determination impossible, so it bails with this message naming the position currency and the instrument's base/quote currencies.
Source
Thrown at crates/adapters/okx/src/common/parse.rs:1087
// Use Decimal arithmetic to avoid floating-point precision errors
let avg_px_str = if position.avg_px.is_empty() {
// If no avg_px, use mark_px as fallback
&position.mark_px
} else {
&position.avg_px
};
let avg_px_dec = Decimal::from_str(avg_px_str)?;
if avg_px_dec.is_zero() {
anyhow::bail!(
"Cannot convert SHORT position from quote to base: avg_px is zero for {instrument_id}"
);
}
let quantity_dec = (pos_dec.abs() / avg_px_dec).round_dp(size_precision as u32);
(PositionSide::Short, quantity_dec)
} else {
anyhow::bail!(
"Unknown position currency '{pos_ccy}' for instrument {instrument_id} (base={base_ccy}, quote={quote_ccy})"
);
}
} else {
// For SWAP/FUTURES/OPTION: use existing logic
// Determine position side based on OKX position mode:
// - Net mode: posSide="net", uses signed quantities (positive=long, negative=short)
// - Long/Short mode: posSide="long"/"short", quantities are always positive, side from field
let side = match position.pos_side {
OKXPositionSide::Net | OKXPositionSide::None => {
// Net mode: derive side from signed quantity
if pos_dec.is_sign_positive() && !pos_dec.is_zero() {
PositionSide::Long
} else if pos_dec.is_sign_negative() {
PositionSide::Short
} else {
PositionSide::Flat
}View on GitHub (pinned to 18893faf8b)
Solutions
- Log the instrument_id with its base/quote currencies and compare against the position's `ccy` field; verify they are consistent
- Confirm the instrument_id used to build the report matches the instrument the OKX position (`instId`) refers to, and re-fetch the instrument definition if not
- Check whether the account is in multi-currency/portfolio margin mode where `ccy` is a settlement currency; use the net-mode/single-currency mapping or extend the currency match logic for that settlement currency
- If a new legit currency case exists (e.g. quote-currency margined inverse positions), update the match arms in parse_position_status_report to accept it
Example fix
// before
anyhow::bail!("Unknown position currency '{pos_ccy}' for instrument {instrument_id} ...");
// after
// ensure pos_ccy matches an expected settlement currency, e.g. accept quote-currency margin
if pos_ccy == quote_ccy {
return Ok((PositionSide::Short, quantity_dec));
}
anyhow::bail!("Unknown position currency '{pos_ccy}' for instrument {instrument_id} ..."); Defensive patterns
Strategy: validation
Validate before calling
// Rust: validate before calling the parser
fn valid_position_ccy(pos_ccy: &str, base: &str, quote: &str) -> bool {
pos_ccy == base || pos_ccy == quote
}
if !valid_position_ccy(position.ccy, instrument.base_currency(), instrument.quote_currency()) {
// skip or map the settlement currency before parsing
return Ok(None);
} Try / catch
match parse_position_status_report(&position, &instrument) {
Ok(report) => reports.push(report),
Err(e) if e.to_string().contains("Unknown position currency") => {
log::warn!("skipping position with unsupported ccy: {e}");
}
Err(e) => return Err(e),
} Prevention
- Confirm the OKX account mode (single vs multi-currency margin) matches what the parser expects
- Keep instrument definitions in sync with positions (same fetch cycle)
- Log ccy/base/quote on mismatch to diagnose settlement-currency cases
When it happens
Trigger: Calling parse_position_status_report with an OKX position payload whose `ccy` field is neither the instrument's base currency nor its quote currency — e.g. a settlement-currency value like 'USD' or 'USDT' for a SWAP where ccy reflects margin/settlement rather than base/quote, or a stale/mismatched instrument_id whose currencies don't match the position's ccy.
Common situations: Developers parsing OKX portfolio-margin or multi-currency-mode accounts where OKX reports positions with the margin currency (USDT/USD) instead of the base currency; using position data fetched for one instrument type with instrument definitions of another; OKX changing the ccy semantics for new instrument types.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Invalid timeframe for `BarSpecification`, was {timeframe}
- Invalid `BarSpecification` for channel, was {bar_spec}
- Invalid `BarSpecification` for mark price channel, was {bar_
- Invalid `BarSpecification` for timeframe, was {bar_spec}
- `tick_sz` is empty for {}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/c80a499500ba527d.
Report an issue: GitHub.