Rust Data Models and Representation Boundaries
Relevant Rust source files
crates/model/src/lib.rscrates/model/src/identifiers/mod.rscrates/model/src/types/mod.rscrates/model/src/data/mod.rscrates/model/src/orders/mod.rscrates/model/src/events/mod.rscrates/model/src/instruments/mod.rscrates/model/src/orderbook/mod.rscrates/core/src/nanos.rscrates/serialization/src/lib.rs
nautilus-model is the canonical in-process market and trading model. Provider schemas, database rows, Arrow arrays, wire frames, and browser payloads are representations of these values; they do not define competing price, instrument, order, or event semantics.
Identity
Typed identifiers prevent accidental substitution across domains. InstrumentId combines symbol and venue. Trader, strategy, client, order, position, account, venue, and component identifiers use dedicated types rather than undifferentiated strings. Parsing occurs at a boundary; engines carry validated identifiers thereafter.
Identity is more than a display symbol. A durable market-data record must retain the provider dataset/schema context and enough symbology provenance to reconstruct the exact InstrumentId. A broker execution report must map its external identifiers to the Nautilus client and venue order identifiers used by the execution engine.
Financial values
Price, Quantity, and Money use scaled integer storage with explicit precision. Checked constructors return correctness errors for invalid precision or range. Convenience constructors may panic on invalid input and should be reserved for trusted constants or tests. Serialization must retain raw value and precision; round-tripping through an unconstrained floating-point column loses the model contract.
Money couples an amount with Currency. It is not interchangeable with Price, even when both render as decimals. Account and portfolio code relies on that distinction for balance, margin, and PnL calculations.
Time
UnixNanos is the common timestamp type. Market data normally carries both event time and initialization/receive time. Persist both. Event time orders the market occurrence; initialization time records when the system observed or constructed the value. Corrections and replay need the distinction.
Market data
The data model includes instruments, quotes, trades, bars, order-book deltas and snapshots, prices, funding updates, and typed custom data. Data and related enums provide closed dispatch over supported values where an engine needs heterogeneous input. Hot paths can use the concrete types directly.
Order-book state is represented by Rust OrderBook, BookOrder, OrderBookDelta, OrderBookDeltas, and depth snapshots. Integrity checks and fill simulation operate on these Rust types. Provider adapters decode into them before publishing to the data engine.
Trading state
Concrete order structs model market, limit, stop, trailing, market-if-touched, and other order behaviors. OrderAny permits engine dispatch without erasing the order contract. Order commands and events are separate: a command expresses intent, while an event records a state transition. Positions, accounts, balances, margin values, and portfolio snapshots likewise remain typed model values.
Representation boundaries
From that canonical model boundary, database rows, event-store envelopes, Arrow or Parquet records, wire codecs, and client-facing payloads are separate projections. None becomes the in-process trading model.
Conversion belongs at the edge shown above. Core services should not accept Databento DBN records, Interactive Brokers SDK objects, SQL row structs, or browser DTOs. Keeping those representations outside the domain prevents an adapter or storage migration from changing trading semantics.
Feature and compatibility discipline
Some model modules are feature-gated, including high precision, optional serialization, DeFi, and foreign bindings. Rust consumers should enable only the features required by their runtime and keep all Nautilus crates on a compatible revision. A persistence schema or network protocol should record its model/schema version so replay does not silently decode bytes under a different representation contract.