Skip to content

Commit

Permalink
Clarify precision mode terminology
Browse files Browse the repository at this point in the history
  • Loading branch information
cjdsellers committed Jan 18, 2025
1 parent c31f404 commit e57f310
Show file tree
Hide file tree
Showing 8 changed files with 41 additions and 22 deletions.
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -99,16 +99,16 @@ cargo-test:
echo "cargo-nextest is not installed. You can install it using 'cargo install cargo-nextest'"; \
exit 1; \
fi
RUST_BACKTRACE=1 && (cd nautilus_core && cargo nextest run --workspace --features "python,ffi,high-precision")
RUST_BACKTRACE=1 && (cd nautilus_core && cargo nextest run --workspace --features "python,ffi")


.PHONY: cargo-test-low-precision
cargo-test-low-precision:
.PHONY: cargo-test-high-precision
cargo-test-high-precision:
@if ! cargo nextest --version >/dev/null 2>&1; then \
echo "cargo-nextest is not installed. You can install it using 'cargo install cargo-nextest'"; \
exit 1; \
fi
RUST_BACKTRACE=1 && (cd nautilus_core && cargo nextest run --workspace --features "python,ffi")
RUST_BACKTRACE=1 && (cd nautilus_core && cargo nextest run --workspace --features "python,ffi,high-precision")


.PHONY: cargo-test-coverage
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,22 @@ We strive to document these changes in the release notes on a **best-effort basi

We aim to follow a **weekly release schedule**, though experimental or larger features may cause delays.

## Precision mode

NautilusTrader supports two precision modes for its core value types (`Price`, `Quantity`, `Money`),
which differ in their internal bit-width and maximum decimal precision.

- **High-precision**: 128-bit integers with up to 16 decimals of precision, and a larger value range.
- **Standard-precision**: 64-bit integers with up to 9 decimals of precision, and a smaller value range.

> [!NOTE]
>
> By default, the official Python wheels **ship** in high-precision (128-bit) mode on Linux and macOS.
> On Windows, only standard-precision (64-bit) is available due to the lack of native 128-bit integer support.
> For the Rust crates, the default is standard-precision unless you explicitly enable the `high-precision` feature flag.
See the [Installation Guide](https://nautilustrader.io/docs/latest/getting_started/installation) for further details.

## Installation

### From PyPI
Expand Down
2 changes: 1 addition & 1 deletion build.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
if IS_WINDOWS and HIGH_PRECISION:
print(
"Warning: high-precision mode not supported on Windows (128-bit integers unavailable)\n"
"Forcing low-precision (64-bit) mode",
"Forcing standard-precision (64-bit) mode",
)
HIGH_PRECISION = False

Expand Down
6 changes: 3 additions & 3 deletions docs/concepts/data.md
Original file line number Diff line number Diff line change
Expand Up @@ -383,11 +383,11 @@ Converts JSON back to Parquet format:
The following migration examples both use trades data (you can also migrate the other data types in the same way).
All commands should be run from the root of the `nautilus_core/persistence/` crate directory.

#### Migrating from low-precision (64-bit) to high-precision (128-bit)
#### Migrating from standard-precision (64-bit) to high-precision (128-bit)

This example describes a scenario where you want to migrate from low-precision schema data to high-precision schema data.
This example describes a scenario where you want to migrate from standard-precision schema data to high-precision schema data.

**1. Convert from low-precision Parquet to JSON**:
**1. Convert from standard-precision Parquet to JSON**:

```bash
cargo run --bin to_json trades.parquet
Expand Down
2 changes: 1 addition & 1 deletion docs/concepts/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ When the `high-precision` feature flag is **enabled** (default), values use the
| `Money` | `i128` | 16 | -17,014,118,346,046 | 17,014,118,346,046 |
| `Quantity` | `u128` | 16 | 0 | 34,028,236,692,093 |

### Low-precision mode (64-bit)
### Standard-precision mode (64-bit)

When the `high-precision` feature flag is **disabled**, values use the specification:

Expand Down
23 changes: 13 additions & 10 deletions docs/getting_started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,28 +206,31 @@ We recommend using [Redis Insight](https://redis.io/insight/) as a GUI to visual
## Precision mode
NautilusTrader supports two precision modes which determine the bit-width of value types like `Price`, `Quantity`, and `Money`.
NautilusTrader supports two precision modes for its core value types (`Price`, `Quantity`, `Money`),
which differ in their internal bit-width and maximum decimal precision.
- **high-precision**: 128-bit integers with up to 16 decimals of precision, and a larger value range.
- **low-precision**: 64-bit integers with up to 9 decimals of precision, and a smaller value range.
- **High-precision**: 128-bit integers with up to 16 decimals of precision, and a larger value range.
- **Standard-precision**: 64-bit integers with up to 9 decimals of precision, and a smaller value range.
:::note
High-precision mode is not available on Windows due to lack of 128-bit integer support in the Microsoft Visual C++ compiler. Windows users must use low-precision mode.
By default, the official Python wheels **ship** in high-precision (128-bit) mode on Linux and macOS.
On Windows, only standard-precision (64-bit) is available due to the lack of native 128-bit integer support.
For the Rust crates, the default is standard-precision unless you explicitly enable the `high-precision` feature flag.
:::
The tradeoff is slightly higher performance for low-precision (~3-5% for backtests),
but with a reduced maximum precision and value range.
The performance tradeoff is that standard-precision is ~3–5% faster in typical backtests,
but has lower decimal precision and a smaller representable value range.
:::note
Performance benchmarks comparing the modes are pending.
:::
### Build configuration
The precision mode is controlled by:
The precision mode is determined by:
- The `HIGH_PRECISION` environment variable during compilation.
- The corresponding Rust feature flag `high-precision`.
- Setting the `HIGH_PRECISION` environment variable during compilation, **and/or**
- Enabling the `high-precision` Rust feature flag explicitly.
#### High-precision mode (128-bit)
Expand All @@ -236,7 +239,7 @@ export HIGH_PRECISION=true
make install-debug
```
#### Low-precision mode (64-bit)
#### Standard-precision mode (64-bit)
```bash
export HIGH_PRECISION=false
Expand Down
4 changes: 2 additions & 2 deletions nautilus_core/model/src/types/fixed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub const FIXED_PRECISION: u8 = 9;
#[no_mangle]
pub static PRECISION_BYTES: i32 = 16;
#[cfg(not(feature = "high-precision"))]
/// The width in bytes for fixed-point value types in low-precision mode (64-bit).
/// The width in bytes for fixed-point value types in standard-precision mode (64-bit).
#[no_mangle]
pub static PRECISION_BYTES: i32 = 8;

Expand All @@ -45,7 +45,7 @@ pub const FIXED_SCALAR: f64 = 10_000_000_000_000_000.0; // 10.0**FIXED_PRECISION
/// The scalar value corresponding to the maximum precision (10^9).
pub const FIXED_SCALAR: f64 = 1_000_000_000.0; // 10.0**FIXED_PRECISION

/// The scalar representing the difference between high-precision and low-precision modes.
/// The scalar representing the difference between high-precision and standard-precision modes.
pub const PRECISION_DIFF_SCALAR: f64 = 10_000_000.0; // 10.0**(16-9)

/// Checks if a given `precision` value is within the allowed fixed-point precision range.
Expand Down
2 changes: 1 addition & 1 deletion nautilus_core/model/src/types/quantity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ impl Quantity {
self.raw / QuantityRaw::pow(10, u32::from(FIXED_PRECISION - self.precision));
// SAFETY: The raw value is guaranteed to be within i128 range after scaling
// because our quantity constraints ensure the maximum raw value times the scaling
// factor cannot exceed i64::MAX (low-precision) or i128::MAX (high-precision).
// factor cannot exceed i128::MAX (high-precision) or i64::MAX (standard-precision).
#[allow(clippy::useless_conversion)] // Required for precision modes
Decimal::from_i128_with_scale(rescaled_raw as i128, u32::from(self.precision))
}
Expand Down

0 comments on commit e57f310

Please sign in to comment.