diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml
index 1d888dbde95a..89a66ac7af0c 100644
--- a/.github/workflows/build-wheels.yml
+++ b/.github/workflows/build-wheels.yml
@@ -1,10 +1,8 @@
name: build-wheels
-# Build wheels on every push to `develop` branch
-
on:
push:
- branches: [develop]
+ branches: [nightly]
jobs:
build-wheels:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 27477ee31fa3..172c4c8ea7cc 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,10 +1,8 @@
name: build
-# Build and test NautilusTrader
-
on:
push:
- branches: [master, develop]
+ branches: [master, nightly, develop]
pull_request:
branches: [develop]
@@ -14,7 +12,7 @@ jobs:
fail-fast: false
matrix:
arch: [x64]
- os: [ubuntu-latest, macos-latest, windows-latest]
+ os: [ubuntu-latest, windows-latest]
python-version: ["3.10", "3.11", "3.12"]
defaults:
run:
@@ -38,21 +36,11 @@ jobs:
working-directory: ${{ github.workspace }}
- name: Set up Rust tool-chain (Linux, Windows) stable
- if: (runner.os == 'Linux') || (runner.os == 'Windows')
uses: actions-rust-lang/setup-rust-toolchain@v1.5
with:
toolchain: ${{ env.RUST_VERSION }}
components: rustfmt, clippy
- # Work around as actions-rust-lang does not seem to work on macOS yet
- - name: Set up Rust tool-chain (macOS) stable
- if: runner.os == 'macOS'
- uses: actions-rs/toolchain@v1
- with:
- toolchain: ${{ env.RUST_VERSION }}
- override: true
- components: rustfmt, clippy
-
- name: Set up Python environment
uses: actions/setup-python@v4
with:
@@ -99,24 +87,20 @@ jobs:
# pre-commit run --hook-stage manual gitlint-ci
pre-commit run --all-files
- - name: Install Redis (macOS)
- if: runner.os == 'macOS'
- run: |
- brew install redis
- redis-server --daemonize yes
-
- name: Install Redis (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get install redis-server
redis-server --daemonize yes
- - name: Run nautilus_core cargo tests (Linux, macOS)
- if: (runner.os == 'Linux') || (runner.os == 'macOS')
- run: make cargo-test
+ - name: Run nautilus_core cargo tests (Linux)
+ if: runner.os == 'Linux'
+ run: |
+ cargo install cargo-nextest
+ make cargo-test
- - name: Run tests (Linux, macOS)
- if: (runner.os == 'Linux') || (runner.os == 'macOS')
+ - name: Run tests (Linux)
+ if: runner.os == 'Linux'
run: |
make pytest
make test-examples
@@ -129,3 +113,95 @@ jobs:
poetry run pytest --ignore=tests/performance_tests --new-first --failed-first
env:
PARALLEL_BUILD: false
+
+ build-macos:
+ if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/nightly'
+ strategy:
+ fail-fast: false
+ matrix:
+ arch: [x64]
+ os: [macos-latest]
+ python-version: ["3.10", "3.11", "3.12"]
+ defaults:
+ run:
+ shell: bash
+ name: build - Python ${{ matrix.python-version }} (${{ matrix.arch }} ${{ matrix.os }})
+ runs-on: ${{ matrix.os }}
+ env:
+ BUILD_MODE: debug
+ RUST_BACKTRACE: 1
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Get Rust version from rust-toolchain.toml
+ id: rust-version
+ run: |
+ version=$(awk -F\" '/version/ {print $2}' nautilus_core/rust-toolchain.toml)
+ echo "Rust toolchain version $version"
+ echo "RUST_VERSION=$version" >> $GITHUB_ENV
+ working-directory: ${{ github.workspace }}
+
+ # Work around as actions-rust-lang does not seem to work on macOS yet
+ - name: Set up Rust tool-chain (macOS) stable
+ uses: actions-rs/toolchain@v1
+ with:
+ toolchain: ${{ env.RUST_VERSION }}
+ override: true
+ components: rustfmt, clippy
+
+ - name: Set up Python environment
+ uses: actions/setup-python@v4
+ with:
+ python-version: ${{ matrix.python-version }}
+
+ - name: Get Poetry version from poetry-version
+ run: |
+ version=$(cat poetry-version)
+ echo "POETRY_VERSION=$version" >> $GITHUB_ENV
+
+ - name: Install Poetry
+ uses: snok/install-poetry@v1
+ with:
+ version: ${{ env.POETRY_VERSION }}
+
+ - name: Install build dependencies
+ run: python -m pip install --upgrade pip setuptools wheel pre-commit msgspec
+
+ - name: Setup cached pre-commit
+ id: cached-pre-commit
+ uses: actions/cache@v3
+ with:
+ path: ~/.cache/pre-commit
+ key: ${{ runner.os }}-${{ matrix.python-version }}-pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
+
+ - name: Set poetry cache-dir
+ run: echo "POETRY_CACHE_DIR=$(poetry config cache-dir)" >> $GITHUB_ENV
+
+ - name: Poetry cache
+ id: cached-poetry
+ uses: actions/cache@v3
+ with:
+ path: ${{ env.POETRY_CACHE_DIR }}
+ key: ${{ runner.os }}-${{ matrix.python-version }}-poetry-${{ hashFiles('**/poetry.lock') }}
+
+ - name: Run pre-commit
+ run: |
+ # pre-commit run --hook-stage manual gitlint-ci
+ pre-commit run --all-files
+
+ - name: Install Redis (macOS)
+ run: |
+ brew install redis
+ redis-server --daemonize yes
+
+ - name: Run nautilus_core cargo tests (macOS)
+ run: |
+ cargo install cargo-nextest
+ make cargo-test
+
+ - name: Run tests (macOS)
+ run: |
+ make pytest
+ make test-examples
diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml
index 7dde774132ec..0f121858aaf1 100644
--- a/.github/workflows/coverage.yml
+++ b/.github/workflows/coverage.yml
@@ -1,10 +1,8 @@
name: coverage
-# Run code coverage analysis for NautilusTrader
-
on:
push:
- branches: [develop]
+ branches: [nightly]
jobs:
build:
@@ -13,7 +11,7 @@ jobs:
matrix:
arch: [x64]
os: [ubuntu-latest]
- python-version: ["3.10"]
+ python-version: ["3.10"] # Fails on 3.11 due Cython
name: build - Python ${{ matrix.python-version }} (${{ matrix.arch }} ${{ matrix.os }})
runs-on: ${{ matrix.os }}
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index d67bc4befb53..d34f76c7db3b 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -1,10 +1,8 @@
name: docker
-# Build NautilusTrader docker images
-
on:
push:
- branches: [master, develop]
+ branches: [master, nightly]
jobs:
build-docker-images:
@@ -47,18 +45,18 @@ jobs:
id: branch-name
uses: tj-actions/branch-names@v7.0.7
- - name: Build nautilus_trader image (develop)
- if: ${{ steps.branch-name.outputs.current_branch == 'develop' }}
- id: docker_build_trader_develop
+ - name: Build nautilus_trader image (nightly)
+ if: ${{ steps.branch-name.outputs.current_branch == 'nightly' }}
+ id: docker_build_trader_nightly
uses: docker/build-push-action@v5
with:
file: ".docker/nautilus_trader.dockerfile"
push: true
- tags: ghcr.io/${{ github.repository_owner }}/nautilus_trader:develop
+ tags: ghcr.io/${{ github.repository_owner }}/nautilus_trader:nightly
cache-from: type=gha
cache-to: type=gha
- name: Digest nautilus_trader image
- run: echo ${{ steps.docker_build_trader_develop.outputs.digest }}
+ run: echo ${{ steps.docker_build_trader_nightly.outputs.digest }}
- name: Build nautilus_trader image (latest)
if: ${{ steps.branch-name.outputs.current_branch == 'master' }}
@@ -73,20 +71,20 @@ jobs:
- name: Digest nautilus_trader image
run: echo ${{ steps.docker_build_trader_latest.outputs.digest }}
- - name: Build jupyterlab image (develop)
- if: ${{ steps.branch-name.outputs.current_branch == 'develop' }}
- id: docker_build_jupyterlab_develop
+ - name: Build jupyterlab image (nightly)
+ if: ${{ steps.branch-name.outputs.current_branch == 'nightly' }}
+ id: docker_build_jupyterlab_nightly
uses: docker/build-push-action@v5
with:
file: ".docker/jupyterlab.dockerfile"
push: true
- tags: ghcr.io/${{ github.repository_owner }}/jupyterlab:develop
+ tags: ghcr.io/${{ github.repository_owner }}/jupyterlab:nightly
cache-from: type=gha
cache-to: type=gha
build-args: |
GIT_TAG=${{ steps.branch-name.outputs.current_branch }}
- name: Digest jupyterlab image
- run: echo ${{ steps.docker_build_jupyterlab_develop.outputs.digest }}
+ run: echo ${{ steps.docker_build_jupyterlab_nightly.outputs.digest }}
- name: Build jupyterlab image (latest)
if: ${{ steps.branch-name.outputs.current_branch == 'master' }}
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 983731bdbcf2..43779b605424 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -1,10 +1,8 @@
name: docs
-# Build and deploy the NautilusTrader documentation
-
on:
push:
- branches: [master, develop]
+ branches: [nightly]
jobs:
docs:
@@ -75,8 +73,8 @@ jobs:
- name: Add CNAME
run: echo "docs.nautilustrader.io" >> docs/build/html/CNAME
- - name: Publish docs (develop)
- if: ${{ steps.branch-name.outputs.current_branch == 'develop' }}
+ - name: Publish docs (nightly)
+ if: ${{ steps.branch-name.outputs.current_branch == 'nightly' }}
uses: s0/git-publish-subdir-action@develop
env:
REPO: self
@@ -84,7 +82,7 @@ jobs:
FOLDER: docs/build/html
SQUASH_HISTORY: false
GITHUB_TOKEN: ${{ secrets.GHPAGES_ACCESS }}
- TARGET_DIR: develop
+ TARGET_DIR: nightly
- name: Publish docs (latest)
if: ${{ steps.branch-name.outputs.current_branch == 'master' }}
diff --git a/.github/workflows/nightly-merge.yml b/.github/workflows/nightly-merge.yml
new file mode 100644
index 000000000000..2cbdbdc71a93
--- /dev/null
+++ b/.github/workflows/nightly-merge.yml
@@ -0,0 +1,31 @@
+name: nightly-merge
+
+on:
+ push:
+ branches: [nightly-merge-test]
+ # schedule:
+ # - cron: '0 14 * * *' # At 14:00 UTC every day
+
+jobs:
+ nightly-merge:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0 # Fetch all history for all branches and tags
+
+ # Temporary config before nautilus-bot account
+ - name: Configure Git
+ run: |
+ git config --local user.email "chris@cjdsellers.io"
+ git config --local user.name "Chris Sellers"
+
+ - name: Merge develop into nightly
+ run: |
+ git checkout nightly
+ git merge --no-ff origin/develop -m "Automated merge of develop into nightly"
+
+ - name: Push changes
+ run: |
+ git push origin nightly
diff --git a/.gitignore b/.gitignore
index 5838517b1215..1fc0632839e6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,10 +33,11 @@
.ruff_cache/
.vscode/
+/catalog/
+
__pycache__
_build/
build/
-catalog/
data_catalog/
dist/
env/
@@ -59,6 +60,7 @@ examples/backtest/notebooks/catalog
nautilus_trader/**/.gitignore
nautilus_trader/test_kit/mocks/.nautilus/
tests/test_data/catalog/
+tests/unit_tests/persistence/catalog
bench_data/
!tests/integration_tests/adapters/betfair/responses/*.log
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 8c38b6cb85b2..49edc95f0bd8 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -73,7 +73,7 @@ repos:
types: [python]
- repo: https://github.com/psf/black
- rev: 23.12.0
+ rev: 23.12.1
hooks:
- id: black
types_or: [python, pyi]
@@ -82,7 +82,7 @@ repos:
exclude: "docs/_pygments/monokai.py"
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.1.9
+ rev: v0.1.12
hooks:
- id: ruff
args: ["--fix"]
diff --git a/Makefile b/Makefile
index b3b568b94762..09a22a32cd9f 100644
--- a/Makefile
+++ b/Makefile
@@ -52,9 +52,8 @@ pre-commit:
ruff:
ruff check . --fix
-.PHONY: update
-update:
- (cd nautilus_core && cargo update)
+.PHONY: update cargo-update
+update: cargo-update
poetry update
poetry install --with dev,test --all-extras --no-root
@@ -79,11 +78,15 @@ cargo-build:
.PHONY: cargo-update
cargo-update:
- (cd nautilus_core && cargo update)
+ (cd nautilus_core && cargo update && cargo install cargo-nextest)
.PHONY: cargo-test
cargo-test:
- RUST_BACKTRACE=1 && (cd nautilus_core && cargo test)
+ @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 --exclude tokio-tungstenite)
.PHONY: cargo-test-nightly
cargo-test-nightly:
@@ -99,7 +102,7 @@ cargo-doc:
.PHONY: docker-build
docker-build: clean
- docker pull ${IMAGE_FULL} || docker pull ${IMAGE}:develop || true
+ docker pull ${IMAGE_FULL} || docker pull ${IMAGE}:nightly || true
docker build -f .docker/nautilus_trader.dockerfile --platform linux/x86_64 -t ${IMAGE_FULL} .
.PHONY: docker-build-force
diff --git a/README.md b/README.md
index c6dad7738d84..96a700f02cfd 100644
--- a/README.md
+++ b/README.md
@@ -10,14 +10,15 @@
| Branch | Version | Status |
| :-------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `master` | ![version](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fnautechsystems%2Fnautilus_trader%2Fmaster%2Fversion.json) | [![build](https://github.com/nautechsystems/nautilus_trader/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/nautechsystems/nautilus_trader/actions/workflows/build.yml) |
+| `nightly` | ![version](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fnautechsystems%2Fnautilus_trader%2Fnightly%2Fversion.json) | [![build](https://github.com/nautechsystems/nautilus_trader/actions/workflows/build.yml/badge.svg?branch=nightly)](https://github.com/nautechsystems/nautilus_trader/actions/workflows/build.yml) |
| `develop` | ![version](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Fnautechsystems%2Fnautilus_trader%2Fdevelop%2Fversion.json) | [![build](https://github.com/nautechsystems/nautilus_trader/actions/workflows/build.yml/badge.svg?branch=develop)](https://github.com/nautechsystems/nautilus_trader/actions/workflows/build.yml) |
| Platform | Rust | Python |
| :----------------- | :------ | :----- |
-| `Linux (x86_64)` | 1.74.1+ | 3.10+ |
-| `macOS (x86_64)` | 1.74.1+ | 3.10+ |
-| `macOS (arm64)` | 1.74.1+ | 3.10+ |
-| `Windows (x86_64)` | 1.74.1+ | 3.10+ |
+| `Linux (x86_64)` | 1.75.0+ | 3.10+ |
+| `macOS (x86_64)` | 1.75.0+ | 3.10+ |
+| `macOS (arm64)` | 1.75.0+ | 3.10+ |
+| `Windows (x86_64)` | 1.75.0+ | 3.10+ |
- **Website:** https://nautilustrader.io
- **Docs:** https://docs.nautilustrader.io
@@ -139,15 +140,15 @@ NautilusTrader is designed in a modular way to work with 'adapters' which provid
connectivity to data providers and/or trading venues - converting their raw API
into a unified interface. The following integrations are currently supported:
-| Name | ID | Type | Status | Docs |
-| :-------------------------------------------------------- | :---------- | :---------------------- | :------------------------------------------------------ | :---------------------------------------------------------------- |
-| [Betfair](https://betfair.com) | `BETFAIR` | Sports betting exchange | ![status](https://img.shields.io/badge/beta-yellow) | [Guide](https://docs.nautilustrader.io/integrations/betfair.html) |
-| [Binance](https://binance.com) | `BINANCE` | Crypto exchange (CEX) | ![status](https://img.shields.io/badge/stable-green) | [Guide](https://docs.nautilustrader.io/integrations/binance.html) |
-| [Binance US](https://binance.us) | `BINANCE` | Crypto exchange (CEX) | ![status](https://img.shields.io/badge/stable-green) | [Guide](https://docs.nautilustrader.io/integrations/binance.html) |
-| [Binance Futures](https://www.binance.com/en/futures) | `BINANCE` | Crypto exchange (CEX) | ![status](https://img.shields.io/badge/stable-green) | [Guide](https://docs.nautilustrader.io/integrations/binance.html) |
-| [Bybit](https://www.bybit.com) | `BYBIT` | Crypto exchange (CEX) | ![status](https://img.shields.io/badge/building-orange) | |
-| [Databento](https://databento.com) | `DATABENTO` | Data provider | ![status](https://img.shields.io/badge/building-orange) | |
-| [Interactive Brokers](https://www.interactivebrokers.com) | `IB` | Brokerage (multi-venue) | ![status](https://img.shields.io/badge/beta-yellow) | [Guide](https://docs.nautilustrader.io/integrations/ib.html) |
+| Name | ID | Type | Status | Docs |
+| :-------------------------------------------------------- | :-------------------- | :---------------------- | :------------------------------------------------------ | :---------------------------------------------------------------- |
+| [Betfair](https://betfair.com) | `BETFAIR` | Sports betting exchange | ![status](https://img.shields.io/badge/beta-yellow) | [Guide](https://docs.nautilustrader.io/integrations/betfair.html) |
+| [Binance](https://binance.com) | `BINANCE` | Crypto exchange (CEX) | ![status](https://img.shields.io/badge/stable-green) | [Guide](https://docs.nautilustrader.io/integrations/binance.html) |
+| [Binance US](https://binance.us) | `BINANCE` | Crypto exchange (CEX) | ![status](https://img.shields.io/badge/stable-green) | [Guide](https://docs.nautilustrader.io/integrations/binance.html) |
+| [Binance Futures](https://www.binance.com/en/futures) | `BINANCE` | Crypto exchange (CEX) | ![status](https://img.shields.io/badge/stable-green) | [Guide](https://docs.nautilustrader.io/integrations/binance.html) |
+| [Bybit](https://www.bybit.com) | `BYBIT` | Crypto exchange (CEX) | ![status](https://img.shields.io/badge/building-orange) | |
+| [Databento](https://databento.com) | `DATABENTO` | Data provider | ![status](https://img.shields.io/badge/building-orange) | |
+| [Interactive Brokers](https://www.interactivebrokers.com) | `INTERACTIVE_BROKERS` | Brokerage (multi-venue) | ![status](https://img.shields.io/badge/beta-yellow) | [Guide](https://docs.nautilustrader.io/integrations/ib.html) |
Refer to the [Integrations](https://docs.nautilustrader.io/integrations/index.html) documentation for further details.
@@ -215,6 +216,7 @@ Documentation of these changes in the release notes are made on a best-effort ba
### Branches
- `master` branch will always reflect the source code for the latest released version
+- `nightly` branch is automatically merged from `develop` branch daily at 14:00 UTC, and also when required
- `develop` branch is normally very active with frequent commits and may contain experimental features. We aim to maintain a stable
passing build on this branch
@@ -251,11 +253,13 @@ The below are some examples of this:
## Docker
-Docker containers are built using a base `python:3.10-slim` with the following image variant tags:
+Docker containers are built using a base `python:3.11-slim` with the following image variant tags:
- `nautilus_trader:latest` has the latest release version installed
-- `nautilus_trader:develop` has the head of the `develop` branch installed
-- `jupyterlab:develop` has the head of the `develop` branch installed along with `jupyterlab` and an
+- `nautilus_trader:nightly` has the head of the `nightly` branch installed
+- `jupyterlab:latest` has the latest release version installed along with `jupyterlab` and an
+ example backtest notebook with accompanying data
+- `jupyterlab:nightly` has the head of the `nightly` branch installed along with `jupyterlab` and an
example backtest notebook with accompanying data
The container images can be pulled as follows:
@@ -264,8 +268,8 @@ The container images can be pulled as follows:
You can launch the backtest example container by running:
- docker pull ghcr.io/nautechsystems/jupyterlab:develop --platform linux/amd64
- docker run -p 8888:8888 ghcr.io/nautechsystems/jupyterlab:develop
+ docker pull ghcr.io/nautechsystems/jupyterlab:nightly --platform linux/amd64
+ docker run -p 8888:8888 ghcr.io/nautechsystems/jupyterlab:nightly
Then open your browser at the following address:
@@ -304,15 +308,15 @@ class EMACross(Strategy):
super().__init__(config)
# Configuration
- self.instrument_id = InstrumentId.from_str(config.instrument_id)
- self.bar_type = BarType.from_str(config.bar_type)
+ self.instrument_id = config.instrument_id
+ self.bar_type = config.bar_type
self.trade_size = Decimal(config.trade_size)
# Create the indicators for the strategy
self.fast_ema = ExponentialMovingAverage(config.fast_ema_period)
self.slow_ema = ExponentialMovingAverage(config.slow_ema_period)
- self.instrument: Optional[Instrument] = None # Initialized in on_start
+ self.instrument: Instrument | None = None # Initialized in on_start
def on_start(self) -> None:
"""
@@ -400,6 +404,10 @@ class EMACross(Strategy):
We aim to provide the most pleasant developer experience possible for this hybrid codebase of Python, Cython and Rust.
Refer to the [Developer Guide](https://docs.nautilustrader.io/developer_guide/index.html) for helpful information.
+[cargo-nextest](https://nexte.st) is the standard Rust test runner for NautilusTrader. You can install it by running:
+
+ cargo install cargo-nextest
+
## Contributing
Thank you for considering contributing to Nautilus Trader! We welcome any and all help to improve
@@ -436,7 +444,7 @@ Nautech Systems is not affiliated with the Rust Foundation, and this project is
work of the Rust Foundation.
For more information, visit https://nautilustrader.io.
-Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
![nautechsystems](https://github.com/nautechsystems/nautilus_trader/blob/develop/docs/_images/ns-logo.png?raw=true "nautechsystems")
diff --git a/RELEASES.md b/RELEASES.md
index f33f036b4137..9f0e08b33a41 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1,3 +1,52 @@
+# NautilusTrader 1.183.0 Beta
+
+Released on 12th January 2024 (UTC).
+
+### Enhancements
+- Added `NautilusConfig.json_primitives` to convert object to Python dictionary with JSON primitive values
+- Added `InstrumentClass.BOND`
+- Added `MessageBusConfig` `use_trader_prefix` and `use_trader_id` options (provides more control over stream names)
+- Added `CacheConfig.drop_instruments_on_reset` (default true to retain current behavior)
+- Implemented core logging interface via the `log` library, thanks @twitu
+- Implemented global atomic clock in Rust (improves performance and ensures properly monotonic timestamps in real-time), thanks @twitu
+- Improved Interactive Brokers adapter raising docker `RuntimeError` only when needed (not when using TWS), thanks @rsmb7z
+- Upgraded core HTTP client to latest `hyper` and `reqwest`, thanks @ayush-sb
+- Optimized Arrow encoding (resulting in ~100x faster writes for the Parquet data catalog)
+
+### Breaking Changes
+- Changed `ParquetDataCatalog` custom data prefix from `geneticdata_` to `custom_` (you will need to rename any catalog subdirs)
+- Changed `ComponentStateChanged` Arrow schema for `config` from `string` to `binary`
+- Changed `OrderInitialized` Arrow schema for `options` from `string` to `binary`
+- Changed `OrderBookDeltas` dictionary representation of `deltas` field from JSON `bytes` to a list of `dict` (standardize with all other data types)
+- Changed external message publishing stream name keys to be `trader-{trader_id}-{instance_id}-streams` (with options allows many traders to publish to the same streams)
+- Renamed all version 2 data wrangler classes with a `V2` suffix for clarity
+- Renamed `GenericData` to `CustomData` (more accurately reflects the nature of the type)
+- Renamed `DataClient.subscribed_generic_data` to `.subscribed_custom_data`
+- Renamed `MessageBusConfig.stream` to `.streams_prefix` (more accurate)
+- Renamed `ParquetDataCatalog.generic_data` to `.custom_data`
+- Renamed `TradeReport` to `FillReport` (more conventional terminology, and more clearly separates market data from user execution reports)
+- Renamed `asset_type` to `instrument_class` across the codebase (more conventional terminology)
+- Renamed `AssetType` enum to `InstrumentClass` (more conventional terminology)
+- Renamed `AssetClass.BOND` to `AssetClass.DEBT` (more conventional terminology)
+- Removed `AssetClass.METAL` (not strictly an asset class, more a futures category)
+- Removed `AssetClass.ENERGY` (not strictly an asset class, more a futures category)
+- Removed `multiplier` param from `Equity` constructor (not applicable)
+- Removed `size_precision`, `size_increment`, and `multiplier` fields from `Equity` dictionary representation (not applicable)
+- Removed `TracingConfig` (now redundant with new logging implementation)
+- Removed `Ticker` data type and associated methods (not a type which can be practically normalized and so becomes adapter specific generic data)
+- Moved `AssetClass.SPORTS_BETTING` to `InstrumentClass.SPORTS_BETTING`
+
+### Fixes
+- Fixed logger thread leak, thanks @twitu
+- Fixed handling of configuration objects to work with `StreamingFeatherWriter`
+- Fixed `BinanceSpotInstrumentProvider` fee loading key error for partial instruments load, thanks for reporting @doublier1
+- Fixed Binance API key configuration parsing for testnet (was falling through to non-testnet env vars)
+- Fixed TWAP execution algorithm scheduled size handling when first order should be for the entire size, thanks for reporting @pcgm-team
+- Added `BinanceErrorCode.SERVER_BUSY` (-1008). Also added to the retry error codes.
+- Added `BinanceOrderStatus.EXPIRED_IN_MATCH` which is when an order was canceled by the exchange due self-trade prevention (STP), thanks for reporting @doublier1
+
+---
+
# NautilusTrader 1.182.0 Beta
Released on 23rd December 2023 (UTC).
diff --git a/docs/_static/version.json b/docs/_static/version.json
index f4b7e84739ae..b14d0b57a3c8 100644
--- a/docs/_static/version.json
+++ b/docs/_static/version.json
@@ -1 +1,4 @@
-{"latest": "", "develop": "develop"}
\ No newline at end of file
+{
+ "latest": "",
+ "nightly": "nightly"
+}
diff --git a/docs/api_reference/index.md b/docs/api_reference/index.md
index 7701bb8e89e6..bad661d9288c 100644
--- a/docs/api_reference/index.md
+++ b/docs/api_reference/index.md
@@ -36,7 +36,7 @@ from the latest NautilusTrader source code using [Sphinx](https://www.sphinx-doc
Please note that there are separate references for different versions of NautilusTrader:
- **Latest**: This API reference is built from the head of the `master` branch and represents the latest stable release.
-- **Develop**: This API reference is built from the head of the `develop` branch and represents bleeding edge and experimental changes/features currently in development.
+- **Nightly**: This API reference is built from the head of the `nightly` branch and represents bleeding edge and experimental changes/features currently in development.
You can select the desired API reference from the **Versions** top right drop down menu.
diff --git a/docs/concepts/adapters.md b/docs/concepts/adapters.md
index 2a37bebcdf25..bd696242792a 100644
--- a/docs/concepts/adapters.md
+++ b/docs/concepts/adapters.md
@@ -132,7 +132,7 @@ from nautilus_trader.model.identifiers import InstrumentId
# nautilus_trader/adapters/binance/spot/data.py
def request_instrument(self, instrument_id: InstrumentId, correlation_id: UUID4):
- instrument: Optional[Instrument] = self._instrument_provider.find(instrument_id)
+ instrument: Instrument | None = self._instrument_provider.find(instrument_id)
if instrument is None:
self._log.error(f"Cannot find instrument for {instrument_id}.")
return
diff --git a/docs/concepts/advanced/custom_data.md b/docs/concepts/advanced/custom_data.md
index 83b5ab75351c..361d9456e8c8 100644
--- a/docs/concepts/advanced/custom_data.md
+++ b/docs/concepts/advanced/custom_data.md
@@ -1,4 +1,4 @@
-# Custom/Generic Data
+# Custom Data
Due to the modular nature of the Nautilus design, it is possible to set up systems
with very flexible data streams, including custom user defined data types. This
guide covers some possible use cases for this functionality.
@@ -92,7 +92,7 @@ self.subscribe_data(
This will result in your actor/strategy passing these received `MyDataPoint`
objects to your `on_data` method. You will need to check the type, as this
-method acts as a flexible handler for all custom/generic data.
+method acts as a flexible handler for all custom data.
```python
def on_data(self, data: Data) -> None:
diff --git a/docs/concepts/advanced/index.md b/docs/concepts/advanced/index.md
index b3fd9de3eccf..fd2bdd4e71bb 100644
--- a/docs/concepts/advanced/index.md
+++ b/docs/concepts/advanced/index.md
@@ -28,7 +28,7 @@ highest to lowest level (although they are self-contained and can be read in any
Explore more advanced concepts of NautilusTrader through these guides:
- [Actors](actors.md)
-- [Custom/Generic data](custom_data.md)
+- [Custom Data](custom_data.md)
- [Advanced Orders](advanced_orders.md)
- [Emulated Orders](emulated_orders.md)
- [Synthetic Instruments](synthetic_instruments.md)
diff --git a/docs/concepts/advanced/portfolio_statistics.md b/docs/concepts/advanced/portfolio_statistics.md
index 77bac0feedfa..7a88e032ba4b 100644
--- a/docs/concepts/advanced/portfolio_statistics.md
+++ b/docs/concepts/advanced/portfolio_statistics.md
@@ -27,7 +27,7 @@ class WinRate(PortfolioStatistic):
Calculates the win rate from a realized PnLs series.
"""
- def calculate_from_realized_pnls(self, realized_pnls: pd.Series) -> Optional[Any]:
+ def calculate_from_realized_pnls(self, realized_pnls: pd.Series) -> Any | None:
# Preconditions
if realized_pnls is None or realized_pnls.empty:
return 0.0
diff --git a/docs/concepts/data.md b/docs/concepts/data.md
index 787a771dec12..ab259a40e7f2 100644
--- a/docs/concepts/data.md
+++ b/docs/concepts/data.md
@@ -23,7 +23,7 @@ This inheritance ensures chronological data ordering (vital for backtesting), wh
Consistency is key; data flows through the platform in exactly the same way for all system environment contexts (`backtest`, `sandbox`, `live`)
primarily through the `MessageBus` to the `DataEngine` and onto subscribed or registered handlers.
-For those seeking customization, the platform supports user-defined data types. Refer to the advanced [Custom/Generic data guide](advanced/custom_data.md) for more details.
+For those seeking customization, the platform supports user-defined data types. Refer to the advanced [Custom data guide](advanced/custom_data.md) for more details.
## Loading data
@@ -88,16 +88,14 @@ Conceretely, this would involve:
The following example shows how to accomplish the above in Python:
```python
-import os
-
-from nautilus_trader import PACKAGE_ROOT
+from nautilus_trader import TEST_DATA_DIR
from nautilus_trader.persistence.loaders import BinanceOrderBookDeltaDataLoader
from nautilus_trader.persistence.wranglers import OrderBookDeltaDataWrangler
from nautilus_trader.test_kit.providers import TestInstrumentProvider
# Load raw data
-data_path = os.path.join(PACKAGE_ROOT, "tests/test_data/binance/binance/btcusdt-depth-snap.csv")
+data_path = TEST_DATA_DIR / "binance" / "btcusdt-depth-snap.csv"
df = BinanceOrderBookDeltaDataLoader.load(data_path)
# Setup a wrangler
@@ -130,11 +128,11 @@ The data catalog can be initialized from a `NAUTILUS_PATH` environment variable,
The following example shows how to initialize a data catalog where there is pre-existing data already written to disk at the given path.
```python
-import os
+from pathlib import Path
from nautilus_trader.persistence.catalog import ParquetDataCatalog
-CATALOG_PATH = os.getcwd() + "/catalog"
+CATALOG_PATH = Path.cwd() / "catalog"
# Create a new catalog instance
catalog = ParquetDataCatalog(CATALOG_PATH)
diff --git a/docs/concepts/instruments.md b/docs/concepts/instruments.md
index 2b95546e2b90..017f07544a44 100644
--- a/docs/concepts/instruments.md
+++ b/docs/concepts/instruments.md
@@ -1,7 +1,7 @@
# Instruments
The `Instrument` base class represents the core specification for any tradable asset/contract. There are
-currently a number of subclasses representing a range of _asset classes_ and _asset types_ which are supported by the platform:
+currently a number of subclasses representing a range of _asset classes_ and _instrument classes_ which are supported by the platform:
- `Equity` (generic Equity)
- `Future` (generic Futures Contract)
- `Option` (generic Options Contract)
diff --git a/docs/concepts/overview.md b/docs/concepts/overview.md
index 06f0e2aa0710..080cd14b66c4 100644
--- a/docs/concepts/overview.md
+++ b/docs/concepts/overview.md
@@ -102,14 +102,15 @@ which are used to aggregate multiple events to determine state.
### Data Types
The following market data types can be requested historically, and also subscribed to as live streams when available from a venue / data provider, and implemented in an integrations adapter.
- `OrderBookDelta` (L1/L2/L3)
-- `Ticker`
+- `OrderBookDeltas` (container type)
+- `OrderBookDepth10` (fixed depth of 10 levels per side)
- `QuoteTick`
- `TradeTick`
- `Bar`
- `Instrument`
-- `VenueStatus`
- `InstrumentStatus`
- `InstrumentClose`
+- `VenueStatus`
The following PriceType options can be used for bar aggregations;
- `BID`
diff --git a/docs/concepts/strategies.md b/docs/concepts/strategies.md
index 331f032e2a09..096ad1337d28 100644
--- a/docs/concepts/strategies.md
+++ b/docs/concepts/strategies.md
@@ -91,12 +91,10 @@ from nautilus_trader.model.data import OrderBookDeltas
from nautilus_trader.model.data import InstrumentClose
from nautilus_trader.model.data import InstrumentStatus
from nautilus_trader.model.data import VenueStatus
-from nautilus_trader.model.data import Ticker
from nautilus_trader.model.instruments import Instrument
def on_order_book_deltas(self, deltas: OrderBookDeltas) -> None:
def on_order_book(self, order_book: OrderBook) -> None:
-def on_ticker(self, ticker: Ticker) -> None:
def on_quote_tick(self, tick: QuoteTick) -> None:
def on_trade_tick(self, tick: TradeTick) -> None:
def on_bar(self, bar: Bar) -> None:
@@ -105,7 +103,7 @@ def on_instrument(self, instrument: Instrument) -> None:
def on_instrument_status(self, data: InstrumentStatus) -> None:
def on_instrument_close(self, data: InstrumentClose) -> None:
def on_historical_data(self, data: Data) -> None:
-def on_data(self, data: Data) -> None: # Generic data passed to this handler
+def on_data(self, data: Data) -> None: # Custom data passed to this handler
```
#### Order management
@@ -515,13 +513,14 @@ Here is an example configuration:
```python
from decimal import Decimal
from nautilus_trader.config import StrategyConfig
+from nautilus_trader.model.data import BarType
from nautilus_trader.model.identifiers import InstrumentId
from nautilus_trader.trading.strategy import Strategy
class MyStrategyConfig(StrategyConfig):
- instrument_id: str
- bar_type: str
+ instrument_id: InstrumentId
+ bar_type: BarType
fast_ema_period: int = 10
slow_ema_period: int = 20
trade_size: Decimal
@@ -542,8 +541,8 @@ class MyStrategy(Strategy):
# trading strategy to initialize.
config = MyStrategyConfig(
- instrument_id="ETHUSDT-PERP.BINANCE",
- bar_type="ETHUSDT-PERP.BINANCE-1000-TICK[LAST]-INTERNAL",
+ instrument_id=InstrumentId.from_str("ETHUSDT-PERP.BINANCE"),
+ bar_type=BarType.from_str("ETHUSDT-PERP.BINANCE-1000-TICK[LAST]-INTERNAL"),
trade_size=Decimal(1),
order_id_tag="001",
)
diff --git a/docs/conf.py b/docs/conf.py
index 437980216e05..3488bb493de5 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -23,7 +23,7 @@
# -- Project information -----------------------------------------------------
project = "NautilusTrader"
author = "Nautech Systems Pty Ltd."
-copyright = "2015-2023 Nautech Systems Pty Ltd"
+copyright = "2015-2024 Nautech Systems Pty Ltd"
version = nautilus_trader.__version__
# -- General configuration ---------------------------------------------------
diff --git a/docs/getting_started/installation.md b/docs/getting_started/installation.md
index 68d82c59a55a..525976acc209 100644
--- a/docs/getting_started/installation.md
+++ b/docs/getting_started/installation.md
@@ -1,6 +1,6 @@
# Installation
-NautilusTrader is tested and supported for Python 3.10-3.11 on the following 64-bit platforms:
+NautilusTrader is tested and supported for Python 3.10-3.12 on the following 64-bit platforms:
| Operating System | Supported Versions | CPU Architecture |
|------------------------|-----------------------|-------------------|
@@ -21,7 +21,8 @@ To install the latest binary wheel (or sdist package) from PyPI using Pythons _p
Install optional dependencies as 'extras' for specific integrations:
-- `betfair`: Betfair adapter
+- `betfair`: Betfair adapter (integration)
+- `databento`: Databento adapter (integration)
- `docker`: Needed for Docker when using the IB gateway
- `ib`: Interactive Brokers adapter
diff --git a/docs/getting_started/quickstart.md b/docs/getting_started/quickstart.md
index dc45e10c7c9b..698818e8d304 100644
--- a/docs/getting_started/quickstart.md
+++ b/docs/getting_started/quickstart.md
@@ -14,9 +14,9 @@ deleted when the container is deleted.
- To get started, install docker:
- Go to [docker.com](https://docs.docker.com/get-docker/) and follow the instructions
- From a terminal, download the latest image
- - `docker pull ghcr.io/nautechsystems/jupyterlab:develop --platform linux/amd64`
+ - `docker pull ghcr.io/nautechsystems/jupyterlab:nightly --platform linux/amd64`
- Run the docker container, exposing the jupyter port:
- - `docker run -p 8888:8888 ghcr.io/nautechsystems/jupyterlab:develop`
+ - `docker run -p 8888:8888 ghcr.io/nautechsystems/jupyterlab:nightly`
- Open your web browser to `localhost:{port}`
- https://localhost:8888
@@ -65,7 +65,6 @@ registering indicators to receive certain data types, however in this example we
`QuoteTick` to the indicator in the `on_quote_tick` method.
```python
-from typing import Optional
from nautilus_trader.core.message import Event
from nautilus_trader.indicators.macd import MovingAverageConvergenceDivergence
from nautilus_trader.model.data import QuoteTick
@@ -80,7 +79,7 @@ from nautilus_trader.trading.strategy import Strategy, StrategyConfig
class MACDConfig(StrategyConfig):
- instrument_id: str
+ instrument_id: InstrumentId
fast_period: int = 12
slow_period: int = 26
trade_size: int = 1_000_000
@@ -92,15 +91,17 @@ class MACDStrategy(Strategy):
super().__init__(config=config)
# Our "trading signal"
self.macd = MovingAverageConvergenceDivergence(
- fast_period=config.fast_period, slow_period=config.slow_period, price_type=PriceType.MID
+ fast_period=config.fast_period,
+ slow_period=config.slow_period,
+ price_type=PriceType.MID,
)
# We copy some config values onto the class to make them easier to reference later on
self.entry_threshold = config.entry_threshold
- self.instrument_id = InstrumentId.from_str(config.instrument_id)
+ self.instrument_id = config.instrument_id
self.trade_size = Quantity.from_int(config.trade_size)
# Convenience
- self.position: Optional[Position] = None
+ self.position: Position | None = None
def on_start(self):
self.subscribe_quote_ticks(instrument_id=self.instrument_id)
@@ -174,7 +175,7 @@ To configure a `BacktestNode`, we first need to create an instance of a `Backtes
following (minimal) aspects of the backtest:
- `engine` - The engine for the backtest representing our core system, which will also contain our strategies
-- `venues` - The simulated venues (exchanges or brokers) available in the backtest
+- `venues` - The simulated execution venues (exchanges or brokers) available in the backtest
- `data` - The input data we would like to perform the backtest on
There are many more configurable features which will be described later in the docs, for now this will get us up and running.
@@ -227,7 +228,7 @@ from nautilus_trader.model.data import QuoteTick
data = BacktestDataConfig(
catalog_path=str(catalog.path),
data_cls=QuoteTick,
- instrument_id=str(instruments[0].id),
+ instrument_id=instruments[0].id,
end_time="2020-01-10",
)
```
@@ -253,7 +254,7 @@ engine = BacktestEngineConfig(
strategy_path="__main__:MACDStrategy",
config_path="__main__:MACDConfig",
config=dict(
- instrument_id=instruments[0].id.value,
+ instrument_id=instruments[0].id,
fast_period=12,
slow_period=26,
),
diff --git a/docs/index.md b/docs/index.md
index 036a5c413064..8ef5929d67f4 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -23,7 +23,7 @@ Welcome to the official documentation for NautilusTrader!
providing quantitative traders with the ability to backtest portfolios of automated trading strategies
on historical data with an event-driven engine, and also deploy those same strategies live, with no code changes.**
-The platform boasts an extensive array of features and capabilities, coupled with open-ended flexibility for assembling
+The platform provides an extensive array of features and capabilities, coupled with open-ended flexibility for assembling
trading systems using the framework. Given the breadth of information, and required pre-requisite knowledge, both beginners and experts alike may find the learning curve steep.
However, this documentation aims to assist you in learning and understanding NautilusTrader, so that you can then leverage it to achieve your algorithmic trading goals.
diff --git a/docs/integrations/ib.md b/docs/integrations/ib.md
index a4a8a09bb684..5de486fd9fb5 100644
--- a/docs/integrations/ib.md
+++ b/docs/integrations/ib.md
@@ -196,7 +196,7 @@ data_client_config = InteractiveBrokersDataClientConfig(
### Execution Client
-The `InteractiveBrokersExecutionClient` facilitates executing trades, accessing account information, and processing order and trade-related details. It encompasses a range of methods for order management, including reporting order statuses, placing new orders, and modifying or canceling existing ones. Additionally, it generates position reports, although trade reports are not yet implemented.
+The `InteractiveBrokersExecutionClient` facilitates executing trades, accessing account information, and processing order and trade-related details. It encompasses a range of methods for order management, including reporting order statuses, placing new orders, and modifying or canceling existing ones. Additionally, it generates position reports, although fill reports are not yet implemented.
```python
from nautilus_trader.adapters.interactive_brokers.config import InteractiveBrokersExecClientConfig
diff --git a/docs/rust.md b/docs/rust.md
index 8dd1c1b4dd5c..0399286e4377 100644
--- a/docs/rust.md
+++ b/docs/rust.md
@@ -16,8 +16,8 @@ Use the following links to explore the Rust docs API references for two differen
## [Latest Rust docs](https://docs.nautilustrader.io/core)
This API reference is built from the HEAD of the `master` branch and represents the latest stable release.
-## [Develop Rust docs](https://docs.nautilustrader.io/develop/core)
-This API reference is built from the HEAD of the `develop` branch and represents bleeding edge and experimental changes/features currently in development.
+## [Nightly Rust docs](https://docs.nautilustrader.io/nightly/core)
+This API reference is built from the HEAD of the `nightly` branch and represents bleeding edge and experimental changes/features currently in development.
## What is Rust?
[Rust](https://www.rust-lang.org/) is a multi-paradigm programming language designed for performance and safety, especially safe
diff --git a/docs/tutorials/backtest_high_level.md b/docs/tutorials/backtest_high_level.md
index 4309fc3134cd..4fc56dbe0cb5 100644
--- a/docs/tutorials/backtest_high_level.md
+++ b/docs/tutorials/backtest_high_level.md
@@ -14,7 +14,6 @@ We'll start with all of our imports for the remainder of this tutorial:
```python
import datetime
-import os
import shutil
from decimal import Decimal
from pathlib import Path
@@ -80,12 +79,12 @@ Next, we simply instantiate a `ParquetDataCatalog` (passing in a directory where
We can then write the instrument and tick data to the catalog, it should only take a couple of minutes to load the data (depending on how many months).
```python
-CATALOG_PATH = os.getcwd() + "/catalog"
+CATALOG_PATH = Path.cwd() / "catalog"
# Clear if it already exists, then create fresh
-if os.path.exists(CATALOG_PATH):
+if CATALOG_PATH.exists():
shutil.rmtree(CATALOG_PATH)
-os.mkdir(CATALOG_PATH)
+CATALOG_PATH.mkdir(parents=True)
# Create a catalog instance
catalog = ParquetDataCatalog(CATALOG_PATH)
@@ -178,4 +177,4 @@ node = BacktestNode(configs=[config])
results = node.run()
results
-```
\ No newline at end of file
+```
diff --git a/examples/backtest/betfair_backtest_orderbook_imbalance.py b/examples/backtest/betfair_backtest_orderbook_imbalance.py
index 3ad455456626..2e96982aa506 100644
--- a/examples/backtest/betfair_backtest_orderbook_imbalance.py
+++ b/examples/backtest/betfair_backtest_orderbook_imbalance.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/backtest/crypto_ema_cross_ethusdt_trade_ticks.py b/examples/backtest/crypto_ema_cross_ethusdt_trade_ticks.py
index 9837c04809bd..834efcdbd5c5 100755
--- a/examples/backtest/crypto_ema_cross_ethusdt_trade_ticks.py
+++ b/examples/backtest/crypto_ema_cross_ethusdt_trade_ticks.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -30,6 +30,7 @@
from nautilus_trader.model.data import BarType
from nautilus_trader.model.enums import AccountType
from nautilus_trader.model.enums import OmsType
+from nautilus_trader.model.identifiers import TraderId
from nautilus_trader.model.identifiers import Venue
from nautilus_trader.model.objects import Money
from nautilus_trader.persistence.wranglers import TradeTickDataWrangler
@@ -40,7 +41,7 @@
if __name__ == "__main__":
# Configure backtest engine
config = BacktestEngineConfig(
- trader_id="BACKTESTER-001",
+ trader_id=TraderId("BACKTESTER-001"),
logging=LoggingConfig(
log_level="INFO",
log_colors=True,
diff --git a/examples/backtest/crypto_ema_cross_ethusdt_trailing_stop.py b/examples/backtest/crypto_ema_cross_ethusdt_trailing_stop.py
index 7c04b9f08bb4..62d364905f6d 100755
--- a/examples/backtest/crypto_ema_cross_ethusdt_trailing_stop.py
+++ b/examples/backtest/crypto_ema_cross_ethusdt_trailing_stop.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/backtest/crypto_ema_cross_with_binance_provider.py b/examples/backtest/crypto_ema_cross_with_binance_provider.py
index 15f1c3a9ef83..047e46977145 100644
--- a/examples/backtest/crypto_ema_cross_with_binance_provider.py
+++ b/examples/backtest/crypto_ema_cross_with_binance_provider.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/backtest/crypto_orderbook_imbalance.py b/examples/backtest/crypto_orderbook_imbalance.py
index 9c536602d5f7..1bdc76d0459a 100644
--- a/examples/backtest/crypto_orderbook_imbalance.py
+++ b/examples/backtest/crypto_orderbook_imbalance.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/backtest/fx_ema_cross_audusd_bars_from_ticks.py b/examples/backtest/fx_ema_cross_audusd_bars_from_ticks.py
index 8b652d73049e..0353746ae9f8 100755
--- a/examples/backtest/fx_ema_cross_audusd_bars_from_ticks.py
+++ b/examples/backtest/fx_ema_cross_audusd_bars_from_ticks.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/backtest/fx_ema_cross_audusd_ticks.py b/examples/backtest/fx_ema_cross_audusd_ticks.py
index 3a2e61ebaf5f..f88effe2c2d3 100644
--- a/examples/backtest/fx_ema_cross_audusd_ticks.py
+++ b/examples/backtest/fx_ema_cross_audusd_ticks.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/backtest/fx_ema_cross_bracket_gbpusd_bars_external.py b/examples/backtest/fx_ema_cross_bracket_gbpusd_bars_external.py
index 7dfedee17e52..55860c4f397b 100755
--- a/examples/backtest/fx_ema_cross_bracket_gbpusd_bars_external.py
+++ b/examples/backtest/fx_ema_cross_bracket_gbpusd_bars_external.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/backtest/fx_ema_cross_bracket_gbpusd_bars_internal.py b/examples/backtest/fx_ema_cross_bracket_gbpusd_bars_internal.py
index e869ab3ab2ab..d7de8d413eb4 100755
--- a/examples/backtest/fx_ema_cross_bracket_gbpusd_bars_internal.py
+++ b/examples/backtest/fx_ema_cross_bracket_gbpusd_bars_internal.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/backtest/fx_market_maker_gbpusd_bars.py b/examples/backtest/fx_market_maker_gbpusd_bars.py
index 144f43faba26..814f7e12b0c5 100755
--- a/examples/backtest/fx_market_maker_gbpusd_bars.py
+++ b/examples/backtest/fx_market_maker_gbpusd_bars.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/backtest/fx_talib_gbpusd_bars_internal.py b/examples/backtest/fx_talib_gbpusd_bars_internal.py
index f1ffd7074b3c..60ca7caf8f53 100644
--- a/examples/backtest/fx_talib_gbpusd_bars_internal.py
+++ b/examples/backtest/fx_talib_gbpusd_bars_internal.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/indicators/ema_python.py b/examples/indicators/ema_python.py
index c3a1286beb6f..3ea2dd275b6a 100644
--- a/examples/indicators/ema_python.py
+++ b/examples/indicators/ema_python.py
@@ -1,5 +1,5 @@
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/live/betfair/betfair.py b/examples/live/betfair/betfair.py
index bf04fb581b41..947c17fe4a0b 100644
--- a/examples/live/betfair/betfair.py
+++ b/examples/live/betfair/betfair.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -25,8 +25,8 @@
from nautilus_trader.adapters.betfair.factories import get_cached_betfair_client
from nautilus_trader.adapters.betfair.factories import get_cached_betfair_instrument_provider
from nautilus_trader.adapters.betfair.providers import BetfairInstrumentProviderConfig
-from nautilus_trader.common.clock import LiveClock
from nautilus_trader.common.logging import Logger
+from nautilus_trader.common.logging import log_level_from_str
from nautilus_trader.config import LoggingConfig
from nautilus_trader.config import TradingNodeConfig
from nautilus_trader.examples.strategies.orderbook_imbalance import OrderBookImbalance
@@ -38,9 +38,12 @@
# *** IT IS NOT INTENDED TO BE USED TO TRADE LIVE WITH REAL MONEY. ***
-async def main(instrument_config: BetfairInstrumentProviderConfig):
+async def main(
+ instrument_config: BetfairInstrumentProviderConfig,
+ log_level: str = "INFO",
+) -> TradingNode:
# Connect to Betfair client early to load instruments and account currency
- logger = Logger(clock=LiveClock())
+ logger = Logger(level_stdout=log_level_from_str(log_level))
client = get_cached_betfair_client(
username=None, # Pass here or will source from the `BETFAIR_USERNAME` env var
password=None, # Pass here or will source from the `BETFAIR_PASSWORD` env var
@@ -67,7 +70,7 @@ async def main(instrument_config: BetfairInstrumentProviderConfig):
timeout_connection=30.0,
timeout_disconnection=30.0,
timeout_post_stop=30.0,
- logging=LoggingConfig(log_level="DEBUG"),
+ logging=LoggingConfig(log_level=log_level),
data_clients={
"BETFAIR": BetfairDataClientConfig(
account_currency=account.currency_code,
@@ -95,7 +98,7 @@ async def main(instrument_config: BetfairInstrumentProviderConfig):
config=OrderBookImbalanceConfig(
instrument_id=instrument.id,
max_trade_size=Decimal(10),
- trigger_min_size=10,
+ trigger_min_size=2,
order_id_tag=instrument.selection_id,
subscribe_ticker=True,
),
@@ -127,6 +130,9 @@ async def main(instrument_config: BetfairInstrumentProviderConfig):
# Update the market ID with something coming up in `Next Races` from
# https://www.betfair.com.au/exchange/plus/
# The market ID will appear in the browser query string.
- config = BetfairInstrumentProviderConfig(market_ids=["1.221718403"])
- node = asyncio.run(main(instrument_config=config))
+ config = BetfairInstrumentProviderConfig(
+ account_currency="AUD",
+ market_ids=["1.223041451"],
+ )
+ node = asyncio.run(main(instrument_config=config, log_level="DEBUG"))
node.dispose()
diff --git a/examples/live/betfair/betfair_sandbox.py b/examples/live/betfair/betfair_sandbox.py
index 31357768ccda..615166956526 100644
--- a/examples/live/betfair/betfair_sandbox.py
+++ b/examples/live/betfair/betfair_sandbox.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -26,7 +26,6 @@
from nautilus_trader.adapters.sandbox.config import SandboxExecutionClientConfig
from nautilus_trader.adapters.sandbox.execution import SandboxExecutionClient
from nautilus_trader.adapters.sandbox.factory import SandboxLiveExecClientFactory
-from nautilus_trader.common.clock import LiveClock
from nautilus_trader.common.logging import Logger
from nautilus_trader.config import LoggingConfig
from nautilus_trader.config import TradingNodeConfig
@@ -39,9 +38,9 @@
# *** IT IS NOT INTENDED TO BE USED TO TRADE LIVE WITH REAL MONEY. ***
-async def main(instrument_config: BetfairInstrumentProviderConfig):
+async def main(instrument_config: BetfairInstrumentProviderConfig) -> TradingNode:
# Connect to Betfair client early to load instruments and account currency
- logger = Logger(clock=LiveClock())
+ logger = Logger()
client = get_cached_betfair_client(
username=None, # Pass here or will source from the `BETFAIR_USERNAME` env var
password=None, # Pass here or will source from the `BETFAIR_PASSWORD` env var
@@ -121,6 +120,7 @@ async def main(instrument_config: BetfairInstrumentProviderConfig):
# The market ID will appear in the browser query string.
config = BetfairInstrumentProviderConfig(
market_ids=["1.199513161"],
+ account_currency="GBP",
)
node = asyncio.run(main(config))
node.dispose()
diff --git a/examples/live/binance/binance_futures_market_maker.py b/examples/live/binance/binance_futures_market_maker.py
index 4788ffcefe6b..21f346d3d481 100644
--- a/examples/live/binance/binance_futures_market_maker.py
+++ b/examples/live/binance/binance_futures_market_maker.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/live/binance/binance_futures_testnet_ema_cross.py b/examples/live/binance/binance_futures_testnet_ema_cross.py
index eb67d998c3a9..cd94444191dc 100644
--- a/examples/live/binance/binance_futures_testnet_ema_cross.py
+++ b/examples/live/binance/binance_futures_testnet_ema_cross.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/live/binance/binance_futures_testnet_ema_cross_bracket.py b/examples/live/binance/binance_futures_testnet_ema_cross_bracket.py
index 19aadb7ac1e3..b6107a8a1d83 100644
--- a/examples/live/binance/binance_futures_testnet_ema_cross_bracket.py
+++ b/examples/live/binance/binance_futures_testnet_ema_cross_bracket.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/live/binance/binance_futures_testnet_ema_cross_bracket_algo.py b/examples/live/binance/binance_futures_testnet_ema_cross_bracket_algo.py
index d587175fbb44..aee1e834f26e 100644
--- a/examples/live/binance/binance_futures_testnet_ema_cross_bracket_algo.py
+++ b/examples/live/binance/binance_futures_testnet_ema_cross_bracket_algo.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/live/binance/binance_futures_testnet_ema_cross_twap.py b/examples/live/binance/binance_futures_testnet_ema_cross_twap.py
index f00df7bad0a3..3c828eda8470 100644
--- a/examples/live/binance/binance_futures_testnet_ema_cross_twap.py
+++ b/examples/live/binance/binance_futures_testnet_ema_cross_twap.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/live/binance/binance_futures_testnet_ema_cross_with_trailing_stop.py b/examples/live/binance/binance_futures_testnet_ema_cross_with_trailing_stop.py
index b4da4af48b41..4000cec359ef 100644
--- a/examples/live/binance/binance_futures_testnet_ema_cross_with_trailing_stop.py
+++ b/examples/live/binance/binance_futures_testnet_ema_cross_with_trailing_stop.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/live/binance/binance_futures_testnet_market_maker.py b/examples/live/binance/binance_futures_testnet_market_maker.py
index 29a652feb19f..f54e56638379 100644
--- a/examples/live/binance/binance_futures_testnet_market_maker.py
+++ b/examples/live/binance/binance_futures_testnet_market_maker.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -47,7 +47,6 @@
# log_file_format="json",
log_colors=True,
),
- # tracing=TracingConfig(stdout_level="DEBUG"),
exec_engine=LiveExecEngineConfig(
reconciliation=True,
reconciliation_lookback_mins=1440,
diff --git a/examples/live/binance/binance_futures_testnet_orderbook_imbalance.py b/examples/live/binance/binance_futures_testnet_orderbook_imbalance.py
index 4af05536da78..3dac6ae0b49d 100644
--- a/examples/live/binance/binance_futures_testnet_orderbook_imbalance.py
+++ b/examples/live/binance/binance_futures_testnet_orderbook_imbalance.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -45,7 +45,6 @@
# log_level_file="DEBUG",
# log_file_format="json",
),
- # tracing=TracingConfig(stdout_level="DEBUG"),
exec_engine=LiveExecEngineConfig(
reconciliation=True,
reconciliation_lookback_mins=1440,
diff --git a/examples/live/binance/binance_spot_ema_cross.py b/examples/live/binance/binance_spot_ema_cross.py
index 6560a97ebe49..3937ee4d0b52 100644
--- a/examples/live/binance/binance_spot_ema_cross.py
+++ b/examples/live/binance/binance_spot_ema_cross.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/live/binance/binance_spot_ema_cross_bracket_algo.py b/examples/live/binance/binance_spot_ema_cross_bracket_algo.py
index 84ba86d37cae..155049dc20e5 100644
--- a/examples/live/binance/binance_spot_ema_cross_bracket_algo.py
+++ b/examples/live/binance/binance_spot_ema_cross_bracket_algo.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/live/binance/binance_spot_market_maker.py b/examples/live/binance/binance_spot_market_maker.py
index 3bf2ea72b5ff..1514f698bc62 100644
--- a/examples/live/binance/binance_spot_market_maker.py
+++ b/examples/live/binance/binance_spot_market_maker.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -40,7 +40,11 @@
# Configure the trading node
config_node = TradingNodeConfig(
trader_id=TraderId("TESTER-001"),
- logging=LoggingConfig(log_level="INFO"),
+ logging=LoggingConfig(
+ log_level="INFO",
+ # log_level_file="DEBUG",
+ # log_colors=False,
+ ),
exec_engine=LiveExecEngineConfig(
reconciliation=True,
reconciliation_lookback_mins=1440,
diff --git a/examples/live/binance/binance_spot_testnet_ema_cross.py b/examples/live/binance/binance_spot_testnet_ema_cross.py
index 2b9bc22dbd0e..d10e19adaa51 100644
--- a/examples/live/binance/binance_spot_testnet_ema_cross.py
+++ b/examples/live/binance/binance_spot_testnet_ema_cross.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/live/bybit/bybit_market_maker.py b/examples/live/bybit/bybit_market_maker.py
index af2b9189eae6..4b5964a537a2 100644
--- a/examples/live/bybit/bybit_market_maker.py
+++ b/examples/live/bybit/bybit_market_maker.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/live/bybit/bybit_request_custom_endpoint.py b/examples/live/bybit/bybit_request_custom_endpoint.py
index 5bc9891169c8..b50d22041de2 100644
--- a/examples/live/bybit/bybit_request_custom_endpoint.py
+++ b/examples/live/bybit/bybit_request_custom_endpoint.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -46,7 +46,7 @@
class RequestDemoStrategyConfig(StrategyConfig, frozen=True):
- instrument_id: str
+ instrument_id: InstrumentId
interval: int
@@ -64,7 +64,7 @@ class RequestDemoStrategy(Strategy):
def __init__(self, config: RequestDemoStrategyConfig):
super().__init__()
self.interval = config.interval
- self.instrument_id = InstrumentId.from_str(config.instrument_id)
+ self.instrument_id = config.instrument_id
def on_start(self):
seconds_delta = timedelta(seconds=self.interval)
@@ -124,7 +124,7 @@ def on_historical_data(self, data: Data):
node = TradingNode(config=config_node)
-instrument_id = "ETHUSDT-LINEAR.BYBIT"
+instrument_id = InstrumentId.from_str("ETHUSDT-LINEAR.BYBIT")
strategy_config = RequestDemoStrategyConfig(
instrument_id=instrument_id,
interval=10,
diff --git a/examples/live/databento/databento_subscriber.py b/examples/live/databento/databento_subscriber.py
index 831b2ca9e419..39f26f440b9b 100644
--- a/examples/live/databento/databento_subscriber.py
+++ b/examples/live/databento/databento_subscriber.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/live/interactive_brokers/historic_download.py b/examples/live/interactive_brokers/historic_download.py
index e7af08ba7829..1b0e2c1b0d30 100644
--- a/examples/live/interactive_brokers/historic_download.py
+++ b/examples/live/interactive_brokers/historic_download.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# -------------------------------------------------------------------------------------------------
+
import asyncio
import datetime
import os
diff --git a/examples/live/interactive_brokers/interactive_brokers_example.py b/examples/live/interactive_brokers/interactive_brokers_example.py
index a8a03821cc9d..63a202d40757 100644
--- a/examples/live/interactive_brokers/interactive_brokers_example.py
+++ b/examples/live/interactive_brokers/interactive_brokers_example.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -------------------------------------------------------------------------------------------------
-# Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+# Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
# https://nautechsystems.io
#
# Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/examples/notebooks/backtest_binance_orderbook.ipynb b/examples/notebooks/backtest_binance_orderbook.ipynb
index f4fb3a877d80..c06857c6b110 100644
--- a/examples/notebooks/backtest_binance_orderbook.ipynb
+++ b/examples/notebooks/backtest_binance_orderbook.ipynb
@@ -48,7 +48,7 @@
"from nautilus_trader.examples.strategies.ema_cross import EMACross, EMACrossConfig\n",
"from nautilus_trader.model.data import OrderBookDelta\n",
"from nautilus_trader.persistence.loaders import BinanceOrderBookDeltaDataLoader\n",
- "from nautilus_trader.persistence.wranglers import OrderBookDeltaDataWrangler\n",
+ "from nautilus_trader.persistence.wranglers import OrderBookDeltaDataWranglerV2\n",
"from nautilus_trader.persistence.catalog import ParquetDataCatalog\n",
"from nautilus_trader.test_kit.providers import TestInstrumentProvider"
]
@@ -113,7 +113,7 @@
"source": [
"# Process deltas using a wrangler\n",
"BTCUSDT_BINANCE = TestInstrumentProvider.btcusdt_binance()\n",
- "wrangler = OrderBookDeltaDataWrangler(BTCUSDT_BINANCE)\n",
+ "wrangler = OrderBookDeltaDataWranglerV2(BTCUSDT_BINANCE)\n",
"\n",
"deltas = wrangler.process(df_snap)\n",
"deltas += wrangler.process(df_update)\n",
diff --git a/examples/notebooks/parquet_explorer.ipynb b/examples/notebooks/parquet_explorer.ipynb
index 1af1175588de..df5c6af9cd72 100644
--- a/examples/notebooks/parquet_explorer.ipynb
+++ b/examples/notebooks/parquet_explorer.ipynb
@@ -39,8 +39,8 @@
"metadata": {},
"outputs": [],
"source": [
- "trade_tick_path = \"../../tests/test_data/trade_tick_data.parquet\"\n",
- "bar_path = \"../../tests/test_data/bar_data.parquet\""
+ "trade_tick_path = \"../../tests/test_data/nautilus/trades.parquet\"\n",
+ "bar_path = \"../../tests/test_data/nautilus/bars.parquet\""
]
},
{
diff --git a/examples/notebooks/quick_start.ipynb b/examples/notebooks/quick_start.ipynb
index e32a29e2eeab..c6924eaf0d5a 100644
--- a/examples/notebooks/quick_start.ipynb
+++ b/examples/notebooks/quick_start.ipynb
@@ -90,7 +90,6 @@
"metadata": {},
"outputs": [],
"source": [
- "from typing import Optional\n",
"from nautilus_trader.core.message import Event\n",
"from nautilus_trader.indicators.macd import MovingAverageConvergenceDivergence\n",
"from nautilus_trader.model.data import QuoteTick\n",
@@ -105,7 +104,7 @@
"\n",
"\n",
"class MACDConfig(StrategyConfig):\n",
- " instrument_id: str\n",
+ " instrument_id: InstrumentId\n",
" fast_period: int = 12\n",
" slow_period: int = 26\n",
" trade_size: int = 1_000_000\n",
@@ -121,12 +120,12 @@
" )\n",
" # We copy some config values onto the class to make them easier to reference later on\n",
" self.entry_threshold = config.entry_threshold\n",
- " self.instrument_id = InstrumentId.from_str(config.instrument_id)\n",
+ " self.instrument_id = config.instrument_id\n",
" self.trade_size = Quantity.from_int(config.trade_size)\n",
" self.entry_threshold = config.entry_threshold\n",
"\n",
" # Convenience\n",
- " self.position: Optional[Position] = None\n",
+ " self.position: Position | None = None\n",
"\n",
" def on_start(self):\n",
" self.subscribe_quote_ticks(instrument_id=self.instrument_id)\n",
@@ -324,7 +323,7 @@
" strategy_path=\"__main__:MACDStrategy\",\n",
" config_path=\"__main__:MACDConfig\",\n",
" config=dict(\n",
- " instrument_id=instruments[0].id.value,\n",
+ " instrument_id=instruments[0].id,\n",
" fast_period=12,\n",
" slow_period=26,\n",
" ),\n",
diff --git a/nautilus_core/Cargo.lock b/nautilus_core/Cargo.lock
index bd95583dcc71..9aa5e2013e1b 100644
--- a/nautilus_core/Cargo.lock
+++ b/nautilus_core/Cargo.lock
@@ -30,9 +30,9 @@ dependencies = [
[[package]]
name = "ahash"
-version = "0.8.6"
+version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a"
+checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01"
dependencies = [
"cfg-if",
"const-random",
@@ -101,9 +101,9 @@ checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
[[package]]
name = "anyhow"
-version = "1.0.76"
+version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59d2a3357dde987206219e78ecfbbb6e8dad06cbb65292758d3270e6254f7355"
+checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
[[package]]
name = "arc-swap"
@@ -129,7 +129,7 @@ version = "49.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bc25126d18a012146a888a0298f2c22e1150327bd2765fc76d710a556b2d614"
dependencies = [
- "ahash 0.8.6",
+ "ahash 0.8.7",
"arrow-arith",
"arrow-array",
"arrow-buffer",
@@ -167,7 +167,7 @@ version = "49.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bda9acea48b25123c08340f3a8ac361aa0f74469bb36f5ee9acf923fce23e9d"
dependencies = [
- "ahash 0.8.6",
+ "ahash 0.8.7",
"arrow-buffer",
"arrow-data",
"arrow-schema",
@@ -294,7 +294,7 @@ version = "49.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "361249898d2d6d4a6eeb7484be6ac74977e48da12a4dd81a708d620cc558117a"
dependencies = [
- "ahash 0.8.6",
+ "ahash 0.8.7",
"arrow-array",
"arrow-buffer",
"arrow-data",
@@ -318,7 +318,7 @@ version = "49.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f6208466590960efc1d2a7172bc4ff18a67d6e25c529381d7f96ddaf0dc4036"
dependencies = [
- "ahash 0.8.6",
+ "ahash 0.8.7",
"arrow-array",
"arrow-buffer",
"arrow-data",
@@ -362,13 +362,13 @@ dependencies = [
[[package]]
name = "async-trait"
-version = "0.1.75"
+version = "0.1.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fdf6721fb0140e4f897002dd086c06f6c27775df19cfe1fccb21181a48fd2c98"
+checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.42",
+ "syn 2.0.48",
]
[[package]]
@@ -407,6 +407,61 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+[[package]]
+name = "axum"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d09dbe0e490df5da9d69b36dca48a76635288a82f92eca90024883a56202026d"
+dependencies = [
+ "async-trait",
+ "axum-core",
+ "bytes",
+ "futures-util",
+ "http 1.0.0",
+ "http-body 1.0.0",
+ "http-body-util",
+ "hyper 1.1.0",
+ "hyper-util",
+ "itoa",
+ "matchit",
+ "memchr",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "rustversion",
+ "serde",
+ "serde_json",
+ "serde_path_to_error",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "axum-core"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e87c8503f93e6d144ee5690907ba22db7ba79ab001a932ab99034f0fe836b3df"
+dependencies = [
+ "async-trait",
+ "bytes",
+ "futures-util",
+ "http 1.0.0",
+ "http-body 1.0.0",
+ "http-body-util",
+ "mime",
+ "pin-project-lite",
+ "rustversion",
+ "sync_wrapper",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
[[package]]
name = "backtrace"
version = "0.3.69"
@@ -424,9 +479,9 @@ dependencies = [
[[package]]
name = "base64"
-version = "0.21.5"
+version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
name = "base64ct"
@@ -521,7 +576,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
- "syn 2.0.42",
+ "syn 2.0.48",
"syn_derive",
]
@@ -676,9 +731,9 @@ dependencies = [
[[package]]
name = "chrono-tz"
-version = "0.8.4"
+version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e23185c0e21df6ed832a12e2bda87c7d1def6842881fb634a8511ced741b0d76"
+checksum = "91d7b79e99bfaa0d47da0687c43aa3b7381938a62ad3a6498599039321f660b7"
dependencies = [
"chrono",
"chrono-tz-build",
@@ -740,18 +795,18 @@ dependencies = [
[[package]]
name = "clap"
-version = "4.4.11"
+version = "4.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2"
+checksum = "58e54881c004cec7895b0068a0a954cd5d62da01aef83fa35b1e594497bf5445"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
-version = "4.4.11"
+version = "4.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb"
+checksum = "59cb82d7f531603d2fd1f507441cdd35184fa81beff7bd489570de7f773460bb"
dependencies = [
"anstyle",
"clap_lex 0.6.0",
@@ -853,9 +908,9 @@ checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]]
name = "cpufeatures"
-version = "0.2.11"
+version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
+checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
dependencies = [
"libc",
]
@@ -893,7 +948,7 @@ dependencies = [
"anes",
"cast",
"ciborium",
- "clap 4.4.11",
+ "clap 4.4.16",
"criterion-plot",
"is-terminal",
"itertools 0.10.5",
@@ -920,57 +975,39 @@ dependencies = [
"itertools 0.10.5",
]
-[[package]]
-name = "crossbeam-channel"
-version = "0.5.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14c3242926edf34aec4ac3a77108ad4854bffaa2e4ddc1824124ce59231302d5"
-dependencies = [
- "cfg-if",
- "crossbeam-utils",
-]
-
[[package]]
name = "crossbeam-deque"
-version = "0.8.4"
+version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751"
+checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
dependencies = [
- "cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
-version = "0.9.16"
+version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d2fe95351b870527a5d09bf563ed3c97c0cffb87cf1c78a591bf48bb218d9aa"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
- "autocfg",
- "cfg-if",
"crossbeam-utils",
- "memoffset",
]
[[package]]
name = "crossbeam-queue"
-version = "0.3.9"
+version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9bcf5bdbfdd6030fb4a1c497b5d5fc5921aa2f60d359a17e249c0e6df3de153"
+checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
dependencies = [
- "cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
-version = "0.8.17"
+version = "0.8.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c06d96137f14f244c37f989d9fff8f95e6c18b918e71f36638f8c49112e4c78f"
-dependencies = [
- "cfg-if",
-]
+checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
[[package]]
name = "crunchy"
@@ -1069,7 +1106,7 @@ version = "34.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "193fd1e7628278d0641c5122860f9a7fd6a1d77d055838d12f55d15bbe28d4d0"
dependencies = [
- "ahash 0.8.6",
+ "ahash 0.8.7",
"arrow",
"arrow-array",
"arrow-schema",
@@ -1116,7 +1153,7 @@ version = "34.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "548bc49c4a489e3de474813831ea556dc9d368f9ed8d867b1493da42e8e9f613"
dependencies = [
- "ahash 0.8.6",
+ "ahash 0.8.7",
"arrow",
"arrow-array",
"arrow-buffer",
@@ -1158,7 +1195,7 @@ version = "34.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c473f72d8d81a532e63f6e562ed66dd9209dfd8e433d9712abd42444ee161e"
dependencies = [
- "ahash 0.8.6",
+ "ahash 0.8.7",
"arrow",
"arrow-array",
"datafusion-common",
@@ -1192,7 +1229,7 @@ version = "34.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1ca7e35ca22f9dc506c2375b92054b03ccf91afe25c0a90b395a1473a09735"
dependencies = [
- "ahash 0.8.6",
+ "ahash 0.8.7",
"arrow",
"arrow-array",
"arrow-buffer",
@@ -1226,7 +1263,7 @@ version = "34.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddde97adefcca3a55257c646ffee2a95b6cac66f74d1146a6e3a6dbb37830631"
dependencies = [
- "ahash 0.8.6",
+ "ahash 0.8.7",
"arrow",
"arrow-array",
"arrow-buffer",
@@ -1265,6 +1302,37 @@ dependencies = [
"sqlparser",
]
+[[package]]
+name = "dbn"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d100eedbab53177dcc235c8521837443915ba5492244923a049e652b91660734"
+dependencies = [
+ "csv",
+ "dbn-macros",
+ "itoa",
+ "json-writer",
+ "num_enum",
+ "pyo3",
+ "streaming-iterator",
+ "strum",
+ "thiserror",
+ "time",
+ "zstd",
+]
+
+[[package]]
+name = "dbn-macros"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c974de15f28d5f0c7abc01570e03adf940228e998c88a96ce0b0c90c37ac68f6"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
[[package]]
name = "der"
version = "0.7.8"
@@ -1278,9 +1346,9 @@ dependencies = [
[[package]]
name = "deranged"
-version = "0.3.10"
+version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc"
+checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
dependencies = [
"powerfmt",
]
@@ -1355,6 +1423,15 @@ dependencies = [
"serde",
]
+[[package]]
+name = "encoding_rs"
+version = "0.8.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
+dependencies = [
+ "cfg-if",
+]
+
[[package]]
name = "env_logger"
version = "0.8.4"
@@ -1513,9 +1590,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]]
name = "futures"
-version = "0.3.29"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335"
+checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
dependencies = [
"futures-channel",
"futures-core",
@@ -1528,9 +1605,9 @@ dependencies = [
[[package]]
name = "futures-channel"
-version = "0.3.29"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb"
+checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
dependencies = [
"futures-core",
"futures-sink",
@@ -1538,15 +1615,15 @@ dependencies = [
[[package]]
name = "futures-core"
-version = "0.3.29"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
+checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]]
name = "futures-executor"
-version = "0.3.29"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc"
+checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
dependencies = [
"futures-core",
"futures-task",
@@ -1566,32 +1643,32 @@ dependencies = [
[[package]]
name = "futures-io"
-version = "0.3.29"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa"
+checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
[[package]]
name = "futures-macro"
-version = "0.3.29"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb"
+checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.42",
+ "syn 2.0.48",
]
[[package]]
name = "futures-sink"
-version = "0.3.29"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
+checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
[[package]]
name = "futures-task"
-version = "0.3.29"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2"
+checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
[[package]]
name = "futures-timer"
@@ -1601,9 +1678,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c"
[[package]]
name = "futures-util"
-version = "0.3.29"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
+checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
"futures-channel",
"futures-core",
@@ -1629,9 +1706,9 @@ dependencies = [
[[package]]
name = "getrandom"
-version = "0.2.11"
+version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
+checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
dependencies = [
"cfg-if",
"libc",
@@ -1650,6 +1727,44 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
+[[package]]
+name = "h2"
+version = "0.3.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b553656127a00601c8ae5590fcfdc118e4083a7924b6cf4ffc1ea4b99dc429d7"
+dependencies = [
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "http 0.2.11",
+ "indexmap 2.1.0",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "h2"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "991910e35c615d8cab86b5ab04be67e6ad24d2bf5f4f11fdbbed26da999bbeab"
+dependencies = [
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "futures-util",
+ "http 1.0.0",
+ "indexmap 2.1.0",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
[[package]]
name = "half"
version = "1.8.2"
@@ -1682,7 +1797,7 @@ version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
dependencies = [
- "ahash 0.8.6",
+ "ahash 0.8.7",
"allocator-api2",
]
@@ -1785,6 +1900,29 @@ dependencies = [
"pin-project-lite",
]
+[[package]]
+name = "http-body"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
+dependencies = [
+ "bytes",
+ "http 1.0.0",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840"
+dependencies = [
+ "bytes",
+ "futures-util",
+ "http 1.0.0",
+ "http-body 1.0.0",
+ "pin-project-lite",
+]
+
[[package]]
name = "httparse"
version = "1.8.0"
@@ -1813,8 +1951,9 @@ dependencies = [
"futures-channel",
"futures-core",
"futures-util",
+ "h2 0.3.23",
"http 0.2.11",
- "http-body",
+ "http-body 0.4.6",
"httparse",
"httpdate",
"itoa",
@@ -1826,6 +1965,25 @@ dependencies = [
"want",
]
+[[package]]
+name = "hyper"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "h2 0.4.1",
+ "http 1.0.0",
+ "http-body 1.0.0",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "tokio",
+]
+
[[package]]
name = "hyper-tls"
version = "0.5.0"
@@ -1833,12 +1991,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
dependencies = [
"bytes",
- "hyper",
+ "hyper 0.14.28",
"native-tls",
"tokio",
"tokio-native-tls",
]
+[[package]]
+name = "hyper-util"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bdea9aac0dbe5a9240d68cfd9501e2db94222c6dc06843e06640b9e07f0fdc67"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "http 1.0.0",
+ "http-body 1.0.0",
+ "hyper 1.1.0",
+ "pin-project-lite",
+ "socket2 0.5.5",
+ "tokio",
+ "tracing",
+]
+
[[package]]
name = "iai"
version = "0.1.1"
@@ -1847,9 +2023,9 @@ checksum = "71a816c97c42258aa5834d07590b718b4c9a598944cd39a52dc25b351185d678"
[[package]]
name = "iana-time-zone"
-version = "0.1.58"
+version = "0.1.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20"
+checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539"
dependencies = [
"android_system_properties",
"core-foundation-sys",
@@ -1916,15 +2092,21 @@ version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02"
+[[package]]
+name = "ipnet"
+version = "2.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
+
[[package]]
name = "is-terminal"
-version = "0.4.9"
+version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
+checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455"
dependencies = [
"hermit-abi 0.3.3",
"rustix",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -1978,6 +2160,16 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "json-writer"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a46aa3d39e9495d71c03e7b68981cd900f17e7ddf2ff97a14583bfbd866f8d23"
+dependencies = [
+ "itoa",
+ "ryu",
+]
+
[[package]]
name = "lazy_static"
version = "1.4.0"
@@ -2053,9 +2245,9 @@ dependencies = [
[[package]]
name = "libc"
-version = "0.2.151"
+version = "0.2.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
+checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
[[package]]
name = "libm"
@@ -2095,12 +2287,16 @@ name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
+dependencies = [
+ "serde",
+ "value-bag",
+]
[[package]]
name = "lz4_flex"
-version = "0.11.1"
+version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ea9b256699eda7b0387ffbc776dd625e28bde3918446381781245b7a50349d8"
+checksum = "912b45c753ff5f7f5208307e8ace7d2a2e30d024e26d3509f3dce546c044ce15"
dependencies = [
"twox-hash",
]
@@ -2125,6 +2321,12 @@ dependencies = [
"regex-automata 0.1.10",
]
+[[package]]
+name = "matchit"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
+
[[package]]
name = "md-5"
version = "0.10.6"
@@ -2137,9 +2339,9 @@ dependencies = [
[[package]]
name = "memchr"
-version = "2.6.4"
+version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
+checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "memoffset"
@@ -2150,6 +2352,12 @@ dependencies = [
"autocfg",
]
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@@ -2194,9 +2402,32 @@ dependencies = [
"tempfile",
]
+[[package]]
+name = "nautilus-adapters"
+version = "0.14.0"
+dependencies = [
+ "anyhow",
+ "chrono",
+ "criterion",
+ "dbn",
+ "indexmap 2.1.0",
+ "itoa",
+ "nautilus-common",
+ "nautilus-core",
+ "nautilus-model",
+ "pyo3",
+ "rand",
+ "rstest",
+ "rust_decimal",
+ "rust_decimal_macros",
+ "thiserror",
+ "tokio",
+ "ustr",
+]
+
[[package]]
name = "nautilus-backtest"
-version = "0.13.0"
+version = "0.14.0"
dependencies = [
"cbindgen",
"nautilus-common",
@@ -2205,16 +2436,18 @@ dependencies = [
"pyo3",
"rstest",
"tempfile",
+ "ustr",
]
[[package]]
name = "nautilus-common"
-version = "0.13.0"
+version = "0.14.0"
dependencies = [
"anyhow",
"cbindgen",
"chrono",
"indexmap 2.1.0",
+ "log",
"nautilus-core",
"nautilus-model",
"pyo3",
@@ -2225,14 +2458,13 @@ dependencies = [
"strum",
"tempfile",
"tracing",
- "tracing-appender",
"tracing-subscriber",
"ustr",
]
[[package]]
name = "nautilus-core"
-version = "0.13.0"
+version = "0.14.0"
dependencies = [
"anyhow",
"cbindgen",
@@ -2251,7 +2483,7 @@ dependencies = [
[[package]]
name = "nautilus-indicators"
-version = "0.13.0"
+version = "0.14.0"
dependencies = [
"anyhow",
"nautilus-core",
@@ -2264,7 +2496,7 @@ dependencies = [
[[package]]
name = "nautilus-infrastructure"
-version = "0.13.0"
+version = "0.14.0"
dependencies = [
"anyhow",
"nautilus-common",
@@ -2281,7 +2513,7 @@ dependencies = [
[[package]]
name = "nautilus-model"
-version = "0.13.0"
+version = "0.14.0"
dependencies = [
"anyhow",
"cbindgen",
@@ -2310,19 +2542,23 @@ dependencies = [
[[package]]
name = "nautilus-network"
-version = "0.13.0"
+version = "0.14.0"
dependencies = [
"anyhow",
+ "axum",
"criterion",
"dashmap",
"futures",
"futures-util",
- "hyper",
+ "http 1.0.0",
+ "http-body-util",
+ "hyper 1.1.0",
"hyper-tls",
"nautilus-core",
"nonzero_ext",
"pyo3",
"pyo3-asyncio",
+ "reqwest",
"rstest",
"serde_json",
"tokio",
@@ -2333,7 +2569,7 @@ dependencies = [
[[package]]
name = "nautilus-persistence"
-version = "0.13.0"
+version = "0.14.0"
dependencies = [
"anyhow",
"binary-heap-plus",
@@ -2357,8 +2593,9 @@ dependencies = [
[[package]]
name = "nautilus-pyo3"
-version = "0.13.0"
+version = "0.14.0"
dependencies = [
+ "nautilus-adapters",
"nautilus-common",
"nautilus-core",
"nautilus-indicators",
@@ -2510,11 +2747,32 @@ dependencies = [
"libc",
]
+[[package]]
+name = "num_enum"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845"
+dependencies = [
+ "num_enum_derive",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.48",
+]
+
[[package]]
name = "object"
-version = "0.32.1"
+version = "0.32.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
+checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
dependencies = [
"memchr",
]
@@ -2575,7 +2833,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.42",
+ "syn 2.0.48",
]
[[package]]
@@ -2667,7 +2925,7 @@ version = "49.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af88740a842787da39b3d69ce5fbf6fce97d20211d3b299fee0a0da6430c74d4"
dependencies = [
- "ahash 0.8.6",
+ "ahash 0.8.7",
"arrow-array",
"arrow-buffer",
"arrow-cast",
@@ -2790,7 +3048,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.42",
+ "syn 2.0.48",
]
[[package]]
@@ -2908,9 +3166,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.71"
+version = "1.0.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8"
+checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
dependencies = [
"unicode-ident",
]
@@ -2937,9 +3195,9 @@ dependencies = [
[[package]]
name = "pyo3"
-version = "0.20.0"
+version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04e8453b658fe480c3e70c8ed4e3d3ec33eb74988bd186561b0cc66b85c3bc4b"
+checksum = "9a89dc7a5850d0e983be1ec2a463a171d20990487c3cfcd68b5363f1ee3d6fe0"
dependencies = [
"cfg-if",
"indoc",
@@ -2980,9 +3238,9 @@ dependencies = [
[[package]]
name = "pyo3-build-config"
-version = "0.20.0"
+version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a96fe70b176a89cff78f2fa7b3c930081e163d5379b4dcdf993e3ae29ca662e5"
+checksum = "07426f0d8fe5a601f26293f300afd1a7b1ed5e78b2a705870c5f30893c5163be"
dependencies = [
"once_cell",
"target-lexicon",
@@ -2990,9 +3248,9 @@ dependencies = [
[[package]]
name = "pyo3-ffi"
-version = "0.20.0"
+version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "214929900fd25e6604661ed9cf349727c8920d47deff196c4e28165a6ef2a96b"
+checksum = "dbb7dec17e17766b46bca4f1a4215a85006b4c2ecde122076c562dd058da6cf1"
dependencies = [
"libc",
"pyo3-build-config",
@@ -3000,26 +3258,26 @@ dependencies = [
[[package]]
name = "pyo3-macros"
-version = "0.20.0"
+version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dac53072f717aa1bfa4db832b39de8c875b7c7af4f4a6fe93cdbf9264cf8383b"
+checksum = "05f738b4e40d50b5711957f142878cfa0f28e054aa0ebdfc3fd137a843f74ed3"
dependencies = [
"proc-macro2",
"pyo3-macros-backend",
"quote",
- "syn 2.0.42",
+ "syn 2.0.48",
]
[[package]]
name = "pyo3-macros-backend"
-version = "0.20.0"
+version = "0.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7774b5a8282bd4f25f803b1f0d945120be959a36c72e08e7cd031c792fdfd424"
+checksum = "0fc910d4851847827daf9d6cdd4a823fbdaab5b8818325c5e97a86da79e8881f"
dependencies = [
"heck",
"proc-macro2",
"quote",
- "syn 2.0.42",
+ "syn 2.0.48",
]
[[package]]
@@ -3046,9 +3304,9 @@ dependencies = [
[[package]]
name = "quote"
-version = "1.0.33"
+version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
@@ -3193,9 +3451,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "relative-path"
-version = "1.9.0"
+version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c707298afce11da2efef2f600116fa93ffa7a032b5d7b628aa17711ec81383ca"
+checksum = "e898588f33fdd5b9420719948f9f2a32c922a246964576f71ba7f24f80610fbc"
[[package]]
name = "rend"
@@ -3206,6 +3464,44 @@ dependencies = [
"bytecheck",
]
+[[package]]
+name = "reqwest"
+version = "0.11.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41"
+dependencies = [
+ "base64",
+ "bytes",
+ "encoding_rs",
+ "futures-core",
+ "futures-util",
+ "h2 0.3.23",
+ "http 0.2.11",
+ "http-body 0.4.6",
+ "hyper 0.14.28",
+ "hyper-tls",
+ "ipnet",
+ "js-sys",
+ "log",
+ "mime",
+ "native-tls",
+ "once_cell",
+ "percent-encoding",
+ "pin-project-lite",
+ "serde",
+ "serde_json",
+ "serde_urlencoded",
+ "system-configuration",
+ "tokio",
+ "tokio-native-tls",
+ "tower-service",
+ "url",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "winreg",
+]
+
[[package]]
name = "ring"
version = "0.16.20"
@@ -3331,7 +3627,7 @@ dependencies = [
"regex",
"relative-path",
"rustc_version",
- "syn 2.0.42",
+ "syn 2.0.48",
"unicode-ident",
]
@@ -3403,14 +3699,14 @@ dependencies = [
[[package]]
name = "rustls"
-version = "0.22.1"
+version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe6b63262c9fcac8659abfaa96cac103d28166d3ff3eaf8f412e19f3ae9e5a48"
+checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41"
dependencies = [
"log",
"ring 0.17.7",
"rustls-pki-types",
- "rustls-webpki 0.102.0",
+ "rustls-webpki 0.102.1",
"subtle",
"zeroize",
]
@@ -3438,9 +3734,9 @@ dependencies = [
[[package]]
name = "rustls-pki-types"
-version = "1.0.1"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7673e0aa20ee4937c6aacfc12bb8341cfbf054cdd21df6bec5fd0629fe9339b"
+checksum = "9e9d979b3ce68192e42760c7810125eb6cf2ea10efae545a156063e61f314e2a"
[[package]]
name = "rustls-webpki"
@@ -3464,9 +3760,9 @@ dependencies = [
[[package]]
name = "rustls-webpki"
-version = "0.102.0"
+version = "0.102.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de2635c8bc2b88d367767c5de8ea1d8db9af3f6219eba28442242d9ab81d1b89"
+checksum = "ef4ca26037c909dedb327b48c3327d0ba91d3dd3c4e05dad328f210ffb68e95b"
dependencies = [
"ring 0.17.7",
"rustls-pki-types",
@@ -3496,11 +3792,11 @@ dependencies = [
[[package]]
name = "schannel"
-version = "0.1.22"
+version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
+checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
dependencies = [
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -3550,9 +3846,9 @@ dependencies = [
[[package]]
name = "semver"
-version = "1.0.20"
+version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
+checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
[[package]]
name = "seq-macro"
@@ -3562,35 +3858,57 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4"
[[package]]
name = "serde"
-version = "1.0.193"
+version = "1.0.195"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
+checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.193"
+version = "1.0.195"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
+checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.42",
+ "syn 2.0.48",
]
[[package]]
name = "serde_json"
-version = "1.0.108"
+version = "1.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
+checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
dependencies = [
"itoa",
"ryu",
"serde",
]
+[[package]]
+name = "serde_path_to_error"
+version = "0.1.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebd154a240de39fdebcf5775d2675c204d7c13cf39a4c697be6493c8e734337c"
+dependencies = [
+ "itoa",
+ "serde",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
[[package]]
name = "sha1"
version = "0.10.6"
@@ -3770,13 +4088,13 @@ dependencies = [
[[package]]
name = "sqlparser_derive"
-version = "0.2.1"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e9c2e1dde0efa87003e7923d94a90f46e3274ad1649f51de96812be561f041f"
+checksum = "01b2e185515564f15375f593fb966b5718bc624ba77fe49fa4616ad619690554"
dependencies = [
"proc-macro2",
"quote",
- "syn 1.0.109",
+ "syn 2.0.48",
]
[[package]]
@@ -3798,7 +4116,7 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d84b0a3c3739e220d94b3239fd69fb1f74bc36e16643423bd99de3b43c21bfbd"
dependencies = [
- "ahash 0.8.6",
+ "ahash 0.8.7",
"atoi",
"byteorder",
"bytes",
@@ -3982,6 +4300,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+[[package]]
+name = "streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520"
+
[[package]]
name = "stringprep"
version = "0.1.4"
@@ -4018,7 +4342,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
- "syn 2.0.42",
+ "syn 2.0.48",
]
[[package]]
@@ -4040,9 +4364,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.42"
+version = "2.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b7d0a2c048d661a1a59fcd7355baa232f7ed34e0ee4df2eef3c1c1c0d3852d8"
+checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
dependencies = [
"proc-macro2",
"quote",
@@ -4058,7 +4382,34 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
- "syn 2.0.42",
+ "syn 2.0.48",
+]
+
+[[package]]
+name = "sync_wrapper"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
+
+[[package]]
+name = "system-configuration"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "system-configuration-sys",
+]
+
+[[package]]
+name = "system-configuration-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
]
[[package]]
@@ -4093,28 +4444,28 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "target-lexicon"
-version = "0.12.12"
+version = "0.12.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a"
+checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae"
[[package]]
name = "tempfile"
-version = "3.8.1"
+version = "3.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
+checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
dependencies = [
"cfg-if",
"fastrand",
"redox_syscall",
"rustix",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
]
[[package]]
name = "termcolor"
-version = "1.4.0"
+version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
@@ -4127,22 +4478,22 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
[[package]]
name = "thiserror"
-version = "1.0.51"
+version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7"
+checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.51"
+version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df"
+checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.42",
+ "syn 2.0.48",
]
[[package]]
@@ -4262,7 +4613,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.42",
+ "syn 2.0.48",
]
[[package]]
@@ -4314,7 +4665,7 @@ dependencies = [
"env_logger 0.10.1",
"futures-channel",
"futures-util",
- "hyper",
+ "hyper 0.14.28",
"log",
"native-tls",
"rustls 0.21.10",
@@ -4367,6 +4718,28 @@ dependencies = [
"winnow",
]
+[[package]]
+name = "tower"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project",
+ "pin-project-lite",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
+
[[package]]
name = "tower-service"
version = "0.3.2"
@@ -4385,18 +4758,6 @@ dependencies = [
"tracing-core",
]
-[[package]]
-name = "tracing-appender"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf"
-dependencies = [
- "crossbeam-channel",
- "thiserror",
- "time",
- "tracing-subscriber",
-]
-
[[package]]
name = "tracing-attributes"
version = "0.1.27"
@@ -4405,7 +4766,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.42",
+ "syn 2.0.48",
]
[[package]]
@@ -4489,7 +4850,7 @@ dependencies = [
"log",
"native-tls",
"rand",
- "rustls 0.22.1",
+ "rustls 0.22.2",
"rustls-pki-types",
"sha1",
"thiserror",
@@ -4592,7 +4953,7 @@ name = "ustr"
version = "0.10.0"
source = "git+https://github.com/anderslanglands/ustr#c78ddc25300c4720ffcb5f8ddef6028cef14535f"
dependencies = [
- "ahash 0.8.6",
+ "ahash 0.8.7",
"byteorder",
"lazy_static",
"parking_lot",
@@ -4620,6 +4981,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+[[package]]
+name = "value-bag"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cdbaf5e132e593e9fc1de6a15bbec912395b11fb9719e061cf64f804524c503"
+
[[package]]
name = "vcpkg"
version = "0.2.15"
@@ -4678,10 +5045,22 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
- "syn 2.0.42",
+ "syn 2.0.48",
"wasm-bindgen-shared",
]
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12"
+dependencies = [
+ "cfg-if",
+ "js-sys",
+ "wasm-bindgen",
+ "web-sys",
+]
+
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.89"
@@ -4700,7 +5079,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.42",
+ "syn 2.0.48",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -4769,11 +5148,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
-version = "0.51.1"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
- "windows-targets 0.48.5",
+ "windows-targets 0.52.0",
]
[[package]]
@@ -4910,13 +5289,23 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "winnow"
-version = "0.5.30"
+version = "0.5.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b5c3db89721d50d0e2a673f5043fc4722f76dcc352d7b1ab8b8288bed4ed2c5"
+checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16"
dependencies = [
"memchr",
]
+[[package]]
+name = "winreg"
+version = "0.50.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
+dependencies = [
+ "cfg-if",
+ "windows-sys 0.48.0",
+]
+
[[package]]
name = "wyz"
version = "0.5.1"
@@ -4952,7 +5341,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.42",
+ "syn 2.0.48",
]
[[package]]
diff --git a/nautilus_core/Cargo.toml b/nautilus_core/Cargo.toml
index ac7967a82598..a7259842c1e0 100644
--- a/nautilus_core/Cargo.toml
+++ b/nautilus_core/Cargo.toml
@@ -1,6 +1,7 @@
[workspace]
resolver = "2"
members = [
+ "adapters",
"backtest",
"common",
"core",
@@ -14,30 +15,31 @@ members = [
]
[workspace.package]
-rust-version = "1.74.1"
-version = "0.13.0"
+rust-version = "1.75.0"
+version = "0.14.0"
edition = "2021"
authors = ["Nautech Systems "]
description = "A high-performance algorithmic trading platform and event-driven backtester"
documentation = "https://docs.nautilustrader.io"
[workspace.dependencies]
-anyhow = "1.0.76"
+anyhow = "1.0.79"
chrono = "0.4.31"
-futures = "0.3.29"
+futures = "0.3.30"
indexmap = "2.1.0"
+itoa = "1.0.10"
once_cell = "1.19.0"
-pyo3 = { version = "0.20.0", features = ["rust_decimal"] }
+pyo3 = { version = "0.20.2", features = ["rust_decimal"] }
pyo3-asyncio = { version = "0.20.0", features = ["tokio-runtime", "tokio", "attributes"] }
rand = "0.8.5"
redis = { version = "0.24.0", features = ["tokio-comp", "tls-rustls", "tokio-rustls-comp", "keep-alive", "connection-manager"] }
rmp-serde = "1.1.2"
rust_decimal = "1.33.1"
rust_decimal_macros = "1.33.1"
-serde = { version = "1.0.193", features = ["derive"] }
-serde_json = "1.0.108"
+serde = { version = "1.0.195", features = ["derive"] }
+serde_json = "1.0.111"
strum = { version = "0.25.0", features = ["derive"] }
-thiserror = "1.0.51"
+thiserror = "1.0.56"
tracing = "0.1.40"
tokio = { version = "1.35.1", features = ["full"] }
ustr = { git = "https://github.com/anderslanglands/ustr", features = ["serde"] }
@@ -48,7 +50,7 @@ criterion = "0.5.1"
float-cmp = "0.9.0"
iai = "0.1"
rstest = "0.18.2"
-tempfile = "3.8.1"
+tempfile = "3.9.0"
# build-dependencies
cbindgen = "0.26.0"
diff --git a/nautilus_core/README.md b/nautilus_core/README.md
index 24f12969e098..9511bd54f779 100644
--- a/nautilus_core/README.md
+++ b/nautilus_core/README.md
@@ -18,7 +18,7 @@ NautilusTrader is developed and maintained by Nautech Systems, a technology
company specializing in the development of high-performance trading systems.
For more information, visit https://nautilustrader.io.
-Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
![nautechsystems](https://github.com/nautechsystems/nautilus_trader/blob/develop/docs/_images/ns-logo.png?raw=true "nautechsystems")
diff --git a/nautilus_core/adapters/Cargo.toml b/nautilus_core/adapters/Cargo.toml
new file mode 100644
index 000000000000..1a674ab0d28f
--- /dev/null
+++ b/nautilus_core/adapters/Cargo.toml
@@ -0,0 +1,43 @@
+[package]
+name = "nautilus-adapters"
+version.workspace = true
+edition.workspace = true
+authors.workspace = true
+description.workspace = true
+documentation.workspace = true
+
+[lib]
+name = "nautilus_adapters"
+crate-type = ["rlib", "staticlib", "cdylib"]
+
+[dependencies]
+nautilus-common = { path = "../common" }
+nautilus-core = { path = "../core" }
+nautilus-model = { path = "../model", features = ["stubs"]}
+anyhow = { workspace = true }
+chrono = { workspace = true }
+indexmap = { workspace = true }
+itoa = { workspace = true }
+pyo3 = { workspace = true, optional = true }
+rand = { workspace = true }
+rust_decimal = { workspace = true }
+rust_decimal_macros = { workspace = true }
+tokio = { workspace = true }
+thiserror = { workspace = true }
+ustr = { workspace = true }
+dbn = { version = "0.14.2", optional = true, features = ["python"] }
+
+[features]
+extension-module = [
+ "pyo3/extension-module",
+ "nautilus-common/extension-module",
+ "nautilus-core/extension-module",
+ "nautilus-model/extension-module",
+]
+databento = ["dbn"]
+python = ["pyo3"]
+default = ["databento", "python"]
+
+[dev-dependencies]
+criterion = { workspace = true }
+rstest = { workspace = true }
diff --git a/nautilus_core/model/src/ffi/data/ticker.rs b/nautilus_core/adapters/src/databento/common.rs
similarity index 58%
rename from nautilus_core/model/src/ffi/data/ticker.rs
rename to nautilus_core/adapters/src/databento/common.rs
index e2c23d9f935d..20e0361574cb 100644
--- a/nautilus_core/model/src/ffi/data/ticker.rs
+++ b/nautilus_core/adapters/src/databento/common.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -13,23 +13,20 @@
// limitations under the License.
// -------------------------------------------------------------------------------------------------
-use std::ffi::c_char;
+use nautilus_model::identifiers::{instrument_id::InstrumentId, symbol::Symbol, venue::Venue};
+use ustr::Ustr;
-use nautilus_core::{ffi::string::str_to_cstr, time::UnixNanos};
+use super::types::DatabentoPublisher;
-use crate::{data::ticker::Ticker, identifiers::instrument_id::InstrumentId};
+#[must_use]
+pub fn nautilus_instrument_id_from_databento(
+ raw_symbol: Ustr,
+ publisher: &DatabentoPublisher,
+) -> InstrumentId {
+ let symbol = Symbol { value: raw_symbol };
+ let venue = Venue {
+ value: Ustr::from(publisher.venue.as_str()),
+ }; // TODO: Optimize
-#[no_mangle]
-pub extern "C" fn ticker_new(
- instrument_id: InstrumentId,
- ts_event: UnixNanos,
- ts_init: UnixNanos,
-) -> Ticker {
- Ticker::new(instrument_id, ts_event, ts_init)
-}
-
-/// Returns a [`Ticker`] as a C string pointer.
-#[no_mangle]
-pub extern "C" fn ticker_to_cstr(ticker: &Ticker) -> *const c_char {
- str_to_cstr(&ticker.to_string())
+ InstrumentId::new(symbol, venue)
}
diff --git a/nautilus_core/adapters/src/databento/mod.rs b/nautilus_core/adapters/src/databento/mod.rs
new file mode 100644
index 000000000000..5e05a7864613
--- /dev/null
+++ b/nautilus_core/adapters/src/databento/mod.rs
@@ -0,0 +1,18 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
+// https://nautechsystems.io
+//
+// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
+// You may not use this file except in compliance with the License.
+// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// -------------------------------------------------------------------------------------------------
+
+pub mod common;
+pub mod parsing;
+pub mod types;
diff --git a/nautilus_core/adapters/src/databento/parsing.rs b/nautilus_core/adapters/src/databento/parsing.rs
new file mode 100644
index 000000000000..51768c263d0e
--- /dev/null
+++ b/nautilus_core/adapters/src/databento/parsing.rs
@@ -0,0 +1,586 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
+// https://nautechsystems.io
+//
+// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
+// You may not use this file except in compliance with the License.
+// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// -------------------------------------------------------------------------------------------------
+
+use std::{
+ cmp,
+ ffi::{c_char, CStr},
+ i64,
+ str::FromStr,
+};
+
+use anyhow::{anyhow, bail, Result};
+use dbn;
+use itoa;
+use nautilus_core::{datetime::NANOSECONDS_IN_SECOND, time::UnixNanos};
+use nautilus_model::{
+ data::{
+ bar::{Bar, BarSpecification, BarType},
+ delta::OrderBookDelta,
+ depth::{OrderBookDepth10, DEPTH10_LEN},
+ order::BookOrder,
+ quote::QuoteTick,
+ trade::TradeTick,
+ Data,
+ },
+ enums::{
+ AggregationSource, AggressorSide, AssetClass, BarAggregation, BookAction, InstrumentClass,
+ OptionKind, OrderSide, PriceType,
+ },
+ identifiers::{instrument_id::InstrumentId, trade_id::TradeId},
+ instruments::{
+ equity::Equity, futures_contract::FuturesContract, options_contract::OptionsContract,
+ Instrument,
+ },
+ types::{currency::Currency, price::Price, quantity::Quantity},
+};
+use ustr::Ustr;
+
+use super::{common::nautilus_instrument_id_from_databento, types::DatabentoPublisher};
+
+const BAR_SPEC_1S: BarSpecification = BarSpecification {
+ step: 1,
+ aggregation: BarAggregation::Second,
+ price_type: PriceType::Last,
+};
+const BAR_SPEC_1M: BarSpecification = BarSpecification {
+ step: 1,
+ aggregation: BarAggregation::Minute,
+ price_type: PriceType::Last,
+};
+const BAR_SPEC_1H: BarSpecification = BarSpecification {
+ step: 1,
+ aggregation: BarAggregation::Hour,
+ price_type: PriceType::Last,
+};
+const BAR_SPEC_1D: BarSpecification = BarSpecification {
+ step: 1,
+ aggregation: BarAggregation::Day,
+ price_type: PriceType::Last,
+};
+
+const BAR_CLOSE_ADJUSTMENT_1S: u64 = NANOSECONDS_IN_SECOND;
+const BAR_CLOSE_ADJUSTMENT_1M: u64 = NANOSECONDS_IN_SECOND * 60;
+const BAR_CLOSE_ADJUSTMENT_1H: u64 = NANOSECONDS_IN_SECOND * 60 * 60;
+const BAR_CLOSE_ADJUSTMENT_1D: u64 = NANOSECONDS_IN_SECOND * 60 * 60 * 24;
+
+#[must_use]
+pub fn parse_order_side(c: c_char) -> OrderSide {
+ match c as u8 as char {
+ 'A' => OrderSide::Sell,
+ 'B' => OrderSide::Buy,
+ _ => OrderSide::NoOrderSide,
+ }
+}
+
+#[must_use]
+pub fn parse_aggressor_side(c: c_char) -> AggressorSide {
+ match c as u8 as char {
+ 'A' => AggressorSide::Seller,
+ 'B' => AggressorSide::Buyer,
+ _ => AggressorSide::NoAggressor,
+ }
+}
+
+pub fn parse_book_action(c: c_char) -> Result {
+ match c as u8 as char {
+ 'A' => Ok(BookAction::Add),
+ 'C' => Ok(BookAction::Delete),
+ 'F' => Ok(BookAction::Update),
+ 'M' => Ok(BookAction::Update),
+ 'R' => Ok(BookAction::Clear),
+ _ => bail!("Invalid `BookAction`, was '{c}'"),
+ }
+}
+
+pub fn parse_option_kind(c: c_char) -> Result {
+ match c as u8 as char {
+ 'C' => Ok(OptionKind::Call),
+ 'P' => Ok(OptionKind::Put),
+ _ => bail!("Invalid `OptionKind`, was '{c}'"),
+ }
+}
+
+pub fn parse_cfi_iso10926(value: &str) -> Result<(Option, Option)> {
+ let chars: Vec = value.chars().collect();
+ if chars.len() < 3 {
+ bail!("Value string is too short");
+ }
+
+ let cfi_category = chars[0];
+ let cfi_group = chars[1];
+ let cfi_attribute1 = chars[2];
+ // let cfi_attribute2 = value[3];
+ // let cfi_attribute3 = value[4];
+ // let cfi_attribute4 = value[5];
+
+ let mut asset_class = match cfi_category {
+ 'D' => Some(AssetClass::Debt),
+ 'E' => Some(AssetClass::Equity),
+ 'S' => None,
+ _ => None,
+ };
+
+ let instrument_class = match cfi_group {
+ 'I' => Some(InstrumentClass::Future),
+ _ => None,
+ };
+
+ if cfi_attribute1 == 'I' {
+ asset_class = Some(AssetClass::Index);
+ }
+
+ Ok((asset_class, instrument_class))
+}
+
+pub fn parse_min_price_increment(value: i64, currency: Currency) -> Result {
+ match value {
+ 0 | i64::MAX => Price::new(
+ 10f64.powi(-i32::from(currency.precision)),
+ currency.precision,
+ ),
+ _ => Price::from_raw(value, currency.precision),
+ }
+}
+
+/// # Safety
+///
+/// - Assumes `ptr` is a valid C string pointer.
+pub unsafe fn parse_raw_ptr_to_string(ptr: *const c_char) -> Result {
+ let c_str: &CStr = unsafe { CStr::from_ptr(ptr) };
+ let str_slice: &str = c_str.to_str().map_err(|e| anyhow!(e))?;
+ Ok(str_slice.to_owned())
+}
+
+/// # Safety
+///
+/// - Assumes `ptr` is a valid C string pointer.
+pub unsafe fn parse_raw_ptr_to_ustr(ptr: *const c_char) -> Result {
+ let c_str: &CStr = unsafe { CStr::from_ptr(ptr) };
+ let str_slice: &str = c_str.to_str().map_err(|e| anyhow!(e))?;
+ Ok(Ustr::from(str_slice))
+}
+
+pub fn parse_equity(
+ record: &dbn::InstrumentDefMsg,
+ instrument_id: InstrumentId,
+ ts_init: UnixNanos,
+) -> Result {
+ let currency = Currency::USD(); // TODO: Temporary hard coding of US equities for now
+
+ Equity::new(
+ instrument_id,
+ instrument_id.symbol,
+ None, // No ISIN available yet
+ currency,
+ currency.precision,
+ parse_min_price_increment(record.min_price_increment, currency)?,
+ Some(Quantity::new(record.min_lot_size_round_lot.into(), 0)?),
+ None, // TBD
+ None, // TBD
+ None, // TBD
+ None, // TBD
+ record.ts_recv, // More accurate and reliable timestamp
+ ts_init,
+ )
+}
+
+pub fn parse_futures_contract(
+ record: &dbn::InstrumentDefMsg,
+ instrument_id: InstrumentId,
+ ts_init: UnixNanos,
+) -> Result {
+ let currency = Currency::USD(); // TODO: Temporary hard coding of US futures for now
+ let cfi_str = unsafe { parse_raw_ptr_to_string(record.cfi.as_ptr())? };
+ let asset = unsafe { parse_raw_ptr_to_ustr(record.asset.as_ptr())? };
+ let (asset_class, _) = parse_cfi_iso10926(&cfi_str)?;
+
+ FuturesContract::new(
+ instrument_id,
+ instrument_id.symbol,
+ asset_class.unwrap_or(AssetClass::Commodity),
+ asset,
+ record.activation,
+ record.expiration,
+ currency,
+ currency.precision,
+ parse_min_price_increment(record.min_price_increment, currency)?,
+ Quantity::new(record.contract_multiplier.into(), 0)?,
+ None, // TBD
+ None, // TBD
+ None, // TBD
+ None, // TBD
+ None, // TBD
+ record.ts_recv, // More accurate and reliable timestamp
+ ts_init,
+ )
+}
+
+pub fn parse_options_contract(
+ record: &dbn::InstrumentDefMsg,
+ instrument_id: InstrumentId,
+ ts_init: UnixNanos,
+) -> Result {
+ let currency_str = unsafe { parse_raw_ptr_to_string(record.currency.as_ptr())? };
+ let cfi_str = unsafe { parse_raw_ptr_to_string(record.cfi.as_ptr())? };
+ let currency = Currency::from_str(¤cy_str)?;
+ let (asset_class, _) = parse_cfi_iso10926(&cfi_str)?;
+ let lot_size = Quantity::new(1.0, 0)?;
+
+ OptionsContract::new(
+ instrument_id,
+ instrument_id.symbol,
+ asset_class.unwrap_or(AssetClass::Commodity),
+ unsafe { parse_raw_ptr_to_ustr(record.asset.as_ptr())? },
+ parse_option_kind(record.instrument_class)?,
+ record.activation,
+ record.expiration,
+ Price::from_raw(record.strike_price, currency.precision)?,
+ currency,
+ currency.precision,
+ parse_min_price_increment(record.min_price_increment, currency)?,
+ Some(lot_size),
+ None, // TBD
+ None, // TBD
+ None, // TBD
+ None, // TBD
+ record.ts_recv, // More accurate and reliable timestamp
+ ts_init,
+ )
+}
+
+#[must_use]
+pub fn is_trade_msg(order_side: OrderSide, action: c_char) -> bool {
+ order_side == OrderSide::NoOrderSide || action as u8 as char == 'T'
+}
+
+pub fn parse_mbo_msg(
+ record: &dbn::MboMsg,
+ instrument_id: InstrumentId,
+ price_precision: u8,
+ ts_init: UnixNanos,
+) -> Result<(Option, Option)> {
+ let side = parse_order_side(record.side);
+ if is_trade_msg(side, record.action) {
+ let trade = TradeTick::new(
+ instrument_id,
+ Price::from_raw(record.price, price_precision)?,
+ Quantity::from_raw(record.size.into(), 0)?,
+ parse_aggressor_side(record.side),
+ TradeId::new(itoa::Buffer::new().format(record.sequence))?,
+ record.ts_recv,
+ ts_init,
+ );
+ return Ok((None, Some(trade)));
+ };
+
+ let order = BookOrder::new(
+ side,
+ Price::from_raw(record.price, price_precision)?,
+ Quantity::from_raw(record.size.into(), 0)?,
+ record.order_id,
+ );
+
+ let delta = OrderBookDelta::new(
+ instrument_id,
+ parse_book_action(record.action)?,
+ order,
+ record.flags,
+ record.sequence.into(),
+ record.ts_recv,
+ ts_init,
+ );
+
+ Ok((Some(delta), None))
+}
+
+pub fn parse_trade_msg(
+ record: &dbn::TradeMsg,
+ instrument_id: InstrumentId,
+ price_precision: u8,
+ ts_init: UnixNanos,
+) -> Result {
+ let trade = TradeTick::new(
+ instrument_id,
+ Price::from_raw(record.price, price_precision)?,
+ Quantity::from_raw(record.size.into(), 0)?,
+ parse_aggressor_side(record.side),
+ TradeId::new(itoa::Buffer::new().format(record.sequence))?,
+ record.ts_recv,
+ ts_init,
+ );
+
+ Ok(trade)
+}
+
+pub fn parse_mbp1_msg(
+ record: &dbn::Mbp1Msg,
+ instrument_id: InstrumentId,
+ price_precision: u8,
+ ts_init: UnixNanos,
+) -> Result<(QuoteTick, Option)> {
+ let top_level = &record.levels[0];
+ let quote = QuoteTick::new(
+ instrument_id,
+ Price::from_raw(top_level.bid_px, price_precision)?,
+ Price::from_raw(top_level.ask_px, price_precision)?,
+ Quantity::from_raw(top_level.bid_sz.into(), 0)?,
+ Quantity::from_raw(top_level.ask_sz.into(), 0)?,
+ record.ts_recv,
+ ts_init,
+ )?;
+
+ let trade = match record.action as u8 as char {
+ 'T' => Some(TradeTick::new(
+ instrument_id,
+ Price::from_raw(record.price, price_precision)?,
+ Quantity::from_raw(record.size.into(), 0)?,
+ parse_aggressor_side(record.side),
+ TradeId::new(itoa::Buffer::new().format(record.sequence))?,
+ record.ts_recv,
+ ts_init,
+ )),
+ _ => None,
+ };
+
+ Ok((quote, trade))
+}
+
+pub fn parse_mbp10_msg(
+ record: &dbn::Mbp10Msg,
+ instrument_id: InstrumentId,
+ price_precision: u8,
+ ts_init: UnixNanos,
+) -> Result {
+ let mut bids = Vec::with_capacity(DEPTH10_LEN);
+ let mut asks = Vec::with_capacity(DEPTH10_LEN);
+ let mut bid_counts = Vec::with_capacity(DEPTH10_LEN);
+ let mut ask_counts = Vec::with_capacity(DEPTH10_LEN);
+
+ for level in &record.levels {
+ let bid_order = BookOrder::new(
+ OrderSide::Buy,
+ Price::from_raw(level.bid_px, price_precision)?,
+ Quantity::from_raw(level.bid_sz.into(), 0)?,
+ 0,
+ );
+
+ let ask_order = BookOrder::new(
+ OrderSide::Sell,
+ Price::from_raw(level.ask_px, price_precision)?,
+ Quantity::from_raw(level.ask_sz.into(), 0)?,
+ 0,
+ );
+
+ bids.push(bid_order);
+ asks.push(ask_order);
+ bid_counts.push(level.bid_ct);
+ ask_counts.push(level.ask_ct);
+ }
+
+ let bids: [BookOrder; DEPTH10_LEN] = bids.try_into().expect("`bids` length != 10");
+ let asks: [BookOrder; DEPTH10_LEN] = asks.try_into().expect("`asks` length != 10");
+ let bid_counts: [u32; DEPTH10_LEN] = bid_counts.try_into().expect("`bid_counts` length != 10");
+ let ask_counts: [u32; DEPTH10_LEN] = ask_counts.try_into().expect("`ask_counts` length != 10");
+
+ let depth = OrderBookDepth10::new(
+ instrument_id,
+ bids,
+ asks,
+ bid_counts,
+ ask_counts,
+ record.flags,
+ record.sequence.into(),
+ record.ts_recv,
+ ts_init,
+ );
+
+ Ok(depth)
+}
+
+pub fn parse_bar_type(record: &dbn::OhlcvMsg, instrument_id: InstrumentId) -> Result {
+ let bar_type = match record.hd.rtype {
+ 32 => {
+ // ohlcv-1s
+ BarType::new(instrument_id, BAR_SPEC_1S, AggregationSource::External)
+ }
+ 33 => {
+ // ohlcv-1m
+ BarType::new(instrument_id, BAR_SPEC_1M, AggregationSource::External)
+ }
+ 34 => {
+ // ohlcv-1h
+ BarType::new(instrument_id, BAR_SPEC_1H, AggregationSource::External)
+ }
+ 35 => {
+ // ohlcv-1d
+ BarType::new(instrument_id, BAR_SPEC_1D, AggregationSource::External)
+ }
+ _ => bail!(
+ "`rtype` is not a supported bar aggregation, was {}",
+ record.hd.rtype
+ ),
+ };
+
+ Ok(bar_type)
+}
+
+pub fn parse_ts_event_adjustment(record: &dbn::OhlcvMsg) -> Result {
+ let adjustment = match record.hd.rtype {
+ 32 => {
+ // ohlcv-1s
+ BAR_CLOSE_ADJUSTMENT_1S
+ }
+ 33 => {
+ // ohlcv-1m
+ BAR_CLOSE_ADJUSTMENT_1M
+ }
+ 34 => {
+ // ohlcv-1h
+ BAR_CLOSE_ADJUSTMENT_1H
+ }
+ 35 => {
+ // ohlcv-1d
+ BAR_CLOSE_ADJUSTMENT_1D
+ }
+ _ => bail!(
+ "`rtype` is not a supported bar aggregation, was {}",
+ record.hd.rtype
+ ),
+ };
+
+ Ok(adjustment)
+}
+
+pub fn parse_ohlcv_msg(
+ record: &dbn::OhlcvMsg,
+ instrument_id: InstrumentId,
+ price_precision: u8,
+ ts_init: UnixNanos,
+) -> Result {
+ let bar_type = parse_bar_type(record, instrument_id)?;
+ let ts_event_adjustment = parse_ts_event_adjustment(record)?;
+
+ // Adjust `ts_event` from open to close of bar
+ let ts_event = record.hd.ts_event + ts_event_adjustment;
+ let ts_init = cmp::max(ts_init, ts_event);
+
+ let bar = Bar::new(
+ bar_type,
+ Price::from_raw(record.open / 100, price_precision)?, // TODO(adjust for display factor)
+ Price::from_raw(record.high / 100, price_precision)?, // TODO(adjust for display factor)
+ Price::from_raw(record.low / 100, price_precision)?, // TODO(adjust for display factor)
+ Price::from_raw(record.close / 100, price_precision)?, // TODO(adjust for display factor)
+ Quantity::from_raw(record.volume, 0)?, // TODO(adjust for display factor)
+ ts_event,
+ ts_init,
+ );
+
+ Ok(bar)
+}
+
+// pub fn parse_record_with_metadata(
+// record: T,
+// publishers: IndexMap,
+// ts_init: UnixNanos,
+// ) -> Result<(Data, Option)> {
+// let publisher_id: PublisherId = record.header().publisher_id;
+// let publisher = publishers
+// .get(&record.header().publisher_id)
+// .ok_or_else(|| anyhow!("Publisher ID {publisher_id} not found in map"))?;
+//
+// let raw_symbol = unsafe { parse_raw_ptr_to_ustr(record.raw_symbol.as_ptr())? };
+// let instrument_id = nautilus_instrument_id_from_databento(raw_symbol, publisher);
+// }
+
+pub fn parse_record(
+ record: T,
+ instrument_id: InstrumentId,
+ ts_init: UnixNanos,
+) -> Result<(Data, Option)>
+where
+ T: dbn::Record + dbn::HasRType,
+{
+ let record_ref = dbn::RecordRef::from(&record);
+
+ let result = match record.rtype()? {
+ dbn::RType::Mbo => {
+ let msg = record_ref.get::().unwrap(); // SAFETY: RType known
+ let result = parse_mbo_msg(msg, instrument_id, 2, ts_init)?;
+ match result {
+ (Some(delta), None) => (Data::Delta(delta), None),
+ (None, Some(trade)) => (Data::Trade(trade), None),
+ _ => bail!("Invalid `MboMsg` parsing combination"),
+ }
+ }
+ dbn::RType::Mbp0 => {
+ let msg = record_ref.get::().unwrap(); // SAFETY: RType known
+ let trade = parse_trade_msg(msg, instrument_id, 2, ts_init)?;
+ (Data::Trade(trade), None)
+ }
+ dbn::RType::Mbp1 => {
+ let msg = record_ref.get::().unwrap(); // SAFETY: RType known
+ let result = parse_mbp1_msg(msg, instrument_id, 2, ts_init)?;
+ match result {
+ (quote, None) => (Data::Quote(quote), None),
+ (quote, Some(trade)) => (Data::Quote(quote), Some(Data::Trade(trade))),
+ }
+ }
+ dbn::RType::Mbp10 => {
+ let msg = record_ref.get::().unwrap(); // SAFETY: RType known
+ let depth = parse_mbp10_msg(msg, instrument_id, 2, ts_init)?;
+ (Data::Depth10(depth), None)
+ }
+ dbn::RType::Ohlcv1S
+ | dbn::RType::Ohlcv1M
+ | dbn::RType::Ohlcv1H
+ | dbn::RType::Ohlcv1D
+ | dbn::RType::OhlcvEod => {
+ let msg = record_ref.get::().unwrap(); // SAFETY: RType known
+ let bar = parse_ohlcv_msg(msg, instrument_id, 2, ts_init)?;
+ (Data::Bar(bar), None)
+ }
+ _ => bail!("RType is currently unsupported by NautilusTrader"),
+ };
+
+ Ok(result)
+}
+
+pub fn parse_instrument_def_msg(
+ record: &dbn::InstrumentDefMsg,
+ publisher: &DatabentoPublisher,
+ ts_init: UnixNanos,
+) -> Result> {
+ let raw_symbol = unsafe { parse_raw_ptr_to_ustr(record.raw_symbol.as_ptr())? };
+ let instrument_id = nautilus_instrument_id_from_databento(raw_symbol, publisher);
+
+ match record.instrument_class as u8 as char {
+ 'C' | 'P' => Ok(Box::new(parse_options_contract(
+ record,
+ instrument_id,
+ ts_init,
+ )?)),
+ 'K' => Ok(Box::new(parse_equity(record, instrument_id, ts_init)?)),
+ 'F' => Ok(Box::new(parse_futures_contract(
+ record,
+ instrument_id,
+ ts_init,
+ )?)),
+ 'X' => bail!("Unsupported `instrument_class` 'X' (FX_SPOT)"),
+ _ => bail!(
+ "Invalid `instrument_class`, was {}",
+ record.instrument_class
+ ),
+ }
+}
diff --git a/nautilus_core/adapters/src/databento/publishers.json b/nautilus_core/adapters/src/databento/publishers.json
new file mode 100644
index 000000000000..5abdb766c350
--- /dev/null
+++ b/nautilus_core/adapters/src/databento/publishers.json
@@ -0,0 +1,260 @@
+[
+ {
+ "publisher_id": 1,
+ "dataset": "GLBX.MDP3",
+ "venue": "GLBX",
+ "description": "CME Globex MDP 3.0"
+ },
+ {
+ "publisher_id": 2,
+ "dataset": "XNAS.ITCH",
+ "venue": "XNAS",
+ "description": "Nasdaq TotalView ITCH"
+ },
+ {
+ "publisher_id": 3,
+ "dataset": "XBOS.ITCH",
+ "venue": "XBOS",
+ "description": "Nasdaq BX TotalView ITCH"
+ },
+ {
+ "publisher_id": 4,
+ "dataset": "XPSX.ITCH",
+ "venue": "XPSX",
+ "description": "Nasdaq PSX TotalView ITCH"
+ },
+ {
+ "publisher_id": 5,
+ "dataset": "BATS.PITCH",
+ "venue": "BATS",
+ "description": "Cboe BZX Depth Pitch"
+ },
+ {
+ "publisher_id": 6,
+ "dataset": "BATY.PITCH",
+ "venue": "BATY",
+ "description": "Cboe BYX Depth Pitch"
+ },
+ {
+ "publisher_id": 7,
+ "dataset": "EDGA.PITCH",
+ "venue": "EDGA",
+ "description": "Cboe EDGA Depth Pitch"
+ },
+ {
+ "publisher_id": 8,
+ "dataset": "EDGX.PITCH",
+ "venue": "EDGX",
+ "description": "Cboe EDGX Depth Pitch"
+ },
+ {
+ "publisher_id": 9,
+ "dataset": "XNYS.PILLAR",
+ "venue": "XNYS",
+ "description": "NYSE Integrated"
+ },
+ {
+ "publisher_id": 10,
+ "dataset": "XCIS.PILLAR",
+ "venue": "XCIS",
+ "description": "NYSE National Integrated"
+ },
+ {
+ "publisher_id": 11,
+ "dataset": "XASE.PILLAR",
+ "venue": "XASE",
+ "description": "NYSE American Integrated"
+ },
+ {
+ "publisher_id": 12,
+ "dataset": "XCHI.PILLAR",
+ "venue": "XCHI",
+ "description": "NYSE Chicago Integrated"
+ },
+ {
+ "publisher_id": 13,
+ "dataset": "XCIS.BBO",
+ "venue": "XCIS",
+ "description": "NYSE National BBO"
+ },
+ {
+ "publisher_id": 14,
+ "dataset": "XCIS.TRADES",
+ "venue": "XCIS",
+ "description": "NYSE National Trades"
+ },
+ {
+ "publisher_id": 15,
+ "dataset": "MEMX.MEMOIR",
+ "venue": "MEMX",
+ "description": "MEMX Memoir Depth"
+ },
+ {
+ "publisher_id": 16,
+ "dataset": "EPRL.DOM",
+ "venue": "EPRL",
+ "description": "MIAX Pearl Depth"
+ },
+ {
+ "publisher_id": 17,
+ "dataset": "FINN.NLS",
+ "venue": "FINN",
+ "description": "FINRA/Nasdaq TRF Carteret"
+ },
+ {
+ "publisher_id": 18,
+ "dataset": "FINN.NLS",
+ "venue": "FINC",
+ "description": "FINRA/Nasdaq TRF Chicago"
+ },
+ {
+ "publisher_id": 19,
+ "dataset": "FINY.TRADES",
+ "venue": "FINY",
+ "description": "FINRA/NYSE TRF"
+ },
+ {
+ "publisher_id": 20,
+ "dataset": "OPRA.PILLAR",
+ "venue": "AMXO",
+ "description": "OPRA - NYSE American"
+ },
+ {
+ "publisher_id": 21,
+ "dataset": "OPRA.PILLAR",
+ "venue": "XBOX",
+ "description": "OPRA - Boston Options Exchange"
+ },
+ {
+ "publisher_id": 22,
+ "dataset": "OPRA.PILLAR",
+ "venue": "XCBO",
+ "description": "OPRA - Cboe Options Exchange"
+ },
+ {
+ "publisher_id": 23,
+ "dataset": "OPRA.PILLAR",
+ "venue": "EMLD",
+ "description": "OPRA - MIAX Emerald"
+ },
+ {
+ "publisher_id": 24,
+ "dataset": "OPRA.PILLAR",
+ "venue": "EDGO",
+ "description": "OPRA - Cboe EDGX Options Exchange"
+ },
+ {
+ "publisher_id": 25,
+ "dataset": "OPRA.PILLAR",
+ "venue": "GMNI",
+ "description": "OPRA - Nasdaq GEMX"
+ },
+ {
+ "publisher_id": 26,
+ "dataset": "OPRA.PILLAR",
+ "venue": "XISX",
+ "description": "OPRA - Nasdaq ISE"
+ },
+ {
+ "publisher_id": 27,
+ "dataset": "OPRA.PILLAR",
+ "venue": "MCRY",
+ "description": "OPRA - Nasdaq MRX"
+ },
+ {
+ "publisher_id": 28,
+ "dataset": "OPRA.PILLAR",
+ "venue": "XMIO",
+ "description": "OPRA - Miami International Securities"
+ },
+ {
+ "publisher_id": 29,
+ "dataset": "OPRA.PILLAR",
+ "venue": "ARCO",
+ "description": "OPRA - NYSE Arca"
+ },
+ {
+ "publisher_id": 30,
+ "dataset": "OPRA.PILLAR",
+ "venue": "OPRA",
+ "description": "OPRA - Options Price Reporting Authority"
+ },
+ {
+ "publisher_id": 31,
+ "dataset": "OPRA.PILLAR",
+ "venue": "MPRL",
+ "description": "OPRA - MIAX Pearl"
+ },
+ {
+ "publisher_id": 32,
+ "dataset": "OPRA.PILLAR",
+ "venue": "XNDQ",
+ "description": "OPRA - Nasdaq Options Market"
+ },
+ {
+ "publisher_id": 33,
+ "dataset": "OPRA.PILLAR",
+ "venue": "XBXO",
+ "description": "OPRA - Nasdaq BX Options"
+ },
+ {
+ "publisher_id": 34,
+ "dataset": "OPRA.PILLAR",
+ "venue": "C2OX",
+ "description": "OPRA - Cboe C2 Options Exchange"
+ },
+ {
+ "publisher_id": 35,
+ "dataset": "OPRA.PILLAR",
+ "venue": "XPHL",
+ "description": "OPRA - Nasdaq PHLX"
+ },
+ {
+ "publisher_id": 36,
+ "dataset": "OPRA.PILLAR",
+ "venue": "BATO",
+ "description": "OPRA - Cboe BZX Options"
+ },
+ {
+ "publisher_id": 37,
+ "dataset": "OPRA.PILLAR",
+ "venue": "MXOP",
+ "description": "OPRA - MEMX Options Exchange"
+ },
+ {
+ "publisher_id": 38,
+ "dataset": "IEXG.TOPS",
+ "venue": "IEXG",
+ "description": "IEX TOPS"
+ },
+ {
+ "publisher_id": 39,
+ "dataset": "DBEQ.BASIC",
+ "venue": "XCHI",
+ "description": "DBEQ Basic - NYSE Chicago"
+ },
+ {
+ "publisher_id": 40,
+ "dataset": "DBEQ.BASIC",
+ "venue": "XCIS",
+ "description": "DBEQ Basic - NYSE National"
+ },
+ {
+ "publisher_id": 41,
+ "dataset": "DBEQ.BASIC",
+ "venue": "IEXG",
+ "description": "DBEQ Basic - IEX"
+ },
+ {
+ "publisher_id": 42,
+ "dataset": "DBEQ.BASIC",
+ "venue": "EPRL",
+ "description": "DBEQ Basic - MIAX Pearl"
+ },
+ {
+ "publisher_id": 43,
+ "dataset": "ARCX.PILLAR",
+ "venue": "ARCX",
+ "description": "NYSE Arca Integrated"
+ }
+]
diff --git a/nautilus_core/adapters/src/databento/types.rs b/nautilus_core/adapters/src/databento/types.rs
new file mode 100644
index 000000000000..cdcf783c3e64
--- /dev/null
+++ b/nautilus_core/adapters/src/databento/types.rs
@@ -0,0 +1,26 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
+// https://nautechsystems.io
+//
+// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
+// You may not use this file except in compliance with the License.
+// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// -------------------------------------------------------------------------------------------------
+
+use dbn;
+
+/// Represents a Databento publisher ID.
+pub type PublisherId = u16;
+
+pub struct DatabentoPublisher {
+ pub publisher_id: PublisherId,
+ pub dataset: dbn::Dataset,
+ pub venue: dbn::Venue,
+ pub description: String,
+}
diff --git a/nautilus_core/adapters/src/lib.rs b/nautilus_core/adapters/src/lib.rs
new file mode 100644
index 000000000000..4f75f8648fd3
--- /dev/null
+++ b/nautilus_core/adapters/src/lib.rs
@@ -0,0 +1,20 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
+// https://nautechsystems.io
+//
+// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
+// You may not use this file except in compliance with the License.
+// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// -------------------------------------------------------------------------------------------------
+
+#[cfg(feature = "databento")]
+pub mod databento;
+
+#[cfg(feature = "python")]
+pub mod python;
diff --git a/nautilus_core/common/src/python/clock.rs b/nautilus_core/adapters/src/python/databento/mod.rs
similarity index 90%
rename from nautilus_core/common/src/python/clock.rs
rename to nautilus_core/adapters/src/python/databento/mod.rs
index 030cfa469344..00563cd1c676 100644
--- a/nautilus_core/common/src/python/clock.rs
+++ b/nautilus_core/adapters/src/python/databento/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -12,3 +12,5 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// -------------------------------------------------------------------------------------------------
+
+pub mod parsing;
diff --git a/nautilus_core/adapters/src/python/databento/parsing.rs b/nautilus_core/adapters/src/python/databento/parsing.rs
new file mode 100644
index 000000000000..85ef378e3de7
--- /dev/null
+++ b/nautilus_core/adapters/src/python/databento/parsing.rs
@@ -0,0 +1,131 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
+// https://nautechsystems.io
+//
+// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
+// You may not use this file except in compliance with the License.
+// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// -------------------------------------------------------------------------------------------------
+
+use dbn;
+use nautilus_core::{python::to_pyvalue_err, time::UnixNanos};
+use nautilus_model::{
+ data::{depth::OrderBookDepth10, trade::TradeTick},
+ identifiers::instrument_id::InstrumentId,
+ instruments::{
+ equity::Equity, futures_contract::FuturesContract, options_contract::OptionsContract,
+ },
+};
+use pyo3::{exceptions::PyRuntimeError, prelude::*, types::PyTuple};
+
+use crate::databento::parsing::{
+ parse_equity, parse_futures_contract, parse_mbo_msg, parse_mbp10_msg, parse_mbp1_msg,
+ parse_options_contract, parse_trade_msg,
+};
+
+#[pyfunction]
+#[pyo3(name = "parse_equity")]
+pub fn py_parse_equity(
+ record: &dbn::InstrumentDefMsg,
+ instrument_id: InstrumentId,
+ ts_init: UnixNanos,
+) -> PyResult {
+ parse_equity(record, instrument_id, ts_init).map_err(to_pyvalue_err)
+}
+
+#[pyfunction]
+#[pyo3(name = "parse_futures_contract")]
+pub fn py_parse_futures_contract(
+ record: &dbn::InstrumentDefMsg,
+ instrument_id: InstrumentId,
+ ts_init: UnixNanos,
+) -> PyResult {
+ parse_futures_contract(record, instrument_id, ts_init).map_err(to_pyvalue_err)
+}
+
+#[pyfunction]
+#[pyo3(name = "parse_options_contract")]
+pub fn py_parse_options_contract(
+ record: &dbn::InstrumentDefMsg,
+ instrument_id: InstrumentId,
+ ts_init: UnixNanos,
+) -> PyResult {
+ parse_options_contract(record, instrument_id, ts_init).map_err(to_pyvalue_err)
+}
+
+#[pyfunction]
+#[pyo3(name = "parse_mbo_msg")]
+pub fn py_parse_mbo_msg(
+ py: Python,
+ record: &dbn::MboMsg,
+ instrument_id: InstrumentId,
+ price_precision: u8,
+ ts_init: UnixNanos,
+) -> PyResult {
+ let result = parse_mbo_msg(record, instrument_id, price_precision, ts_init);
+
+ match result {
+ Ok((Some(delta), None)) => Ok(delta.into_py(py)),
+ Ok((None, Some(trade))) => Ok(trade.into_py(py)),
+ Err(e) => Err(to_pyvalue_err(e)),
+ _ => Err(PyRuntimeError::new_err("Error parsing MBO message")),
+ }
+}
+
+#[pyfunction]
+#[pyo3(name = "parse_trade_msg")]
+pub fn py_parse_trade_msg(
+ record: &dbn::TradeMsg,
+ instrument_id: InstrumentId,
+ price_precision: u8,
+ ts_init: UnixNanos,
+) -> PyResult {
+ parse_trade_msg(record, instrument_id, price_precision, ts_init).map_err(to_pyvalue_err)
+}
+
+#[pyfunction]
+#[pyo3(name = "parse_mbp1_msg")]
+pub fn py_parse_mbp1_msg(
+ py: Python,
+ record: &dbn::Mbp1Msg,
+ instrument_id: InstrumentId,
+ price_precision: u8,
+ ts_init: UnixNanos,
+) -> PyResult {
+ let result = parse_mbp1_msg(record, instrument_id, price_precision, ts_init);
+
+ match result {
+ Ok((quote, Some(trade))) => {
+ let quote_py = quote.into_py(py);
+ let trade_py = trade.into_py(py);
+ Ok(PyTuple::new(py, &[quote_py, trade_py.into_py(py)]).into_py(py))
+ }
+ Ok((quote, None)) => {
+ let quote_py = quote.into_py(py);
+ Ok(PyTuple::new(py, &[quote_py, py.None()]).into_py(py))
+ }
+ Err(e) => {
+ // Convert the Rust error to a Python exception
+ Err(PyErr::new::(format!(
+ "Error parsing MBP1 message: {e}"
+ )))
+ }
+ }
+}
+
+#[pyfunction]
+#[pyo3(name = "parse_mbp10_msg")]
+pub fn py_parse_mbp10_msg(
+ record: &dbn::Mbp10Msg,
+ instrument_id: InstrumentId,
+ price_precision: u8,
+ ts_init: UnixNanos,
+) -> PyResult {
+ parse_mbp10_msg(record, instrument_id, price_precision, ts_init).map_err(to_pyvalue_err)
+}
diff --git a/nautilus_core/adapters/src/python/mod.rs b/nautilus_core/adapters/src/python/mod.rs
new file mode 100644
index 000000000000..43a90522ae1a
--- /dev/null
+++ b/nautilus_core/adapters/src/python/mod.rs
@@ -0,0 +1,17 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
+// https://nautechsystems.io
+//
+// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
+// You may not use this file except in compliance with the License.
+// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// -------------------------------------------------------------------------------------------------
+
+#[cfg(feature = "databento")]
+pub mod databento;
diff --git a/nautilus_core/backtest/Cargo.toml b/nautilus_core/backtest/Cargo.toml
index bd6e83e57053..1ea8e22a31a5 100644
--- a/nautilus_core/backtest/Cargo.toml
+++ b/nautilus_core/backtest/Cargo.toml
@@ -15,6 +15,7 @@ nautilus-common = { path = "../common" }
nautilus-core = { path = "../core" }
nautilus-model = { path = "../model" }
pyo3 = { workspace = true, optional = true }
+ustr = { workspace = true }
[dev-dependencies]
tempfile = { workspace = true }
diff --git a/nautilus_core/backtest/build.rs b/nautilus_core/backtest/build.rs
index b793faea6cfb..3b1c048c551a 100644
--- a/nautilus_core/backtest/build.rs
+++ b/nautilus_core/backtest/build.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/backtest/src/engine.rs b/nautilus_core/backtest/src/engine.rs
index 1919438d7b14..94686c3540d0 100644
--- a/nautilus_core/backtest/src/engine.rs
+++ b/nautilus_core/backtest/src/engine.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -113,6 +113,7 @@ mod tests {
use nautilus_core::uuid::UUID4;
use pyo3::{types::PyList, Py, Python};
use rstest::*;
+ use ustr::Ustr;
use super::*;
@@ -126,9 +127,12 @@ mod tests {
let mut accumulator = TimeEventAccumulator::new();
- let time_event1 = TimeEvent::new("TEST_EVENT_1", UUID4::new(), 100, 100).unwrap();
- let time_event2 = TimeEvent::new("TEST_EVENT_2", UUID4::new(), 300, 300).unwrap();
- let time_event3 = TimeEvent::new("TEST_EVENT_3", UUID4::new(), 200, 200).unwrap();
+ let time_event1 =
+ TimeEvent::new(Ustr::from("TEST_EVENT_1"), UUID4::new(), 100, 100).unwrap();
+ let time_event2 =
+ TimeEvent::new(Ustr::from("TEST_EVENT_2"), UUID4::new(), 300, 300).unwrap();
+ let time_event3 =
+ TimeEvent::new(Ustr::from("TEST_EVENT_3"), UUID4::new(), 200, 200).unwrap();
// Note: as_ptr returns a borrowed pointer. It is valid as long
// as the object is in scope. In this case `callback_ptr` is valid
diff --git a/nautilus_core/backtest/src/lib.rs b/nautilus_core/backtest/src/lib.rs
index 1144926f24e4..83e930fd7e61 100644
--- a/nautilus_core/backtest/src/lib.rs
+++ b/nautilus_core/backtest/src/lib.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/common/Cargo.toml b/nautilus_core/common/Cargo.toml
index 160a52667aa4..f1596d5855f4 100644
--- a/nautilus_core/common/Cargo.toml
+++ b/nautilus_core/common/Cargo.toml
@@ -23,8 +23,9 @@ serde_json = { workspace = true }
strum = { workspace = true }
ustr = { workspace = true }
tracing = { workspace = true }
-tracing-appender = "0.2.3"
-tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
+# Disable default feature "tracing-log" since it interferes with custom logging
+tracing-subscriber = { version = "0.3.18", default-features = false, features = ["smallvec", "fmt", "ansi", "std", "env-filter"] }
+log = { version = "0.4.20", features = ["std", "kv_unstable", "serde", "release_max_level_debug"] }
[dev-dependencies]
tempfile = { workspace = true }
diff --git a/nautilus_core/common/build.rs b/nautilus_core/common/build.rs
index 6ad7f47a6e63..5e20ed2f406c 100644
--- a/nautilus_core/common/build.rs
+++ b/nautilus_core/common/build.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/common/src/clock.rs b/nautilus_core/common/src/clock.rs
index 780ec77c6840..eba142cb176d 100644
--- a/nautilus_core/common/src/clock.rs
+++ b/nautilus_core/common/src/clock.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -17,8 +17,9 @@ use std::{collections::HashMap, ops::Deref};
use nautilus_core::{
correctness::check_valid_string,
- time::{AtomicTime, ClockMode, UnixNanos},
+ time::{get_atomic_clock_realtime, AtomicTime, UnixNanos},
};
+use ustr::Ustr;
use crate::{
handlers::EventHandler,
@@ -44,7 +45,7 @@ pub trait Clock {
/// callback gets used to handle generated events.
fn set_time_alert_ns(
&mut self,
- name: String,
+ name: &str,
alert_time_ns: UnixNanos,
callback: Option,
);
@@ -54,7 +55,7 @@ pub trait Clock {
/// used to handle generated event.
fn set_timer_ns(
&mut self,
- name: String,
+ name: &str,
interval_ns: u64,
start_time_ns: UnixNanos,
stop_time_ns: Option,
@@ -66,34 +67,26 @@ pub trait Clock {
fn cancel_timers(&mut self);
}
-#[cfg_attr(
- feature = "python",
- pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.common")
-)]
pub struct TestClock {
time: AtomicTime,
- timers: HashMap,
+ timers: HashMap,
default_callback: Option,
- callbacks: HashMap,
+ callbacks: HashMap,
}
impl TestClock {
#[must_use]
pub fn new() -> Self {
Self {
- time: AtomicTime::new(ClockMode::STATIC, 0),
+ time: AtomicTime::new(false, 0),
timers: HashMap::new(),
default_callback: None,
callbacks: HashMap::new(),
}
}
- pub fn get_time_clone(&self) -> AtomicTime {
- self.time.clone()
- }
-
#[must_use]
- pub fn get_timers(&self) -> &HashMap {
+ pub fn get_timers(&self) -> &HashMap {
&self.timers
}
@@ -101,7 +94,7 @@ impl TestClock {
// Time should increase monotonically
assert!(
to_time_ns >= self.time.get_time_ns(),
- "`to_time_ns` was < `self._time_ns`"
+ "`to_time_ns` was < `self.time.get_time_ns()`"
);
if set_time {
@@ -125,15 +118,11 @@ impl TestClock {
events
.into_iter()
.map(|event| {
- let handler = self
- .callbacks
- .get(event.name.as_str())
- .cloned()
- .unwrap_or_else(|| {
- // If callback_py is None, use the default_callback_py
- // TODO: clone for now
- self.default_callback.clone().unwrap()
- });
+ let handler = self.callbacks.get(&event.name).cloned().unwrap_or_else(|| {
+ // If callback_py is None, use the default_callback_py
+ // TODO: clone for now
+ self.default_callback.clone().unwrap()
+ });
TimeEventHandler {
event,
callback_ptr: handler.as_ptr(),
@@ -179,58 +168,55 @@ impl Clock for TestClock {
fn set_time_alert_ns(
&mut self,
- name: String,
+ name: &str,
alert_time_ns: UnixNanos,
callback: Option,
) {
- check_valid_string(&name, "`Timer` name").unwrap();
+ check_valid_string(name, "`Timer` name").unwrap();
assert!(
callback.is_some() | self.default_callback.is_some(),
"All Python callbacks were `None`"
);
+ let name_ustr = Ustr::from(name);
match callback {
- Some(callback_py) => self.callbacks.insert(name.clone(), callback_py),
+ Some(callback_py) => self.callbacks.insert(name_ustr, callback_py),
None => None,
};
// TODO: should the atomic clock be shared
// currently share timestamp nanoseconds
let time_ns = self.time.get_time_ns();
- let timer = TestTimer::new(
- name.clone(),
- alert_time_ns - time_ns,
- time_ns,
- Some(alert_time_ns),
- );
- self.timers.insert(name, timer);
+ let timer = TestTimer::new(name, alert_time_ns - time_ns, time_ns, Some(alert_time_ns));
+ self.timers.insert(name_ustr, timer);
}
fn set_timer_ns(
&mut self,
- name: String,
+ name: &str,
interval_ns: u64,
start_time_ns: UnixNanos,
stop_time_ns: Option,
callback: Option,
) {
- check_valid_string(&name, "`Timer` name").unwrap();
+ check_valid_string(name, "`Timer` name").unwrap();
assert!(
callback.is_some() | self.default_callback.is_some(),
"All Python callbacks were `None`"
);
+ let name_ustr = Ustr::from(name);
match callback {
- Some(callback_py) => self.callbacks.insert(name.clone(), callback_py),
+ Some(callback_py) => self.callbacks.insert(name_ustr, callback_py),
None => None,
};
- let timer = TestTimer::new(name.clone(), interval_ns, start_time_ns, stop_time_ns);
- self.timers.insert(name, timer);
+ let timer = TestTimer::new(name, interval_ns, start_time_ns, stop_time_ns);
+ self.timers.insert(name_ustr, timer);
}
fn next_time_ns(&self, name: &str) -> UnixNanos {
- let timer = self.timers.get(name);
+ let timer = self.timers.get(&Ustr::from(name));
match timer {
None => 0,
Some(timer) => timer.next_time_ns,
@@ -238,7 +224,7 @@ impl Clock for TestClock {
}
fn cancel_timer(&mut self, name: &str) {
- let timer = self.timers.remove(name);
+ let timer = self.timers.remove(&Ustr::from(name));
match timer {
None => {}
Some(mut timer) => timer.cancel(),
@@ -253,22 +239,18 @@ impl Clock for TestClock {
}
}
-#[cfg_attr(
- feature = "python",
- pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.common")
-)]
pub struct LiveClock {
- internal: AtomicTime,
- timers: HashMap,
+ time: &'static AtomicTime,
+ timers: HashMap,
default_callback: Option,
- callbacks: HashMap,
+ callbacks: HashMap,
}
impl LiveClock {
#[must_use]
pub fn new() -> Self {
Self {
- internal: AtomicTime::new(ClockMode::LIVE, 0),
+ time: get_atomic_clock_realtime(),
timers: HashMap::new(),
default_callback: None,
callbacks: HashMap::new(),
@@ -286,7 +268,7 @@ impl Deref for LiveClock {
type Target = AtomicTime;
fn deref(&self) -> &Self::Target {
- &self.internal
+ self.time
}
}
@@ -312,57 +294,54 @@ impl Clock for LiveClock {
fn set_time_alert_ns(
&mut self,
- name: String,
+ name: &str,
mut alert_time_ns: UnixNanos,
callback: Option,
) {
- check_valid_string(&name, "`Timer` name").unwrap();
+ check_valid_string(name, "`Timer` name").unwrap();
assert!(
callback.is_some() | self.default_callback.is_some(),
"All Python callbacks were `None`"
);
+ let name_ustr = Ustr::from(name);
match callback {
- Some(callback_py) => self.callbacks.insert(name.clone(), callback_py),
+ Some(callback_py) => self.callbacks.insert(name_ustr, callback_py),
None => None,
};
let ts_now = self.get_time_ns();
alert_time_ns = std::cmp::max(alert_time_ns, ts_now);
- let timer = TestTimer::new(
- name.clone(),
- alert_time_ns - ts_now,
- ts_now,
- Some(alert_time_ns),
- );
- self.timers.insert(name, timer);
+ let timer = TestTimer::new(name, alert_time_ns - ts_now, ts_now, Some(alert_time_ns));
+ self.timers.insert(name_ustr, timer);
}
fn set_timer_ns(
&mut self,
- name: String,
+ name: &str,
interval_ns: u64,
start_time_ns: UnixNanos,
stop_time_ns: Option,
callback: Option,
) {
- check_valid_string(&name, "`Timer` name").unwrap();
+ check_valid_string(name, "`Timer` name").unwrap();
assert!(
callback.is_some() | self.default_callback.is_some(),
"All Python callbacks were `None`"
);
+ let name_ustr = Ustr::from(name);
match callback {
- Some(callback) => self.callbacks.insert(name.clone(), callback),
+ Some(callback) => self.callbacks.insert(name_ustr, callback),
None => None,
};
- let timer = TestTimer::new(name.clone(), interval_ns, start_time_ns, stop_time_ns);
- self.timers.insert(name, timer);
+ let timer = TestTimer::new(name, interval_ns, start_time_ns, stop_time_ns);
+ self.timers.insert(name_ustr, timer);
}
fn next_time_ns(&self, name: &str) -> UnixNanos {
- let timer = self.timers.get(name);
+ let timer = self.timers.get(&Ustr::from(name));
match timer {
None => 0,
Some(timer) => timer.next_time_ns,
@@ -370,7 +349,7 @@ impl Clock for LiveClock {
}
fn cancel_timer(&mut self, name: &str) {
- let timer = self.timers.remove(name);
+ let timer = self.timers.remove(&Ustr::from(name));
match timer {
None => {}
Some(mut timer) => timer.cancel(),
@@ -421,9 +400,10 @@ mod tests {
let handler = EventHandler::new(Some(py_append), None);
test_clock.register_default_handler(handler);
- test_clock.set_timer_ns(String::from("TEST_TIME1"), 10, 0, None, None);
+ let timer_name = "TEST_TIME1";
+ test_clock.set_timer_ns(timer_name, 10, 0, None, None);
- assert_eq!(test_clock.timer_names(), ["TEST_TIME1"]);
+ assert_eq!(test_clock.timer_names(), [timer_name]);
assert_eq!(test_clock.timer_count(), 1);
});
}
@@ -438,8 +418,9 @@ mod tests {
let handler = EventHandler::new(Some(py_append), None);
test_clock.register_default_handler(handler);
- test_clock.set_timer_ns(String::from("TEST_TIME1"), 10, 0, None, None);
- test_clock.cancel_timer(String::from("TEST_TIME1").as_str());
+ let timer_name = "TEST_TIME1";
+ test_clock.set_timer_ns(timer_name, 10, 0, None, None);
+ test_clock.cancel_timer(timer_name);
assert!(test_clock.timer_names().is_empty());
assert_eq!(test_clock.timer_count(), 0);
@@ -456,7 +437,8 @@ mod tests {
let handler = EventHandler::new(Some(py_append), None);
test_clock.register_default_handler(handler);
- test_clock.set_timer_ns(String::from("TEST_TIME1"), 10, 0, None, None);
+ let timer_name = "TEST_TIME1";
+ test_clock.set_timer_ns(timer_name, 10, 0, None, None);
test_clock.cancel_timers();
assert!(test_clock.timer_names().is_empty());
@@ -474,10 +456,11 @@ mod tests {
let handler = EventHandler::new(Some(py_append), None);
test_clock.register_default_handler(handler);
- test_clock.set_timer_ns(String::from("TEST_TIME1"), 1, 1, Some(3), None);
+ let timer_name = "TEST_TIME1";
+ test_clock.set_timer_ns(timer_name, 1, 1, Some(3), None);
test_clock.advance_time(2, true);
- assert_eq!(test_clock.timer_names(), ["TEST_TIME1"]);
+ assert_eq!(test_clock.timer_names(), [timer_name]);
assert_eq!(test_clock.timer_count(), 1);
});
}
@@ -492,7 +475,7 @@ mod tests {
let handler = EventHandler::new(Some(py_append), None);
test_clock.register_default_handler(handler);
- test_clock.set_timer_ns(String::from("TEST_TIME1"), 2, 0, Some(3), None);
+ test_clock.set_timer_ns("TEST_TIME1", 2, 0, Some(3), None);
test_clock.advance_time(3, true);
assert_eq!(test_clock.timer_names().len(), 1);
@@ -511,7 +494,7 @@ mod tests {
let handler = EventHandler::new(Some(py_append), None);
test_clock.register_default_handler(handler);
- test_clock.set_timer_ns(String::from("TEST_TIME1"), 2, 0, Some(3), None);
+ test_clock.set_timer_ns("TEST_TIME1", 2, 0, Some(3), None);
test_clock.advance_time(3, false);
assert_eq!(test_clock.timer_names().len(), 1);
diff --git a/nautilus_core/common/src/enums.rs b/nautilus_core/common/src/enums.rs
index 5fe4b48a80e8..01fb4ee6f2d1 100644
--- a/nautilus_core/common/src/enums.rs
+++ b/nautilus_core/common/src/enums.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -171,10 +171,6 @@ pub enum LogLevel {
#[strum(serialize = "ERR", serialize = "ERROR")]
#[serde(rename = "ERROR")]
Error = 40,
- /// The **CRT** critical log level.
- #[strum(serialize = "CRT", serialize = "CRITICAL")]
- #[serde(rename = "CRITICAL")]
- Critical = 50,
}
// Override `strum` implementation
@@ -185,7 +181,6 @@ impl std::fmt::Display for LogLevel {
Self::Info => "INF",
Self::Warning => "WRN",
Self::Error => "ERR",
- Self::Critical => "CRT",
};
write!(f, "{display}")
}
@@ -234,15 +229,29 @@ pub enum LogColor {
/// The yellow log color, typically used with [`LogLevel::Warning`] log levels.
#[strum(serialize = "\x1b[1;33m")]
Yellow = 5,
- /// The red log color, typically used with [`LogLevel::Error`] or [`LogLevel::Critical`] log levels.
+ /// The red log color, typically used with [`LogLevel::Error`] level.
#[strum(serialize = "\x1b[1;31m")]
Red = 6,
}
+impl From for LogColor {
+ fn from(value: u8) -> Self {
+ match value {
+ 1 => Self::Green,
+ 2 => Self::Blue,
+ 3 => Self::Magenta,
+ 4 => Self::Cyan,
+ 5 => Self::Yellow,
+ 6 => Self::Red,
+ _ => Self::Normal,
+ }
+ }
+}
+
/// An ANSI log line format specifier.
/// This is used for formatting log messages with ANSI escape codes.
#[repr(C)]
-#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, FromRepr, EnumString, Display)]
+#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, FromRepr, EnumString, Display)]
#[strum(ascii_case_insensitive)]
#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
#[cfg_attr(
diff --git a/nautilus_core/common/src/factories.rs b/nautilus_core/common/src/factories.rs
index 161a1d7f000f..c675ff6ba547 100644
--- a/nautilus_core/common/src/factories.rs
+++ b/nautilus_core/common/src/factories.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -34,39 +34,39 @@ use crate::generators::{
#[repr(C)]
pub struct OrderFactory {
+ clock: &'static AtomicTime,
trader_id: TraderId,
strategy_id: StrategyId,
order_id_generator: ClientOrderIdGenerator,
order_list_id_generator: OrderListIdGenerator,
- clock: AtomicTime,
}
impl OrderFactory {
pub fn new(
trader_id: TraderId,
strategy_id: StrategyId,
- clock: AtomicTime,
init_order_id_count: Option,
init_order_list_id_count: Option,
+ clock: &'static AtomicTime,
) -> Self {
let order_id_generator = ClientOrderIdGenerator::new(
trader_id,
strategy_id,
- clock.clone(),
init_order_id_count.unwrap_or(0),
+ clock,
);
let order_list_id_generator = OrderListIdGenerator::new(
trader_id,
strategy_id,
- clock.clone(),
init_order_list_id_count.unwrap_or(0),
+ clock,
);
Self {
+ clock,
trader_id,
strategy_id,
order_id_generator,
order_list_id_generator,
- clock,
}
}
@@ -222,8 +222,8 @@ pub mod tests {
assert_eq!(market_order.side, OrderSide::Buy);
assert_eq!(market_order.quantity, 100.into());
assert_eq!(market_order.time_in_force, TimeInForce::Gtc);
- assert_eq!(market_order.is_reduce_only, false);
- assert_eq!(market_order.is_quote_quantity, false);
+ assert!(!market_order.is_reduce_only);
+ assert!(!market_order.is_quote_quantity);
assert_eq!(market_order.exec_algorithm_id, None);
assert_eq!(market_order.exec_algorithm_params, None);
assert_eq!(market_order.exec_spawn_id, None);
diff --git a/nautilus_core/common/src/ffi/clock.rs b/nautilus_core/common/src/ffi/clock.rs
index d510837c9360..c2e0e225089c 100644
--- a/nautilus_core/common/src/ffi/clock.rs
+++ b/nautilus_core/common/src/ffi/clock.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -150,7 +150,7 @@ pub unsafe extern "C" fn test_clock_set_time_alert_ns(
});
let handler = EventHandler::new(callback_py.clone(), None);
- clock.set_time_alert_ns(name, alert_time_ns, callback_py.map(|_| handler));
+ clock.set_time_alert_ns(&name, alert_time_ns, callback_py.map(|_| handler));
}
/// # Safety
@@ -181,7 +181,7 @@ pub unsafe extern "C" fn test_clock_set_timer_ns(
let handler = EventHandler::new(callback_py.clone(), None);
clock.set_timer_ns(
- name,
+ &name,
interval_ns,
start_time_ns,
stop_time_ns,
diff --git a/nautilus_core/common/src/ffi/enums.rs b/nautilus_core/common/src/ffi/enums.rs
index b205f82047ab..d8837ebd3484 100644
--- a/nautilus_core/common/src/ffi/enums.rs
+++ b/nautilus_core/common/src/ffi/enums.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -27,6 +27,7 @@ pub extern "C" fn component_state_to_cstr(value: ComponentState) -> *const c_cha
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn component_state_from_cstr(ptr: *const c_char) -> ComponentState {
@@ -43,6 +44,7 @@ pub extern "C" fn component_trigger_to_cstr(value: ComponentTrigger) -> *const c
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn component_trigger_from_cstr(ptr: *const c_char) -> ComponentTrigger {
@@ -59,6 +61,7 @@ pub extern "C" fn log_level_to_cstr(value: LogLevel) -> *const c_char {
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn log_level_from_cstr(ptr: *const c_char) -> LogLevel {
@@ -75,6 +78,7 @@ pub extern "C" fn log_color_to_cstr(value: LogColor) -> *const c_char {
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn log_color_from_cstr(ptr: *const c_char) -> LogColor {
diff --git a/nautilus_core/common/src/ffi/logging.rs b/nautilus_core/common/src/ffi/logging.rs
index cb2b32d7c8d7..44ca33c252ff 100644
--- a/nautilus_core/common/src/ffi/logging.rs
+++ b/nautilus_core/common/src/ffi/logging.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -13,124 +13,73 @@
// limitations under the License.
// -------------------------------------------------------------------------------------------------
-use std::{
- ffi::c_char,
- ops::{Deref, DerefMut},
-};
+use std::ffi::{c_char, CStr};
use nautilus_core::{
- ffi::{
- parsing::{optional_bytes_to_json, u8_as_bool},
- string::{cstr_to_string, optional_cstr_to_string, str_to_cstr},
- },
+ ffi::string::{cstr_to_string, cstr_to_ustr, optional_cstr_to_string},
uuid::UUID4,
};
use nautilus_model::identifiers::trader_id::TraderId;
use crate::{
enums::{LogColor, LogLevel},
- logging::Logger,
+ logging::{self},
};
-/// Provides a C compatible Foreign Function Interface (FFI) for an underlying [`Logger`].
+/// Initialize tracing.
///
-/// This struct wraps `Logger` in a way that makes it compatible with C function
-/// calls, enabling interaction with `Logger` in a C environment.
+/// Tracing is meant to be used to trace/debug async Rust code. It can be
+/// configured to filter modules and write up to a specific level only using
+/// by passing a configuration using the `RUST_LOG` environment variable.
///
-/// It implements the `Deref` trait, allowing instances of `Logger_API` to be
-/// dereferenced to `Logger`, providing access to `Logger`'s methods without
-/// having to manually access the underlying `Logger` instance.
-#[repr(C)]
-#[allow(non_camel_case_types)]
-pub struct Logger_API(Box);
-
-impl Deref for Logger_API {
- type Target = Logger;
-
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
-
-impl DerefMut for Logger_API {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.0
- }
+/// # Safety
+///
+/// Should only be called once during an applications run, ideally at the
+/// beginning of the run.
+#[no_mangle]
+pub extern "C" fn tracing_init() {
+ logging::init_tracing();
}
-/// Creates a new logger.
+/// Initialize logging.
+///
+/// Logging should be used for Python and sync Rust logic which is most of
+/// the components in the main `nautilus_trader` package.
+/// Logging can be configured to filter components and write up to a specific level only
+/// by passing a configuration using the `NAUTILUS_LOG` environment variable.
///
/// # Safety
///
-/// - Assumes `trader_id_ptr` is a valid C string pointer.
-/// - Assumes `machine_id_ptr` is a valid C string pointer.
-/// - Assumes `instance_id_ptr` is a valid C string pointer.
-/// - Assumes `directory_ptr` is a valid C string pointer or NULL.
-/// - Assumes `file_name_ptr` is a valid C string pointer or NULL.
-/// - Assumes `file_format_ptr` is a valid C string pointer or NULL.
-/// - Assumes `component_levels_ptr` is a valid C string pointer or NULL.
+/// Should only be called once during an applications run, ideally at the
+/// beginning of the run.
+///
+/// - Assume `config_spec_ptr` is a valid C string pointer.
+/// - Assume `directory_ptr` is either NULL or a valid C string pointer.
+/// - Assume `file_name_ptr` is either NULL or a valid C string pointer.
+/// - Assume `file_format_ptr` is either NULL or a valid C string pointer.
#[no_mangle]
-pub unsafe extern "C" fn logger_new(
- trader_id_ptr: *const c_char,
- machine_id_ptr: *const c_char,
- instance_id_ptr: *const c_char,
- level_stdout: LogLevel,
- level_file: LogLevel,
- file_logging: u8,
+pub unsafe extern "C" fn logging_init(
+ trader_id: TraderId,
+ instance_id: UUID4,
+ config_spec_ptr: *const c_char,
directory_ptr: *const c_char,
file_name_ptr: *const c_char,
file_format_ptr: *const c_char,
- component_levels_ptr: *const c_char,
- is_colored: u8,
- is_bypassed: u8,
-) -> Logger_API {
- Logger_API(Box::new(Logger::new(
- TraderId::from(cstr_to_string(trader_id_ptr).as_str()),
- String::from(&cstr_to_string(machine_id_ptr)),
- UUID4::from(cstr_to_string(instance_id_ptr).as_str()),
- level_stdout,
- if u8_as_bool(file_logging) {
- Some(level_file)
- } else {
- None
- },
- optional_cstr_to_string(directory_ptr),
- optional_cstr_to_string(file_name_ptr),
- optional_cstr_to_string(file_format_ptr),
- optional_bytes_to_json(component_levels_ptr),
- u8_as_bool(is_colored),
- u8_as_bool(is_bypassed),
- )))
-}
-
-#[no_mangle]
-pub extern "C" fn logger_drop(logger: Logger_API) {
- drop(logger); // Memory freed here
-}
-
-#[no_mangle]
-pub extern "C" fn logger_get_trader_id_cstr(logger: &Logger_API) -> *const c_char {
- str_to_cstr(&logger.trader_id.to_string())
-}
-
-#[no_mangle]
-pub extern "C" fn logger_get_machine_id_cstr(logger: &Logger_API) -> *const c_char {
- str_to_cstr(&logger.machine_id)
-}
+) {
+ let config_spec = cstr_to_string(config_spec_ptr);
-#[no_mangle]
-pub extern "C" fn logger_get_instance_id(logger: &Logger_API) -> UUID4 {
- logger.instance_id
-}
+ let directory = optional_cstr_to_string(directory_ptr);
+ let file_name = optional_cstr_to_string(file_name_ptr);
+ let file_format = optional_cstr_to_string(file_format_ptr);
-#[no_mangle]
-pub extern "C" fn logger_is_colored(logger: &Logger_API) -> u8 {
- u8::from(logger.is_colored)
-}
-
-#[no_mangle]
-pub extern "C" fn logger_is_bypassed(logger: &Logger_API) -> u8 {
- u8::from(logger.is_bypassed)
+ logging::init_logging(
+ trader_id,
+ instance_id,
+ config_spec,
+ directory,
+ file_name,
+ file_format,
+ );
}
/// Create a new log event.
@@ -141,14 +90,20 @@ pub extern "C" fn logger_is_bypassed(logger: &Logger_API) -> u8 {
/// - Assumes `message_ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn logger_log(
- logger: &mut Logger_API,
timestamp_ns: u64,
level: LogLevel,
color: LogColor,
component_ptr: *const c_char,
message_ptr: *const c_char,
) {
- let component = cstr_to_string(component_ptr);
- let message = cstr_to_string(message_ptr);
- logger.send(timestamp_ns, level, color, component, message);
+ let component = cstr_to_ustr(component_ptr);
+ let message = CStr::from_ptr(message_ptr).to_string_lossy();
+
+ logging::log(timestamp_ns, level, color, component, message);
+}
+
+/// Flush logger buffers.
+#[no_mangle]
+pub extern "C" fn logger_flush() {
+ log::logger().flush()
}
diff --git a/nautilus_core/common/src/ffi/mod.rs b/nautilus_core/common/src/ffi/mod.rs
index 705e0b70cb4d..aafb5d2a4aca 100644
--- a/nautilus_core/common/src/ffi/mod.rs
+++ b/nautilus_core/common/src/ffi/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/common/src/ffi/msgbus.rs b/nautilus_core/common/src/ffi/msgbus.rs
index 3eb55d40eb1b..a85db7c86e3b 100644
--- a/nautilus_core/common/src/ffi/msgbus.rs
+++ b/nautilus_core/common/src/ffi/msgbus.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/common/src/ffi/timer.rs b/nautilus_core/common/src/ffi/timer.rs
index 53946247b166..9976cd9609ef 100644
--- a/nautilus_core/common/src/ffi/timer.rs
+++ b/nautilus_core/common/src/ffi/timer.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -16,7 +16,7 @@
use std::ffi::c_char;
use nautilus_core::{
- ffi::string::{cstr_to_string, str_to_cstr},
+ ffi::string::{cstr_to_ustr, str_to_cstr},
uuid::UUID4,
};
@@ -32,7 +32,7 @@ pub unsafe extern "C" fn time_event_new(
ts_event: u64,
ts_init: u64,
) -> TimeEvent {
- TimeEvent::new(&cstr_to_string(name_ptr), event_id, ts_event, ts_init).unwrap()
+ TimeEvent::new(cstr_to_ustr(name_ptr), event_id, ts_event, ts_init).unwrap()
}
/// Returns a [`TimeEvent`] as a C string pointer.
diff --git a/nautilus_core/common/src/generators/client_order_id.rs b/nautilus_core/common/src/generators/client_order_id.rs
index 330383ac5726..1063484ac27f 100644
--- a/nautilus_core/common/src/generators/client_order_id.rs
+++ b/nautilus_core/common/src/generators/client_order_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -22,9 +22,9 @@ use super::get_datetime_tag;
#[repr(C)]
pub struct ClientOrderIdGenerator {
+ clock: &'static AtomicTime,
trader_id: TraderId,
strategy_id: StrategyId,
- time: AtomicTime,
count: usize,
}
@@ -33,14 +33,14 @@ impl ClientOrderIdGenerator {
pub fn new(
trader_id: TraderId,
strategy_id: StrategyId,
- time: AtomicTime,
initial_count: usize,
+ clock: &'static AtomicTime,
) -> Self {
Self {
trader_id,
strategy_id,
- time,
count: initial_count,
+ clock,
}
}
@@ -58,7 +58,7 @@ impl ClientOrderIdGenerator {
}
pub fn generate(&mut self) -> ClientOrderId {
- let datetime_tag = get_datetime_tag(self.time.get_time_ms());
+ let datetime_tag = get_datetime_tag(self.clock.get_time_ms());
let trader_tag = self.trader_id.get_tag();
let strategy_tag = self.strategy_id.get_tag();
self.count += 1;
@@ -75,41 +75,40 @@ impl ClientOrderIdGenerator {
////////////////////////////////////////////////////////////////////////////////
#[cfg(test)]
mod tests {
- use nautilus_core::time::AtomicTime;
+ use nautilus_core::time::get_atomic_clock_static;
use nautilus_model::identifiers::{
client_order_id::ClientOrderId, strategy_id::StrategyId, trader_id::TraderId,
};
use rstest::rstest;
- use crate::{
- clock::{stubs::test_clock, TestClock},
- generators::client_order_id::ClientOrderIdGenerator,
- };
+ use crate::generators::client_order_id::ClientOrderIdGenerator;
- fn get_client_order_id_generator(
- time: AtomicTime,
- initial_count: Option,
- ) -> ClientOrderIdGenerator {
+ fn get_client_order_id_generator(initial_count: Option) -> ClientOrderIdGenerator {
let trader_id = TraderId::from("TRADER-001");
let strategy_id = StrategyId::from("EMACross-001");
- ClientOrderIdGenerator::new(trader_id, strategy_id, time, initial_count.unwrap_or(0))
+ ClientOrderIdGenerator::new(
+ trader_id,
+ strategy_id,
+ initial_count.unwrap_or(0),
+ get_atomic_clock_static(),
+ )
}
#[rstest]
- fn test_init(test_clock: TestClock) {
- let generator = get_client_order_id_generator(test_clock.get_time_clone(), None);
+ fn test_init() {
+ let generator = get_client_order_id_generator(None);
assert_eq!(generator.count(), 0);
}
#[rstest]
- fn test_init_with_initial_count(test_clock: TestClock) {
- let generator = get_client_order_id_generator(test_clock.get_time_clone(), Some(7));
+ fn test_init_with_initial_count() {
+ let generator = get_client_order_id_generator(Some(7));
assert_eq!(generator.count(), 7);
}
#[rstest]
- fn test_generate_client_order_id_from_start(test_clock: TestClock) {
- let mut generator = get_client_order_id_generator(test_clock.get_time_clone(), None);
+ fn test_generate_client_order_id_from_start() {
+ let mut generator = get_client_order_id_generator(None);
let result1 = generator.generate();
let result2 = generator.generate();
let result3 = generator.generate();
@@ -129,8 +128,8 @@ mod tests {
}
#[rstest]
- fn test_generate_client_order_id_from_initial(test_clock: TestClock) {
- let mut generator = get_client_order_id_generator(test_clock.get_time_clone(), Some(5));
+ fn test_generate_client_order_id_from_initial() {
+ let mut generator = get_client_order_id_generator(Some(5));
let result1 = generator.generate();
let result2 = generator.generate();
let result3 = generator.generate();
@@ -150,8 +149,8 @@ mod tests {
}
#[rstest]
- fn test_reset(test_clock: TestClock) {
- let mut generator = get_client_order_id_generator(test_clock.get_time_clone(), None);
+ fn test_reset() {
+ let mut generator = get_client_order_id_generator(None);
generator.generate();
generator.generate();
generator.reset();
diff --git a/nautilus_core/common/src/generators/mod.rs b/nautilus_core/common/src/generators/mod.rs
index 48e34f19af98..f8eb56dcfe00 100644
--- a/nautilus_core/common/src/generators/mod.rs
+++ b/nautilus_core/common/src/generators/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/common/src/generators/order_list_id.rs b/nautilus_core/common/src/generators/order_list_id.rs
index 4d139cc02839..a0a3ff8fa983 100644
--- a/nautilus_core/common/src/generators/order_list_id.rs
+++ b/nautilus_core/common/src/generators/order_list_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -22,9 +22,9 @@ use super::get_datetime_tag;
#[repr(C)]
pub struct OrderListIdGenerator {
+ clock: &'static AtomicTime,
trader_id: TraderId,
strategy_id: StrategyId,
- time: AtomicTime,
count: usize,
}
@@ -33,13 +33,13 @@ impl OrderListIdGenerator {
pub fn new(
trader_id: TraderId,
strategy_id: StrategyId,
- time: AtomicTime,
initial_count: usize,
+ clock: &'static AtomicTime,
) -> Self {
Self {
+ clock,
trader_id,
strategy_id,
- time,
count: initial_count,
}
}
@@ -58,7 +58,7 @@ impl OrderListIdGenerator {
}
pub fn generate(&mut self) -> OrderListId {
- let datetime_tag = get_datetime_tag(self.time.get_time_ms());
+ let datetime_tag = get_datetime_tag(self.clock.get_time_ms());
let trader_tag = self.trader_id.get_tag();
let strategy_tag = self.strategy_id.get_tag();
self.count += 1;
@@ -75,41 +75,40 @@ impl OrderListIdGenerator {
////////////////////////////////////////////////////////////////////////////////
#[cfg(test)]
mod tests {
- use nautilus_core::time::AtomicTime;
+ use nautilus_core::time::get_atomic_clock_static;
use nautilus_model::identifiers::{
order_list_id::OrderListId, strategy_id::StrategyId, trader_id::TraderId,
};
use rstest::rstest;
- use crate::{
- clock::{stubs::test_clock, TestClock},
- generators::order_list_id::OrderListIdGenerator,
- };
+ use crate::generators::order_list_id::OrderListIdGenerator;
- fn get_order_list_id_generator(
- time: AtomicTime,
- initial_count: Option,
- ) -> OrderListIdGenerator {
+ fn get_order_list_id_generator(initial_count: Option) -> OrderListIdGenerator {
let trader_id = TraderId::from("TRADER-001");
let strategy_id = StrategyId::from("EMACross-001");
- OrderListIdGenerator::new(trader_id, strategy_id, time, initial_count.unwrap_or(0))
+ OrderListIdGenerator::new(
+ trader_id,
+ strategy_id,
+ initial_count.unwrap_or(0),
+ get_atomic_clock_static(),
+ )
}
#[rstest]
- fn test_init(test_clock: TestClock) {
- let generator = get_order_list_id_generator(test_clock.get_time_clone(), None);
+ fn test_init() {
+ let generator = get_order_list_id_generator(None);
assert_eq!(generator.count(), 0);
}
#[rstest]
- fn test_init_with_initial_count(test_clock: TestClock) {
- let generator = get_order_list_id_generator(test_clock.get_time_clone(), Some(7));
+ fn test_init_with_initial_count() {
+ let generator = get_order_list_id_generator(Some(7));
assert_eq!(generator.count(), 7);
}
#[rstest]
- fn test_generate_order_list_id_from_start(test_clock: TestClock) {
- let mut generator = get_order_list_id_generator(test_clock.get_time_clone(), None);
+ fn test_generate_order_list_id_from_start() {
+ let mut generator = get_order_list_id_generator(None);
let result1 = generator.generate();
let result2 = generator.generate();
let result3 = generator.generate();
@@ -129,8 +128,8 @@ mod tests {
}
#[rstest]
- fn test_generate_order_list_id_from_initial(test_clock: TestClock) {
- let mut generator = get_order_list_id_generator(test_clock.get_time_clone(), Some(5));
+ fn test_generate_order_list_id_from_initial() {
+ let mut generator = get_order_list_id_generator(Some(5));
let result1 = generator.generate();
let result2 = generator.generate();
let result3 = generator.generate();
@@ -150,8 +149,8 @@ mod tests {
}
#[rstest]
- fn test_reset(test_clock: TestClock) {
- let mut generator = get_order_list_id_generator(test_clock.get_time_clone(), None);
+ fn test_reset() {
+ let mut generator = get_order_list_id_generator(None);
generator.generate();
generator.generate();
generator.reset();
diff --git a/nautilus_core/common/src/generators/position_id.rs b/nautilus_core/common/src/generators/position_id.rs
index 7415f02e2acb..93f7cde6c89a 100644
--- a/nautilus_core/common/src/generators/position_id.rs
+++ b/nautilus_core/common/src/generators/position_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -24,17 +24,17 @@ use super::get_datetime_tag;
#[repr(C)]
pub struct PositionIdGenerator {
+ clock: &'static AtomicTime,
trader_id: TraderId,
- time: AtomicTime,
counts: HashMap,
}
impl PositionIdGenerator {
#[must_use]
- pub fn new(trader_id: TraderId, time: AtomicTime) -> Self {
+ pub fn new(trader_id: TraderId, clock: &'static AtomicTime) -> Self {
Self {
+ clock,
trader_id,
- time,
counts: HashMap::new(),
}
}
@@ -56,7 +56,7 @@ impl PositionIdGenerator {
let strategy = strategy_id;
let next_count = self.count(strategy_id) + 1;
self.set_count(next_count, strategy_id);
- let datetime_tag = get_datetime_tag(self.time.get_time_ms());
+ let datetime_tag = get_datetime_tag(self.clock.get_time_ms());
let trader_tag = self.trader_id.get_tag();
let strategy_tag = strategy.get_tag();
let flipped = if flipped { "F" } else { "" };
@@ -70,25 +70,22 @@ impl PositionIdGenerator {
////////////////////////////////////////////////////////////////////////////////
#[cfg(test)]
mod tests {
- use nautilus_core::time::AtomicTime;
+ use nautilus_core::time::get_atomic_clock_static;
use nautilus_model::identifiers::{
position_id::PositionId, strategy_id::StrategyId, trader_id::TraderId,
};
use rstest::rstest;
- use crate::{
- clock::{stubs::test_clock, TestClock},
- generators::position_id::PositionIdGenerator,
- };
+ use crate::generators::position_id::PositionIdGenerator;
- fn get_position_id_generator(time: AtomicTime) -> PositionIdGenerator {
+ fn get_position_id_generator() -> PositionIdGenerator {
let trader_id = TraderId::from("TRADER-001");
- PositionIdGenerator::new(trader_id, time)
+ PositionIdGenerator::new(trader_id, get_atomic_clock_static())
}
#[rstest]
- fn test_generate_position_id_one_strategy(test_clock: TestClock) {
- let mut generator = get_position_id_generator(test_clock.get_time_clone());
+ fn test_generate_position_id_one_strategy() {
+ let mut generator = get_position_id_generator();
let result1 = generator.generate(StrategyId::from("S-001"), false);
let result2 = generator.generate(StrategyId::from("S-001"), false);
@@ -97,8 +94,8 @@ mod tests {
}
#[rstest]
- fn test_generate_position_id_multiple_strategies(test_clock: TestClock) {
- let mut generator = get_position_id_generator(test_clock.get_time_clone());
+ fn test_generate_position_id_multiple_strategies() {
+ let mut generator = get_position_id_generator();
let result1 = generator.generate(StrategyId::from("S-001"), false);
let result2 = generator.generate(StrategyId::from("S-002"), false);
let result3 = generator.generate(StrategyId::from("S-002"), false);
@@ -109,8 +106,8 @@ mod tests {
}
#[rstest]
- fn test_generate_position_id_with_flipped_appends_correctly(test_clock: TestClock) {
- let mut generator = get_position_id_generator(test_clock.get_time_clone());
+ fn test_generate_position_id_with_flipped_appends_correctly() {
+ let mut generator = get_position_id_generator();
let result1 = generator.generate(StrategyId::from("S-001"), false);
let result2 = generator.generate(StrategyId::from("S-002"), true);
let result3 = generator.generate(StrategyId::from("S-001"), true);
@@ -121,16 +118,16 @@ mod tests {
}
#[rstest]
- fn test_get_count_when_strategy_id_has_not_been_used(test_clock: TestClock) {
- let generator = get_position_id_generator(test_clock.get_time_clone());
+ fn test_get_count_when_strategy_id_has_not_been_used() {
+ let generator = get_position_id_generator();
let result = generator.count(StrategyId::from("S-001"));
assert_eq!(result, 0);
}
#[rstest]
- fn set_count_with_valid_strategy(test_clock: TestClock) {
- let mut generator = get_position_id_generator(test_clock.get_time_clone());
+ fn set_count_with_valid_strategy() {
+ let mut generator = get_position_id_generator();
generator.set_count(7, StrategyId::from("S-001"));
let result = generator.count(StrategyId::from("S-001"));
@@ -138,8 +135,8 @@ mod tests {
}
#[rstest]
- fn test_reset(test_clock: TestClock) {
- let mut generator = get_position_id_generator(test_clock.get_time_clone());
+ fn test_reset() {
+ let mut generator = get_position_id_generator();
generator.generate(StrategyId::from("S-001"), false);
generator.generate(StrategyId::from("S-001"), false);
generator.reset();
diff --git a/nautilus_core/common/src/handlers.rs b/nautilus_core/common/src/handlers.rs
index eb32543d6a00..8771b6442ae4 100644
--- a/nautilus_core/common/src/handlers.rs
+++ b/nautilus_core/common/src/handlers.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -22,7 +22,7 @@ use ustr::Ustr;
use crate::timer::TimeEvent;
#[repr(C)]
-#[derive(Copy, Clone, Debug)]
+#[derive(Clone, Copy, Debug)]
pub struct PyCallableWrapper {
pub ptr: *mut ffi::PyObject,
}
diff --git a/nautilus_core/common/src/lib.rs b/nautilus_core/common/src/lib.rs
index 4d9178ea0f72..1039a583ad1c 100644
--- a/nautilus_core/common/src/lib.rs
+++ b/nautilus_core/common/src/lib.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/common/src/logging.rs b/nautilus_core/common/src/logging.rs
index 4902f0fa678d..89eb36346d4f 100644
--- a/nautilus_core/common/src/logging.rs
+++ b/nautilus_core/common/src/logging.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -14,8 +14,9 @@
// -------------------------------------------------------------------------------------------------
use std::{
+ borrow::Cow,
collections::HashMap,
- fmt,
+ env, fmt,
fs::{create_dir_all, File},
io::{self, BufWriter, Stderr, Stdout, Write},
path::{Path, PathBuf},
@@ -25,125 +26,209 @@ use std::{
};
use chrono::{prelude::*, Utc};
+use log::{
+ debug, error, info,
+ kv::{Key, ToValue, Value},
+ set_boxed_logger, set_max_level, warn, Level, LevelFilter, Log, STATIC_MAX_LEVEL,
+};
use nautilus_core::{datetime::unix_nanos_to_iso8601, time::UnixNanos, uuid::UUID4};
use nautilus_model::identifiers::trader_id::TraderId;
-use pyo3::prelude::*;
use serde::{Deserialize, Serialize};
-use serde_json::Value;
-use tracing::Level;
-use tracing_appender::{
- non_blocking::WorkerGuard,
- rolling::{RollingFileAppender, Rotation},
-};
-use tracing_subscriber::{fmt::Layer, prelude::*, EnvFilter, Registry};
+use tracing_subscriber::EnvFilter;
+use ustr::Ustr;
use crate::enums::{LogColor, LogLevel};
-/// Guards the log collector and flushes it when dropped
-///
-/// This struct must be dropped when the application has completed operation
-/// it ensures that the any pending log lines are flushed before the application
-/// closes.
-#[pyclass]
-pub struct LogGuard {
- #[allow(dead_code)]
- guards: Vec,
+#[cfg_attr(
+ feature = "python",
+ pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.common")
+)]
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct LoggerConfig {
+ /// Maximum log level to write to stdout.
+ stdout_level: LevelFilter,
+ /// Maximum log level to write to file.
+ fileout_level: LevelFilter,
+ /// Maximum log level to write for a given component.
+ component_level: HashMap,
+ /// If logger is using ANSI color codes.
+ pub is_colored: bool,
+ /// If logging is bypassed.
+ pub is_bypassed: bool,
+ /// If the configuration should be printed to stdout at initialization.
+ pub print_config: bool,
}
-/// Sets the global log collector
-///
-/// stdout_level: Set the level for the stdout writer
-/// stderr_level: Set the level for the stderr writer
-/// file_level: Set the level, the directory and the prefix for the file writer
+impl Default for LoggerConfig {
+ fn default() -> Self {
+ Self {
+ stdout_level: LevelFilter::Info,
+ fileout_level: LevelFilter::Off,
+ component_level: HashMap::new(),
+ is_colored: false,
+ is_bypassed: false,
+ print_config: false,
+ }
+ }
+}
+
+impl LoggerConfig {
+ pub fn from_spec(spec: &str) -> Self {
+ let Self {
+ mut stdout_level,
+ mut fileout_level,
+ mut component_level,
+ mut is_colored,
+ mut is_bypassed,
+ mut print_config,
+ } = Self::default();
+ spec.split(';').for_each(|kv| {
+ if kv == "is_colored" {
+ is_colored = true;
+ } else if kv == "is_bypassed" {
+ is_bypassed = true;
+ } else if kv == "print_config" {
+ print_config = true;
+ } else {
+ let mut kv = kv.split('=');
+ if let (Some(k), Some(Ok(lvl))) = (kv.next(), kv.next().map(LevelFilter::from_str))
+ {
+ if k == "stdout" {
+ stdout_level = lvl;
+ } else if k == "fileout" {
+ fileout_level = lvl;
+ } else {
+ component_level.insert(Ustr::from(k), lvl);
+ }
+ }
+ }
+ });
+
+ Self {
+ stdout_level,
+ fileout_level,
+ component_level,
+ is_colored,
+ is_bypassed,
+ print_config,
+ }
+ }
+
+ pub fn from_env() -> Self {
+ match env::var("NAUTILUS_LOG") {
+ Ok(spec) => LoggerConfig::from_spec(&spec),
+ Err(e) => panic!("Error parsing `LoggerConfig` spec: {e}"),
+ }
+ }
+}
+
+#[cfg_attr(
+ feature = "python",
+ pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.common")
+)]
+#[derive(Debug, Clone, Default)]
+pub struct FileWriterConfig {
+ directory: Option,
+ file_name: Option,
+ file_format: Option,
+}
+
+impl FileWriterConfig {
+ pub fn new(
+ directory: Option,
+ file_name: Option,
+ file_format: Option,
+ ) -> Self {
+ Self {
+ directory,
+ file_name,
+ file_format,
+ }
+ }
+}
+
+/// Initialize tracing.
///
-/// It also configures a top level filter based on module/component name.
-/// The format for the string is component1=info,component2=debug.
-/// For e.g. network=error,kernel=info
+/// Tracing is meant to be used to trace/debug async Rust code. It can be
+/// configured to filter modules and write up to a specific level only using
+/// by passing a configuration using the `RUST_LOG` environment variable.
///
/// # Safety
+///
/// Should only be called once during an applications run, ideally at the
/// beginning of the run.
-#[pyfunction]
-#[must_use]
-pub fn set_global_log_collector(
- stdout_level: Option,
- stderr_level: Option,
- file_level: Option<(String, String, String)>,
-) -> LogGuard {
- let mut guards = Vec::new();
- let stdout_sub_builder = stdout_level.map(|stdout_level| {
- let stdout_level = Level::from_str(&stdout_level).unwrap();
- let (non_blocking, guard) = tracing_appender::non_blocking(std::io::stdout());
- guards.push(guard);
- Layer::default().with_writer(non_blocking.with_max_level(stdout_level))
- });
- let stderr_sub_builder = stderr_level.map(|stderr_level| {
- let stderr_level = Level::from_str(&stderr_level).unwrap();
- let (non_blocking, guard) = tracing_appender::non_blocking(std::io::stdout());
- guards.push(guard);
- Layer::default().with_writer(non_blocking.with_max_level(stderr_level))
- });
- let file_sub_builder = file_level.map(|(dir_path, file_prefix, file_level)| {
- let file_level = Level::from_str(&file_level).unwrap();
- let rolling_log = RollingFileAppender::new(Rotation::NEVER, dir_path, file_prefix);
- let (non_blocking, guard) = tracing_appender::non_blocking(rolling_log);
- guards.push(guard);
- Layer::default()
- .with_ansi(false) // turn off unicode colors when writing to file
- .with_writer(non_blocking.with_max_level(file_level))
- });
-
- if let Err(e) = Registry::default()
- .with(stderr_sub_builder)
- .with(stdout_sub_builder)
- .with(file_sub_builder)
- .with(EnvFilter::from_default_env())
- .try_init()
- {
- println!("Failed to set global default dispatcher because of error: {e}");
- };
+pub fn init_tracing() {
+ // Skip tracing initialization if `RUST_LOG` is not set
+ if let Ok(v) = env::var("RUST_LOG") {
+ tracing_subscriber::fmt()
+ .with_env_filter(EnvFilter::new(v.clone()))
+ .try_init()
+ .unwrap_or_else(|e| eprintln!("Cannot set tracing subscriber because of error: {e}"));
+ println!("Initialized tracing logs with RUST_LOG={v}");
+ }
+}
- LogGuard { guards }
+/// Initialize logging.
+///
+/// Logging should be used for Python and sync Rust logic which is most of
+/// the components in the main `nautilus_trader` package.
+/// Logging can be configured to filter components and write up to a specific level only
+/// by passing a configuration using the `NAUTILUS_LOG` environment variable.
+///
+/// # Safety
+///
+/// Should only be called once during an applications run, ideally at the
+/// beginning of the run.
+pub fn init_logging(
+ trader_id: TraderId,
+ instance_id: UUID4,
+ config_spec: String,
+ directory: Option,
+ file_name: Option,
+ file_format: Option,
+) {
+ let config = LoggerConfig::from_spec(&config_spec);
+ let file_writer_config = FileWriterConfig::new(directory, file_name, file_format);
+
+ Logger::init_with_config(trader_id, instance_id, file_writer_config, config);
}
/// Provides a high-performance logger utilizing a MPSC channel under the hood.
///
/// A separate thead is spawned at initialization which receives [`LogEvent`] structs over the
/// channel.
+#[derive(Debug)]
pub struct Logger {
+ /// Send log events to a different thread.
tx: Sender,
- /// The trader ID for the logger.
- pub trader_id: TraderId,
- /// The machine ID for the logger.
- pub machine_id: String,
- /// The instance ID for the logger.
- pub instance_id: UUID4,
- /// The minimum log level to write to stdout.
- pub level_stdout: LogLevel,
- /// The minimum log level to write to a log file.
- pub level_file: Option,
- /// If logger is using ANSI color codes.
- pub is_colored: bool,
- /// If logging is bypassed.
- pub is_bypassed: bool,
+ /// Configure maximum levels for components and IO.
+ pub config: LoggerConfig,
+}
+
+/// Represents a type of log event.
+pub enum LogEvent {
+ /// A log line event.
+ Log(LogLine),
+ /// A command to flush all logger buffers.
+ Flush,
}
/// Represents a log event which includes a message.
#[derive(Clone, Debug, Serialize, Deserialize)]
-pub struct LogEvent {
+pub struct LogLine {
/// The UNIX nanoseconds timestamp when the log event occurred.
timestamp: UnixNanos,
/// The log level for the event.
- level: LogLevel,
+ level: Level,
/// The color for the log message content.
color: LogColor,
/// The Nautilus system component the log event originated from.
- component: String,
+ component: Ustr,
/// The log message content.
message: String,
}
-impl fmt::Display for LogEvent {
+impl fmt::Display for LogLine {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
@@ -153,88 +238,137 @@ impl fmt::Display for LogEvent {
}
}
+impl Log for Logger {
+ fn enabled(&self, metadata: &log::Metadata) -> bool {
+ !self.config.is_bypassed
+ && (metadata.level() == Level::Error
+ || metadata.level() >= self.config.stdout_level
+ || metadata.level() >= self.config.fileout_level)
+ }
+
+ fn log(&self, record: &log::Record) {
+ // TODO remove unwraps
+ if self.enabled(record.metadata()) {
+ let key_values = record.key_values();
+ let timestamp = key_values
+ .get(Key::from_str("timestamp"))
+ .and_then(|v| v.to_u64())
+ .expect("No timestamp included in log `Record`");
+ let color = key_values
+ .get("color".into())
+ .and_then(|v| v.to_u64().map(|v| (v as u8).into()))
+ .unwrap_or(LogColor::Normal);
+ let component = key_values
+ .get("component".into())
+ .map(|v| Ustr::from(&v.to_string()))
+ .unwrap_or_else(|| Ustr::from(record.metadata().target()));
+
+ let line = LogLine {
+ timestamp,
+ level: record.level(),
+ color,
+ component,
+ message: format!("{}", record.args()).to_string(),
+ };
+ if let Err(SendError(LogEvent::Log(line))) = self.tx.send(LogEvent::Log(line)) {
+ eprintln!("Error sending log event: {line}");
+ }
+ }
+ }
+
+ fn flush(&self) {
+ self.tx.send(LogEvent::Flush).unwrap();
+ }
+}
+
#[allow(clippy::too_many_arguments)]
impl Logger {
- #[must_use]
- pub fn new(
+ pub fn init_with_env(
trader_id: TraderId,
- machine_id: String,
instance_id: UUID4,
- level_stdout: LogLevel,
- level_file: Option,
- directory: Option,
- file_name: Option,
- file_format: Option,
- component_levels: Option>,
- is_colored: bool,
- is_bypassed: bool,
- ) -> Self {
- let (tx, rx) = channel::();
- let mut level_filters = HashMap::::new();
+ file_writer_config: FileWriterConfig,
+ ) {
+ let config = LoggerConfig::from_env();
+ Logger::init_with_config(trader_id, instance_id, file_writer_config, config);
+ }
- if let Some(component_levels_map) = component_levels {
- for (key, value) in component_levels_map {
- match serde_json::from_value::(value) {
- Ok(level) => {
- level_filters.insert(key, level);
- }
- Err(e) => {
- // Handle the error, e.g. log a warning or ignore the entry
- eprintln!("Error parsing log level: {e:?}");
- }
- }
- }
- }
+ pub fn init_with_config(
+ trader_id: TraderId,
+ instance_id: UUID4,
+ file_writer_config: FileWriterConfig,
+ config: LoggerConfig,
+ ) {
+ let (tx, rx) = channel::();
let trader_id_clone = trader_id.value.to_string();
let instance_id_clone = instance_id.to_string();
- thread::spawn(move || {
- Self::handle_messages(
- &trader_id_clone,
- &instance_id_clone,
- level_stdout,
- level_file,
- directory,
- file_name,
- file_format,
- level_filters,
- is_colored,
- rx,
- );
- });
-
- Self {
+ let logger = Self {
tx,
- trader_id,
- machine_id,
- instance_id,
- level_stdout,
- level_file,
- is_colored,
- is_bypassed,
+ config: config.clone(),
+ };
+
+ let print_config = config.print_config;
+ if print_config {
+ println!("STATIC_MAX_LEVEL={STATIC_MAX_LEVEL}");
+ println!("Logger initialized with {:?}", config);
+ }
+
+ match set_boxed_logger(Box::new(logger)) {
+ Ok(_) => {
+ thread::spawn(move || {
+ Self::handle_messages(
+ &trader_id_clone,
+ &instance_id_clone,
+ file_writer_config,
+ config,
+ rx,
+ );
+ });
+
+ let max_level = log::LevelFilter::Debug;
+ set_max_level(max_level);
+ if print_config {
+ println!("Logger set as `log` implementation with max level {max_level}");
+ }
+ }
+ Err(e) => {
+ eprintln!("Cannot set logger because of error: {e}")
+ }
}
}
- #[allow(clippy::useless_format)] // Format is not actually useless as we escape braces
+ #[allow(unused_variables)] // `is_bypassed` is unused
fn handle_messages(
trader_id: &str,
instance_id: &str,
- level_stdout: LogLevel,
- level_file: Option,
- directory: Option,
- file_name: Option,
- file_format: Option,
- level_filters: HashMap,
- is_colored: bool,
+ file_writer_config: FileWriterConfig,
+ config: LoggerConfig,
rx: Receiver,
) {
+ if config.print_config {
+ println!("Logger thread `handle_messages` initialized")
+ }
+
+ let LoggerConfig {
+ stdout_level,
+ fileout_level,
+ component_level,
+ is_colored,
+ is_bypassed,
+ print_config: _,
+ } = config;
+
// Setup std I/O buffers
let mut out_buf = BufWriter::new(io::stdout());
let mut err_buf = BufWriter::new(io::stderr());
// Setup log file
- let is_json_format = match file_format.as_ref().map(|s| s.to_lowercase()) {
+ let is_json_format = match file_writer_config
+ .file_format
+ .as_ref()
+ .map(|s| s.to_lowercase())
+ {
Some(ref format) if format == "json" => true,
None => false,
Some(ref unrecognized) => {
@@ -246,10 +380,9 @@ impl Logger {
};
let file_path = PathBuf::new();
- let file = if level_file.is_some() {
+ let file = if fileout_level > LevelFilter::Off {
let file_path = Self::create_log_file_path(
- &directory,
- &file_name,
+ &file_writer_config,
trader_id,
instance_id,
is_json_format,
@@ -268,69 +401,65 @@ impl Logger {
let mut file_buf = file.map(BufWriter::new);
- // Setup templates for formatting
- let template_console = match is_colored {
- true => format!("\x1b[1m{{ts}}\x1b[0m {{color}}[{{level}}] {{trader_id}}.{{component}}: {{message}}\x1b[0m\n"),
- false => format!("{{ts}} [{{level}}] {{trader_id}}.{{component}}: {{message}}\n")
- };
-
- let template_file = String::from("{ts} [{level}] {trader_id}.{component}: {message}\n");
-
// Continue to receive and handle log events until channel is hung up
while let Ok(event) = rx.recv() {
- let component_level = level_filters.get(&event.component);
-
- // Check if the component exists in level_filters and if its level is greater than event.level
- if let Some(&filter_level) = component_level {
- if event.level < filter_level {
- continue;
+ match event {
+ LogEvent::Flush => {
+ Self::flush_stderr(&mut err_buf);
+ Self::flush_stdout(&mut out_buf);
+ file_buf.as_mut().map(Self::flush_file);
}
- }
-
- if event.level >= LogLevel::Error {
- let line = Self::format_log_line_console(&event, trader_id, &template_console);
- Self::write_stderr(&mut err_buf, &line);
- Self::flush_stderr(&mut err_buf);
- } else if event.level >= level_stdout {
- let line = Self::format_log_line_console(&event, trader_id, &template_console);
- Self::write_stdout(&mut out_buf, &line);
- Self::flush_stdout(&mut out_buf);
- }
-
- if let Some(level_file) = level_file {
- if Self::should_rotate_file(&file_path) {
- // Ensure previous file buffer flushed
- if let Some(file_buf) = file_buf.as_mut() {
- Self::flush_file(file_buf);
- };
-
- let file_path = Self::create_log_file_path(
- &directory,
- &file_name,
- trader_id,
- instance_id,
- is_json_format,
- );
-
- let file = File::options()
- .create(true)
- .append(true)
- .open(file_path)
- .expect("Error creating log file");
+ LogEvent::Log(line) => {
+ let component_level = component_level.get(&line.component);
+
+ // Check if the component exists in level_filters,
+ // and if its level is greater than event.level.
+ if let Some(&filter_level) = component_level {
+ if line.level > filter_level {
+ continue;
+ }
+ }
- file_buf = Some(BufWriter::new(file));
- }
+ if line.level == LevelFilter::Error {
+ let line = Self::format_console_log(&line, trader_id, is_colored);
+ Self::write_stderr(&mut err_buf, &line);
+ Self::flush_stderr(&mut err_buf);
+ } else if line.level <= stdout_level {
+ let line = Self::format_console_log(&line, trader_id, is_colored);
+ Self::write_stdout(&mut out_buf, &line);
+ Self::flush_stdout(&mut out_buf);
+ }
- if event.level >= level_file {
- if let Some(file_buf) = file_buf.as_mut() {
- let line = Self::format_log_line_file(
- &event,
- trader_id,
- &template_file,
- is_json_format,
- );
- Self::write_file(file_buf, &line);
- Self::flush_file(file_buf);
+ if fileout_level != LevelFilter::Off {
+ if Self::should_rotate_file(&file_path) {
+ // Ensure previous file buffer flushed
+ if let Some(file_buf) = file_buf.as_mut() {
+ Self::flush_file(file_buf);
+ };
+
+ let file_path = Self::create_log_file_path(
+ &file_writer_config,
+ trader_id,
+ instance_id,
+ is_json_format,
+ );
+
+ let file = File::options()
+ .create(true)
+ .append(true)
+ .open(file_path)
+ .expect("Error creating log file");
+
+ file_buf = Some(BufWriter::new(file));
+ }
+
+ if line.level <= fileout_level {
+ if let Some(file_buf) = file_buf.as_mut() {
+ let line = Self::format_file_log(&line, trader_id, is_json_format);
+ Self::write_file(file_buf, &line);
+ Self::flush_file(file_buf);
+ }
+ }
}
}
}
@@ -366,13 +495,12 @@ impl Logger {
}
fn create_log_file_path(
- directory: &Option,
- file_name: &Option,
+ file_writer_config: &FileWriterConfig,
trader_id: &str,
instance_id: &str,
is_json_format: bool,
) -> PathBuf {
- let basename = if let Some(file_name) = file_name {
+ let basename = if let Some(file_name) = file_writer_config.file_name.as_ref() {
file_name.clone()
} else {
Self::default_log_file_basename(trader_id, instance_id)
@@ -381,7 +509,7 @@ impl Logger {
let suffix = if is_json_format { "json" } else { "log" };
let mut file_path = PathBuf::new();
- if let Some(directory) = directory {
+ if let Some(directory) = file_writer_config.directory.as_ref() {
file_path.push(directory);
create_dir_all(&file_path).expect("Failed to create directories for log file");
}
@@ -391,33 +519,42 @@ impl Logger {
file_path
}
- fn format_log_line_console(event: &LogEvent, trader_id: &str, template: &str) -> String {
- template
- .replace("{ts}", &unix_nanos_to_iso8601(event.timestamp))
- .replace("{color}", &event.color.to_string())
- .replace("{level}", &event.level.to_string())
- .replace("{trader_id}", trader_id)
- .replace("{component}", &event.component)
- .replace("{message}", &event.message)
+ fn format_console_log(event: &LogLine, trader_id: &str, is_colored: bool) -> String {
+ match is_colored {
+ true => format!(
+ "\x1b[1m{}\x1b[0m {}[{}] {}.{}: {}\x1b[0m\n",
+ unix_nanos_to_iso8601(event.timestamp),
+ &event.color.to_string(),
+ event.level,
+ &trader_id,
+ &event.component,
+ &event.message
+ ),
+ false => format!(
+ "{} [{}] {}.{}: {}\n",
+ unix_nanos_to_iso8601(event.timestamp),
+ event.level,
+ &trader_id,
+ &event.component,
+ &event.message
+ ),
+ }
}
- fn format_log_line_file(
- event: &LogEvent,
- trader_id: &str,
- template: &str,
- is_json_format: bool,
- ) -> String {
+ fn format_file_log(event: &LogLine, trader_id: &str, is_json_format: bool) -> String {
if is_json_format {
let json_string =
serde_json::to_string(event).expect("Error serializing log event to string");
format!("{json_string}\n")
} else {
- template
- .replace("{ts}", &unix_nanos_to_iso8601(event.timestamp))
- .replace("{level}", &event.level.to_string())
- .replace("{trader_id}", trader_id)
- .replace("{component}", &event.component)
- .replace("{message}", &event.message)
+ format!(
+ "{} [{}] {}.{}: {}\n",
+ &unix_nanos_to_iso8601(event.timestamp),
+ event.level,
+ trader_id,
+ &event.component,
+ &event.message,
+ )
}
}
@@ -462,80 +599,30 @@ impl Logger {
Err(e) => eprintln!("Error writing to file: {e:?}"),
}
}
-
- pub fn send(
- &mut self,
- timestamp: u64,
- level: LogLevel,
- color: LogColor,
- component: String,
- message: String,
- ) {
- let event = LogEvent {
- timestamp,
- level,
- color,
- component,
- message,
- };
- if let Err(SendError(e)) = self.tx.send(event) {
- eprintln!("Error sending log event: {e}");
- }
- }
-
- pub fn debug(&mut self, timestamp: u64, color: LogColor, component: String, message: String) {
- self.send(timestamp, LogLevel::Debug, color, component, message);
- }
-
- pub fn info(&mut self, timestamp: u64, color: LogColor, component: String, message: String) {
- self.send(timestamp, LogLevel::Info, color, component, message);
- }
-
- pub fn warn(&mut self, timestamp: u64, color: LogColor, component: String, message: String) {
- self.send(timestamp, LogLevel::Warning, color, component, message);
- }
-
- pub fn error(&mut self, timestamp: u64, color: LogColor, component: String, message: String) {
- self.send(timestamp, LogLevel::Error, color, component, message);
- }
-
- pub fn critical(
- &mut self,
- timestamp: u64,
- color: LogColor,
- component: String,
- message: String,
- ) {
- self.send(timestamp, LogLevel::Critical, color, component, message);
- }
}
-////////////////////////////////////////////////////////////////////////////////
-// Stubs
-////////////////////////////////////////////////////////////////////////////////
-#[cfg(test)]
-pub mod stubs {
- use nautilus_core::uuid::UUID4;
- use nautilus_model::identifiers::trader_id::TraderId;
- use rstest::fixture;
-
- use crate::{enums::LogLevel, logging::Logger};
-
- #[fixture]
- pub fn logger() -> Logger {
- Logger::new(
- TraderId::from("TRADER-001"),
- String::from("user-01"),
- UUID4::new(),
- LogLevel::Info,
- None,
- None,
- None,
- None,
- None,
- true,
- false,
- )
+pub fn log(
+ timestamp_ns: UnixNanos,
+ level: LogLevel,
+ color: LogColor,
+ component: Ustr,
+ message: Cow<'_, str>,
+) {
+ let color = Value::from(color as u8);
+
+ match level {
+ LogLevel::Debug => {
+ debug!(timestamp = timestamp_ns, component = component.to_value(), color = color; "{}", message);
+ }
+ LogLevel::Info => {
+ info!(timestamp = timestamp_ns, component = component.to_value(), color = color; "{}", message);
+ }
+ LogLevel::Warning => {
+ warn!(timestamp = timestamp_ns, component = component.to_value(), color = color; "{}", message);
+ }
+ LogLevel::Error => {
+ error!(timestamp = timestamp_ns, component = component.to_value(), color = color; "{}", message);
+ }
}
}
@@ -544,23 +631,30 @@ pub mod stubs {
////////////////////////////////////////////////////////////////////////////////
#[cfg(test)]
mod tests {
- use std::time::Duration;
+ use std::{collections::HashMap, time::Duration};
+ use log::{info, kv::ToValue, LevelFilter};
use nautilus_core::uuid::UUID4;
use nautilus_model::identifiers::trader_id::TraderId;
use rstest::*;
+ use serde_json::Value;
use tempfile::tempdir;
+ use ustr::Ustr;
- use super::{stubs::*, *};
- use crate::testing::wait_until;
+ use super::FileWriterConfig;
+ use crate::{
+ enums::LogColor,
+ logging::{LogLine, Logger, LoggerConfig},
+ testing::wait_until,
+ };
#[rstest]
fn log_message_serialization() {
- let log_message = LogEvent {
+ let log_message = LogLine {
timestamp: 1_000_000_000,
- level: LogLevel::Info,
+ level: log::Level::Info,
color: LogColor::Normal,
- component: "Portfolio".to_string(),
+ component: Ustr::from("Portfolio"),
message: "This is a log message".to_string(),
};
@@ -574,76 +668,49 @@ mod tests {
}
#[rstest]
- fn test_new_logger(logger: Logger) {
- assert_eq!(logger.trader_id, TraderId::from("TRADER-001"));
- assert_eq!(logger.level_stdout, LogLevel::Info);
- assert_eq!(logger.level_file, None);
- assert!(!logger.is_bypassed);
- }
-
- #[rstest]
- fn test_logger_debug(mut logger: Logger) {
- logger.debug(
- 1_650_000_000_000_000,
- LogColor::Normal,
- String::from("RiskEngine"),
- String::from("This is a test debug message."),
- );
- }
-
- #[rstest]
- fn test_logger_info(mut logger: Logger) {
- logger.info(
- 1_650_000_000_000_000,
- LogColor::Normal,
- String::from("RiskEngine"),
- String::from("This is a test info message."),
- );
- }
-
- #[rstest]
- fn test_logger_error(mut logger: Logger) {
- logger.error(
- 1_650_000_000_000_000,
- LogColor::Normal,
- String::from("RiskEngine"),
- String::from("This is a test error message."),
- );
- }
-
- #[rstest]
- fn test_logger_critical(mut logger: Logger) {
- logger.critical(
- 1_650_000_000_000_000,
- LogColor::Normal,
- String::from("RiskEngine"),
- String::from("This is a test critical message."),
- );
+ fn log_config_parsing() {
+ let config =
+ LoggerConfig::from_spec("stdout=Info;is_colored;fileout=Debug;RiskEngine=Error");
+ assert_eq!(
+ config,
+ LoggerConfig {
+ stdout_level: LevelFilter::Info,
+ fileout_level: LevelFilter::Debug,
+ component_level: HashMap::from_iter(vec![(
+ Ustr::from("RiskEngine"),
+ LevelFilter::Error
+ )]),
+ is_colored: true,
+ is_bypassed: false,
+ print_config: false,
+ }
+ )
}
#[rstest]
fn test_logging_to_file() {
+ let config = LoggerConfig {
+ fileout_level: LevelFilter::Debug,
+ ..Default::default()
+ };
+
let temp_dir = tempdir().expect("Failed to create temporary directory");
+ let file_writer_config = FileWriterConfig {
+ directory: Some(temp_dir.path().to_str().unwrap().to_string()),
+ ..Default::default()
+ };
- let mut logger = Logger::new(
+ Logger::init_with_config(
TraderId::from("TRADER-001"),
- String::from("user-01"),
UUID4::new(),
- LogLevel::Info,
- Some(LogLevel::Debug),
- Some(temp_dir.path().to_str().unwrap().to_string()),
- None,
- None,
- None,
- true,
- false,
+ file_writer_config,
+ config,
);
- logger.info(
- 1_650_000_000_000_000,
- LogColor::Normal,
- String::from("RiskEngine"),
- String::from("This is a test."),
+ info!(
+ timestamp = 1_650_000_000_000_000i64.to_value(),
+ component = "RiskEngine";
+ "This is a test."
);
let mut log_contents = String::new();
@@ -666,6 +733,7 @@ mod tests {
.find(|entry| entry.path().is_file())
.expect("No files found in directory")
.path();
+ dbg!(&log_file_path);
log_contents =
std::fs::read_to_string(log_file_path).expect("Error while reading log file");
!log_contents.is_empty()
@@ -675,36 +743,31 @@ mod tests {
assert_eq!(
log_contents,
- "1970-01-20T02:20:00.000000000Z [INF] TRADER-001.RiskEngine: This is a test.\n"
+ "1970-01-20T02:20:00.000000000Z [INFO] TRADER-001.RiskEngine: This is a test.\n"
);
}
#[rstest]
fn test_log_component_level_filtering() {
+ let config = LoggerConfig::from_spec("stdout=Info;fileout=Debug;RiskEngine=Error");
+
let temp_dir = tempdir().expect("Failed to create temporary directory");
+ let file_writer_config = FileWriterConfig {
+ directory: Some(temp_dir.path().to_str().unwrap().to_string()),
+ ..Default::default()
+ };
- let mut logger = Logger::new(
+ Logger::init_with_config(
TraderId::from("TRADER-001"),
- String::from("user-01"),
UUID4::new(),
- LogLevel::Info,
- Some(LogLevel::Debug),
- Some(temp_dir.path().to_str().unwrap().to_string()),
- None,
- None,
- Some(HashMap::from_iter(std::iter::once((
- String::from("RiskEngine"),
- Value::from("ERROR"), // <-- This should be filtered
- )))),
- true,
- false,
+ file_writer_config,
+ config,
);
- logger.info(
- 1_650_000_000_000_000,
- LogColor::Normal,
- String::from("RiskEngine"),
- String::from("This is a test."),
+ info!(
+ timestamp = 1_650_000_000_000_000i64.to_value(),
+ component = "RiskEngine";
+ "This is a test."
);
wait_until(
@@ -736,27 +799,27 @@ mod tests {
#[rstest]
fn test_logging_to_file_in_json_format() {
+ let config =
+ LoggerConfig::from_spec("stdout=Info;is_colored;fileout=Debug;RiskEngine=Info");
+
let temp_dir = tempdir().expect("Failed to create temporary directory");
+ let file_writer_config = FileWriterConfig {
+ directory: Some(temp_dir.path().to_str().unwrap().to_string()),
+ file_format: Some("json".to_string()),
+ ..Default::default()
+ };
- let mut logger = Logger::new(
+ Logger::init_with_config(
TraderId::from("TRADER-001"),
- String::from("user-01"),
UUID4::new(),
- LogLevel::Info,
- Some(LogLevel::Debug),
- Some(temp_dir.path().to_str().unwrap().to_string()),
- None,
- Some("json".to_string()),
- None,
- true,
- false,
+ file_writer_config,
+ config,
);
- logger.info(
- 1_650_000_000_000_000,
- LogColor::Normal,
- String::from("RiskEngine"),
- String::from("This is a test."),
+ info!(
+ timestamp = 1_650_000_000_000_000i64.to_value(),
+ component = "RiskEngine";
+ "This is a test."
);
let mut log_contents = String::new();
diff --git a/nautilus_core/common/src/msgbus.rs b/nautilus_core/common/src/msgbus.rs
index dbf470bd113a..e1d4490fe060 100644
--- a/nautilus_core/common/src/msgbus.rs
+++ b/nautilus_core/common/src/msgbus.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/common/src/python/logging.rs b/nautilus_core/common/src/python/logging.rs
new file mode 100644
index 000000000000..43dd08ee10f8
--- /dev/null
+++ b/nautilus_core/common/src/python/logging.rs
@@ -0,0 +1,113 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// https://nautechsystems.io
+//
+// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
+// You may not use this file except in compliance with the License.
+// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// -------------------------------------------------------------------------------------------------
+
+use std::borrow::Cow;
+
+use nautilus_core::{time::UnixNanos, uuid::UUID4};
+use nautilus_model::identifiers::trader_id::TraderId;
+use pyo3::prelude::*;
+use ustr::Ustr;
+
+use crate::{
+ enums::{LogColor, LogLevel},
+ logging::{self, FileWriterConfig, LoggerConfig},
+};
+
+/// Initialize tracing.
+///
+/// Tracing is meant to be used to trace/debug async Rust code. It can be
+/// configured to filter modules and write up to a specific level only using
+/// by passing a configuration using the `RUST_LOG` environment variable.
+///
+/// # Safety
+///
+/// Should only be called once during an applications run, ideally at the
+/// beginning of the run.
+#[pyfunction()]
+#[pyo3(name = "init_tracing")]
+pub fn py_init_tracing() {
+ logging::init_tracing();
+}
+
+/// Initialize logging.
+///
+/// Logging should be used for Python and sync Rust logic which is most of
+/// the components in the main `nautilus_trader` package.
+/// Logging can be configured to filter components and write up to a specific level only
+/// by passing a configuration using the `NAUTILUS_LOG` environment variable.
+///
+/// # Safety
+///
+/// Should only be called once during an applications run, ideally at the
+/// beginning of the run.
+#[pyfunction]
+#[pyo3(name = "init_logging")]
+pub fn py_init_logging(
+ trader_id: TraderId,
+ instance_id: UUID4,
+ config_spec: String,
+ directory: Option,
+ file_name: Option,
+ file_format: Option,
+) {
+ logging::init_logging(
+ trader_id,
+ instance_id,
+ config_spec,
+ directory,
+ file_name,
+ file_format,
+ );
+}
+
+/// Create a new log event.
+#[pyfunction]
+#[pyo3(name = "logger_log")]
+pub fn py_logger_log(
+ timestamp_ns: UnixNanos,
+ level: LogLevel,
+ color: LogColor,
+ component: String,
+ message: String,
+) {
+ logging::log(
+ timestamp_ns,
+ level,
+ color,
+ Ustr::from(&component),
+ Cow::from(message),
+ );
+}
+
+#[pymethods]
+impl FileWriterConfig {
+ #[new]
+ pub fn py_new(
+ directory: Option,
+ file_name: Option,
+ file_format: Option,
+ ) -> Self {
+ Self::new(directory, file_name, file_format)
+ }
+}
+
+#[pymethods]
+impl LoggerConfig {
+ #[staticmethod]
+ #[pyo3(name = "from_spec")]
+ pub fn py_from_spec(spec: String) -> Self {
+ LoggerConfig::from_spec(&spec)
+ }
+}
diff --git a/nautilus_core/common/src/python/mod.rs b/nautilus_core/common/src/python/mod.rs
index d1b2465cea1c..db92ea24ce42 100644
--- a/nautilus_core/common/src/python/mod.rs
+++ b/nautilus_core/common/src/python/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -13,12 +13,15 @@
// limitations under the License.
// -------------------------------------------------------------------------------------------------
-pub mod clock;
+pub mod logging;
pub mod timer;
use pyo3::prelude::*;
-use crate::{enums, logging};
+use crate::{
+ enums,
+ logging::{FileWriterConfig, LoggerConfig},
+};
/// Loaded as nautilus_pyo3.common
#[pymodule]
@@ -28,8 +31,11 @@ pub fn common(_: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_class::()?;
m.add_class::()?;
m.add_class::()?;
- m.add_class::()?;
- m.add_function(wrap_pyfunction!(logging::set_global_log_collector, m)?)?;
+ m.add_class::()?;
+ m.add_class::()?;
+ m.add_function(wrap_pyfunction!(logging::py_init_tracing, m)?)?;
+ m.add_function(wrap_pyfunction!(logging::py_init_logging, m)?)?;
+ m.add_function(wrap_pyfunction!(logging::py_logger_log, m)?)?;
Ok(())
}
diff --git a/nautilus_core/common/src/python/timer.rs b/nautilus_core/common/src/python/timer.rs
index bcca6e6c0a12..735c33ca68d2 100644
--- a/nautilus_core/common/src/python/timer.rs
+++ b/nautilus_core/common/src/python/timer.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -35,7 +35,7 @@ impl TimeEvent {
ts_event: UnixNanos,
ts_init: UnixNanos,
) -> PyResult {
- Self::new(name, event_id, ts_event, ts_init).map_err(to_pyvalue_err)
+ Self::new(Ustr::from(name), event_id, ts_event, ts_init).map_err(to_pyvalue_err)
}
fn __setstate__(&mut self, py: Python, state: PyObject) -> PyResult<()> {
@@ -67,7 +67,7 @@ impl TimeEvent {
#[staticmethod]
fn _safe_constructor() -> PyResult {
- Ok(Self::new("NULL", UUID4::new(), 0, 0).unwrap()) // Safe default
+ Ok(Self::new(Ustr::from("NULL"), UUID4::new(), 0, 0).unwrap()) // Safe default
}
fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py {
@@ -83,7 +83,7 @@ impl TimeEvent {
}
fn __repr__(&self) -> String {
- format!("{}('{}')", stringify!(UUID4), self)
+ format!("{}('{}')", stringify!(TimeEvent), self)
}
#[getter]
diff --git a/nautilus_core/common/src/redis.rs b/nautilus_core/common/src/redis.rs
index 4f00e162428d..e49ebacfbb56 100644
--- a/nautilus_core/common/src/redis.rs
+++ b/nautilus_core/common/src/redis.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -182,21 +182,28 @@ fn get_stream_name(
) -> String {
let mut stream_name = String::new();
- if let Some(Value::String(s)) = config.get("stream") {
- if !s.is_empty() {
- stream_name.push_str(s.trim_matches('"'));
- stream_name.push(DELIMITER);
- }
+ if let Some(json!(true)) = config.get("use_trader_prefix") {
+ stream_name.push_str("trader-");
}
- stream_name.push_str(trader_id.value.as_str());
- stream_name.push(DELIMITER);
+ if let Some(json!(true)) = config.get("use_trader_id") {
+ stream_name.push_str(trader_id.value.as_str());
+ stream_name.push(DELIMITER);
+ }
if let Some(json!(true)) = config.get("use_instance_id") {
stream_name.push_str(&format!("{instance_id}"));
stream_name.push(DELIMITER);
}
+ let stream_prefix = config
+ .get("streams_prefix")
+ .expect("Invalid configuration: no `streams_prefix` key found")
+ .as_str()
+ .expect("Invalid configuration: `streams_prefix` is not a string");
+ stream_name.push_str(stream_prefix);
+ stream_name.push(DELIMITER);
+
stream_name
}
@@ -210,17 +217,31 @@ mod tests {
use super::*;
#[rstest]
- fn test_get_stream_name_with_stream_prefix_and_instance_id() {
+ fn test_get_stream_name_with_trader_prefix_and_instance_id() {
let trader_id = TraderId::from("tester-123");
let instance_id = UUID4::new();
let mut config = HashMap::new();
- config.insert("stream".to_string(), json!("quoters"));
+ config.insert("use_trader_prefix".to_string(), json!(true));
+ config.insert("use_trader_id".to_string(), json!(true));
config.insert("use_instance_id".to_string(), json!(true));
+ config.insert("streams_prefix".to_string(), json!("streams"));
+
+ let key = get_stream_name(trader_id, instance_id, &config);
+ assert_eq!(key, format!("trader-tester-123:{instance_id}:streams:"));
+ }
+
+ #[rstest]
+ fn test_get_stream_name_without_trader_prefix_or_instance_id() {
+ let trader_id = TraderId::from("tester-123");
+ let instance_id = UUID4::new();
+ let mut config = HashMap::new();
+ config.insert("use_trader_prefix".to_string(), json!(false));
+ config.insert("use_trader_id".to_string(), json!(false));
+ config.insert("use_instance_id".to_string(), json!(false));
+ config.insert("streams_prefix".to_string(), json!("streams"));
let key = get_stream_name(trader_id, instance_id, &config);
- let expected_suffix = format!("{instance_id}:");
- assert!(key.starts_with("quoters:tester-123:"));
- assert!(key.ends_with(&expected_suffix));
+ assert_eq!(key, format!("streams:"));
}
#[rstest]
diff --git a/nautilus_core/common/src/stubs.rs b/nautilus_core/common/src/stubs.rs
index 0a97f7fe8d43..ddfa1dfa25d7 100644
--- a/nautilus_core/common/src/stubs.rs
+++ b/nautilus_core/common/src/stubs.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -13,7 +13,7 @@
// limitations under the License.
// -------------------------------------------------------------------------------------------------
-use nautilus_core::time::{AtomicTime, ClockMode};
+use nautilus_core::time::get_atomic_clock_static;
use nautilus_model::identifiers::stubs::*;
use rstest::fixture;
@@ -23,6 +23,11 @@ use crate::factories::OrderFactory;
pub fn order_factory() -> OrderFactory {
let trader_id = trader_id();
let strategy_id = strategy_id_ema_cross();
- let clock = AtomicTime::new(ClockMode::STATIC, 0);
- OrderFactory::new(trader_id, strategy_id, clock, None, None)
+ OrderFactory::new(
+ trader_id,
+ strategy_id,
+ None,
+ None,
+ get_atomic_clock_static(),
+ )
}
diff --git a/nautilus_core/common/src/testing.rs b/nautilus_core/common/src/testing.rs
index 739e1f32bc73..048a8c6834ad 100644
--- a/nautilus_core/common/src/testing.rs
+++ b/nautilus_core/common/src/testing.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/common/src/timer.rs b/nautilus_core/common/src/timer.rs
index c83cd1d65e9b..18bf5f42f3b6 100644
--- a/nautilus_core/common/src/timer.rs
+++ b/nautilus_core/common/src/timer.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -48,15 +48,13 @@ pub struct TimeEvent {
impl TimeEvent {
pub fn new(
- name: &str,
+ name: Ustr,
event_id: UUID4,
ts_event: UnixNanos,
ts_init: UnixNanos,
) -> Result {
- check_valid_string(name, "`TimeEvent` name")?;
-
Ok(Self {
- name: Ustr::from(name),
+ name,
event_id,
ts_event,
ts_init,
@@ -112,7 +110,7 @@ impl Ord for TimeEventHandler {
pub trait Timer {
fn new(
- name: String,
+ name: Ustr,
interval_ns: TimedeltaNanos,
start_time_ns: UnixNanos,
stop_time_ns: Option,
@@ -122,9 +120,9 @@ pub trait Timer {
fn cancel(&mut self);
}
-#[derive(Clone)]
+#[derive(Clone, Copy)]
pub struct TestTimer {
- pub name: String,
+ pub name: Ustr,
pub interval_ns: u64,
pub start_time_ns: UnixNanos,
pub stop_time_ns: Option,
@@ -135,15 +133,15 @@ pub struct TestTimer {
impl TestTimer {
#[must_use]
pub fn new(
- name: String,
+ name: &str,
interval_ns: u64,
start_time_ns: UnixNanos,
stop_time_ns: Option,
) -> Self {
- check_valid_string(&name, "`TestTimer` name").unwrap();
+ check_valid_string(name, "`TestTimer` name").unwrap();
Self {
- name,
+ name: Ustr::from(name),
interval_ns,
start_time_ns,
stop_time_ns,
@@ -186,7 +184,7 @@ impl Iterator for TestTimer {
} else {
let item = (
TimeEvent {
- name: Ustr::from(&self.name),
+ name: self.name,
event_id: UUID4::new(),
ts_event: self.next_time_ns,
ts_init: self.next_time_ns,
@@ -219,8 +217,7 @@ mod tests {
#[rstest]
fn test_pop_event() {
- let name = String::from("test_timer");
- let mut timer = TestTimer::new(name, 0, 1, None);
+ let mut timer = TestTimer::new("test_timer", 0, 1, None);
assert!(timer.next().is_some());
assert!(timer.next().is_some());
@@ -230,8 +227,7 @@ mod tests {
#[rstest]
fn test_advance_within_next_time_ns() {
- let name = String::from("test_timer");
- let mut timer = TestTimer::new(name, 5, 0, None);
+ let mut timer = TestTimer::new("test_timer", 5, 0, None);
let _: Vec = timer.advance(1).collect();
let _: Vec = timer.advance(2).collect();
let _: Vec = timer.advance(3).collect();
@@ -242,32 +238,28 @@ mod tests {
#[rstest]
fn test_advance_up_to_next_time_ns() {
- let name = String::from("test_timer");
- let mut timer = TestTimer::new(name, 1, 0, None);
+ let mut timer = TestTimer::new("test_timer", 1, 0, None);
assert_eq!(timer.advance(1).count(), 1);
assert!(!timer.is_expired);
}
#[rstest]
fn test_advance_up_to_next_time_ns_with_stop_time() {
- let name = String::from("test_timer");
- let mut timer = TestTimer::new(name, 1, 0, Some(2));
+ let mut timer = TestTimer::new("test_timer", 1, 0, Some(2));
assert_eq!(timer.advance(2).count(), 2);
assert!(timer.is_expired);
}
#[rstest]
fn test_advance_beyond_next_time_ns() {
- let name = String::from("test_timer");
- let mut timer = TestTimer::new(name, 1, 0, Some(5));
+ let mut timer = TestTimer::new("test_timer", 1, 0, Some(5));
assert_eq!(timer.advance(5).count(), 5);
assert!(timer.is_expired);
}
#[rstest]
fn test_advance_beyond_stop_time() {
- let name = String::from("test_timer");
- let mut timer = TestTimer::new(name, 1, 0, Some(5));
+ let mut timer = TestTimer::new("test_timer", 1, 0, Some(5));
assert_eq!(timer.advance(10).count(), 5);
assert!(timer.is_expired);
}
diff --git a/nautilus_core/core/build.rs b/nautilus_core/core/build.rs
index e796832f9680..fc97a8613cf2 100644
--- a/nautilus_core/core/build.rs
+++ b/nautilus_core/core/build.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/core/src/correctness.rs b/nautilus_core/core/src/correctness.rs
index 3a538e6ab5b2..4a33565efeb9 100644
--- a/nautilus_core/core/src/correctness.rs
+++ b/nautilus_core/core/src/correctness.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -208,7 +208,7 @@ mod tests {
#[case] r: u64,
#[case] desc: &str,
) {
- assert!(check_u64_in_range_inclusive(value, l, r, desc).is_err())
+ assert!(check_u64_in_range_inclusive(value, l, r, desc).is_err());
}
#[rstest]
diff --git a/nautilus_core/core/src/datetime.rs b/nautilus_core/core/src/datetime.rs
index bd2bc8a34826..7adab03de47b 100644
--- a/nautilus_core/core/src/datetime.rs
+++ b/nautilus_core/core/src/datetime.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -221,10 +221,10 @@ mod tests {
}
#[rstest]
- #[case(2023, 12, 15, 1702598400000000000)] // Fri
- #[case(2023, 12, 16, 1702598400000000000)] // Sat
- #[case(2023, 12, 17, 1702598400000000000)] // Sun
- #[case(2023, 12, 18, 1702857600000000000)] // Mon
+ #[case(2023, 12, 15, 1_702_598_400_000_000_000)] // Fri
+ #[case(2023, 12, 16, 1_702_598_400_000_000_000)] // Sat
+ #[case(2023, 12, 17, 1_702_598_400_000_000_000)] // Sun
+ #[case(2023, 12, 18, 1_702_857_600_000_000_000)] // Mon
fn test_last_closest_weekday_nanos_with_valid_date(
#[case] year: i32,
#[case] month: u32,
diff --git a/nautilus_core/core/src/ffi/cvec.rs b/nautilus_core/core/src/ffi/cvec.rs
index 40ea1ffefa01..15da67e155a3 100644
--- a/nautilus_core/core/src/ffi/cvec.rs
+++ b/nautilus_core/core/src/ffi/cvec.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -130,7 +130,7 @@ mod tests {
assert_eq!(len, vec_len);
assert_eq!(cap, vec_cap);
- let data = ptr as *mut u64;
+ let data = ptr.cast::();
unsafe {
assert_eq!(*data, test_data[0]);
assert_eq!(*data.add(1), test_data[1]);
@@ -139,7 +139,7 @@ mod tests {
unsafe {
// reconstruct the struct and drop the memory to deallocate
- let _ = Vec::from_raw_parts(ptr as *mut u64, len, cap);
+ let _ = Vec::from_raw_parts(ptr.cast::(), len, cap);
}
}
@@ -156,10 +156,10 @@ mod tests {
};
let CVec { ptr, len, cap } = cvec;
- let data = ptr as *mut u64;
+ let data = ptr.cast::();
unsafe {
- let data: Vec = Vec::from_raw_parts(ptr as *mut u64, len, cap);
+ let data: Vec = Vec::from_raw_parts(ptr.cast::(), len, cap);
drop(data);
}
@@ -175,6 +175,6 @@ mod tests {
fn empty_vec_should_give_null_ptr() {
let data: Vec = vec![];
let cvec: CVec = data.into();
- assert_eq!(cvec.ptr as *mut u64, null() as *const u64 as *mut u64);
+ assert_eq!(cvec.ptr.cast::(), null::().cast_mut());
}
}
diff --git a/nautilus_core/core/src/ffi/datetime.rs b/nautilus_core/core/src/ffi/datetime.rs
index 9aa69c4146b9..97e8ddd3cf9f 100644
--- a/nautilus_core/core/src/ffi/datetime.rs
+++ b/nautilus_core/core/src/ffi/datetime.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/core/src/ffi/mod.rs b/nautilus_core/core/src/ffi/mod.rs
index 5f6f51a41c06..aeb11cf7be64 100644
--- a/nautilus_core/core/src/ffi/mod.rs
+++ b/nautilus_core/core/src/ffi/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/core/src/ffi/parsing.rs b/nautilus_core/core/src/ffi/parsing.rs
index 8079d6c0c320..268a628dfb2b 100644
--- a/nautilus_core/core/src/ffi/parsing.rs
+++ b/nautilus_core/core/src/ffi/parsing.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -170,7 +170,7 @@ mod tests {
#[rstest]
fn test_optional_bytes_to_json_empty() {
let json_str = CString::new("{}").unwrap();
- let ptr = json_str.as_ptr() as *const c_char;
+ let ptr = json_str.as_ptr().cast::();
let result = unsafe { optional_bytes_to_json(ptr) };
assert_eq!(result, Some(HashMap::new()));
}
@@ -200,7 +200,7 @@ mod tests {
#[rstest]
fn test_bytes_to_string_vec_valid() {
let json_str = CString::new(r#"["value1", "value2", "value3"]"#).unwrap();
- let ptr = json_str.as_ptr() as *const c_char;
+ let ptr = json_str.as_ptr().cast::();
let result = unsafe { bytes_to_string_vec(ptr) };
let expected_vec = vec!["value1", "value2", "value3"]
@@ -214,7 +214,7 @@ mod tests {
#[rstest]
fn test_bytes_to_string_vec_invalid() {
let json_str = CString::new(r#"["value1", 42, "value3"]"#).unwrap();
- let ptr = json_str.as_ptr() as *const c_char;
+ let ptr = json_str.as_ptr().cast::();
let result = unsafe { bytes_to_string_vec(ptr) };
let expected_vec = vec!["value1", "value3"]
@@ -228,7 +228,7 @@ mod tests {
#[rstest]
fn test_optional_bytes_to_json_valid() {
let json_str = CString::new(r#"{"key1": "value1", "key2": 2}"#).unwrap();
- let ptr = json_str.as_ptr() as *const c_char;
+ let ptr = json_str.as_ptr().cast::();
let result = unsafe { optional_bytes_to_json(ptr) };
let mut expected_map = HashMap::new();
expected_map.insert("key1".to_owned(), Value::String("value1".to_owned()));
@@ -242,7 +242,7 @@ mod tests {
#[rstest]
fn test_optional_bytes_to_json_invalid() {
let json_str = CString::new(r#"{"key1": "value1", "key2": }"#).unwrap();
- let ptr = json_str.as_ptr() as *const c_char;
+ let ptr = json_str.as_ptr().cast::();
let result = unsafe { optional_bytes_to_json(ptr) };
assert_eq!(result, None);
}
diff --git a/nautilus_core/core/src/ffi/string.rs b/nautilus_core/core/src/ffi/string.rs
index 2b971b5c1997..390447ad2995 100644
--- a/nautilus_core/core/src/ffi/string.rs
+++ b/nautilus_core/core/src/ffi/string.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/core/src/ffi/uuid.rs b/nautilus_core/core/src/ffi/uuid.rs
index f3b72427268c..643a4051fccc 100644
--- a/nautilus_core/core/src/ffi/uuid.rs
+++ b/nautilus_core/core/src/ffi/uuid.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/core/src/lib.rs b/nautilus_core/core/src/lib.rs
index d7b429e41361..ed7b6f6e3c33 100644
--- a/nautilus_core/core/src/lib.rs
+++ b/nautilus_core/core/src/lib.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/core/src/message.rs b/nautilus_core/core/src/message.rs
index 1fbf3a85017f..724339d44cfc 100644
--- a/nautilus_core/core/src/message.rs
+++ b/nautilus_core/core/src/message.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/core/src/parsing.rs b/nautilus_core/core/src/parsing.rs
index f024b6a28ad8..d571c75a2e78 100644
--- a/nautilus_core/core/src/parsing.rs
+++ b/nautilus_core/core/src/parsing.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -94,7 +94,7 @@ mod tests {
fn test_bytes_to_usize_valid() {
let payload: Vec = vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
let result = bytes_to_usize(&payload).unwrap();
- assert_eq!(result, 0x0807060504030201);
- assert_eq!(result, 578437695752307201);
+ assert_eq!(result, 0x0807_0605_0403_0201);
+ assert_eq!(result, 578_437_695_752_307_201);
}
}
diff --git a/nautilus_core/core/src/python/casing.rs b/nautilus_core/core/src/python/casing.rs
index 77c10ab675e7..99e47bf0cbc0 100644
--- a/nautilus_core/core/src/python/casing.rs
+++ b/nautilus_core/core/src/python/casing.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/core/src/python/datetime.rs b/nautilus_core/core/src/python/datetime.rs
index cc459aec0b41..a336995beed7 100644
--- a/nautilus_core/core/src/python/datetime.rs
+++ b/nautilus_core/core/src/python/datetime.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/core/src/python/mod.rs b/nautilus_core/core/src/python/mod.rs
index 2ac1e7c628d7..c0269dc044c1 100644
--- a/nautilus_core/core/src/python/mod.rs
+++ b/nautilus_core/core/src/python/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -20,6 +20,8 @@ use pyo3::{
prelude::*,
wrap_pyfunction,
};
+
+use crate::uuid::UUID4;
pub mod casing;
pub mod datetime;
pub mod serialization;
@@ -48,7 +50,7 @@ pub fn to_pyruntime_err(e: impl fmt::Display) -> PyErr {
/// Loaded as nautilus_pyo3.core
#[pymodule]
pub fn core(_: Python<'_>, m: &PyModule) -> PyResult<()> {
- m.add_class::()?;
+ m.add_class::()?;
m.add_function(wrap_pyfunction!(casing::py_convert_to_snake_case, m)?)?;
m.add_function(wrap_pyfunction!(datetime::py_secs_to_nanos, m)?)?;
m.add_function(wrap_pyfunction!(datetime::py_secs_to_millis, m)?)?;
diff --git a/nautilus_core/core/src/python/serialization.rs b/nautilus_core/core/src/python/serialization.rs
index 83d859bca6dc..6fa6429e2de0 100644
--- a/nautilus_core/core/src/python/serialization.rs
+++ b/nautilus_core/core/src/python/serialization.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/core/src/python/uuid.rs b/nautilus_core/core/src/python/uuid.rs
index aca43f0e71e1..4aff83a0c1fc 100644
--- a/nautilus_core/core/src/python/uuid.rs
+++ b/nautilus_core/core/src/python/uuid.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/core/src/serialization.rs b/nautilus_core/core/src/serialization.rs
index 5433b5e5d39e..580a35d5ad0b 100644
--- a/nautilus_core/core/src/serialization.rs
+++ b/nautilus_core/core/src/serialization.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/core/src/time.rs b/nautilus_core/core/src/time.rs
index 68f0ae94601b..c7433cc912d8 100644
--- a/nautilus_core/core/src/time.rs
+++ b/nautilus_core/core/src/time.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -16,8 +16,8 @@
use std::{
ops::Deref,
sync::{
- atomic::{AtomicU64, Ordering},
- Arc,
+ atomic::{AtomicBool, AtomicU64, Ordering},
+ OnceLock,
},
time::{Duration, SystemTime, UNIX_EPOCH},
};
@@ -32,6 +32,22 @@ pub type UnixNanos = u64;
/// Represents a timedelta in nanoseconds.
pub type TimedeltaNanos = i64;
+/// Provides a global atomic time in real-time mode for use across the system.
+pub static ATOMIC_CLOCK_REALTIME: OnceLock = OnceLock::new();
+
+/// Provides a global atomic time in static mode for use across the system.
+pub static ATOMIC_CLOCK_STATIC: OnceLock = OnceLock::new();
+
+/// Returns a static reference to the global atomic clock in real-time mode.
+pub fn get_atomic_clock_realtime() -> &'static AtomicTime {
+ ATOMIC_CLOCK_REALTIME.get_or_init(AtomicTime::default)
+}
+
+/// Returns a static reference to the global atomic clock in static mode.
+pub fn get_atomic_clock_static() -> &'static AtomicTime {
+ ATOMIC_CLOCK_STATIC.get_or_init(|| AtomicTime::new(false, 0))
+}
+
#[must_use]
pub fn duration_since_unix_epoch() -> Duration {
SystemTime::now()
@@ -39,24 +55,28 @@ pub fn duration_since_unix_epoch() -> Duration {
.expect("Error calling `SystemTime::now.duration_since`")
}
-#[derive(Debug, Clone, Copy)]
-pub enum ClockMode {
- LIVE,
- STATIC,
-}
-
-/// Atomic clock stores the last recorded time in nanoseconds.
+/// Represents an atomic timekeeping structure.
///
+/// `AtomicTime` can act as a real-time clock or static clock based on its mode.
/// It uses `AtomicU64` to atomically update the value using only immutable
/// references.
///
-/// `AtomicClock` can act as a live clock and static clock based on its mode.
-#[derive(Debug, Clone)]
+/// This struct provides thread-safe access to a stored nanosecond time value,
+/// useful for when concurrent access to time information is required.
+///
+/// Fields:
+/// - `realtime`: Indicates whether the clock is operating in real-time mode.
+/// When `true`, the clock reflects real-world time progression. When `false`,
+/// the clock is in a manual or static mode, allowing for controlled time setting.
+/// - `timestamp_ns`: The last recorded time for the clock in Unix nanoseconds.
+/// This value is atomically updated and represents the precise time measurement.
+#[repr(C)]
+#[derive(Debug)]
pub struct AtomicTime {
- /// Atomic clock is operating in live or static mode.
- mode: ClockMode,
- /// The last recorded time in nanoseconds for the clock.
- timestamp_ns: Arc,
+ /// Atomic clock is operating in real-time mode if true, otherwise clock is operating in manual static mode.
+ pub realtime: AtomicBool,
+ /// The last recorded time for the clock in UNIX nanoseconds.
+ pub timestamp_ns: AtomicU64,
}
impl Deref for AtomicTime {
@@ -67,25 +87,31 @@ impl Deref for AtomicTime {
}
}
+impl Default for AtomicTime {
+ fn default() -> Self {
+ Self::new(true, 0)
+ }
+}
+
impl AtomicTime {
- /// New atomic clock set with the given time.
+ /// New atomic clock set with the given UNIX time (nanoseconds).
#[must_use]
- pub fn new(mode: ClockMode, time: u64) -> Self {
+ pub fn new(realtime: bool, time: UnixNanos) -> Self {
Self {
- mode,
- timestamp_ns: Arc::new(AtomicU64::new(time)),
+ realtime: AtomicBool::new(realtime),
+ timestamp_ns: AtomicU64::new(time),
}
}
/// Get time in nanoseconds.
///
- /// * Live mode returns current wall clock time since UNIX epoch (unique and monotonic)
- /// * Static mode returns currently stored time.
+ /// - Real-time mode returns current wall clock time since UNIX epoch (unique and monotonic).
+ /// - Static mode returns currently stored time.
#[must_use]
- pub fn get_time_ns(&self) -> u64 {
- match self.mode {
- ClockMode::LIVE => self.time_since_epoch(),
- ClockMode::STATIC => self.timestamp_ns.load(Ordering::Relaxed),
+ pub fn get_time_ns(&self) -> UnixNanos {
+ match self.realtime.load(Ordering::Relaxed) {
+ true => self.time_since_epoch(),
+ false => self.timestamp_ns.load(Ordering::Relaxed),
}
}
@@ -108,25 +134,31 @@ impl AtomicTime {
}
/// Sets new time for the clock.
- pub fn set_time(&self, time: u64) {
+ pub fn set_time(&self, time: UnixNanos) {
self.store(time, Ordering::Relaxed);
}
/// Increments current time with a delta and returns the updated time.
- #[must_use]
- pub fn increment_time(&self, delta: u64) -> u64 {
+ pub fn increment_time(&self, delta: u64) -> UnixNanos {
self.fetch_add(delta, Ordering::Relaxed) + delta
}
/// Stores and returns current time.
- #[must_use]
- pub fn time_since_epoch(&self) -> u64 {
+ pub fn time_since_epoch(&self) -> UnixNanos {
// Increment by 1 nanosecond to keep increasing time
let now = duration_since_unix_epoch().as_nanos() as u64 + 1;
let last = self.load(Ordering::SeqCst) + 1;
- let new = now.max(last);
- self.store(new, Ordering::SeqCst);
- new
+ let time = now.max(last);
+ self.store(time, Ordering::SeqCst);
+ time
+ }
+
+ pub fn make_realtime(&self) {
+ self.realtime.store(true, Ordering::Relaxed);
+ }
+
+ pub fn make_static(&self) {
+ self.realtime.store(false, Ordering::Relaxed);
}
}
@@ -143,7 +175,7 @@ mod tests {
#[rstest]
fn test_duration_since_unix_epoch() {
- let time = AtomicTime::new(ClockMode::LIVE, 0);
+ let time = AtomicTime::new(true, 0);
let duration = Duration::from_nanos(time.get_time_ns());
let now = SystemTime::now();
@@ -160,7 +192,7 @@ mod tests {
#[rstest]
fn test_unix_timestamp_is_monotonic_increasing() {
- let time = AtomicTime::new(ClockMode::LIVE, 0);
+ let time = AtomicTime::new(true, 0);
let result1 = time.get_time();
let result2 = time.get_time();
let result3 = time.get_time();
@@ -176,7 +208,7 @@ mod tests {
#[rstest]
fn test_unix_timestamp_ms_is_monotonic_increasing() {
- let time = AtomicTime::new(ClockMode::LIVE, 0);
+ let time = AtomicTime::new(true, 0);
let result1 = time.get_time_ms();
let result2 = time.get_time_ms();
let result3 = time.get_time_ms();
@@ -192,7 +224,7 @@ mod tests {
#[rstest]
fn test_unix_timestamp_us_is_monotonic_increasing() {
- let time = AtomicTime::new(ClockMode::LIVE, 0);
+ let time = AtomicTime::new(true, 0);
let result1 = time.get_time_us();
let result2 = time.get_time_us();
let result3 = time.get_time_us();
@@ -208,7 +240,7 @@ mod tests {
#[rstest]
fn test_unix_timestamp_ns_is_monotonic_increasing() {
- let time = AtomicTime::new(ClockMode::LIVE, 0);
+ let time = AtomicTime::new(true, 0);
let result1 = time.get_time_ns();
let result2 = time.get_time_ns();
let result3 = time.get_time_ns();
diff --git a/nautilus_core/core/src/uuid.rs b/nautilus_core/core/src/uuid.rs
index 0cd13c4d8d15..12783908f129 100644
--- a/nautilus_core/core/src/uuid.rs
+++ b/nautilus_core/core/src/uuid.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/average/ama.rs b/nautilus_core/indicators/src/average/ama.rs
index 3f2d6fdf4aa3..d84fff681634 100644
--- a/nautilus_core/indicators/src/average/ama.rs
+++ b/nautilus_core/indicators/src/average/ama.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/average/dema.rs b/nautilus_core/indicators/src/average/dema.rs
index c81898716a9a..6b86dd213fa2 100644
--- a/nautilus_core/indicators/src/average/dema.rs
+++ b/nautilus_core/indicators/src/average/dema.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/average/ema.rs b/nautilus_core/indicators/src/average/ema.rs
index 293cc273a913..4c07fb5b5529 100644
--- a/nautilus_core/indicators/src/average/ema.rs
+++ b/nautilus_core/indicators/src/average/ema.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/average/hma.rs b/nautilus_core/indicators/src/average/hma.rs
index b853b5ffe8de..a4ff4672592d 100644
--- a/nautilus_core/indicators/src/average/hma.rs
+++ b/nautilus_core/indicators/src/average/hma.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/average/mod.rs b/nautilus_core/indicators/src/average/mod.rs
index df174e098bdf..c8adf0a2c43f 100644
--- a/nautilus_core/indicators/src/average/mod.rs
+++ b/nautilus_core/indicators/src/average/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/average/rma.rs b/nautilus_core/indicators/src/average/rma.rs
index 65e5973dcaac..479e63576a81 100644
--- a/nautilus_core/indicators/src/average/rma.rs
+++ b/nautilus_core/indicators/src/average/rma.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/average/sma.rs b/nautilus_core/indicators/src/average/sma.rs
index 406000ed8dad..4752e1ddaaea 100644
--- a/nautilus_core/indicators/src/average/sma.rs
+++ b/nautilus_core/indicators/src/average/sma.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/average/wma.rs b/nautilus_core/indicators/src/average/wma.rs
index 03aa1acaf6d9..22ca40b08581 100644
--- a/nautilus_core/indicators/src/average/wma.rs
+++ b/nautilus_core/indicators/src/average/wma.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/indicator.rs b/nautilus_core/indicators/src/indicator.rs
index 8d5dab28f6b7..7041fdd3c82d 100644
--- a/nautilus_core/indicators/src/indicator.rs
+++ b/nautilus_core/indicators/src/indicator.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/lib.rs b/nautilus_core/indicators/src/lib.rs
index da03add6f2c6..b34b5b1f3773 100644
--- a/nautilus_core/indicators/src/lib.rs
+++ b/nautilus_core/indicators/src/lib.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/momentum/mod.rs b/nautilus_core/indicators/src/momentum/mod.rs
index fc49c3b4cf36..87cd1bc776c1 100644
--- a/nautilus_core/indicators/src/momentum/mod.rs
+++ b/nautilus_core/indicators/src/momentum/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/momentum/rsi.rs b/nautilus_core/indicators/src/momentum/rsi.rs
index 0dba6ac89abf..07623dbee708 100644
--- a/nautilus_core/indicators/src/momentum/rsi.rs
+++ b/nautilus_core/indicators/src/momentum/rsi.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/python/average/ama.rs b/nautilus_core/indicators/src/python/average/ama.rs
index d27568e7b50d..082f625d7e56 100644
--- a/nautilus_core/indicators/src/python/average/ama.rs
+++ b/nautilus_core/indicators/src/python/average/ama.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/python/average/dema.rs b/nautilus_core/indicators/src/python/average/dema.rs
index 71f0cc6b5784..c26d4a147bbc 100644
--- a/nautilus_core/indicators/src/python/average/dema.rs
+++ b/nautilus_core/indicators/src/python/average/dema.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/python/average/ema.rs b/nautilus_core/indicators/src/python/average/ema.rs
index ff0977751d2a..4a8e35c2d3ef 100644
--- a/nautilus_core/indicators/src/python/average/ema.rs
+++ b/nautilus_core/indicators/src/python/average/ema.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/python/average/hma.rs b/nautilus_core/indicators/src/python/average/hma.rs
index 54bd0f2a267b..4ba74e77bdab 100644
--- a/nautilus_core/indicators/src/python/average/hma.rs
+++ b/nautilus_core/indicators/src/python/average/hma.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/python/average/mod.rs b/nautilus_core/indicators/src/python/average/mod.rs
index 72743e6158ba..dc1c7f2f8bf9 100644
--- a/nautilus_core/indicators/src/python/average/mod.rs
+++ b/nautilus_core/indicators/src/python/average/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/python/average/rma.rs b/nautilus_core/indicators/src/python/average/rma.rs
index bee821eaa4be..a126f606af5b 100644
--- a/nautilus_core/indicators/src/python/average/rma.rs
+++ b/nautilus_core/indicators/src/python/average/rma.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/python/average/sma.rs b/nautilus_core/indicators/src/python/average/sma.rs
index 1b297e95db03..4b4bd6a0fb9c 100644
--- a/nautilus_core/indicators/src/python/average/sma.rs
+++ b/nautilus_core/indicators/src/python/average/sma.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/python/average/wma.rs b/nautilus_core/indicators/src/python/average/wma.rs
index 84d212d6cc7a..ef234e4e2cee 100644
--- a/nautilus_core/indicators/src/python/average/wma.rs
+++ b/nautilus_core/indicators/src/python/average/wma.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/python/mod.rs b/nautilus_core/indicators/src/python/mod.rs
index b0e9b2b634ce..842eb002fa53 100644
--- a/nautilus_core/indicators/src/python/mod.rs
+++ b/nautilus_core/indicators/src/python/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/python/momentum/mod.rs b/nautilus_core/indicators/src/python/momentum/mod.rs
index fc49c3b4cf36..87cd1bc776c1 100644
--- a/nautilus_core/indicators/src/python/momentum/mod.rs
+++ b/nautilus_core/indicators/src/python/momentum/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/python/momentum/rsi.rs b/nautilus_core/indicators/src/python/momentum/rsi.rs
index ae87fd3bce52..fa0b1798da54 100644
--- a/nautilus_core/indicators/src/python/momentum/rsi.rs
+++ b/nautilus_core/indicators/src/python/momentum/rsi.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/python/ratio/efficiency_ratio.rs b/nautilus_core/indicators/src/python/ratio/efficiency_ratio.rs
index 43ee463e2721..33475e26882d 100644
--- a/nautilus_core/indicators/src/python/ratio/efficiency_ratio.rs
+++ b/nautilus_core/indicators/src/python/ratio/efficiency_ratio.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/python/ratio/mod.rs b/nautilus_core/indicators/src/python/ratio/mod.rs
index 1d3e5ababfb9..5ca24611bf69 100644
--- a/nautilus_core/indicators/src/python/ratio/mod.rs
+++ b/nautilus_core/indicators/src/python/ratio/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/ratio/efficiency_ratio.rs b/nautilus_core/indicators/src/ratio/efficiency_ratio.rs
index 25fb558a60a1..6735969a0045 100644
--- a/nautilus_core/indicators/src/ratio/efficiency_ratio.rs
+++ b/nautilus_core/indicators/src/ratio/efficiency_ratio.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/ratio/mod.rs b/nautilus_core/indicators/src/ratio/mod.rs
index 1d3e5ababfb9..5ca24611bf69 100644
--- a/nautilus_core/indicators/src/ratio/mod.rs
+++ b/nautilus_core/indicators/src/ratio/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/indicators/src/stubs.rs b/nautilus_core/indicators/src/stubs.rs
index 03b6d0bff5d4..a4450369ebc4 100644
--- a/nautilus_core/indicators/src/stubs.rs
+++ b/nautilus_core/indicators/src/stubs.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/infrastructure/src/cache.rs b/nautilus_core/infrastructure/src/cache.rs
index 8825a1a1b531..893c7dd0a4e5 100644
--- a/nautilus_core/infrastructure/src/cache.rs
+++ b/nautilus_core/infrastructure/src/cache.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/infrastructure/src/lib.rs b/nautilus_core/infrastructure/src/lib.rs
index 5d5670e3fb72..a05090fafc7d 100644
--- a/nautilus_core/infrastructure/src/lib.rs
+++ b/nautilus_core/infrastructure/src/lib.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/infrastructure/src/python/cache.rs b/nautilus_core/infrastructure/src/python/cache.rs
index fa5080d45b61..6257f3098f33 100644
--- a/nautilus_core/infrastructure/src/python/cache.rs
+++ b/nautilus_core/infrastructure/src/python/cache.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/infrastructure/src/python/mod.rs b/nautilus_core/infrastructure/src/python/mod.rs
index b616d467a1e2..c8e4adbffbf7 100644
--- a/nautilus_core/infrastructure/src/python/mod.rs
+++ b/nautilus_core/infrastructure/src/python/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/infrastructure/src/redis.rs b/nautilus_core/infrastructure/src/redis.rs
index fa99d8b95e6b..8b1f6f69736d 100644
--- a/nautilus_core/infrastructure/src/redis.rs
+++ b/nautilus_core/infrastructure/src/redis.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -213,17 +213,49 @@ fn drain_buffer(conn: &mut Connection, trader_key: &str, buffer: &mut VecDeque {
- if let Err(e) = insert(&mut pipe, collection, &key, msg.payload) {
+ if msg.payload.is_none() {
+ eprintln!("Null `payload` for `insert`");
+ continue; // Continue to next message
+ };
+
+ let payload = msg
+ .payload
+ .as_ref()
+ .unwrap()
+ .iter()
+ .map(|v| v.as_slice())
+ .collect::>();
+
+ if let Err(e) = insert(&mut pipe, collection, &key, payload) {
eprintln!("{e}");
}
}
DatabaseOperation::Update => {
- if let Err(e) = update(&mut pipe, collection, &key, msg.payload) {
+ if msg.payload.is_none() {
+ eprintln!("Null `payload` for `update`");
+ continue; // Continue to next message
+ };
+
+ let payload = msg
+ .payload
+ .as_ref()
+ .unwrap()
+ .iter()
+ .map(|v| v.as_slice())
+ .collect::>();
+
+ if let Err(e) = update(&mut pipe, collection, &key, payload) {
eprintln!("{e}");
}
}
DatabaseOperation::Delete => {
- if let Err(e) = delete(&mut pipe, collection, &key, msg.payload) {
+ // `payload` can be `None` for a delete operation
+ let payload = msg
+ .payload
+ .as_ref()
+ .map(|v| v.iter().map(|v| v.as_slice()).collect::>());
+
+ if let Err(e) = delete(&mut pipe, collection, &key, payload) {
eprintln!("{e}");
}
}
@@ -279,13 +311,7 @@ fn read_list(conn: &mut Connection, key: &str) -> Result>> {
Ok(result)
}
-fn insert(
- pipe: &mut Pipeline,
- collection: &str,
- key: &str,
- value: Option>>,
-) -> Result<()> {
- let value = value.ok_or_else(|| anyhow!("Null `payload` for `insert`"))?;
+fn insert(pipe: &mut Pipeline, collection: &str, key: &str, value: Vec<&[u8]>) -> Result<()> {
if value.is_empty() {
bail!("Empty `payload` for `insert`")
}
@@ -293,149 +319,143 @@ fn insert(
match collection {
INDEX => insert_index(pipe, key, &value),
GENERAL => {
- insert_string(pipe, key, &value[0]);
+ insert_string(pipe, key, value[0]);
Ok(())
}
CURRENCIES => {
- insert_string(pipe, key, &value[0]);
+ insert_string(pipe, key, value[0]);
Ok(())
}
INSTRUMENTS => {
- insert_string(pipe, key, &value[0]);
+ insert_string(pipe, key, value[0]);
Ok(())
}
SYNTHETICS => {
- insert_string(pipe, key, &value[0]);
+ insert_string(pipe, key, value[0]);
Ok(())
}
ACCOUNTS => {
- insert_list(pipe, key, &value[0]);
+ insert_list(pipe, key, value[0]);
Ok(())
}
ORDERS => {
- insert_list(pipe, key, &value[0]);
+ insert_list(pipe, key, value[0]);
Ok(())
}
POSITIONS => {
- insert_list(pipe, key, &value[0]);
+ insert_list(pipe, key, value[0]);
Ok(())
}
ACTORS => {
- insert_string(pipe, key, &value[0]);
+ insert_string(pipe, key, value[0]);
Ok(())
}
STRATEGIES => {
- insert_string(pipe, key, &value[0]);
+ insert_string(pipe, key, value[0]);
Ok(())
}
SNAPSHOTS => {
- insert_list(pipe, key, &value[0]);
+ insert_list(pipe, key, value[0]);
Ok(())
}
HEALTH => {
- insert_string(pipe, key, &value[0]);
+ insert_string(pipe, key, value[0]);
Ok(())
}
_ => bail!("Unsupported operation: `insert` for collection '{collection}'"),
}
}
-fn insert_index(pipe: &mut Pipeline, key: &str, value: &[Vec]) -> Result<()> {
+fn insert_index(pipe: &mut Pipeline, key: &str, value: &[&[u8]]) -> Result<()> {
let index_key = get_index_key(key)?;
match index_key {
INDEX_ORDER_IDS => {
- insert_set(pipe, key, &value[0]);
+ insert_set(pipe, key, value[0]);
Ok(())
}
INDEX_ORDER_POSITION => {
- insert_hset(pipe, key, &value[0], &value[1]);
+ insert_hset(pipe, key, value[0], value[1]);
Ok(())
}
INDEX_ORDER_CLIENT => {
- insert_hset(pipe, key, &value[0], &value[1]);
+ insert_hset(pipe, key, value[0], value[1]);
Ok(())
}
INDEX_ORDERS => {
- insert_set(pipe, key, &value[0]);
+ insert_set(pipe, key, value[0]);
Ok(())
}
INDEX_ORDERS_OPEN => {
- insert_set(pipe, key, &value[0]);
+ insert_set(pipe, key, value[0]);
Ok(())
}
INDEX_ORDERS_CLOSED => {
- insert_set(pipe, key, &value[0]);
+ insert_set(pipe, key, value[0]);
Ok(())
}
INDEX_ORDERS_EMULATED => {
- insert_set(pipe, key, &value[0]);
+ insert_set(pipe, key, value[0]);
Ok(())
}
INDEX_ORDERS_INFLIGHT => {
- insert_set(pipe, key, &value[0]);
+ insert_set(pipe, key, value[0]);
Ok(())
}
INDEX_POSITIONS => {
- insert_set(pipe, key, &value[0]);
+ insert_set(pipe, key, value[0]);
Ok(())
}
INDEX_POSITIONS_OPEN => {
- insert_set(pipe, key, &value[0]);
+ insert_set(pipe, key, value[0]);
Ok(())
}
INDEX_POSITIONS_CLOSED => {
- insert_set(pipe, key, &value[0]);
+ insert_set(pipe, key, value[0]);
Ok(())
}
_ => bail!("Index unknown '{index_key}' on insert"),
}
}
-fn insert_string(pipe: &mut Pipeline, key: &str, value: &Vec) {
+fn insert_string(pipe: &mut Pipeline, key: &str, value: &[u8]) {
pipe.set(key, value);
}
-fn insert_set(pipe: &mut Pipeline, key: &str, value: &Vec) {
+fn insert_set(pipe: &mut Pipeline, key: &str, value: &[u8]) {
pipe.sadd(key, value);
}
-fn insert_hset(pipe: &mut Pipeline, key: &str, name: &Vec, value: &Vec) {
+fn insert_hset(pipe: &mut Pipeline, key: &str, name: &[u8], value: &[u8]) {
pipe.hset(key, name, value);
}
-fn insert_list(pipe: &mut Pipeline, key: &str, value: &Vec) {
+fn insert_list(pipe: &mut Pipeline, key: &str, value: &[u8]) {
pipe.rpush(key, value);
}
-fn update(
- pipe: &mut Pipeline,
- collection: &str,
- key: &str,
- value: Option>>,
-) -> Result<()> {
- let value = value.ok_or_else(|| anyhow!("Null `payload` for `update`"))?;
+fn update(pipe: &mut Pipeline, collection: &str, key: &str, value: Vec<&[u8]>) -> Result<()> {
if value.is_empty() {
bail!("Empty `payload` for `update`")
}
match collection {
ACCOUNTS => {
- update_list(pipe, key, &value[0]);
+ update_list(pipe, key, value[0]);
Ok(())
}
ORDERS => {
- update_list(pipe, key, &value[0]);
+ update_list(pipe, key, value[0]);
Ok(())
}
POSITIONS => {
- update_list(pipe, key, &value[0]);
+ update_list(pipe, key, value[0]);
Ok(())
}
_ => bail!("Unsupported operation: `update` for collection '{collection}'"),
}
}
-fn update_list(pipe: &mut Pipeline, key: &str, value: &Vec) {
+fn update_list(pipe: &mut Pipeline, key: &str, value: &[u8]) {
pipe.rpush_exists(key, value);
}
@@ -443,7 +463,7 @@ fn delete(
pipe: &mut Pipeline,
collection: &str,
key: &str,
- value: Option>>,
+ value: Option>,
) -> Result<()> {
match collection {
INDEX => remove_index(pipe, key, value),
@@ -459,40 +479,40 @@ fn delete(
}
}
-fn remove_index(pipe: &mut Pipeline, key: &str, value: Option>>) -> Result<()> {
+fn remove_index(pipe: &mut Pipeline, key: &str, value: Option>) -> Result<()> {
let value = value.ok_or_else(|| anyhow!("Empty `payload` for `delete` '{key}'"))?;
let index_key = get_index_key(key)?;
match index_key {
INDEX_ORDERS_OPEN => {
- remove_from_set(pipe, key, &value[0]);
+ remove_from_set(pipe, key, value[0]);
Ok(())
}
INDEX_ORDERS_CLOSED => {
- remove_from_set(pipe, key, &value[0]);
+ remove_from_set(pipe, key, value[0]);
Ok(())
}
INDEX_ORDERS_EMULATED => {
- remove_from_set(pipe, key, &value[0]);
+ remove_from_set(pipe, key, value[0]);
Ok(())
}
INDEX_ORDERS_INFLIGHT => {
- remove_from_set(pipe, key, &value[0]);
+ remove_from_set(pipe, key, value[0]);
Ok(())
}
INDEX_POSITIONS_OPEN => {
- remove_from_set(pipe, key, &value[0]);
+ remove_from_set(pipe, key, value[0]);
Ok(())
}
INDEX_POSITIONS_CLOSED => {
- remove_from_set(pipe, key, &value[0]);
+ remove_from_set(pipe, key, value[0]);
Ok(())
}
_ => bail!("Unsupported index operation: remove from '{index_key}'"),
}
}
-fn remove_from_set(pipe: &mut Pipeline, key: &str, member: &Vec) {
+fn remove_from_set(pipe: &mut Pipeline, key: &str, member: &[u8]) {
pipe.srem(key, member);
}
diff --git a/nautilus_core/model/Cargo.toml b/nautilus_core/model/Cargo.toml
index 165785350ee6..70eb4aa7a766 100644
--- a/nautilus_core/model/Cargo.toml
+++ b/nautilus_core/model/Cargo.toml
@@ -39,7 +39,8 @@ extension-module = [
ffi = ["cbindgen"]
python = ["pyo3"]
stubs = ["rstest"]
-default = ["ffi", "python"]
+trivial_copy = [] # Enables deriving the `Copy` trait for data types (should be included in default)
+default = ["ffi", "python", "stubs", "trivial_copy"]
[dev-dependencies]
criterion = { workspace = true }
diff --git a/nautilus_core/model/build.rs b/nautilus_core/model/build.rs
index 35997a9fb56c..c925769bfabb 100644
--- a/nautilus_core/model/build.rs
+++ b/nautilus_core/model/build.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/cbindgen.toml b/nautilus_core/model/cbindgen.toml
index bab00f3e75d7..adeba901c586 100644
--- a/nautilus_core/model/cbindgen.toml
+++ b/nautilus_core/model/cbindgen.toml
@@ -34,6 +34,8 @@ exclude = [
"Money" = "Money_t"
"OrderId" = "uint64_t"
"OrderBookDelta" = "OrderBookDelta_t"
+"OrderBookDeltas" = "OrderBookDeltas_t"
+"OrderBookDepth10" = "OrderBookDepth10_t"
"OrderInitialized" = "OrderInitialized_t"
"OrderDenied" = "OrderDenied_t"
"OrderEmulated" = "OrderEmulated_t"
diff --git a/nautilus_core/model/cbindgen_cython.toml b/nautilus_core/model/cbindgen_cython.toml
index 4177323314f4..b9ab372505d0 100644
--- a/nautilus_core/model/cbindgen_cython.toml
+++ b/nautilus_core/model/cbindgen_cython.toml
@@ -12,6 +12,7 @@ header = '"../includes/model.h"'
"libc.stdint" = [
"uint8_t",
"uint16_t",
+ "uint32_t",
"uint64_t",
"uintptr_t",
"int64_t",
@@ -50,6 +51,8 @@ exclude = [
"Money" = "Money_t"
"OrderId" = "uint64_t"
"OrderBookDelta" = "OrderBookDelta_t"
+"OrderBookDeltas" = "OrderBookDeltas_t"
+"OrderBookDepth10" = "OrderBookDepth10_t"
"OrderInitialized" = "OrderInitialized_t"
"OrderDenied" = "OrderDenied_t"
"OrderEmulated" = "OrderEmulated_t"
diff --git a/nautilus_core/model/src/currencies.rs b/nautilus_core/model/src/currencies.rs
index 7bc0012a34a1..dfdd5f78a641 100644
--- a/nautilus_core/model/src/currencies.rs
+++ b/nautilus_core/model/src/currencies.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/data/bar.rs b/nautilus_core/model/src/data/bar.rs
index 46973507cadc..bf021dd2b0ca 100644
--- a/nautilus_core/model/src/data/bar.rs
+++ b/nautilus_core/model/src/data/bar.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -35,11 +35,12 @@ use crate::{
/// Represents a bar aggregation specification including a step, aggregation
/// method/rule and price type.
#[repr(C)]
-#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize)]
+#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize)]
#[cfg_attr(
feature = "python",
pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
)]
+#[cfg_attr(feature = "trivial_copy", derive(Copy))]
pub struct BarSpecification {
/// The step for binning samples for bar aggregation.
pub step: usize,
@@ -49,6 +50,17 @@ pub struct BarSpecification {
pub price_type: PriceType,
}
+impl BarSpecification {
+ #[must_use]
+ pub fn new(step: usize, aggregation: BarAggregation, price_type: PriceType) -> Self {
+ Self {
+ step,
+ aggregation,
+ price_type,
+ }
+ }
+}
+
impl Display for BarSpecification {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}-{}-{}", self.step, self.aggregation, self.price_type)
@@ -72,6 +84,21 @@ pub struct BarType {
pub aggregation_source: AggregationSource,
}
+impl BarType {
+ #[must_use]
+ pub fn new(
+ instrument_id: InstrumentId,
+ spec: BarSpecification,
+ aggregation_source: AggregationSource,
+ ) -> Self {
+ Self {
+ instrument_id,
+ spec,
+ aggregation_source,
+ }
+ }
+}
+
#[derive(thiserror::Error, Debug)]
#[error("Error parsing `BarType` from '{input}', invalid token: '{token}' at position {position}")]
pub struct BarTypeParseError {
@@ -310,7 +337,7 @@ impl Display for Bar {
////////////////////////////////////////////////////////////////////////////////
// Stubs
////////////////////////////////////////////////////////////////////////////////
-#[cfg(test)]
+#[cfg(feature = "stubs")]
pub mod stubs {
use rstest::fixture;
@@ -322,7 +349,7 @@ pub mod stubs {
};
#[fixture]
- pub fn bar_audusd_sim_minute_bid() -> Bar {
+ pub fn stub_bar() -> Bar {
let instrument_id = InstrumentId {
symbol: Symbol::new("AUDUSD").unwrap(),
venue: Venue::new("SIM").unwrap(),
@@ -574,16 +601,16 @@ mod tests {
}
#[rstest]
- fn test_json_serialization(bar_audusd_sim_minute_bid: Bar) {
- let bar = bar_audusd_sim_minute_bid;
+ fn test_json_serialization(stub_bar: Bar) {
+ let bar = stub_bar;
let serialized = bar.as_json_bytes().unwrap();
let deserialized = Bar::from_json_bytes(serialized).unwrap();
assert_eq!(deserialized, bar);
}
#[rstest]
- fn test_msgpack_serialization(bar_audusd_sim_minute_bid: Bar) {
- let bar = bar_audusd_sim_minute_bid;
+ fn test_msgpack_serialization(stub_bar: Bar) {
+ let bar = stub_bar;
let serialized = bar.as_msgpack_bytes().unwrap();
let deserialized = Bar::from_msgpack_bytes(serialized).unwrap();
assert_eq!(deserialized, bar);
diff --git a/nautilus_core/model/src/data/delta.rs b/nautilus_core/model/src/data/delta.rs
index 155c2c634037..7bf17b21e628 100644
--- a/nautilus_core/model/src/data/delta.rs
+++ b/nautilus_core/model/src/data/delta.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -34,12 +34,13 @@ use crate::{
/// Represents a single change/delta in an order book.
#[repr(C)]
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(tag = "type")]
#[cfg_attr(
feature = "python",
pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
)]
+#[cfg_attr(feature = "trivial_copy", derive(Copy))]
pub struct OrderBookDelta {
/// The instrument ID for the book.
pub instrument_id: InstrumentId,
@@ -80,6 +81,24 @@ impl OrderBookDelta {
}
}
+ #[must_use]
+ pub fn clear(
+ instrument_id: InstrumentId,
+ sequence: u64,
+ ts_event: UnixNanos,
+ ts_init: UnixNanos,
+ ) -> Self {
+ Self {
+ instrument_id,
+ action: BookAction::Clear,
+ order: NULL_ORDER,
+ flags: 32, // TODO: Flags constants
+ sequence,
+ ts_event,
+ ts_init,
+ }
+ }
+
/// Returns the metadata for the type, for use with serialization formats.
pub fn get_metadata(
instrument_id: &InstrumentId,
@@ -164,8 +183,6 @@ impl OrderBookDelta {
}
}
-impl Serializable for OrderBookDelta {}
-
impl Display for OrderBookDelta {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
@@ -182,10 +199,12 @@ impl Display for OrderBookDelta {
}
}
+impl Serializable for OrderBookDelta {}
+
////////////////////////////////////////////////////////////////////////////////
// Stubs
////////////////////////////////////////////////////////////////////////////////
-#[cfg(test)]
+#[cfg(feature = "stubs")]
pub mod stubs {
use rstest::fixture;
@@ -197,7 +216,7 @@ pub mod stubs {
#[fixture]
pub fn stub_delta() -> OrderBookDelta {
- let instrument_id = InstrumentId::from("AAPL.NASDAQ");
+ let instrument_id = InstrumentId::from("AAPL.XNAS");
let action = BookAction::Add;
let price = Price::from("100.00");
let size = Quantity::from("10");
@@ -236,7 +255,7 @@ mod tests {
#[rstest]
fn test_new() {
- let instrument_id = InstrumentId::from("AAPL.NASDAQ");
+ let instrument_id = InstrumentId::from("AAPL.XNAS");
let action = BookAction::Add;
let price = Price::from("100.00");
let size = Quantity::from("10");
@@ -271,12 +290,33 @@ mod tests {
assert_eq!(delta.ts_init, ts_init);
}
+ #[rstest]
+ fn test_clear() {
+ let instrument_id = InstrumentId::from("AAPL.XNAS");
+ let sequence = 1;
+ let ts_event = 2;
+ let ts_init = 3;
+
+ let delta = OrderBookDelta::clear(instrument_id, sequence, ts_event, ts_init);
+
+ assert_eq!(delta.instrument_id, instrument_id);
+ assert_eq!(delta.action, BookAction::Clear);
+ assert!(delta.order.price.is_zero());
+ assert!(delta.order.size.is_zero());
+ assert_eq!(delta.order.side, OrderSide::NoOrderSide);
+ assert_eq!(delta.order.order_id, 0);
+ assert_eq!(delta.flags, 32);
+ assert_eq!(delta.sequence, sequence);
+ assert_eq!(delta.ts_event, ts_event);
+ assert_eq!(delta.ts_init, ts_init);
+ }
+
#[rstest]
fn test_display(stub_delta: OrderBookDelta) {
let delta = stub_delta;
assert_eq!(
format!("{}", delta),
- "AAPL.NASDAQ,ADD,100.00,10,BUY,123456,0,1,1,2".to_string()
+ "AAPL.XNAS,ADD,100.00,10,BUY,123456,0,1,1,2".to_string()
);
}
diff --git a/nautilus_core/model/src/data/deltas.rs b/nautilus_core/model/src/data/deltas.rs
new file mode 100644
index 000000000000..c6a66745be22
--- /dev/null
+++ b/nautilus_core/model/src/data/deltas.rs
@@ -0,0 +1,336 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
+// https://nautechsystems.io
+//
+// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
+// You may not use this file except in compliance with the License.
+// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// -------------------------------------------------------------------------------------------------
+
+use std::fmt::{Display, Formatter};
+
+use nautilus_core::time::UnixNanos;
+use pyo3::prelude::*;
+
+use super::delta::OrderBookDelta;
+use crate::identifiers::instrument_id::InstrumentId;
+
+/// Represents a grouped batch of `OrderBookDelta` updates for an `OrderBook`.
+#[repr(C)]
+#[derive(Clone, Debug)]
+#[cfg_attr(
+ feature = "python",
+ pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
+)]
+pub struct OrderBookDeltas {
+ /// The instrument ID for the book.
+ pub instrument_id: InstrumentId,
+ /// The order book deltas.
+ pub deltas: Vec,
+ /// A combination of packet end with matching engine status.
+ pub flags: u8,
+ /// The message sequence number assigned at the venue.
+ pub sequence: u64,
+ /// The UNIX timestamp (nanoseconds) when the data event occurred.
+ pub ts_event: UnixNanos,
+ /// The UNIX timestamp (nanoseconds) when the data object was initialized.
+ pub ts_init: UnixNanos,
+}
+
+impl OrderBookDeltas {
+ #[allow(clippy::too_many_arguments)]
+ #[must_use]
+ pub fn new(
+ instrument_id: InstrumentId,
+ deltas: Vec,
+ flags: u8,
+ sequence: u64,
+ ts_event: UnixNanos,
+ ts_init: UnixNanos,
+ ) -> Self {
+ Self {
+ instrument_id,
+ deltas,
+ flags,
+ sequence,
+ ts_event,
+ ts_init,
+ }
+ }
+}
+
+// TODO: Potentially implement later
+// impl Serializable for OrderBookDeltas {}
+
+// TODO: Exact format for Debug and Display TBD
+impl Display for OrderBookDeltas {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "{},len={},flags={},sequence={},ts_event={},ts_init={}",
+ self.instrument_id,
+ self.deltas.len(),
+ self.flags,
+ self.sequence,
+ self.ts_event,
+ self.ts_init
+ )
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Stubs
+////////////////////////////////////////////////////////////////////////////////
+#[cfg(feature = "stubs")]
+pub mod stubs {
+ use rstest::fixture;
+
+ use super::*;
+ use crate::{
+ data::{delta::OrderBookDelta, order::BookOrder},
+ enums::{BookAction, OrderSide},
+ identifiers::instrument_id::InstrumentId,
+ types::{price::Price, quantity::Quantity},
+ };
+
+ #[fixture]
+ pub fn stub_deltas() -> OrderBookDeltas {
+ let instrument_id = InstrumentId::from("AAPL.XNAS");
+ let flags = 32; // Snapshot flag
+ let sequence = 0;
+ let ts_event = 1;
+ let ts_init = 2;
+
+ let delta0 = OrderBookDelta::clear(instrument_id, sequence, ts_event, ts_init);
+ let delta1 = OrderBookDelta::new(
+ instrument_id,
+ BookAction::Add,
+ BookOrder::new(
+ OrderSide::Sell,
+ Price::from("102.00"),
+ Quantity::from("300"),
+ 1,
+ ),
+ flags,
+ sequence,
+ ts_event,
+ ts_init,
+ );
+ let delta2 = OrderBookDelta::new(
+ instrument_id,
+ BookAction::Add,
+ BookOrder::new(
+ OrderSide::Sell,
+ Price::from("101.00"),
+ Quantity::from("200"),
+ 2,
+ ),
+ flags,
+ sequence,
+ ts_event,
+ ts_init,
+ );
+ let delta3 = OrderBookDelta::new(
+ instrument_id,
+ BookAction::Add,
+ BookOrder::new(
+ OrderSide::Sell,
+ Price::from("100.00"),
+ Quantity::from("100"),
+ 3,
+ ),
+ flags,
+ sequence,
+ ts_event,
+ ts_init,
+ );
+ let delta4 = OrderBookDelta::new(
+ instrument_id,
+ BookAction::Add,
+ BookOrder::new(
+ OrderSide::Buy,
+ Price::from("99.00"),
+ Quantity::from("100"),
+ 4,
+ ),
+ flags,
+ sequence,
+ ts_event,
+ ts_init,
+ );
+ let delta5 = OrderBookDelta::new(
+ instrument_id,
+ BookAction::Add,
+ BookOrder::new(
+ OrderSide::Buy,
+ Price::from("98.00"),
+ Quantity::from("200"),
+ 5,
+ ),
+ flags,
+ sequence,
+ ts_event,
+ ts_init,
+ );
+ let delta6 = OrderBookDelta::new(
+ instrument_id,
+ BookAction::Add,
+ BookOrder::new(
+ OrderSide::Buy,
+ Price::from("97.00"),
+ Quantity::from("300"),
+ 6,
+ ),
+ flags,
+ sequence,
+ ts_event,
+ ts_init,
+ );
+
+ let deltas = vec![delta0, delta1, delta2, delta3, delta4, delta5, delta6];
+
+ OrderBookDeltas::new(instrument_id, deltas, flags, sequence, ts_event, ts_init)
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Tests
+////////////////////////////////////////////////////////////////////////////////
+#[cfg(test)]
+mod tests {
+ use rstest::rstest;
+
+ use super::{stubs::*, *};
+ use crate::{
+ data::order::BookOrder,
+ enums::{BookAction, OrderSide},
+ types::{price::Price, quantity::Quantity},
+ };
+
+ #[rstest]
+ fn test_new() {
+ let instrument_id = InstrumentId::from("AAPL.XNAS");
+ let flags = 32; // Snapshot flag
+ let sequence = 0;
+ let ts_event = 1;
+ let ts_init = 2;
+
+ let delta0 = OrderBookDelta::clear(instrument_id, sequence, ts_event, ts_init);
+ let delta1 = OrderBookDelta::new(
+ instrument_id,
+ BookAction::Add,
+ BookOrder::new(
+ OrderSide::Sell,
+ Price::from("102.00"),
+ Quantity::from("300"),
+ 1,
+ ),
+ flags,
+ sequence,
+ ts_event,
+ ts_init,
+ );
+ let delta2 = OrderBookDelta::new(
+ instrument_id,
+ BookAction::Add,
+ BookOrder::new(
+ OrderSide::Sell,
+ Price::from("101.00"),
+ Quantity::from("200"),
+ 2,
+ ),
+ flags,
+ sequence,
+ ts_event,
+ ts_init,
+ );
+ let delta3 = OrderBookDelta::new(
+ instrument_id,
+ BookAction::Add,
+ BookOrder::new(
+ OrderSide::Sell,
+ Price::from("100.00"),
+ Quantity::from("100"),
+ 3,
+ ),
+ flags,
+ sequence,
+ ts_event,
+ ts_init,
+ );
+ let delta4 = OrderBookDelta::new(
+ instrument_id,
+ BookAction::Add,
+ BookOrder::new(
+ OrderSide::Buy,
+ Price::from("99.00"),
+ Quantity::from("100"),
+ 4,
+ ),
+ flags,
+ sequence,
+ ts_event,
+ ts_init,
+ );
+ let delta5 = OrderBookDelta::new(
+ instrument_id,
+ BookAction::Add,
+ BookOrder::new(
+ OrderSide::Buy,
+ Price::from("98.00"),
+ Quantity::from("200"),
+ 5,
+ ),
+ flags,
+ sequence,
+ ts_event,
+ ts_init,
+ );
+ let delta6 = OrderBookDelta::new(
+ instrument_id,
+ BookAction::Add,
+ BookOrder::new(
+ OrderSide::Buy,
+ Price::from("97.00"),
+ Quantity::from("300"),
+ 6,
+ ),
+ flags,
+ sequence,
+ ts_event,
+ ts_init,
+ );
+
+ let deltas = OrderBookDeltas::new(
+ instrument_id,
+ vec![delta0, delta1, delta2, delta3, delta4, delta5, delta6],
+ flags,
+ sequence,
+ ts_event,
+ ts_init,
+ );
+
+ assert_eq!(deltas.instrument_id, instrument_id);
+ assert_eq!(deltas.deltas.len(), 7);
+ assert_eq!(deltas.flags, flags);
+ assert_eq!(deltas.sequence, sequence);
+ assert_eq!(deltas.ts_event, ts_event);
+ assert_eq!(deltas.ts_init, ts_init);
+ }
+
+ // TODO: Exact format for Debug and Display TBD
+ #[rstest]
+ fn test_display(stub_deltas: OrderBookDeltas) {
+ let deltas = stub_deltas;
+ assert_eq!(
+ format!("{}", deltas),
+ "AAPL.XNAS,len=7,flags=32,sequence=0,ts_event=1,ts_init=2".to_string()
+ );
+ }
+}
diff --git a/nautilus_core/model/src/data/depth.rs b/nautilus_core/model/src/data/depth.rs
new file mode 100644
index 000000000000..4183359d89b3
--- /dev/null
+++ b/nautilus_core/model/src/data/depth.rs
@@ -0,0 +1,320 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
+// https://nautechsystems.io
+//
+// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
+// You may not use this file except in compliance with the License.
+// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// -------------------------------------------------------------------------------------------------
+
+use std::{
+ collections::HashMap,
+ fmt::{Display, Formatter},
+};
+
+use indexmap::IndexMap;
+use nautilus_core::{serialization::Serializable, time::UnixNanos};
+use pyo3::prelude::*;
+use serde::{Deserialize, Serialize};
+
+use super::order::BookOrder;
+use crate::identifiers::instrument_id::InstrumentId;
+
+pub const DEPTH10_LEN: usize = 10;
+
+/// Represents a self-contained order book update with a fixed depth of 10 levels per side.
+///
+/// This struct is specifically designed for scenarios where a snapshot of the top 10 bid and
+/// ask levels in an order book is needed. It differs from `OrderBookDelta` or `OrderBookDeltas`
+/// in its fixed-depth nature and is optimized for cases where a full depth representation is not
+/// required or practical.
+///
+/// Note: This type is not compatible with `OrderBookDelta` or `OrderBookDeltas` due to
+/// its specialized structure and limited depth use case.
+#[repr(C)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
+#[cfg_attr(
+ feature = "python",
+ pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
+)]
+#[cfg_attr(feature = "trivial_copy", derive(Copy))]
+pub struct OrderBookDepth10 {
+ /// The instrument ID for the book.
+ pub instrument_id: InstrumentId,
+ /// The bid orders for the depth update.
+ pub bids: [BookOrder; DEPTH10_LEN],
+ /// The ask orders for the depth update.
+ pub asks: [BookOrder; DEPTH10_LEN],
+ /// The count of bid orders per level for the depth update.
+ pub bid_counts: [u32; DEPTH10_LEN],
+ /// The count of ask orders per level for the depth update.
+ pub ask_counts: [u32; DEPTH10_LEN],
+ /// A combination of packet end with matching engine status.
+ pub flags: u8,
+ /// The message sequence number assigned at the venue.
+ pub sequence: u64,
+ /// The UNIX timestamp (nanoseconds) when the data event occurred.
+ pub ts_event: UnixNanos,
+ /// The UNIX timestamp (nanoseconds) when the data object was initialized.
+ pub ts_init: UnixNanos,
+}
+
+impl OrderBookDepth10 {
+ #[allow(clippy::too_many_arguments)]
+ #[must_use]
+ pub fn new(
+ instrument_id: InstrumentId,
+ bids: [BookOrder; DEPTH10_LEN],
+ asks: [BookOrder; DEPTH10_LEN],
+ bid_counts: [u32; DEPTH10_LEN],
+ ask_counts: [u32; DEPTH10_LEN],
+ flags: u8,
+ sequence: u64,
+ ts_event: UnixNanos,
+ ts_init: UnixNanos,
+ ) -> Self {
+ Self {
+ instrument_id,
+ bids,
+ asks,
+ bid_counts,
+ ask_counts,
+ flags,
+ sequence,
+ ts_event,
+ ts_init,
+ }
+ }
+
+ /// Returns the metadata for the type, for use with serialization formats.
+ pub fn get_metadata(
+ instrument_id: &InstrumentId,
+ price_precision: u8,
+ size_precision: u8,
+ ) -> HashMap {
+ let mut metadata = HashMap::new();
+ metadata.insert("instrument_id".to_string(), instrument_id.to_string());
+ metadata.insert("price_precision".to_string(), price_precision.to_string());
+ metadata.insert("size_precision".to_string(), size_precision.to_string());
+ metadata
+ }
+
+ /// Returns the field map for the type, for use with Arrow schemas.
+ pub fn get_fields() -> IndexMap {
+ let mut metadata = IndexMap::new();
+ metadata.insert("bid_price_0".to_string(), "Int64".to_string());
+ metadata.insert("bid_price_1".to_string(), "Int64".to_string());
+ metadata.insert("bid_price_2".to_string(), "Int64".to_string());
+ metadata.insert("bid_price_3".to_string(), "Int64".to_string());
+ metadata.insert("bid_price_4".to_string(), "Int64".to_string());
+ metadata.insert("bid_price_5".to_string(), "Int64".to_string());
+ metadata.insert("bid_price_6".to_string(), "Int64".to_string());
+ metadata.insert("bid_price_7".to_string(), "Int64".to_string());
+ metadata.insert("bid_price_8".to_string(), "Int64".to_string());
+ metadata.insert("bid_price_9".to_string(), "Int64".to_string());
+ metadata.insert("ask_price_0".to_string(), "Int64".to_string());
+ metadata.insert("ask_price_1".to_string(), "Int64".to_string());
+ metadata.insert("ask_price_2".to_string(), "Int64".to_string());
+ metadata.insert("ask_price_3".to_string(), "Int64".to_string());
+ metadata.insert("ask_price_4".to_string(), "Int64".to_string());
+ metadata.insert("ask_price_5".to_string(), "Int64".to_string());
+ metadata.insert("ask_price_6".to_string(), "Int64".to_string());
+ metadata.insert("ask_price_7".to_string(), "Int64".to_string());
+ metadata.insert("ask_price_8".to_string(), "Int64".to_string());
+ metadata.insert("ask_price_9".to_string(), "Int64".to_string());
+ metadata.insert("bid_size_0".to_string(), "UInt64".to_string());
+ metadata.insert("bid_size_1".to_string(), "UInt64".to_string());
+ metadata.insert("bid_size_2".to_string(), "UInt64".to_string());
+ metadata.insert("bid_size_3".to_string(), "UInt64".to_string());
+ metadata.insert("bid_size_4".to_string(), "UInt64".to_string());
+ metadata.insert("bid_size_5".to_string(), "UInt64".to_string());
+ metadata.insert("bid_size_6".to_string(), "UInt64".to_string());
+ metadata.insert("bid_size_7".to_string(), "UInt64".to_string());
+ metadata.insert("bid_size_8".to_string(), "UInt64".to_string());
+ metadata.insert("bid_size_9".to_string(), "UInt64".to_string());
+ metadata.insert("ask_size_0".to_string(), "UInt64".to_string());
+ metadata.insert("ask_size_1".to_string(), "UInt64".to_string());
+ metadata.insert("ask_size_2".to_string(), "UInt64".to_string());
+ metadata.insert("ask_size_3".to_string(), "UInt64".to_string());
+ metadata.insert("ask_size_4".to_string(), "UInt64".to_string());
+ metadata.insert("ask_size_5".to_string(), "UInt64".to_string());
+ metadata.insert("ask_size_6".to_string(), "UInt64".to_string());
+ metadata.insert("ask_size_7".to_string(), "UInt64".to_string());
+ metadata.insert("ask_size_8".to_string(), "UInt64".to_string());
+ metadata.insert("ask_size_9".to_string(), "UInt64".to_string());
+ metadata.insert("bid_count_0".to_string(), "UInt32".to_string());
+ metadata.insert("bid_count_1".to_string(), "UInt32".to_string());
+ metadata.insert("bid_count_2".to_string(), "UInt32".to_string());
+ metadata.insert("bid_count_3".to_string(), "UInt32".to_string());
+ metadata.insert("bid_count_4".to_string(), "UInt32".to_string());
+ metadata.insert("bid_count_5".to_string(), "UInt32".to_string());
+ metadata.insert("bid_count_6".to_string(), "UInt32".to_string());
+ metadata.insert("bid_count_7".to_string(), "UInt32".to_string());
+ metadata.insert("bid_count_8".to_string(), "UInt32".to_string());
+ metadata.insert("bid_count_9".to_string(), "UInt32".to_string());
+ metadata.insert("ask_count_0".to_string(), "UInt32".to_string());
+ metadata.insert("ask_count_1".to_string(), "UInt32".to_string());
+ metadata.insert("ask_count_2".to_string(), "UInt32".to_string());
+ metadata.insert("ask_count_3".to_string(), "UInt32".to_string());
+ metadata.insert("ask_count_4".to_string(), "UInt32".to_string());
+ metadata.insert("ask_count_5".to_string(), "UInt32".to_string());
+ metadata.insert("ask_count_6".to_string(), "UInt32".to_string());
+ metadata.insert("ask_count_7".to_string(), "UInt32".to_string());
+ metadata.insert("ask_count_8".to_string(), "UInt32".to_string());
+ metadata.insert("ask_count_9".to_string(), "UInt32".to_string());
+ metadata.insert("flags".to_string(), "UInt8".to_string());
+ metadata.insert("sequence".to_string(), "UInt64".to_string());
+ metadata.insert("ts_event".to_string(), "UInt64".to_string());
+ metadata.insert("ts_init".to_string(), "UInt64".to_string());
+ metadata
+ }
+}
+
+// TODO: Exact format for Debug and Display TBD
+impl Display for OrderBookDepth10 {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "{},flags={},sequence={},ts_event={},ts_init={}",
+ self.instrument_id, self.flags, self.sequence, self.ts_event, self.ts_init
+ )
+ }
+}
+
+impl Serializable for OrderBookDepth10 {}
+
+////////////////////////////////////////////////////////////////////////////////
+// Stubs
+////////////////////////////////////////////////////////////////////////////////
+#[cfg(feature = "stubs")]
+#[allow(clippy::needless_range_loop)] // False positive?
+pub mod stubs {
+ use rstest::fixture;
+
+ use super::*;
+ use crate::{
+ data::order::BookOrder,
+ enums::OrderSide,
+ identifiers::instrument_id::InstrumentId,
+ types::{price::Price, quantity::Quantity},
+ };
+
+ #[fixture]
+ pub fn stub_depth10() -> OrderBookDepth10 {
+ let instrument_id = InstrumentId::from("AAPL.XNAS");
+ let flags = 0;
+ let sequence = 0;
+ let ts_event = 1;
+ let ts_init = 2;
+
+ let mut bids: [BookOrder; DEPTH10_LEN] = [BookOrder::default(); DEPTH10_LEN];
+ let mut asks: [BookOrder; DEPTH10_LEN] = [BookOrder::default(); DEPTH10_LEN];
+
+ // Create bids
+ let mut price = 99.00;
+ let mut quantity = 100.0;
+ let mut order_id = 1;
+
+ for i in 0..DEPTH10_LEN {
+ let order = BookOrder::new(
+ OrderSide::Buy,
+ Price::new(price, 2).unwrap(),
+ Quantity::new(quantity, 0).unwrap(),
+ order_id,
+ );
+
+ bids[i] = order;
+
+ price -= 1.0;
+ quantity += 100.0;
+ order_id += 1;
+ }
+
+ // Create asks
+ let mut price = 100.00;
+ let mut quantity = 100.0;
+ let mut order_id = 11;
+
+ for i in 0..DEPTH10_LEN {
+ let order = BookOrder::new(
+ OrderSide::Sell,
+ Price::new(price, 2).unwrap(),
+ Quantity::new(quantity, 0).unwrap(),
+ order_id,
+ );
+
+ asks[i] = order;
+
+ price += 1.0;
+ quantity += 100.0;
+ order_id += 1;
+ }
+
+ let bid_counts: [u32; 10] = [1; 10];
+ let ask_counts: [u32; 10] = [1; 10];
+
+ OrderBookDepth10::new(
+ instrument_id,
+ bids,
+ asks,
+ bid_counts,
+ ask_counts,
+ flags,
+ sequence,
+ ts_event,
+ ts_init,
+ )
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Tests
+////////////////////////////////////////////////////////////////////////////////
+#[cfg(test)]
+mod tests {
+ use rstest::rstest;
+
+ use super::{stubs::*, *};
+
+ #[rstest]
+ fn test_new(stub_depth10: OrderBookDepth10) {
+ let depth = stub_depth10;
+ let instrument_id = InstrumentId::from("AAPL.XNAS");
+ let flags = 0;
+ let sequence = 0;
+ let ts_event = 1;
+ let ts_init = 2;
+
+ assert_eq!(depth.instrument_id, instrument_id);
+ assert_eq!(depth.bids.len(), 10);
+ assert_eq!(depth.asks.len(), 10);
+ assert_eq!(depth.asks[9].price.as_f64(), 109.0);
+ assert_eq!(depth.asks[0].price.as_f64(), 100.0);
+ assert_eq!(depth.bids[0].price.as_f64(), 99.0);
+ assert_eq!(depth.bids[9].price.as_f64(), 90.0);
+ assert_eq!(depth.bid_counts.len(), 10);
+ assert_eq!(depth.ask_counts.len(), 10);
+ assert_eq!(depth.bid_counts[0], 1);
+ assert_eq!(depth.ask_counts[0], 1);
+ assert_eq!(depth.flags, flags);
+ assert_eq!(depth.sequence, sequence);
+ assert_eq!(depth.ts_event, ts_event);
+ assert_eq!(depth.ts_init, ts_init);
+ }
+
+ // TODO: Exact format for Debug and Display TBD
+ #[rstest]
+ fn test_display(stub_depth10: OrderBookDepth10) {
+ let depth = stub_depth10;
+ assert_eq!(
+ format!("{}", depth),
+ "AAPL.XNAS,flags=0,sequence=0,ts_event=1,ts_init=2".to_string()
+ );
+ }
+}
diff --git a/nautilus_core/model/src/data/mod.rs b/nautilus_core/model/src/data/mod.rs
index 2e454e7cba91..9ac16901fc1a 100644
--- a/nautilus_core/model/src/data/mod.rs
+++ b/nautilus_core/model/src/data/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -15,19 +15,26 @@
pub mod bar;
pub mod delta;
+pub mod deltas;
+pub mod depth;
pub mod order;
pub mod quote;
-pub mod ticker;
pub mod trade;
use nautilus_core::time::UnixNanos;
-use self::{bar::Bar, delta::OrderBookDelta, quote::QuoteTick, trade::TradeTick};
+use self::{
+ bar::Bar, delta::OrderBookDelta, deltas::OrderBookDeltas, depth::OrderBookDepth10,
+ quote::QuoteTick, trade::TradeTick,
+};
#[repr(C)]
-#[derive(Debug, Clone, Copy)]
+#[derive(Clone, Debug)]
+#[cfg_attr(feature = "trivial_copy", derive(Copy))]
+#[allow(clippy::large_enum_variant)] // TODO: Optimize this (largest variant 1008 vs 136 bytes)
pub enum Data {
Delta(OrderBookDelta),
+ Depth10(OrderBookDepth10),
Quote(QuoteTick),
Trade(TradeTick),
Bar(Bar),
@@ -41,6 +48,7 @@ impl HasTsInit for Data {
fn get_ts_init(&self) -> UnixNanos {
match self {
Data::Delta(d) => d.ts_init,
+ Data::Depth10(d) => d.ts_init,
Data::Quote(q) => q.ts_init,
Data::Trade(t) => t.ts_init,
Data::Bar(b) => b.ts_init,
@@ -54,6 +62,18 @@ impl HasTsInit for OrderBookDelta {
}
}
+impl HasTsInit for OrderBookDepth10 {
+ fn get_ts_init(&self) -> UnixNanos {
+ self.ts_init
+ }
+}
+
+impl HasTsInit for OrderBookDeltas {
+ fn get_ts_init(&self) -> UnixNanos {
+ self.ts_init
+ }
+}
+
impl HasTsInit for QuoteTick {
fn get_ts_init(&self) -> UnixNanos {
self.ts_init
@@ -83,6 +103,12 @@ impl From for Data {
}
}
+impl From for Data {
+ fn from(value: OrderBookDepth10) -> Self {
+ Self::Depth10(value)
+ }
+}
+
impl From for Data {
fn from(value: QuoteTick) -> Self {
Self::Quote(value)
@@ -103,5 +129,5 @@ impl From for Data {
#[no_mangle]
pub extern "C" fn data_clone(data: &Data) -> Data {
- *data
+ *data // Actually a copy
}
diff --git a/nautilus_core/model/src/data/order.rs b/nautilus_core/model/src/data/order.rs
index db0114a03a9e..b2fa5b313b45 100644
--- a/nautilus_core/model/src/data/order.rs
+++ b/nautilus_core/model/src/data/order.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -46,11 +46,12 @@ pub const NULL_ORDER: BookOrder = BookOrder {
/// Represents an order in a book.
#[repr(C)]
-#[derive(Copy, Clone, Eq, Debug, Serialize, Deserialize)]
+#[derive(Clone, Eq, Debug, Serialize, Deserialize)]
#[cfg_attr(
feature = "python",
pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
)]
+#[cfg_attr(feature = "trivial_copy", derive(Copy))]
pub struct BookOrder {
/// The order side.
pub side: OrderSide,
@@ -128,7 +129,11 @@ impl BookOrder {
}
}
-impl Serializable for BookOrder {}
+impl Default for BookOrder {
+ fn default() -> Self {
+ NULL_ORDER
+ }
+}
impl PartialEq for BookOrder {
fn eq(&self, other: &Self) -> bool {
@@ -152,10 +157,12 @@ impl Display for BookOrder {
}
}
+impl Serializable for BookOrder {}
+
////////////////////////////////////////////////////////////////////////////////
// Stubs
////////////////////////////////////////////////////////////////////////////////
-#[cfg(test)]
+#[cfg(feature = "stubs")]
pub mod stubs {
use rstest::fixture;
diff --git a/nautilus_core/model/src/data/quote.rs b/nautilus_core/model/src/data/quote.rs
index ea56eaa983f7..5c6a6ec29364 100644
--- a/nautilus_core/model/src/data/quote.rs
+++ b/nautilus_core/model/src/data/quote.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -38,12 +38,13 @@ use crate::{
/// Represents a single quote tick in a financial market.
#[repr(C)]
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(tag = "type")]
#[cfg_attr(
feature = "python",
pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
)]
+#[cfg_attr(feature = "trivial_copy", derive(Copy))]
pub struct QuoteTick {
/// The quotes instrument ID.
pub instrument_id: InstrumentId,
@@ -189,8 +190,6 @@ impl QuoteTick {
}
}
-impl Serializable for QuoteTick {}
-
impl Display for QuoteTick {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
@@ -206,10 +205,12 @@ impl Display for QuoteTick {
}
}
+impl Serializable for QuoteTick {}
+
////////////////////////////////////////////////////////////////////////////////
// Stubs
////////////////////////////////////////////////////////////////////////////////
-#[cfg(test)]
+#[cfg(feature = "stubs")]
pub mod stubs {
use rstest::fixture;
diff --git a/nautilus_core/model/src/data/ticker.rs b/nautilus_core/model/src/data/ticker.rs
deleted file mode 100644
index 18df9a905e8e..000000000000
--- a/nautilus_core/model/src/data/ticker.rs
+++ /dev/null
@@ -1,65 +0,0 @@
-// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
-// https://nautechsystems.io
-//
-// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
-// You may not use this file except in compliance with the License.
-// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// -------------------------------------------------------------------------------------------------
-
-use std::{
- fmt::{Display, Formatter},
- hash::Hash,
-};
-
-use nautilus_core::{serialization::Serializable, time::UnixNanos};
-use pyo3::prelude::*;
-use serde::{Deserialize, Serialize};
-
-use crate::identifiers::instrument_id::InstrumentId;
-
-/// Represents a single quote tick in a financial market.
-#[repr(C)]
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
-#[serde(tag = "type")]
-#[cfg_attr(
- feature = "python",
- pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
-)]
-pub struct Ticker {
- /// The quotes instrument ID.
- pub instrument_id: InstrumentId,
- /// The UNIX timestamp (nanoseconds) when the tick event occurred.
- pub ts_event: UnixNanos,
- /// The UNIX timestamp (nanoseconds) when the data object was initialized.
- pub ts_init: UnixNanos,
-}
-
-impl Ticker {
- #[must_use]
- pub fn new(instrument_id: InstrumentId, ts_event: UnixNanos, ts_init: UnixNanos) -> Self {
- Self {
- instrument_id,
- ts_event,
- ts_init,
- }
- }
-}
-
-impl Serializable for Ticker {}
-
-impl Display for Ticker {
- fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
- write!(
- f,
- "{},{},{}",
- self.instrument_id, self.ts_event, self.ts_init,
- )
- }
-}
diff --git a/nautilus_core/model/src/data/trade.rs b/nautilus_core/model/src/data/trade.rs
index 5cd2a0cbbc9c..504bdd62333c 100644
--- a/nautilus_core/model/src/data/trade.rs
+++ b/nautilus_core/model/src/data/trade.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -33,12 +33,13 @@ use crate::{
/// Represents a single trade tick in a financial market.
#[repr(C)]
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(tag = "type")]
#[cfg_attr(
feature = "python",
pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
)]
+#[cfg_attr(feature = "trivial_copy", derive(Copy))]
pub struct TradeTick {
/// The trade instrument ID.
pub instrument_id: InstrumentId,
@@ -142,8 +143,6 @@ impl TradeTick {
}
}
-impl Serializable for TradeTick {}
-
impl Display for TradeTick {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
@@ -159,10 +158,12 @@ impl Display for TradeTick {
}
}
+impl Serializable for TradeTick {}
+
////////////////////////////////////////////////////////////////////////////////
// Stubs
////////////////////////////////////////////////////////////////////////////////
-#[cfg(test)]
+#[cfg(feature = "stubs")]
pub mod stubs {
use rstest::fixture;
@@ -174,7 +175,7 @@ pub mod stubs {
};
#[fixture]
- pub fn trade_tick_ethusdt_buyer() -> TradeTick {
+ pub fn stub_trade_tick_ethusdt_buyer() -> TradeTick {
TradeTick {
instrument_id: InstrumentId::from("ETHUSDT-PERP.BINANCE"),
price: Price::from("10000.0000"),
@@ -200,8 +201,8 @@ mod tests {
use crate::{data::trade::TradeTick, enums::AggressorSide};
#[rstest]
- fn test_to_string(trade_tick_ethusdt_buyer: TradeTick) {
- let tick = trade_tick_ethusdt_buyer;
+ fn test_to_string(stub_trade_tick_ethusdt_buyer: TradeTick) {
+ let tick = stub_trade_tick_ethusdt_buyer;
assert_eq!(
tick.to_string(),
"ETHUSDT-PERP.BINANCE,10000.0000,1.00000000,BUYER,123456789,0"
@@ -227,9 +228,9 @@ mod tests {
}
#[rstest]
- fn test_from_pyobject(trade_tick_ethusdt_buyer: TradeTick) {
+ fn test_from_pyobject(stub_trade_tick_ethusdt_buyer: TradeTick) {
pyo3::prepare_freethreaded_python();
- let tick = trade_tick_ethusdt_buyer;
+ let tick = stub_trade_tick_ethusdt_buyer;
Python::with_gil(|py| {
let tick_pyobject = tick.into_py(py);
@@ -239,16 +240,16 @@ mod tests {
}
#[rstest]
- fn test_json_serialization(trade_tick_ethusdt_buyer: TradeTick) {
- let tick = trade_tick_ethusdt_buyer;
+ fn test_json_serialization(stub_trade_tick_ethusdt_buyer: TradeTick) {
+ let tick = stub_trade_tick_ethusdt_buyer;
let serialized = tick.as_json_bytes().unwrap();
let deserialized = TradeTick::from_json_bytes(serialized).unwrap();
assert_eq!(deserialized, tick);
}
#[rstest]
- fn test_msgpack_serialization(trade_tick_ethusdt_buyer: TradeTick) {
- let tick = trade_tick_ethusdt_buyer;
+ fn test_msgpack_serialization(stub_trade_tick_ethusdt_buyer: TradeTick) {
+ let tick = stub_trade_tick_ethusdt_buyer;
let serialized = tick.as_msgpack_bytes().unwrap();
let deserialized = TradeTick::from_msgpack_bytes(serialized).unwrap();
assert_eq!(deserialized, tick);
diff --git a/nautilus_core/model/src/enums.rs b/nautilus_core/model/src/enums.rs
index cfa516bb4e57..055f7540eb09 100644
--- a/nautilus_core/model/src/enums.rs
+++ b/nautilus_core/model/src/enums.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -121,7 +121,7 @@ pub enum AggregationSource {
)]
pub enum AggressorSide {
/// There was no specific aggressor for the trade.
- NoAggressor = 0, // Will be replaced by `Option`
+ NoAggressor = 0,
/// The BUY order was the aggressor for the trade.
#[pyo3(name = "BUYER")]
Buyer = 1,
@@ -167,6 +167,7 @@ impl FromU8 for AggressorSide {
#[allow(non_camel_case_types)]
pub enum AssetClass {
/// Foreign exchange (FOREX) assets.
+ #[pyo3(name = "FX")]
FX = 1,
/// Equity / stock assets.
#[pyo3(name = "EQUITY")]
@@ -174,24 +175,18 @@ pub enum AssetClass {
/// Commodity assets.
#[pyo3(name = "COMMODITY")]
Commodity = 3,
- /// Metal commodity assets.
- #[pyo3(name = "METAL")]
- Metal = 4,
- /// Energy commodity assets.
- #[pyo3(name = "ENERGY")]
- Energy = 5,
- /// Fixed income bond assets.
- #[pyo3(name = "BOND")]
- Bond = 6,
- /// Index based assets.
+ /// Debt based assets.
+ #[pyo3(name = "DEBT")]
+ Debt = 4,
+ /// Index based assets (baskets).
#[pyo3(name = "INDEX")]
- Index = 7,
+ Index = 5,
/// Cryptocurrency or crypto token assets.
- #[pyo3(name = "CRYPTO_CURRENCY")]
- Cryptocurrency = 8,
- /// Sports betting instruments.
- #[pyo3(name = "SPORTS_BETTING")]
- SportsBetting = 9,
+ #[pyo3(name = "CRYPTOCURRENCY")]
+ Cryptocurrency = 6,
+ /// Alternative assets.
+ #[pyo3(name = "ALTERNATIVE")]
+ Alternative = 7,
}
/// The asset type for a financial market product.
@@ -217,28 +212,34 @@ pub enum AssetClass {
feature = "python",
pyclass(module = "nautilus_trader.core.nautilus_pyo3.model.enums")
)]
-pub enum AssetType {
- /// A spot market asset type. The current market price of an asset that is bought or sold for immediate delivery and payment.
+pub enum InstrumentClass {
+ /// A spot market instrument class. The current market price of an instrument that is bought or sold for immediate delivery and payment.
#[pyo3(name = "SPOT")]
Spot = 1,
- /// A swap asset type. A derivative contract through which two parties exchange the cash flows or liabilities from two different financial instruments.
+ /// A swap instrument class. A derivative contract through which two parties exchange the cash flows or liabilities from two different financial instruments.
#[pyo3(name = "SWAP")]
Swap = 2,
- /// A futures contract asset type. A legal agreement to buy or sell an asset at a predetermined price at a specified time in the future.
+ /// A futures contract instrument class. A legal agreement to buy or sell an asset at a predetermined price at a specified time in the future.
#[pyo3(name = "FUTURE")]
Future = 3,
- /// A forward derivative asset type. A customized contract between two parties to buy or sell an asset at a specified price on a future date.
+ /// A forward derivative instrument class. A customized contract between two parties to buy or sell an asset at a specified price on a future date.
#[pyo3(name = "FORWARD")]
Forward = 4,
- /// A contract-for-difference (CFD) asset type. A contract between an investor and a CFD broker to exchange the difference in the value of a financial product between the time the contract opens and closes.
+ /// A contract-for-difference (CFD) instrument class. A contract between an investor and a CFD broker to exchange the difference in the value of a financial product between the time the contract opens and closes.
#[pyo3(name = "CFD")]
Cfd = 5,
- /// An options contract asset type. A type of derivative that gives the holder the right, but not the obligation, to buy or sell an underlying asset at a predetermined price before or at a certain future date.
+ /// A bond instrument class. A type of debt investment where an investor loans money to an entity (typically corporate or governmental) which borrows the funds for a defined period of time at a variable or fixed interest rate.
+ #[pyo3(name = "BOND")]
+ Bond = 6,
+ /// An options contract instrument class. A type of derivative that gives the holder the right, but not the obligation, to buy or sell an underlying asset at a predetermined price before or at a certain future date.
#[pyo3(name = "OPTION")]
- Option = 6,
- /// A warrant asset type. A derivative that gives the holder the right, but not the obligation, to buy or sell a security—most commonly an equity—at a certain price before expiration.
+ Option = 7,
+ /// A warrant instrument class. A derivative that gives the holder the right, but not the obligation, to buy or sell a security—most commonly an equity—at a certain price before expiration.
#[pyo3(name = "WARRANT")]
- Warrant = 7,
+ Warrant = 8,
+ /// A warrant instrument class. A derivative that gives the holder the right, but not the obligation, to buy or sell a security—most commonly an equity—at a certain price before expiration.
+ #[pyo3(name = "SPORTS_BETTING")]
+ SportsBetting = 9,
}
/// The aggregation method through which a bar is generated and closed.
@@ -725,8 +726,8 @@ pub enum OptionKind {
pyclass(module = "nautilus_trader.core.nautilus_pyo3.model.enums")
)]
pub enum OrderSide {
- /// No order side is specified (only valid in the context of a filter for actions involving orders).
- NoOrderSide = 0, // Will be replaced by `Option`
+ /// No order side is specified.
+ NoOrderSide = 0,
/// The order is a BUY.
#[pyo3(name = "BUY")]
Buy = 1,
@@ -1144,7 +1145,7 @@ enum_strum_serde!(AccountType);
enum_strum_serde!(AggregationSource);
enum_strum_serde!(AggressorSide);
enum_strum_serde!(AssetClass);
-enum_strum_serde!(AssetType);
+enum_strum_serde!(InstrumentClass);
enum_strum_serde!(BarAggregation);
enum_strum_serde!(BookAction);
enum_strum_serde!(BookType);
diff --git a/nautilus_core/model/src/events/account/mod.rs b/nautilus_core/model/src/events/account/mod.rs
index ab26b27e6c66..faaa2dfcce88 100644
--- a/nautilus_core/model/src/events/account/mod.rs
+++ b/nautilus_core/model/src/events/account/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/events/account/state.rs b/nautilus_core/model/src/events/account/state.rs
index f745731f0b1a..bf0dd4d4aff6 100644
--- a/nautilus_core/model/src/events/account/state.rs
+++ b/nautilus_core/model/src/events/account/state.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/events/mod.rs b/nautilus_core/model/src/events/mod.rs
index ccc74e178eb7..718817de89d0 100644
--- a/nautilus_core/model/src/events/mod.rs
+++ b/nautilus_core/model/src/events/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/events/order/accepted.rs b/nautilus_core/model/src/events/order/accepted.rs
index e37a89e47f35..fb055eb3963d 100644
--- a/nautilus_core/model/src/events/order/accepted.rs
+++ b/nautilus_core/model/src/events/order/accepted.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -27,7 +27,7 @@ use crate::identifiers::{
};
#[repr(C)]
-#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, Builder)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Serialize, Deserialize, Builder)]
#[builder(default)]
#[serde(tag = "type")]
#[cfg_attr(
diff --git a/nautilus_core/model/src/events/order/cancel_rejected.rs b/nautilus_core/model/src/events/order/cancel_rejected.rs
index 594b96e8f522..3ffd389dd72e 100644
--- a/nautilus_core/model/src/events/order/cancel_rejected.rs
+++ b/nautilus_core/model/src/events/order/cancel_rejected.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -28,7 +28,7 @@ use crate::identifiers::{
};
#[repr(C)]
-#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, Builder)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Serialize, Deserialize, Builder)]
#[builder(default)]
#[serde(tag = "type")]
#[cfg_attr(
diff --git a/nautilus_core/model/src/events/order/canceled.rs b/nautilus_core/model/src/events/order/canceled.rs
index 889ab44958b0..4a4a4ca8bc03 100644
--- a/nautilus_core/model/src/events/order/canceled.rs
+++ b/nautilus_core/model/src/events/order/canceled.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -27,7 +27,7 @@ use crate::identifiers::{
};
#[repr(C)]
-#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, Builder)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Serialize, Deserialize, Builder)]
#[builder(default)]
#[serde(tag = "type")]
#[cfg_attr(
diff --git a/nautilus_core/model/src/events/order/denied.rs b/nautilus_core/model/src/events/order/denied.rs
index 372fef16d76e..ee118746ba9c 100644
--- a/nautilus_core/model/src/events/order/denied.rs
+++ b/nautilus_core/model/src/events/order/denied.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/events/order/emulated.rs b/nautilus_core/model/src/events/order/emulated.rs
index ab2f3244e7c3..5024e05b1838 100644
--- a/nautilus_core/model/src/events/order/emulated.rs
+++ b/nautilus_core/model/src/events/order/emulated.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/events/order/event.rs b/nautilus_core/model/src/events/order/event.rs
index 4fe8f13dea63..787caa4d010e 100644
--- a/nautilus_core/model/src/events/order/event.rs
+++ b/nautilus_core/model/src/events/order/event.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/events/order/expired.rs b/nautilus_core/model/src/events/order/expired.rs
index 339bde3c8400..7c360a5ea26b 100644
--- a/nautilus_core/model/src/events/order/expired.rs
+++ b/nautilus_core/model/src/events/order/expired.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -27,7 +27,7 @@ use crate::identifiers::{
};
#[repr(C)]
-#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, Builder)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Serialize, Deserialize, Builder)]
#[builder(default)]
#[serde(tag = "type")]
#[cfg_attr(
diff --git a/nautilus_core/model/src/events/order/filled.rs b/nautilus_core/model/src/events/order/filled.rs
index fb0c3794e0eb..6fc271ac9386 100644
--- a/nautilus_core/model/src/events/order/filled.rs
+++ b/nautilus_core/model/src/events/order/filled.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -32,7 +32,7 @@ use crate::{
};
#[repr(C)]
-#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize, Builder)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize, Builder)]
#[builder(default)]
#[serde(tag = "type")]
#[cfg_attr(
diff --git a/nautilus_core/model/src/events/order/initialized.rs b/nautilus_core/model/src/events/order/initialized.rs
index 7ee7af6755cd..adb05e2f6aa5 100644
--- a/nautilus_core/model/src/events/order/initialized.rs
+++ b/nautilus_core/model/src/events/order/initialized.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/events/order/mod.rs b/nautilus_core/model/src/events/order/mod.rs
index f08647a71f36..ebb347470a46 100644
--- a/nautilus_core/model/src/events/order/mod.rs
+++ b/nautilus_core/model/src/events/order/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/events/order/modify_rejected.rs b/nautilus_core/model/src/events/order/modify_rejected.rs
index 3a4076cefbdf..7dee042a446e 100644
--- a/nautilus_core/model/src/events/order/modify_rejected.rs
+++ b/nautilus_core/model/src/events/order/modify_rejected.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -28,7 +28,7 @@ use crate::identifiers::{
};
#[repr(C)]
-#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, Builder)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Serialize, Deserialize, Builder)]
#[builder(default)]
#[serde(tag = "type")]
#[cfg_attr(
diff --git a/nautilus_core/model/src/events/order/pending_cancel.rs b/nautilus_core/model/src/events/order/pending_cancel.rs
index 2fb1a0d7dcd9..747d7368c149 100644
--- a/nautilus_core/model/src/events/order/pending_cancel.rs
+++ b/nautilus_core/model/src/events/order/pending_cancel.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -27,7 +27,7 @@ use crate::identifiers::{
};
#[repr(C)]
-#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, Builder)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Serialize, Deserialize, Builder)]
#[builder(default)]
#[serde(tag = "type")]
#[cfg_attr(
diff --git a/nautilus_core/model/src/events/order/pending_update.rs b/nautilus_core/model/src/events/order/pending_update.rs
index e81bf06f9fb2..3fdd8bf37c3b 100644
--- a/nautilus_core/model/src/events/order/pending_update.rs
+++ b/nautilus_core/model/src/events/order/pending_update.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -27,7 +27,7 @@ use crate::identifiers::{
};
#[repr(C)]
-#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, Builder)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Serialize, Deserialize, Builder)]
#[builder(default)]
#[serde(tag = "type")]
#[cfg_attr(
diff --git a/nautilus_core/model/src/events/order/rejected.rs b/nautilus_core/model/src/events/order/rejected.rs
index 2e0c8c68f9f6..da1dea8ee6a5 100644
--- a/nautilus_core/model/src/events/order/rejected.rs
+++ b/nautilus_core/model/src/events/order/rejected.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -28,7 +28,7 @@ use crate::identifiers::{
};
#[repr(C)]
-#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, Builder)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Serialize, Deserialize, Builder)]
#[builder(default)]
#[serde(tag = "type")]
#[cfg_attr(
diff --git a/nautilus_core/model/src/events/order/released.rs b/nautilus_core/model/src/events/order/released.rs
index 56421f69a34e..462b29c3e45b 100644
--- a/nautilus_core/model/src/events/order/released.rs
+++ b/nautilus_core/model/src/events/order/released.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/events/order/stubs.rs b/nautilus_core/model/src/events/order/stubs.rs
index 785c10643157..d63325b3629e 100644
--- a/nautilus_core/model/src/events/order/stubs.rs
+++ b/nautilus_core/model/src/events/order/stubs.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/events/order/submitted.rs b/nautilus_core/model/src/events/order/submitted.rs
index ae0a033bc5a0..5ee207887f5f 100644
--- a/nautilus_core/model/src/events/order/submitted.rs
+++ b/nautilus_core/model/src/events/order/submitted.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -27,7 +27,7 @@ use crate::identifiers::{
};
#[repr(C)]
-#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, Builder)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Serialize, Deserialize, Builder)]
#[builder(default)]
#[serde(tag = "type")]
#[cfg_attr(
diff --git a/nautilus_core/model/src/events/order/triggered.rs b/nautilus_core/model/src/events/order/triggered.rs
index 355fe359877b..2248750eeb66 100644
--- a/nautilus_core/model/src/events/order/triggered.rs
+++ b/nautilus_core/model/src/events/order/triggered.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -27,7 +27,7 @@ use crate::identifiers::{
};
#[repr(C)]
-#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, Builder)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Serialize, Deserialize, Builder)]
#[builder(default)]
#[serde(tag = "type")]
#[cfg_attr(
diff --git a/nautilus_core/model/src/events/order/updated.rs b/nautilus_core/model/src/events/order/updated.rs
index 646c7b9ce76c..1b5fc30f2c5f 100644
--- a/nautilus_core/model/src/events/order/updated.rs
+++ b/nautilus_core/model/src/events/order/updated.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -30,7 +30,7 @@ use crate::{
};
#[repr(C)]
-#[derive(Clone, PartialEq, Eq, Debug, Default, Serialize, Deserialize, Builder)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Serialize, Deserialize, Builder)]
#[builder(default)]
#[serde(tag = "type")]
#[cfg_attr(
diff --git a/nautilus_core/model/src/events/position/changed.rs b/nautilus_core/model/src/events/position/changed.rs
index 22aca7f0c474..15eb2911797a 100644
--- a/nautilus_core/model/src/events/position/changed.rs
+++ b/nautilus_core/model/src/events/position/changed.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/events/position/closed.rs b/nautilus_core/model/src/events/position/closed.rs
index 45ccc45afd1a..661e05fea306 100644
--- a/nautilus_core/model/src/events/position/closed.rs
+++ b/nautilus_core/model/src/events/position/closed.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/events/position/mod.rs b/nautilus_core/model/src/events/position/mod.rs
index 0e1bb60a599c..41b70f5ea746 100644
--- a/nautilus_core/model/src/events/position/mod.rs
+++ b/nautilus_core/model/src/events/position/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/events/position/opened.rs b/nautilus_core/model/src/events/position/opened.rs
index 7b37ab73b687..b343bad8da0b 100644
--- a/nautilus_core/model/src/events/position/opened.rs
+++ b/nautilus_core/model/src/events/position/opened.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/events/position/state.rs b/nautilus_core/model/src/events/position/state.rs
index b280208f5fdb..debaa604f59c 100644
--- a/nautilus_core/model/src/events/position/state.rs
+++ b/nautilus_core/model/src/events/position/state.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/data/bar.rs b/nautilus_core/model/src/ffi/data/bar.rs
index 70a3a90db102..a021896c14c4 100644
--- a/nautilus_core/model/src/ffi/data/bar.rs
+++ b/nautilus_core/model/src/ffi/data/bar.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/data/delta.rs b/nautilus_core/model/src/ffi/data/delta.rs
index 6b6b000ea398..9cd13270f609 100644
--- a/nautilus_core/model/src/ffi/data/delta.rs
+++ b/nautilus_core/model/src/ffi/data/delta.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/data/depth.rs b/nautilus_core/model/src/ffi/data/depth.rs
new file mode 100644
index 000000000000..a76f9e6bd54c
--- /dev/null
+++ b/nautilus_core/model/src/ffi/data/depth.rs
@@ -0,0 +1,107 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
+// https://nautechsystems.io
+//
+// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
+// You may not use this file except in compliance with the License.
+// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// -------------------------------------------------------------------------------------------------
+
+use std::{
+ collections::hash_map::DefaultHasher,
+ hash::{Hash, Hasher},
+};
+
+use nautilus_core::time::UnixNanos;
+
+use crate::{
+ data::{
+ depth::{OrderBookDepth10, DEPTH10_LEN},
+ order::BookOrder,
+ },
+ identifiers::instrument_id::InstrumentId,
+};
+
+/// # Safety
+///
+/// - Assumes `bids` and `asks` are valid pointers to arrays of `BookOrder` of length 10.
+/// - Assumes `bid_counts` and `ask_counts` are valid pointers to arrays of `u32` of length 10.
+#[no_mangle]
+pub unsafe extern "C" fn orderbook_depth10_new(
+ instrument_id: InstrumentId,
+ bids_ptr: *const BookOrder,
+ asks_ptr: *const BookOrder,
+ bid_counts_ptr: *const u32,
+ ask_counts_ptr: *const u32,
+ flags: u8,
+ sequence: u64,
+ ts_event: UnixNanos,
+ ts_init: UnixNanos,
+) -> OrderBookDepth10 {
+ // Safety: Ensure that `bids_ptr` and `asks_ptr` are valid pointers.
+ // The caller must guarantee that they point to arrays of `BookOrder` of at least `DEPTH10_LEN` length.
+ assert!(!bids_ptr.is_null());
+ assert!(!asks_ptr.is_null());
+ assert!(!bid_counts_ptr.is_null());
+ assert!(!ask_counts_ptr.is_null());
+
+ let bids_slice = std::slice::from_raw_parts(bids_ptr, DEPTH10_LEN);
+ let asks_slice = std::slice::from_raw_parts(asks_ptr, DEPTH10_LEN);
+ let bids: [BookOrder; DEPTH10_LEN] = bids_slice.try_into().expect("Slice length != 10");
+ let asks: [BookOrder; DEPTH10_LEN] = asks_slice.try_into().expect("Slice length != 10");
+
+ let bid_counts_slice = std::slice::from_raw_parts(bid_counts_ptr, DEPTH10_LEN);
+ let ask_counts_slice = std::slice::from_raw_parts(ask_counts_ptr, DEPTH10_LEN);
+ let bid_counts: [u32; DEPTH10_LEN] = bid_counts_slice.try_into().expect("Slice length != 10");
+ let ask_counts: [u32; DEPTH10_LEN] = ask_counts_slice.try_into().expect("Slice length != 10");
+
+ OrderBookDepth10::new(
+ instrument_id,
+ bids,
+ asks,
+ bid_counts,
+ ask_counts,
+ flags,
+ sequence,
+ ts_event,
+ ts_init,
+ )
+}
+
+#[no_mangle]
+pub extern "C" fn orderbook_depth10_eq(lhs: &OrderBookDepth10, rhs: &OrderBookDepth10) -> u8 {
+ u8::from(lhs == rhs)
+}
+
+#[no_mangle]
+pub extern "C" fn orderbook_depth10_hash(delta: &OrderBookDepth10) -> u64 {
+ let mut hasher = DefaultHasher::new();
+ delta.hash(&mut hasher);
+ hasher.finish()
+}
+
+#[no_mangle]
+pub extern "C" fn orderbook_depth10_bids_array(depth: &OrderBookDepth10) -> *const BookOrder {
+ depth.bids.as_ptr()
+}
+
+#[no_mangle]
+pub extern "C" fn orderbook_depth10_asks_array(depth: &OrderBookDepth10) -> *const BookOrder {
+ depth.asks.as_ptr()
+}
+
+#[no_mangle]
+pub extern "C" fn orderbook_depth10_bid_counts_array(depth: &OrderBookDepth10) -> *const u32 {
+ depth.bid_counts.as_ptr()
+}
+
+#[no_mangle]
+pub extern "C" fn orderbook_depth10_ask_counts_array(depth: &OrderBookDepth10) -> *const u32 {
+ depth.ask_counts.as_ptr()
+}
diff --git a/nautilus_core/model/src/ffi/data/mod.rs b/nautilus_core/model/src/ffi/data/mod.rs
index 39541658833a..e1a81c7edec2 100644
--- a/nautilus_core/model/src/ffi/data/mod.rs
+++ b/nautilus_core/model/src/ffi/data/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -15,7 +15,7 @@
pub mod bar;
pub mod delta;
+pub mod depth;
pub mod order;
pub mod quote;
-pub mod ticker;
pub mod trade;
diff --git a/nautilus_core/model/src/ffi/data/order.rs b/nautilus_core/model/src/ffi/data/order.rs
index 16488ca0ddbe..852115d82a68 100644
--- a/nautilus_core/model/src/ffi/data/order.rs
+++ b/nautilus_core/model/src/ffi/data/order.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/data/quote.rs b/nautilus_core/model/src/ffi/data/quote.rs
index 6440b6945510..be0c12152c6e 100644
--- a/nautilus_core/model/src/ffi/data/quote.rs
+++ b/nautilus_core/model/src/ffi/data/quote.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/data/trade.rs b/nautilus_core/model/src/ffi/data/trade.rs
index 2809b8159c95..011069ae21ee 100644
--- a/nautilus_core/model/src/ffi/data/trade.rs
+++ b/nautilus_core/model/src/ffi/data/trade.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/enums.rs b/nautilus_core/model/src/ffi/enums.rs
index 018aed3a16d4..aa929911c4e9 100644
--- a/nautilus_core/model/src/ffi/enums.rs
+++ b/nautilus_core/model/src/ffi/enums.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -18,8 +18,8 @@ use std::{ffi::c_char, str::FromStr};
use nautilus_core::ffi::string::{cstr_to_str, str_to_cstr};
use crate::enums::{
- AccountType, AggregationSource, AggressorSide, AssetClass, AssetType, BarAggregation,
- BookAction, BookType, ContingencyType, CurrencyType, HaltReason, InstrumentCloseType,
+ AccountType, AggregationSource, AggressorSide, AssetClass, BarAggregation, BookAction,
+ BookType, ContingencyType, CurrencyType, HaltReason, InstrumentClass, InstrumentCloseType,
LiquiditySide, MarketStatus, OmsType, OptionKind, OrderSide, OrderStatus, OrderType,
PositionSide, PriceType, TimeInForce, TradingState, TrailingOffsetType, TriggerType,
};
@@ -32,6 +32,7 @@ pub extern "C" fn account_type_to_cstr(value: AccountType) -> *const c_char {
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn account_type_from_cstr(ptr: *const c_char) -> AccountType {
@@ -48,6 +49,7 @@ pub extern "C" fn aggregation_source_to_cstr(value: AggregationSource) -> *const
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn aggregation_source_from_cstr(ptr: *const c_char) -> AggregationSource {
@@ -64,6 +66,7 @@ pub extern "C" fn aggressor_side_to_cstr(value: AggressorSide) -> *const c_char
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn aggressor_side_from_cstr(ptr: *const c_char) -> AggressorSide {
@@ -80,6 +83,7 @@ pub extern "C" fn asset_class_to_cstr(value: AssetClass) -> *const c_char {
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn asset_class_from_cstr(ptr: *const c_char) -> AssetClass {
@@ -89,19 +93,20 @@ pub unsafe extern "C" fn asset_class_from_cstr(ptr: *const c_char) -> AssetClass
}
#[no_mangle]
-pub extern "C" fn asset_type_to_cstr(value: AssetType) -> *const c_char {
+pub extern "C" fn instrument_class_to_cstr(value: InstrumentClass) -> *const c_char {
str_to_cstr(value.as_ref())
}
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
-pub unsafe extern "C" fn asset_type_from_cstr(ptr: *const c_char) -> AssetType {
+pub unsafe extern "C" fn instrument_class_from_cstr(ptr: *const c_char) -> InstrumentClass {
let value = cstr_to_str(ptr);
- AssetType::from_str(value)
- .unwrap_or_else(|_| panic!("invalid `AssetType` enum string value, was '{value}'"))
+ InstrumentClass::from_str(value)
+ .unwrap_or_else(|_| panic!("invalid `InstrumentClass` enum string value, was '{value}'"))
}
#[no_mangle]
@@ -112,6 +117,7 @@ pub extern "C" fn bar_aggregation_to_cstr(value: BarAggregation) -> *const c_cha
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn bar_aggregation_from_cstr(ptr: *const c_char) -> BarAggregation {
@@ -128,6 +134,7 @@ pub extern "C" fn book_action_to_cstr(value: BookAction) -> *const c_char {
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn book_action_from_cstr(ptr: *const c_char) -> BookAction {
@@ -144,6 +151,7 @@ pub extern "C" fn book_type_to_cstr(value: BookType) -> *const c_char {
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn book_type_from_cstr(ptr: *const c_char) -> BookType {
@@ -160,6 +168,7 @@ pub extern "C" fn contingency_type_to_cstr(value: ContingencyType) -> *const c_c
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn contingency_type_from_cstr(ptr: *const c_char) -> ContingencyType {
@@ -176,6 +185,7 @@ pub extern "C" fn currency_type_to_cstr(value: CurrencyType) -> *const c_char {
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn currency_type_from_cstr(ptr: *const c_char) -> CurrencyType {
@@ -187,6 +197,7 @@ pub unsafe extern "C" fn currency_type_from_cstr(ptr: *const c_char) -> Currency
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn instrument_close_type_from_cstr(
@@ -211,6 +222,7 @@ pub extern "C" fn liquidity_side_to_cstr(value: LiquiditySide) -> *const c_char
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn liquidity_side_from_cstr(ptr: *const c_char) -> LiquiditySide {
@@ -227,6 +239,7 @@ pub extern "C" fn market_status_to_cstr(value: MarketStatus) -> *const c_char {
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn market_status_from_cstr(ptr: *const c_char) -> MarketStatus {
@@ -243,6 +256,7 @@ pub extern "C" fn halt_reason_to_cstr(value: HaltReason) -> *const c_char {
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn halt_reason_from_cstr(ptr: *const c_char) -> HaltReason {
@@ -259,6 +273,7 @@ pub extern "C" fn oms_type_to_cstr(value: OmsType) -> *const c_char {
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn oms_type_from_cstr(ptr: *const c_char) -> OmsType {
@@ -275,6 +290,7 @@ pub extern "C" fn option_kind_to_cstr(value: OptionKind) -> *const c_char {
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn option_kind_from_cstr(ptr: *const c_char) -> OptionKind {
@@ -291,6 +307,7 @@ pub extern "C" fn order_side_to_cstr(value: OrderSide) -> *const c_char {
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn order_side_from_cstr(ptr: *const c_char) -> OrderSide {
@@ -307,6 +324,7 @@ pub extern "C" fn order_status_to_cstr(value: OrderStatus) -> *const c_char {
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn order_status_from_cstr(ptr: *const c_char) -> OrderStatus {
@@ -323,6 +341,7 @@ pub extern "C" fn order_type_to_cstr(value: OrderType) -> *const c_char {
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn order_type_from_cstr(ptr: *const c_char) -> OrderType {
@@ -339,6 +358,7 @@ pub extern "C" fn position_side_to_cstr(value: PositionSide) -> *const c_char {
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn position_side_from_cstr(ptr: *const c_char) -> PositionSide {
@@ -355,6 +375,7 @@ pub extern "C" fn price_type_to_cstr(value: PriceType) -> *const c_char {
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn price_type_from_cstr(ptr: *const c_char) -> PriceType {
@@ -371,6 +392,7 @@ pub extern "C" fn time_in_force_to_cstr(value: TimeInForce) -> *const c_char {
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn time_in_force_from_cstr(ptr: *const c_char) -> TimeInForce {
@@ -387,6 +409,7 @@ pub extern "C" fn trading_state_to_cstr(value: TradingState) -> *const c_char {
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn trading_state_from_cstr(ptr: *const c_char) -> TradingState {
@@ -403,6 +426,7 @@ pub extern "C" fn trailing_offset_type_to_cstr(value: TrailingOffsetType) -> *co
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn trailing_offset_type_from_cstr(ptr: *const c_char) -> TrailingOffsetType {
@@ -419,6 +443,7 @@ pub extern "C" fn trigger_type_to_cstr(value: TriggerType) -> *const c_char {
/// Returns an enum from a Python string.
///
/// # Safety
+///
/// - Assumes `ptr` is a valid C string pointer.
#[no_mangle]
pub unsafe extern "C" fn trigger_type_from_cstr(ptr: *const c_char) -> TriggerType {
diff --git a/nautilus_core/model/src/ffi/events/mod.rs b/nautilus_core/model/src/ffi/events/mod.rs
index ce9d7f22d783..0e1acff68393 100644
--- a/nautilus_core/model/src/ffi/events/mod.rs
+++ b/nautilus_core/model/src/ffi/events/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/events/order.rs b/nautilus_core/model/src/ffi/events/order.rs
index d0e4bfcd4eff..41813d1f73c6 100644
--- a/nautilus_core/model/src/ffi/events/order.rs
+++ b/nautilus_core/model/src/ffi/events/order.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/identifiers/account_id.rs b/nautilus_core/model/src/ffi/identifiers/account_id.rs
index 5faa000875d8..c1868fce08fd 100644
--- a/nautilus_core/model/src/ffi/identifiers/account_id.rs
+++ b/nautilus_core/model/src/ffi/identifiers/account_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/identifiers/client_id.rs b/nautilus_core/model/src/ffi/identifiers/client_id.rs
index b1fcbc315be4..3d797e02b7db 100644
--- a/nautilus_core/model/src/ffi/identifiers/client_id.rs
+++ b/nautilus_core/model/src/ffi/identifiers/client_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/identifiers/client_order_id.rs b/nautilus_core/model/src/ffi/identifiers/client_order_id.rs
index 9be5371890e5..325e3396d39a 100644
--- a/nautilus_core/model/src/ffi/identifiers/client_order_id.rs
+++ b/nautilus_core/model/src/ffi/identifiers/client_order_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/identifiers/component_id.rs b/nautilus_core/model/src/ffi/identifiers/component_id.rs
index df65d316c25a..efa696daf088 100644
--- a/nautilus_core/model/src/ffi/identifiers/component_id.rs
+++ b/nautilus_core/model/src/ffi/identifiers/component_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/identifiers/exec_algorithm_id.rs b/nautilus_core/model/src/ffi/identifiers/exec_algorithm_id.rs
index f9af6b499e46..a031f0a80c0d 100644
--- a/nautilus_core/model/src/ffi/identifiers/exec_algorithm_id.rs
+++ b/nautilus_core/model/src/ffi/identifiers/exec_algorithm_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/identifiers/instrument_id.rs b/nautilus_core/model/src/ffi/identifiers/instrument_id.rs
index 50f24040cc11..99d4b8750c3e 100644
--- a/nautilus_core/model/src/ffi/identifiers/instrument_id.rs
+++ b/nautilus_core/model/src/ffi/identifiers/instrument_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/identifiers/mod.rs b/nautilus_core/model/src/ffi/identifiers/mod.rs
index 6c3963d56203..ce8b516f3b2a 100644
--- a/nautilus_core/model/src/ffi/identifiers/mod.rs
+++ b/nautilus_core/model/src/ffi/identifiers/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/identifiers/order_list_id.rs b/nautilus_core/model/src/ffi/identifiers/order_list_id.rs
index b2dca18973ac..0f74753e1f4c 100644
--- a/nautilus_core/model/src/ffi/identifiers/order_list_id.rs
+++ b/nautilus_core/model/src/ffi/identifiers/order_list_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/identifiers/position_id.rs b/nautilus_core/model/src/ffi/identifiers/position_id.rs
index 6d99ef357bae..c68425c56b74 100644
--- a/nautilus_core/model/src/ffi/identifiers/position_id.rs
+++ b/nautilus_core/model/src/ffi/identifiers/position_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/identifiers/strategy_id.rs b/nautilus_core/model/src/ffi/identifiers/strategy_id.rs
index 458b2d7fcaca..8fc42eb56542 100644
--- a/nautilus_core/model/src/ffi/identifiers/strategy_id.rs
+++ b/nautilus_core/model/src/ffi/identifiers/strategy_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/identifiers/symbol.rs b/nautilus_core/model/src/ffi/identifiers/symbol.rs
index 142f450119c8..dee1c443cb29 100644
--- a/nautilus_core/model/src/ffi/identifiers/symbol.rs
+++ b/nautilus_core/model/src/ffi/identifiers/symbol.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/identifiers/trade_id.rs b/nautilus_core/model/src/ffi/identifiers/trade_id.rs
index c6c9574da84e..5d4ab24cc353 100644
--- a/nautilus_core/model/src/ffi/identifiers/trade_id.rs
+++ b/nautilus_core/model/src/ffi/identifiers/trade_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/identifiers/trader_id.rs b/nautilus_core/model/src/ffi/identifiers/trader_id.rs
index 581cee52ec66..b1bfd540acd8 100644
--- a/nautilus_core/model/src/ffi/identifiers/trader_id.rs
+++ b/nautilus_core/model/src/ffi/identifiers/trader_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/identifiers/venue.rs b/nautilus_core/model/src/ffi/identifiers/venue.rs
index 1cedb0155dc6..9773bb8e14a3 100644
--- a/nautilus_core/model/src/ffi/identifiers/venue.rs
+++ b/nautilus_core/model/src/ffi/identifiers/venue.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/identifiers/venue_order_id.rs b/nautilus_core/model/src/ffi/identifiers/venue_order_id.rs
index 14083582da30..e9f0d4aa8a44 100644
--- a/nautilus_core/model/src/ffi/identifiers/venue_order_id.rs
+++ b/nautilus_core/model/src/ffi/identifiers/venue_order_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/instruments/mod.rs b/nautilus_core/model/src/ffi/instruments/mod.rs
new file mode 100644
index 000000000000..2061bd68627f
--- /dev/null
+++ b/nautilus_core/model/src/ffi/instruments/mod.rs
@@ -0,0 +1,16 @@
+// -------------------------------------------------------------------------------------------------
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
+// https://nautechsystems.io
+//
+// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
+// You may not use this file except in compliance with the License.
+// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// -------------------------------------------------------------------------------------------------
+
+pub mod synthetic;
diff --git a/nautilus_core/model/src/instruments/synthetic_api.rs b/nautilus_core/model/src/ffi/instruments/synthetic.rs
similarity index 98%
rename from nautilus_core/model/src/instruments/synthetic_api.rs
rename to nautilus_core/model/src/ffi/instruments/synthetic.rs
index 2e5d866e7788..f860f47d8093 100644
--- a/nautilus_core/model/src/instruments/synthetic_api.rs
+++ b/nautilus_core/model/src/ffi/instruments/synthetic.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -27,9 +27,9 @@ use nautilus_core::{
time::UnixNanos,
};
-use super::synthetic::SyntheticInstrument;
use crate::{
identifiers::{instrument_id::InstrumentId, symbol::Symbol},
+ instruments::synthetic::SyntheticInstrument,
types::price::{Price, ERROR_PRICE},
};
diff --git a/nautilus_core/model/src/ffi/mod.rs b/nautilus_core/model/src/ffi/mod.rs
index 4b13d08ae910..349b67048cb8 100644
--- a/nautilus_core/model/src/ffi/mod.rs
+++ b/nautilus_core/model/src/ffi/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -17,5 +17,6 @@ pub mod data;
pub mod enums;
pub mod events;
pub mod identifiers;
+pub mod instruments;
pub mod orderbook;
pub mod types;
diff --git a/nautilus_core/model/src/ffi/orderbook/book.rs b/nautilus_core/model/src/ffi/orderbook/book.rs
index 44ff6a47b156..5d208b58cb89 100644
--- a/nautilus_core/model/src/ffi/orderbook/book.rs
+++ b/nautilus_core/model/src/ffi/orderbook/book.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -22,7 +22,10 @@ use nautilus_core::ffi::{cvec::CVec, string::str_to_cstr};
use super::level::Level_API;
use crate::{
- data::{delta::OrderBookDelta, order::BookOrder, quote::QuoteTick, trade::TradeTick},
+ data::{
+ delta::OrderBookDelta, depth::OrderBookDepth10, order::BookOrder, quote::QuoteTick,
+ trade::TradeTick,
+ },
enums::{BookType, OrderSide},
identifiers::instrument_id::InstrumentId,
orderbook::book::OrderBook,
@@ -145,6 +148,11 @@ pub extern "C" fn orderbook_apply_delta(book: &mut OrderBook_API, delta: OrderBo
book.apply_delta(delta)
}
+#[no_mangle]
+pub extern "C" fn orderbook_apply_depth(book: &mut OrderBook_API, depth: OrderBookDepth10) {
+ book.apply_depth(depth)
+}
+
#[no_mangle]
pub extern "C" fn orderbook_bids(book: &mut OrderBook_API) -> CVec {
book.bids()
diff --git a/nautilus_core/model/src/ffi/orderbook/level.rs b/nautilus_core/model/src/ffi/orderbook/level.rs
index 4eecb0214aef..5f862cc84dac 100644
--- a/nautilus_core/model/src/ffi/orderbook/level.rs
+++ b/nautilus_core/model/src/ffi/orderbook/level.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/orderbook/mod.rs b/nautilus_core/model/src/ffi/orderbook/mod.rs
index 56ca69d417e8..6f48823c5966 100644
--- a/nautilus_core/model/src/ffi/orderbook/mod.rs
+++ b/nautilus_core/model/src/ffi/orderbook/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/types/currency.rs b/nautilus_core/model/src/ffi/types/currency.rs
index 3a74d6f91b55..8b99c8671bf6 100644
--- a/nautilus_core/model/src/ffi/types/currency.rs
+++ b/nautilus_core/model/src/ffi/types/currency.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/types/mod.rs b/nautilus_core/model/src/ffi/types/mod.rs
index 3390a2bbb91b..4ba98224c433 100644
--- a/nautilus_core/model/src/ffi/types/mod.rs
+++ b/nautilus_core/model/src/ffi/types/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/types/money.rs b/nautilus_core/model/src/ffi/types/money.rs
index d7b57e289990..c78e30f5d0ab 100644
--- a/nautilus_core/model/src/ffi/types/money.rs
+++ b/nautilus_core/model/src/ffi/types/money.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/types/price.rs b/nautilus_core/model/src/ffi/types/price.rs
index 8e81ac29e8dd..182db374ced9 100644
--- a/nautilus_core/model/src/ffi/types/price.rs
+++ b/nautilus_core/model/src/ffi/types/price.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/ffi/types/quantity.rs b/nautilus_core/model/src/ffi/types/quantity.rs
index e8ae33ba6abd..68795d7a99dc 100644
--- a/nautilus_core/model/src/ffi/types/quantity.rs
+++ b/nautilus_core/model/src/ffi/types/quantity.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/identifiers/account_id.rs b/nautilus_core/model/src/identifiers/account_id.rs
index a17bf66cfdb0..9b586a4153ae 100644
--- a/nautilus_core/model/src/identifiers/account_id.rs
+++ b/nautilus_core/model/src/identifiers/account_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/identifiers/client_id.rs b/nautilus_core/model/src/identifiers/client_id.rs
index 2946c533aaa5..91567cea588f 100644
--- a/nautilus_core/model/src/identifiers/client_id.rs
+++ b/nautilus_core/model/src/identifiers/client_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/identifiers/client_order_id.rs b/nautilus_core/model/src/identifiers/client_order_id.rs
index 4b452f069ea0..82feb9500b69 100644
--- a/nautilus_core/model/src/identifiers/client_order_id.rs
+++ b/nautilus_core/model/src/identifiers/client_order_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/identifiers/component_id.rs b/nautilus_core/model/src/identifiers/component_id.rs
index 020e35300b97..ced0a8c39b56 100644
--- a/nautilus_core/model/src/identifiers/component_id.rs
+++ b/nautilus_core/model/src/identifiers/component_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/identifiers/exec_algorithm_id.rs b/nautilus_core/model/src/identifiers/exec_algorithm_id.rs
index b23c1d731c5c..7380d8edfc87 100644
--- a/nautilus_core/model/src/identifiers/exec_algorithm_id.rs
+++ b/nautilus_core/model/src/identifiers/exec_algorithm_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/identifiers/instrument_id.rs b/nautilus_core/model/src/identifiers/instrument_id.rs
index e30ed8c9a5cb..64a10977eb3d 100644
--- a/nautilus_core/model/src/identifiers/instrument_id.rs
+++ b/nautilus_core/model/src/identifiers/instrument_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/identifiers/macros.rs b/nautilus_core/model/src/identifiers/macros.rs
index 5c2bf6fdc076..827b73e3110c 100644
--- a/nautilus_core/model/src/identifiers/macros.rs
+++ b/nautilus_core/model/src/identifiers/macros.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/identifiers/mod.rs b/nautilus_core/model/src/identifiers/mod.rs
index 152f03adb222..dd452acec5d1 100644
--- a/nautilus_core/model/src/identifiers/mod.rs
+++ b/nautilus_core/model/src/identifiers/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/identifiers/order_list_id.rs b/nautilus_core/model/src/identifiers/order_list_id.rs
index f51d2bb28863..856979e87dfd 100644
--- a/nautilus_core/model/src/identifiers/order_list_id.rs
+++ b/nautilus_core/model/src/identifiers/order_list_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/identifiers/position_id.rs b/nautilus_core/model/src/identifiers/position_id.rs
index 04e9f7f787fb..38f72a8ac070 100644
--- a/nautilus_core/model/src/identifiers/position_id.rs
+++ b/nautilus_core/model/src/identifiers/position_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/identifiers/strategy_id.rs b/nautilus_core/model/src/identifiers/strategy_id.rs
index 4c2b3cd0a187..cc33a6ceb441 100644
--- a/nautilus_core/model/src/identifiers/strategy_id.rs
+++ b/nautilus_core/model/src/identifiers/strategy_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/identifiers/stubs.rs b/nautilus_core/model/src/identifiers/stubs.rs
index dc793d80dc3b..4a22883db5e4 100644
--- a/nautilus_core/model/src/identifiers/stubs.rs
+++ b/nautilus_core/model/src/identifiers/stubs.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/identifiers/symbol.rs b/nautilus_core/model/src/identifiers/symbol.rs
index 80c51af04de0..2cc0f514a5e8 100644
--- a/nautilus_core/model/src/identifiers/symbol.rs
+++ b/nautilus_core/model/src/identifiers/symbol.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/identifiers/trade_id.rs b/nautilus_core/model/src/identifiers/trade_id.rs
index f7a32ee527ee..592b272670a7 100644
--- a/nautilus_core/model/src/identifiers/trade_id.rs
+++ b/nautilus_core/model/src/identifiers/trade_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/identifiers/trader_id.rs b/nautilus_core/model/src/identifiers/trader_id.rs
index d60245abd296..7aa24ff4fd12 100644
--- a/nautilus_core/model/src/identifiers/trader_id.rs
+++ b/nautilus_core/model/src/identifiers/trader_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/identifiers/venue.rs b/nautilus_core/model/src/identifiers/venue.rs
index 09bcf2903ffa..3db92c0f756a 100644
--- a/nautilus_core/model/src/identifiers/venue.rs
+++ b/nautilus_core/model/src/identifiers/venue.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/identifiers/venue_order_id.rs b/nautilus_core/model/src/identifiers/venue_order_id.rs
index 7a46f8ec24d6..17105d0568ae 100644
--- a/nautilus_core/model/src/identifiers/venue_order_id.rs
+++ b/nautilus_core/model/src/identifiers/venue_order_id.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/instruments/crypto_future.rs b/nautilus_core/model/src/instruments/crypto_future.rs
index 927950f132ce..ec211dfeb5b7 100644
--- a/nautilus_core/model/src/instruments/crypto_future.rs
+++ b/nautilus_core/model/src/instruments/crypto_future.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -13,25 +13,22 @@
// limitations under the License.
// -------------------------------------------------------------------------------------------------
-#![allow(dead_code)] // Allow for development
-
use std::hash::{Hash, Hasher};
use anyhow::Result;
use nautilus_core::time::UnixNanos;
use pyo3::prelude::*;
-use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use super::Instrument;
use crate::{
- enums::{AssetClass, AssetType},
+ enums::{AssetClass, InstrumentClass},
identifiers::{instrument_id::InstrumentId, symbol::Symbol},
types::{currency::Currency, money::Money, price::Price, quantity::Quantity},
};
#[repr(C)]
-#[derive(Clone, Debug, Serialize, Deserialize)]
+#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[cfg_attr(
feature = "python",
pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
@@ -48,10 +45,6 @@ pub struct CryptoFuture {
pub size_precision: u8,
pub price_increment: Price,
pub size_increment: Quantity,
- pub margin_init: Decimal,
- pub margin_maint: Decimal,
- pub maker_fee: Decimal,
- pub taker_fee: Decimal,
pub lot_size: Option,
pub max_quantity: Option,
pub min_quantity: Option,
@@ -59,6 +52,8 @@ pub struct CryptoFuture {
pub min_notional: Option,
pub max_price: Option,
pub min_price: Option,
+ pub ts_event: UnixNanos,
+ pub ts_init: UnixNanos,
}
impl CryptoFuture {
@@ -75,10 +70,6 @@ impl CryptoFuture {
size_precision: u8,
price_increment: Price,
size_increment: Quantity,
- margin_init: Decimal,
- margin_maint: Decimal,
- maker_fee: Decimal,
- taker_fee: Decimal,
lot_size: Option,
max_quantity: Option,
min_quantity: Option,
@@ -86,6 +77,8 @@ impl CryptoFuture {
min_notional: Option,
max_price: Option,
min_price: Option,
+ ts_event: UnixNanos,
+ ts_init: UnixNanos,
) -> Result {
Ok(Self {
id,
@@ -99,10 +92,6 @@ impl CryptoFuture {
size_precision,
price_increment,
size_increment,
- margin_init,
- margin_maint,
- maker_fee,
- taker_fee,
lot_size,
max_quantity,
min_quantity,
@@ -110,6 +99,8 @@ impl CryptoFuture {
min_notional,
max_price,
min_price,
+ ts_event,
+ ts_init,
})
}
}
@@ -141,8 +132,8 @@ impl Instrument for CryptoFuture {
AssetClass::Cryptocurrency
}
- fn asset_type(&self) -> AssetType {
- AssetType::Future
+ fn instrument_class(&self) -> InstrumentClass {
+ InstrumentClass::Future
}
fn quote_currency(&self) -> &Currency {
@@ -202,20 +193,12 @@ impl Instrument for CryptoFuture {
self.min_price
}
- fn margin_init(&self) -> Decimal {
- self.margin_init
- }
-
- fn margin_maint(&self) -> Decimal {
- self.margin_maint
- }
-
- fn maker_fee(&self) -> Decimal {
- self.maker_fee
+ fn ts_event(&self) -> UnixNanos {
+ self.ts_event
}
- fn taker_fee(&self) -> Decimal {
- self.taker_fee
+ fn ts_init(&self) -> UnixNanos {
+ self.ts_init
}
}
diff --git a/nautilus_core/model/src/instruments/crypto_perpetual.rs b/nautilus_core/model/src/instruments/crypto_perpetual.rs
index 8359e00a2ef6..230e1017e98e 100644
--- a/nautilus_core/model/src/instruments/crypto_perpetual.rs
+++ b/nautilus_core/model/src/instruments/crypto_perpetual.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -13,24 +13,22 @@
// limitations under the License.
// -------------------------------------------------------------------------------------------------
-#![allow(dead_code)] // Allow for development
-
use std::hash::{Hash, Hasher};
use anyhow::Result;
+use nautilus_core::time::UnixNanos;
use pyo3::prelude::*;
-use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use crate::{
- enums::{AssetClass, AssetType},
+ enums::{AssetClass, InstrumentClass},
identifiers::{instrument_id::InstrumentId, symbol::Symbol},
instruments::Instrument,
types::{currency::Currency, money::Money, price::Price, quantity::Quantity},
};
#[repr(C)]
-#[derive(Clone, Debug, Serialize, Deserialize)]
+#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[cfg_attr(
feature = "python",
pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
@@ -46,10 +44,6 @@ pub struct CryptoPerpetual {
pub size_precision: u8,
pub price_increment: Price,
pub size_increment: Quantity,
- pub margin_init: Decimal,
- pub margin_maint: Decimal,
- pub maker_fee: Decimal,
- pub taker_fee: Decimal,
pub lot_size: Option,
pub max_quantity: Option,
pub min_quantity: Option,
@@ -57,6 +51,8 @@ pub struct CryptoPerpetual {
pub min_notional: Option,
pub max_price: Option,
pub min_price: Option,
+ pub ts_event: UnixNanos,
+ pub ts_init: UnixNanos,
}
impl CryptoPerpetual {
@@ -72,10 +68,6 @@ impl CryptoPerpetual {
size_precision: u8,
price_increment: Price,
size_increment: Quantity,
- margin_init: Decimal,
- margin_maint: Decimal,
- maker_fee: Decimal,
- taker_fee: Decimal,
lot_size: Option,
max_quantity: Option,
min_quantity: Option,
@@ -83,6 +75,8 @@ impl CryptoPerpetual {
min_notional: Option,
max_price: Option,
min_price: Option,
+ ts_event: UnixNanos,
+ ts_init: UnixNanos,
) -> Result {
Ok(Self {
id,
@@ -95,10 +89,6 @@ impl CryptoPerpetual {
size_precision,
price_increment,
size_increment,
- margin_init,
- margin_maint,
- maker_fee,
- taker_fee,
lot_size,
max_quantity,
min_quantity,
@@ -106,6 +96,8 @@ impl CryptoPerpetual {
min_notional,
max_price,
min_price,
+ ts_event,
+ ts_init,
})
}
}
@@ -137,8 +129,8 @@ impl Instrument for CryptoPerpetual {
AssetClass::Cryptocurrency
}
- fn asset_type(&self) -> AssetType {
- AssetType::Swap
+ fn instrument_class(&self) -> InstrumentClass {
+ InstrumentClass::Swap
}
fn quote_currency(&self) -> &Currency {
@@ -197,20 +189,12 @@ impl Instrument for CryptoPerpetual {
self.min_price
}
- fn margin_init(&self) -> Decimal {
- self.margin_init
- }
-
- fn margin_maint(&self) -> Decimal {
- self.margin_maint
- }
-
- fn maker_fee(&self) -> Decimal {
- self.maker_fee
+ fn ts_event(&self) -> UnixNanos {
+ self.ts_event
}
- fn taker_fee(&self) -> Decimal {
- self.taker_fee
+ fn ts_init(&self) -> UnixNanos {
+ self.ts_init
}
}
diff --git a/nautilus_core/model/src/instruments/currency_pair.rs b/nautilus_core/model/src/instruments/currency_pair.rs
index 83622152b683..0430a9408737 100644
--- a/nautilus_core/model/src/instruments/currency_pair.rs
+++ b/nautilus_core/model/src/instruments/currency_pair.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -13,24 +13,22 @@
// limitations under the License.
// -------------------------------------------------------------------------------------------------
-#![allow(dead_code)] // Allow for development
-
use std::hash::{Hash, Hasher};
use anyhow::Result;
+use nautilus_core::time::UnixNanos;
use pyo3::prelude::*;
-use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use super::Instrument;
use crate::{
- enums::{AssetClass, AssetType},
+ enums::{AssetClass, InstrumentClass},
identifiers::{instrument_id::InstrumentId, symbol::Symbol},
types::{currency::Currency, price::Price, quantity::Quantity},
};
#[repr(C)]
-#[derive(Clone, Debug, Serialize, Deserialize)]
+#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[cfg_attr(
feature = "python",
pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
@@ -44,15 +42,13 @@ pub struct CurrencyPair {
pub size_precision: u8,
pub price_increment: Price,
pub size_increment: Quantity,
- pub margin_init: Decimal,
- pub margin_maint: Decimal,
- pub maker_fee: Decimal,
- pub taker_fee: Decimal,
pub lot_size: Option,
pub max_quantity: Option,
pub min_quantity: Option,
pub max_price: Option,
pub min_price: Option,
+ pub ts_event: UnixNanos,
+ pub ts_init: UnixNanos,
}
impl CurrencyPair {
@@ -66,15 +62,13 @@ impl CurrencyPair {
size_precision: u8,
price_increment: Price,
size_increment: Quantity,
- margin_init: Decimal,
- margin_maint: Decimal,
- maker_fee: Decimal,
- taker_fee: Decimal,
lot_size: Option,
max_quantity: Option,
min_quantity: Option,
max_price: Option,
min_price: Option,
+ ts_event: UnixNanos,
+ ts_init: UnixNanos,
) -> Result {
Ok(Self {
id,
@@ -85,15 +79,13 @@ impl CurrencyPair {
size_precision,
price_increment,
size_increment,
- margin_init,
- margin_maint,
- maker_fee,
- taker_fee,
lot_size,
max_quantity,
min_quantity,
max_price,
min_price,
+ ts_event,
+ ts_init,
})
}
}
@@ -125,8 +117,8 @@ impl Instrument for CurrencyPair {
AssetClass::FX
}
- fn asset_type(&self) -> AssetType {
- AssetType::Spot
+ fn instrument_class(&self) -> InstrumentClass {
+ InstrumentClass::Spot
}
fn quote_currency(&self) -> &Currency {
@@ -186,20 +178,12 @@ impl Instrument for CurrencyPair {
self.min_price
}
- fn margin_init(&self) -> Decimal {
- self.margin_init
- }
-
- fn margin_maint(&self) -> Decimal {
- self.margin_maint
- }
-
- fn maker_fee(&self) -> Decimal {
- self.maker_fee
+ fn ts_event(&self) -> UnixNanos {
+ self.ts_event
}
- fn taker_fee(&self) -> Decimal {
- self.taker_fee
+ fn ts_init(&self) -> UnixNanos {
+ self.ts_init
}
}
diff --git a/nautilus_core/model/src/instruments/equity.rs b/nautilus_core/model/src/instruments/equity.rs
index 085049e0ab8f..5015c21566a1 100644
--- a/nautilus_core/model/src/instruments/equity.rs
+++ b/nautilus_core/model/src/instruments/equity.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -13,24 +13,23 @@
// limitations under the License.
// -------------------------------------------------------------------------------------------------
-#![allow(dead_code)] // Allow for development
-
use std::hash::{Hash, Hasher};
use anyhow::Result;
+use nautilus_core::time::UnixNanos;
use pyo3::prelude::*;
-use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
+use ustr::Ustr;
use super::Instrument;
use crate::{
- enums::{AssetClass, AssetType},
+ enums::{AssetClass, InstrumentClass},
identifiers::{instrument_id::InstrumentId, symbol::Symbol},
types::{currency::Currency, price::Price, quantity::Quantity},
};
#[repr(C)]
-#[derive(Clone, Debug, Serialize, Deserialize)]
+#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[cfg_attr(
feature = "python",
pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
@@ -39,20 +38,17 @@ pub struct Equity {
pub id: InstrumentId,
pub raw_symbol: Symbol,
/// The instruments ISIN (International Securities Identification Number).
- pub isin: String,
+ pub isin: Option,
pub currency: Currency,
pub price_precision: u8,
pub price_increment: Price,
- pub multiplier: Quantity,
- pub margin_init: Decimal,
- pub margin_maint: Decimal,
- pub maker_fee: Decimal,
- pub taker_fee: Decimal,
pub lot_size: Option,
pub max_quantity: Option,
pub min_quantity: Option,
pub max_price: Option,
pub min_price: Option,
+ pub ts_event: UnixNanos,
+ pub ts_init: UnixNanos,
}
impl Equity {
@@ -60,20 +56,17 @@ impl Equity {
pub fn new(
id: InstrumentId,
raw_symbol: Symbol,
- isin: String,
+ isin: Option,
currency: Currency,
price_precision: u8,
price_increment: Price,
- multiplier: Quantity,
- margin_init: Decimal,
- margin_maint: Decimal,
- maker_fee: Decimal,
- taker_fee: Decimal,
lot_size: Option,
max_quantity: Option,
min_quantity: Option,
max_price: Option,
min_price: Option,
+ ts_event: UnixNanos,
+ ts_init: UnixNanos,
) -> Result {
Ok(Self {
id,
@@ -82,16 +75,13 @@ impl Equity {
currency,
price_precision,
price_increment,
- multiplier,
- margin_init,
- margin_maint,
- maker_fee,
- taker_fee,
lot_size,
max_quantity,
min_quantity,
max_price,
min_price,
+ ts_event,
+ ts_init,
})
}
}
@@ -123,8 +113,8 @@ impl Instrument for Equity {
AssetClass::Equity
}
- fn asset_type(&self) -> AssetType {
- AssetType::Spot
+ fn instrument_class(&self) -> InstrumentClass {
+ InstrumentClass::Spot
}
fn quote_currency(&self) -> &Currency {
@@ -160,7 +150,7 @@ impl Instrument for Equity {
}
fn multiplier(&self) -> Quantity {
- self.multiplier
+ Quantity::from(1)
}
fn lot_size(&self) -> Option {
@@ -183,20 +173,12 @@ impl Instrument for Equity {
self.min_price
}
- fn margin_init(&self) -> Decimal {
- self.margin_init
- }
-
- fn margin_maint(&self) -> Decimal {
- self.margin_maint
- }
-
- fn maker_fee(&self) -> Decimal {
- self.maker_fee
+ fn ts_event(&self) -> UnixNanos {
+ self.ts_event
}
- fn taker_fee(&self) -> Decimal {
- self.taker_fee
+ fn ts_init(&self) -> UnixNanos {
+ self.ts_init
}
}
diff --git a/nautilus_core/model/src/instruments/futures_contract.rs b/nautilus_core/model/src/instruments/futures_contract.rs
index 866aff7b4811..b38112e19cd9 100644
--- a/nautilus_core/model/src/instruments/futures_contract.rs
+++ b/nautilus_core/model/src/instruments/futures_contract.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -13,25 +13,23 @@
// limitations under the License.
// -------------------------------------------------------------------------------------------------
-#![allow(dead_code)] // Allow for development
-
use std::hash::{Hash, Hasher};
use anyhow::Result;
use nautilus_core::time::UnixNanos;
use pyo3::prelude::*;
-use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
+use ustr::Ustr;
use super::Instrument;
use crate::{
- enums::{AssetClass, AssetType},
+ enums::{AssetClass, InstrumentClass},
identifiers::{instrument_id::InstrumentId, symbol::Symbol},
types::{currency::Currency, price::Price, quantity::Quantity},
};
#[repr(C)]
-#[derive(Clone, Debug, Serialize, Deserialize)]
+#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[cfg_attr(
feature = "python",
pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
@@ -40,22 +38,20 @@ pub struct FuturesContract {
pub id: InstrumentId,
pub raw_symbol: Symbol,
pub asset_class: AssetClass,
- pub underlying: String,
+ pub underlying: Ustr,
pub activation_ns: UnixNanos,
pub expiration_ns: UnixNanos,
pub currency: Currency,
pub price_precision: u8,
pub price_increment: Price,
- pub margin_init: Decimal,
- pub margin_maint: Decimal,
- pub maker_fee: Decimal,
- pub taker_fee: Decimal,
pub multiplier: Quantity,
pub lot_size: Option,
pub max_quantity: Option,
pub min_quantity: Option,
pub max_price: Option,
pub min_price: Option,
+ pub ts_event: UnixNanos,
+ pub ts_init: UnixNanos,
}
impl FuturesContract {
@@ -64,22 +60,20 @@ impl FuturesContract {
id: InstrumentId,
raw_symbol: Symbol,
asset_class: AssetClass,
- underlying: String,
+ underlying: Ustr,
activation_ns: UnixNanos,
expiration_ns: UnixNanos,
currency: Currency,
price_precision: u8,
price_increment: Price,
- margin_init: Decimal,
- margin_maint: Decimal,
- maker_fee: Decimal,
- taker_fee: Decimal,
multiplier: Quantity,
lot_size: Option,
max_quantity: Option,
min_quantity: Option,
max_price: Option,
min_price: Option,
+ ts_event: UnixNanos,
+ ts_init: UnixNanos,
) -> Result {
Ok(Self {
id,
@@ -91,16 +85,14 @@ impl FuturesContract {
currency,
price_precision,
price_increment,
- margin_init,
- margin_maint,
- maker_fee,
- taker_fee,
multiplier,
lot_size,
max_quantity,
min_quantity,
max_price,
min_price,
+ ts_event,
+ ts_init,
})
}
}
@@ -132,8 +124,8 @@ impl Instrument for FuturesContract {
self.asset_class
}
- fn asset_type(&self) -> AssetType {
- AssetType::Future
+ fn instrument_class(&self) -> InstrumentClass {
+ InstrumentClass::Future
}
fn quote_currency(&self) -> &Currency {
@@ -192,20 +184,12 @@ impl Instrument for FuturesContract {
self.min_price
}
- fn margin_init(&self) -> Decimal {
- self.margin_init
- }
-
- fn margin_maint(&self) -> Decimal {
- self.margin_maint
- }
-
- fn maker_fee(&self) -> Decimal {
- self.maker_fee
+ fn ts_event(&self) -> UnixNanos {
+ self.ts_event
}
- fn taker_fee(&self) -> Decimal {
- self.taker_fee
+ fn ts_init(&self) -> UnixNanos {
+ self.ts_init
}
}
diff --git a/nautilus_core/model/src/instruments/mod.rs b/nautilus_core/model/src/instruments/mod.rs
index bb36cc626fbc..27727de8f5df 100644
--- a/nautilus_core/model/src/instruments/mod.rs
+++ b/nautilus_core/model/src/instruments/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -20,16 +20,17 @@ pub mod equity;
pub mod futures_contract;
pub mod options_contract;
pub mod synthetic;
-pub mod synthetic_api;
#[cfg(feature = "stubs")]
pub mod stubs;
use anyhow::Result;
+use nautilus_core::time::UnixNanos;
use rust_decimal::Decimal;
+use rust_decimal_macros::dec;
use crate::{
- enums::{AssetClass, AssetType},
+ enums::{AssetClass, InstrumentClass},
identifiers::{instrument_id::InstrumentId, symbol::Symbol, venue::Venue},
types::{currency::Currency, money::Money, price::Price, quantity::Quantity},
};
@@ -44,7 +45,7 @@ pub trait Instrument {
}
fn raw_symbol(&self) -> &Symbol;
fn asset_class(&self) -> AssetClass;
- fn asset_type(&self) -> AssetType;
+ fn instrument_class(&self) -> InstrumentClass;
fn base_currency(&self) -> Option<&Currency>;
fn quote_currency(&self) -> &Currency;
fn settlement_currency(&self) -> &Currency;
@@ -59,10 +60,23 @@ pub trait Instrument {
fn min_quantity(&self) -> Option;
fn max_price(&self) -> Option;
fn min_price(&self) -> Option;
- fn margin_init(&self) -> Decimal;
- fn margin_maint(&self) -> Decimal;
- fn maker_fee(&self) -> Decimal;
- fn taker_fee(&self) -> Decimal;
+ fn margin_init(&self) -> Decimal {
+ dec!(0) // Temporary until separate fee models
+ }
+
+ fn margin_maint(&self) -> Decimal {
+ dec!(0) // Temporary until separate fee models
+ }
+
+ fn maker_fee(&self) -> Decimal {
+ dec!(0) // Temporary until separate fee models
+ }
+
+ fn taker_fee(&self) -> Decimal {
+ dec!(0) // Temporary until separate fee models
+ }
+ fn ts_event(&self) -> UnixNanos;
+ fn ts_init(&self) -> UnixNanos;
/// Creates a new price from the given `value` with the correct price precision for the instrument.
fn make_price(&self, value: f64) -> Result {
diff --git a/nautilus_core/model/src/instruments/options_contract.rs b/nautilus_core/model/src/instruments/options_contract.rs
index db995f9d76e0..8f8b9357387c 100644
--- a/nautilus_core/model/src/instruments/options_contract.rs
+++ b/nautilus_core/model/src/instruments/options_contract.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -13,25 +13,23 @@
// limitations under the License.
// -------------------------------------------------------------------------------------------------
-#![allow(dead_code)] // Allow for development
-
use std::hash::{Hash, Hasher};
use anyhow::Result;
use nautilus_core::time::UnixNanos;
use pyo3::prelude::*;
-use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
+use ustr::Ustr;
use super::Instrument;
use crate::{
- enums::{AssetClass, AssetType, OptionKind},
+ enums::{AssetClass, InstrumentClass, OptionKind},
identifiers::{instrument_id::InstrumentId, symbol::Symbol},
types::{currency::Currency, price::Price, quantity::Quantity},
};
#[repr(C)]
-#[derive(Clone, Debug, Serialize, Deserialize)]
+#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[cfg_attr(
feature = "python",
pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
@@ -40,7 +38,7 @@ pub struct OptionsContract {
pub id: InstrumentId,
pub raw_symbol: Symbol,
pub asset_class: AssetClass,
- pub underlying: String,
+ pub underlying: Ustr,
pub option_kind: OptionKind,
pub activation_ns: UnixNanos,
pub expiration_ns: UnixNanos,
@@ -48,15 +46,13 @@ pub struct OptionsContract {
pub currency: Currency,
pub price_precision: u8,
pub price_increment: Price,
- pub margin_init: Decimal,
- pub margin_maint: Decimal,
- pub maker_fee: Decimal,
- pub taker_fee: Decimal,
pub lot_size: Option,
pub max_quantity: Option,
pub min_quantity: Option,
pub max_price: Option,
pub min_price: Option,
+ pub ts_event: UnixNanos,
+ pub ts_init: UnixNanos,
}
impl OptionsContract {
@@ -65,7 +61,7 @@ impl OptionsContract {
id: InstrumentId,
raw_symbol: Symbol,
asset_class: AssetClass,
- underlying: String,
+ underlying: Ustr,
option_kind: OptionKind,
activation_ns: UnixNanos,
expiration_ns: UnixNanos,
@@ -73,15 +69,13 @@ impl OptionsContract {
currency: Currency,
price_precision: u8,
price_increment: Price,
- margin_init: Decimal,
- margin_maint: Decimal,
- maker_fee: Decimal,
- taker_fee: Decimal,
lot_size: Option,
max_quantity: Option,
min_quantity: Option,
max_price: Option,
min_price: Option,
+ ts_event: UnixNanos,
+ ts_init: UnixNanos,
) -> Result {
Ok(Self {
id,
@@ -100,10 +94,8 @@ impl OptionsContract {
min_quantity,
max_price,
min_price,
- margin_init,
- margin_maint,
- maker_fee,
- taker_fee,
+ ts_event,
+ ts_init,
})
}
}
@@ -135,8 +127,8 @@ impl Instrument for OptionsContract {
self.asset_class
}
- fn asset_type(&self) -> AssetType {
- AssetType::Option
+ fn instrument_class(&self) -> InstrumentClass {
+ InstrumentClass::Option
}
fn quote_currency(&self) -> &Currency {
@@ -195,20 +187,12 @@ impl Instrument for OptionsContract {
self.min_price
}
- fn margin_init(&self) -> Decimal {
- self.margin_init
- }
-
- fn margin_maint(&self) -> Decimal {
- self.margin_maint
- }
-
- fn maker_fee(&self) -> Decimal {
- self.maker_fee
+ fn ts_event(&self) -> UnixNanos {
+ self.ts_event
}
- fn taker_fee(&self) -> Decimal {
- self.taker_fee
+ fn ts_init(&self) -> UnixNanos {
+ self.ts_init
}
}
diff --git a/nautilus_core/model/src/instruments/stubs.rs b/nautilus_core/model/src/instruments/stubs.rs
index aa70798044f9..5708791c5918 100644
--- a/nautilus_core/model/src/instruments/stubs.rs
+++ b/nautilus_core/model/src/instruments/stubs.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -13,12 +13,10 @@
// limitations under the License.
// -------------------------------------------------------------------------------------------------
-use std::str::FromStr;
-
use chrono::{TimeZone, Utc};
use nautilus_core::time::UnixNanos;
use rstest::fixture;
-use rust_decimal::Decimal;
+use ustr::Ustr;
use crate::{
enums::{AssetClass, OptionKind},
@@ -51,10 +49,6 @@ pub fn crypto_future_btcusdt() -> CryptoFuture {
6,
Price::from("0.01"),
Quantity::from("0.000001"),
- Decimal::from_str("0.0").unwrap(),
- Decimal::from_str("0.0").unwrap(),
- Decimal::from_str("0.001").unwrap(),
- Decimal::from_str("0.001").unwrap(),
None,
Some(Quantity::from("9000.0")),
Some(Quantity::from("0.000001")),
@@ -62,6 +56,8 @@ pub fn crypto_future_btcusdt() -> CryptoFuture {
Some(Money::new(10.00, Currency::from("USDT")).unwrap()),
Some(Price::from("1000000.00")),
Some(Price::from("0.01")),
+ 0,
+ 0,
)
.unwrap()
}
@@ -83,10 +79,6 @@ pub fn crypto_perpetual_ethusdt() -> CryptoPerpetual {
0,
Price::from("0.01"),
Quantity::from("0.001"),
- Decimal::from_str("0.0").unwrap(),
- Decimal::from_str("0.0").unwrap(),
- Decimal::from_str("0.001").unwrap(),
- Decimal::from_str("0.001").unwrap(),
None,
Some(Quantity::from("10000.0")),
Some(Quantity::from("0.001")),
@@ -94,13 +86,15 @@ pub fn crypto_perpetual_ethusdt() -> CryptoPerpetual {
Some(Money::new(10.00, Currency::from("USDT")).unwrap()),
Some(Price::from("15000.00")),
Some(Price::from("1.0")),
+ 0,
+ 0,
)
.unwrap()
}
#[fixture]
pub fn xbtusd_bitmex() -> CryptoPerpetual {
- return CryptoPerpetual::new(
+ CryptoPerpetual::new(
InstrumentId::from("BTCUSDT.BITMEX"),
Symbol::from("XBTUSD"),
Currency::BTC(),
@@ -111,10 +105,6 @@ pub fn xbtusd_bitmex() -> CryptoPerpetual {
0,
Price::from("0.5"),
Quantity::from("1"),
- Decimal::from_str("0.01").unwrap(),
- Decimal::from_str("0.0035").unwrap(),
- Decimal::from_str("-0.00025").unwrap(),
- Decimal::from_str("0.00075").unwrap(),
None,
None,
None,
@@ -122,8 +112,10 @@ pub fn xbtusd_bitmex() -> CryptoPerpetual {
Some(Money::from("1 USD")),
Some(Price::from("10000000")),
Some(Price::from("0.01")),
+ 0,
+ 0,
)
- .unwrap();
+ .unwrap()
}
////////////////////////////////////////////////////////////////////////////////
@@ -141,15 +133,13 @@ pub fn currency_pair_btcusdt() -> CurrencyPair {
6,
Price::from("0.01"),
Quantity::from("0.000001"),
- Decimal::from_str("0.0").unwrap(),
- Decimal::from_str("0.0").unwrap(),
- Decimal::from_str("0.001").unwrap(),
- Decimal::from_str("0.001").unwrap(),
None,
Some(Quantity::from("9000")),
Some(Quantity::from("0.000001")),
Some(Price::from("1000000")),
Some(Price::from("0.01")),
+ 0,
+ 0,
)
.unwrap()
}
@@ -161,22 +151,19 @@ pub fn currency_pair_btcusdt() -> CurrencyPair {
#[fixture]
pub fn equity_aapl() -> Equity {
Equity::new(
- InstrumentId::from("AAPL.NASDAQ"),
+ InstrumentId::from("AAPL.XNAS"),
Symbol::from("AAPL"),
- String::from("US0378331005"),
+ Some(Ustr::from("US0378331005")),
Currency::from("USD"),
2,
Price::from("0.01"),
- Quantity::from(1),
- Decimal::from_str("0.0").unwrap(),
- Decimal::from_str("0.0").unwrap(),
- Decimal::from_str("0.001").unwrap(),
- Decimal::from_str("0.001").unwrap(),
Some(Quantity::from(1)),
None,
None,
None,
None,
+ 0,
+ 0,
)
.unwrap()
}
@@ -189,22 +176,20 @@ pub fn futures_contract_es() -> FuturesContract {
InstrumentId::new(Symbol::from("ESZ21"), Venue::from("CME")),
Symbol::from("ESZ21"),
AssetClass::Index,
- String::from("ES"),
+ Ustr::from("ES"),
activation.timestamp_nanos_opt().unwrap() as UnixNanos,
expiration.timestamp_nanos_opt().unwrap() as UnixNanos,
Currency::USD(),
2,
Price::from("0.01"),
- Decimal::from_str("0.0").unwrap(),
- Decimal::from_str("0.0").unwrap(),
- Decimal::from_str("0.001").unwrap(),
- Decimal::from_str("0.001").unwrap(),
Quantity::from("1.0"),
Some(Quantity::from("1.0")),
None,
None,
None,
None,
+ 0,
+ 0,
)
.unwrap()
}
@@ -221,7 +206,7 @@ pub fn options_contract_appl() -> OptionsContract {
InstrumentId::from("AAPL211217C00150000.OPRA"),
Symbol::from("AAPL211217C00150000"),
AssetClass::Equity,
- String::from("AAPL"),
+ Ustr::from("AAPL"),
OptionKind::Call,
activation.timestamp_nanos_opt().unwrap() as UnixNanos,
expiration.timestamp_nanos_opt().unwrap() as UnixNanos,
@@ -229,15 +214,13 @@ pub fn options_contract_appl() -> OptionsContract {
Currency::USD(),
2,
Price::from("0.01"),
- Decimal::from_str("0.0").unwrap(),
- Decimal::from_str("0.0").unwrap(),
- Decimal::from_str("0.001").unwrap(),
- Decimal::from_str("0.001").unwrap(),
Some(Quantity::from("1.0")),
None,
None,
None,
None,
+ 0,
+ 0,
)
.unwrap()
}
diff --git a/nautilus_core/model/src/instruments/synthetic.rs b/nautilus_core/model/src/instruments/synthetic.rs
index 26b028d58c9f..d59313c287d4 100644
--- a/nautilus_core/model/src/instruments/synthetic.rs
+++ b/nautilus_core/model/src/instruments/synthetic.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/lib.rs b/nautilus_core/model/src/lib.rs
index 079e402c170e..8f8f6e02e117 100644
--- a/nautilus_core/model/src/lib.rs
+++ b/nautilus_core/model/src/lib.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/macros.rs b/nautilus_core/model/src/macros.rs
index aff0ab18af3b..b59ae112a045 100644
--- a/nautilus_core/model/src/macros.rs
+++ b/nautilus_core/model/src/macros.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/orderbook/book.rs b/nautilus_core/model/src/orderbook/book.rs
index f142255bd5a7..1ec846e15dd5 100644
--- a/nautilus_core/model/src/orderbook/book.rs
+++ b/nautilus_core/model/src/orderbook/book.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -19,7 +19,10 @@ use thiserror::Error;
use super::{ladder::BookPrice, level::Level};
use crate::{
- data::{delta::OrderBookDelta, order::BookOrder, quote::QuoteTick, trade::TradeTick},
+ data::{
+ delta::OrderBookDelta, depth::OrderBookDepth10, order::BookOrder, quote::QuoteTick,
+ trade::TradeTick,
+ },
enums::{BookAction, BookType, OrderSide},
identifiers::instrument_id::InstrumentId,
orderbook::ladder::Ladder,
@@ -36,8 +39,8 @@ pub enum InvalidBookOperation {
#[derive(Error, Debug)]
pub enum BookIntegrityError {
- #[error("Invalid book operation: order ID {0} not found")]
- OrderNotFound(u64),
+ #[error("Integrity error: order not found: order_id={0}, ts_event={1}, sequence={2}")]
+ OrderNotFound(u64, u64, u64),
#[error("Integrity error: invalid `NoOrderSide` in book")]
NoOrderSide,
#[error("Integrity error: orders in cross [{0} @ {1}]")]
@@ -131,8 +134,8 @@ impl OrderBook {
};
match order.side {
- OrderSide::Buy => self.bids.delete(order),
- OrderSide::Sell => self.asks.delete(order),
+ OrderSide::Buy => self.bids.delete(order, ts_event, sequence),
+ OrderSide::Sell => self.asks.delete(order, ts_event, sequence),
_ => panic!("{}", BookIntegrityError::NoOrderSide),
}
@@ -164,6 +167,19 @@ impl OrderBook {
}
}
+ pub fn apply_depth(&mut self, depth: OrderBookDepth10) {
+ self.bids.clear();
+ self.asks.clear();
+
+ for order in depth.bids {
+ self.add(order, depth.ts_event, depth.sequence);
+ }
+
+ for order in depth.asks {
+ self.add(order, depth.ts_event, depth.sequence);
+ }
+ }
+
pub fn bids(&self) -> Vec<&Level> {
self.bids.levels.values().collect()
}
@@ -278,13 +294,29 @@ impl OrderBook {
}
pub fn update_quote_tick(&mut self, tick: &QuoteTick) {
- self.update_bid(BookOrder::from_quote_tick(tick, OrderSide::Buy));
- self.update_ask(BookOrder::from_quote_tick(tick, OrderSide::Sell));
+ self.update_bid(
+ BookOrder::from_quote_tick(tick, OrderSide::Buy),
+ tick.ts_event,
+ 0,
+ );
+ self.update_ask(
+ BookOrder::from_quote_tick(tick, OrderSide::Sell),
+ tick.ts_event,
+ 0,
+ );
}
pub fn update_trade_tick(&mut self, tick: &TradeTick) {
- self.update_bid(BookOrder::from_trade_tick(tick, OrderSide::Buy));
- self.update_ask(BookOrder::from_trade_tick(tick, OrderSide::Sell));
+ self.update_bid(
+ BookOrder::from_trade_tick(tick, OrderSide::Buy),
+ tick.ts_event,
+ 0,
+ );
+ self.update_ask(
+ BookOrder::from_trade_tick(tick, OrderSide::Sell),
+ tick.ts_event,
+ 0,
+ );
}
pub fn simulate_fills(&self, order: &BookOrder) -> Vec<(Price, Quantity)> {
@@ -441,12 +473,12 @@ impl OrderBook {
}
}
- fn update_bid(&mut self, order: BookOrder) {
+ fn update_bid(&mut self, order: BookOrder, ts_event: u64, sequence: u64) {
match self.bids.top() {
Some(top_bids) => match top_bids.first() {
Some(top_bid) => {
let order_id = top_bid.order_id;
- self.bids.remove(order_id);
+ self.bids.remove(order_id, ts_event, sequence);
self.bids.add(order);
}
None => {
@@ -459,12 +491,12 @@ impl OrderBook {
}
}
- fn update_ask(&mut self, order: BookOrder) {
+ fn update_ask(&mut self, order: BookOrder, ts_event: u64, sequence: u64) {
match self.asks.top() {
Some(top_asks) => match top_asks.first() {
Some(top_ask) => {
let order_id = top_ask.order_id;
- self.asks.remove(order_id);
+ self.asks.remove(order_id, ts_event, sequence);
self.asks.add(order);
}
None => {
@@ -502,7 +534,7 @@ mod tests {
use super::*;
use crate::{
- data::order::BookOrder,
+ data::{depth::stubs::stub_depth10, order::BookOrder},
enums::{AggressorSide, OrderSide},
identifiers::{instrument_id::InstrumentId, trade_id::TradeId},
types::{price::Price, quantity::Quantity},
@@ -760,6 +792,20 @@ mod tests {
);
}
+ #[rstest]
+ fn test_apply_depth(stub_depth10: OrderBookDepth10) {
+ let depth = stub_depth10;
+ let instrument_id = InstrumentId::from("AAPL.XNAS");
+ let mut book = OrderBook::new(instrument_id, BookType::L2_MBP);
+
+ book.apply_depth(depth);
+
+ assert_eq!(book.best_bid_price().unwrap().as_f64(), 99.00);
+ assert_eq!(book.best_ask_price().unwrap().as_f64(), 100.00);
+ assert_eq!(book.best_bid_size().unwrap().as_f64(), 100.0);
+ assert_eq!(book.best_ask_size().unwrap().as_f64(), 100.0);
+ }
+
#[rstest]
fn test_update_quote_tick_l1() {
let instrument_id = InstrumentId::from("ETHUSDT-PERP.BINANCE");
diff --git a/nautilus_core/model/src/orderbook/ladder.rs b/nautilus_core/model/src/orderbook/ladder.rs
index f47e29a220ba..2470b2176b8f 100644
--- a/nautilus_core/model/src/orderbook/ladder.rs
+++ b/nautilus_core/model/src/orderbook/ladder.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -28,7 +28,7 @@ use crate::{
};
/// Represents a price level with a specified side in an order books ladder.
-#[derive(Copy, Clone, Debug, Eq)]
+#[derive(Clone, Copy, Debug, Eq)]
pub struct BookPrice {
pub value: Price,
pub side: OrderSide,
@@ -108,23 +108,27 @@ impl Ladder {
}
pub fn add(&mut self, order: BookOrder) {
+ let order_id = order.order_id;
let book_price = order.to_book_price();
+
+ self.cache.insert(order_id, book_price);
+
match self.levels.get_mut(&book_price) {
Some(level) => {
level.add(order);
}
None => {
- let order_id = order.order_id;
let level = Level::from_order(order);
- self.cache.insert(order_id, book_price);
self.levels.insert(book_price, level);
}
}
}
pub fn update(&mut self, order: BookOrder) {
- if let Some(price) = self.cache.get(&order.order_id) {
- if let Some(level) = self.levels.get_mut(price) {
+ let price_opt = self.cache.get(&order.order_id).copied();
+
+ if let Some(price) = price_opt {
+ if let Some(level) = self.levels.get_mut(&price) {
if order.price == level.price.value {
// Update at current price level
level.update(order);
@@ -132,9 +136,10 @@ impl Ladder {
}
// Price update: delete and insert at new level
+ self.cache.remove(&order.order_id);
level.delete(&order);
if level.is_empty() {
- self.levels.remove(price);
+ self.levels.remove(&price);
}
}
}
@@ -142,14 +147,14 @@ impl Ladder {
self.add(order);
}
- pub fn delete(&mut self, order: BookOrder) {
- self.remove(order.order_id);
+ pub fn delete(&mut self, order: BookOrder, ts_event: u64, sequence: u64) {
+ self.remove(order.order_id, ts_event, sequence);
}
- pub fn remove(&mut self, order_id: OrderId) {
+ pub fn remove(&mut self, order_id: OrderId, ts_event: u64, sequence: u64) {
if let Some(price) = self.cache.remove(&order_id) {
if let Some(level) = self.levels.get_mut(&price) {
- level.remove_by_id(order_id);
+ level.remove_by_id(order_id, ts_event, sequence);
if level.is_empty() {
self.levels.remove(&price);
}
@@ -402,7 +407,7 @@ mod tests {
let mut ladder = Ladder::new(OrderSide::Buy);
let order = BookOrder::new(OrderSide::Buy, Price::from("10.00"), Quantity::from(20), 1);
- ladder.delete(order);
+ ladder.delete(order, 0, 0);
assert_eq!(ladder.len(), 0);
}
@@ -416,7 +421,7 @@ mod tests {
let order = BookOrder::new(OrderSide::Buy, Price::from("11.00"), Quantity::from(10), 1);
- ladder.delete(order);
+ ladder.delete(order, 0, 0);
assert_eq!(ladder.len(), 0);
assert_eq!(ladder.sizes(), 0.0);
assert_eq!(ladder.exposures(), 0.0);
@@ -432,7 +437,7 @@ mod tests {
let order = BookOrder::new(OrderSide::Sell, Price::from("10.00"), Quantity::from(10), 1);
- ladder.delete(order);
+ ladder.delete(order, 0, 0);
assert_eq!(ladder.len(), 0);
assert_eq!(ladder.sizes(), 0.0);
assert_eq!(ladder.exposures(), 0.0);
diff --git a/nautilus_core/model/src/orderbook/level.rs b/nautilus_core/model/src/orderbook/level.rs
index db871438c550..4c8a77ca7d90 100644
--- a/nautilus_core/model/src/orderbook/level.rs
+++ b/nautilus_core/model/src/orderbook/level.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -99,9 +99,12 @@ impl Level {
self.update_insertion_order();
}
- pub fn remove_by_id(&mut self, order_id: OrderId) {
+ pub fn remove_by_id(&mut self, order_id: OrderId, ts_event: u64, sequence: u64) {
if self.orders.remove(&order_id).is_none() {
- panic!("{}", &BookIntegrityError::OrderNotFound(order_id));
+ panic!(
+ "{}",
+ &BookIntegrityError::OrderNotFound(order_id, ts_event, sequence)
+ );
}
self.update_insertion_order();
}
@@ -311,7 +314,7 @@ mod tests {
level.add(order1);
level.add(order2);
- level.remove_by_id(order2_id);
+ level.remove_by_id(order2_id, 0, 0);
assert_eq!(level.len(), 1);
assert!(level.orders.contains_key(&order1_id));
assert_eq!(level.size(), 10.0);
@@ -344,10 +347,12 @@ mod tests {
}
#[rstest]
- #[should_panic(expected = "Invalid book operation: order ID 1 not found")]
+ #[should_panic(
+ expected = "Integrity error: order not found: order_id=1, ts_event=2, sequence=3"
+ )]
fn test_remove_nonexistent_order() {
let mut level = Level::new(BookPrice::new(Price::from("1.00"), OrderSide::Buy));
- level.remove_by_id(1);
+ level.remove_by_id(1, 2, 3);
}
#[rstest]
diff --git a/nautilus_core/model/src/orderbook/mod.rs b/nautilus_core/model/src/orderbook/mod.rs
index dcf47c80eb1f..4a003c664161 100644
--- a/nautilus_core/model/src/orderbook/mod.rs
+++ b/nautilus_core/model/src/orderbook/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/orders/base.rs b/nautilus_core/model/src/orders/base.rs
index ed7b8d5fd70b..c681135d7cf3 100644
--- a/nautilus_core/model/src/orders/base.rs
+++ b/nautilus_core/model/src/orders/base.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/orders/default.rs b/nautilus_core/model/src/orders/default.rs
index 97709ec677ca..ec9cf1036ca7 100644
--- a/nautilus_core/model/src/orders/default.rs
+++ b/nautilus_core/model/src/orders/default.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/orders/limit.rs b/nautilus_core/model/src/orders/limit.rs
index 0ee62218f4ce..bb15070baf57 100644
--- a/nautilus_core/model/src/orders/limit.rs
+++ b/nautilus_core/model/src/orders/limit.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/orders/limit_if_touched.rs b/nautilus_core/model/src/orders/limit_if_touched.rs
index 2d40ca2ec1a9..99270b12e011 100644
--- a/nautilus_core/model/src/orders/limit_if_touched.rs
+++ b/nautilus_core/model/src/orders/limit_if_touched.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/orders/market.rs b/nautilus_core/model/src/orders/market.rs
index 591511121d05..eefd073a63b9 100644
--- a/nautilus_core/model/src/orders/market.rs
+++ b/nautilus_core/model/src/orders/market.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/orders/market_if_touched.rs b/nautilus_core/model/src/orders/market_if_touched.rs
index 7a6ba0df65ec..99d7ad431d06 100644
--- a/nautilus_core/model/src/orders/market_if_touched.rs
+++ b/nautilus_core/model/src/orders/market_if_touched.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/orders/market_to_limit.rs b/nautilus_core/model/src/orders/market_to_limit.rs
index b0528fcaeea3..421fb4670401 100644
--- a/nautilus_core/model/src/orders/market_to_limit.rs
+++ b/nautilus_core/model/src/orders/market_to_limit.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/orders/mod.rs b/nautilus_core/model/src/orders/mod.rs
index 47d718840f4d..242af66dfcfa 100644
--- a/nautilus_core/model/src/orders/mod.rs
+++ b/nautilus_core/model/src/orders/mod.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/orders/stop_limit.rs b/nautilus_core/model/src/orders/stop_limit.rs
index 56368a2a0f88..024e887d82a4 100644
--- a/nautilus_core/model/src/orders/stop_limit.rs
+++ b/nautilus_core/model/src/orders/stop_limit.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/orders/stop_market.rs b/nautilus_core/model/src/orders/stop_market.rs
index d01011ad31a5..c4f257a1763d 100644
--- a/nautilus_core/model/src/orders/stop_market.rs
+++ b/nautilus_core/model/src/orders/stop_market.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/orders/stubs.rs b/nautilus_core/model/src/orders/stubs.rs
index 74918c4f18df..2202d56ee402 100644
--- a/nautilus_core/model/src/orders/stubs.rs
+++ b/nautilus_core/model/src/orders/stubs.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/orders/trailing_stop_limit.rs b/nautilus_core/model/src/orders/trailing_stop_limit.rs
index dcf33191b661..0cb348cff7ce 100644
--- a/nautilus_core/model/src/orders/trailing_stop_limit.rs
+++ b/nautilus_core/model/src/orders/trailing_stop_limit.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/orders/trailing_stop_market.rs b/nautilus_core/model/src/orders/trailing_stop_market.rs
index ea5aa170c430..92aaf5fdf98a 100644
--- a/nautilus_core/model/src/orders/trailing_stop_market.rs
+++ b/nautilus_core/model/src/orders/trailing_stop_market.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
diff --git a/nautilus_core/model/src/position.rs b/nautilus_core/model/src/position.rs
index d6bb62522f9c..3a2b8e35af09 100644
--- a/nautilus_core/model/src/position.rs
+++ b/nautilus_core/model/src/position.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -144,7 +144,7 @@ impl Position {
self.realized_pnl = None;
}
- self.events.push(fill.clone()); // Potentially do this last
+ self.events.push(fill); // Potentially do this last
self.trade_ids.push(fill.trade_id);
// Calculate cumulative commissions
diff --git a/nautilus_core/model/src/python/data/bar.rs b/nautilus_core/model/src/python/data/bar.rs
index 398fddb69b4b..7570f4d69c6e 100644
--- a/nautilus_core/model/src/python/data/bar.rs
+++ b/nautilus_core/model/src/python/data/bar.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -16,6 +16,7 @@
use std::{
collections::{hash_map::DefaultHasher, HashMap},
hash::{Hash, Hasher},
+ str::FromStr,
};
use nautilus_core::{
@@ -116,6 +117,12 @@ impl BarType {
fn py_fully_qualified_name() -> String {
format!("{}:{}", PY_MODULE_MODEL, stringify!(BarType))
}
+
+ #[staticmethod]
+ #[pyo3(name = "from_str")]
+ fn py_from_str(value: &str) -> PyResult {
+ BarType::from_str(value).map_err(to_pyvalue_err)
+ }
}
#[pymethods]
@@ -282,12 +289,12 @@ mod tests {
use pyo3::{IntoPy, Python};
use rstest::rstest;
- use crate::data::bar::{stubs::bar_audusd_sim_minute_bid, Bar};
+ use crate::data::bar::{stubs::stub_bar, Bar};
#[rstest]
- fn test_as_dict(bar_audusd_sim_minute_bid: Bar) {
+ fn test_as_dict(stub_bar: Bar) {
pyo3::prepare_freethreaded_python();
- let bar = bar_audusd_sim_minute_bid;
+ let bar = stub_bar;
Python::with_gil(|py| {
let dict_string = bar.py_as_dict(py).unwrap().to_string();
@@ -297,9 +304,9 @@ mod tests {
}
#[rstest]
- fn test_as_from_dict(bar_audusd_sim_minute_bid: Bar) {
+ fn test_as_from_dict(stub_bar: Bar) {
pyo3::prepare_freethreaded_python();
- let bar = bar_audusd_sim_minute_bid;
+ let bar = stub_bar;
Python::with_gil(|py| {
let dict = bar.py_as_dict(py).unwrap();
@@ -309,9 +316,9 @@ mod tests {
}
#[rstest]
- fn test_from_pyobject(bar_audusd_sim_minute_bid: Bar) {
+ fn test_from_pyobject(stub_bar: Bar) {
pyo3::prepare_freethreaded_python();
- let bar = bar_audusd_sim_minute_bid;
+ let bar = stub_bar;
Python::with_gil(|py| {
let bar_pyobject = bar.into_py(py);
diff --git a/nautilus_core/model/src/python/data/delta.rs b/nautilus_core/model/src/python/data/delta.rs
index c524ab494945..47fc63ef6f30 100644
--- a/nautilus_core/model/src/python/data/delta.rs
+++ b/nautilus_core/model/src/python/data/delta.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -206,7 +206,7 @@ mod tests {
Python::with_gil(|py| {
let dict_string = delta.py_as_dict(py).unwrap().to_string();
- let expected_string = r#"{'type': 'OrderBookDelta', 'instrument_id': 'AAPL.NASDAQ', 'action': 'ADD', 'order': {'side': 'BUY', 'price': '100.00', 'size': '10', 'order_id': 123456}, 'flags': 0, 'sequence': 1, 'ts_event': 1, 'ts_init': 2}"#;
+ let expected_string = r#"{'type': 'OrderBookDelta', 'instrument_id': 'AAPL.XNAS', 'action': 'ADD', 'order': {'side': 'BUY', 'price': '100.00', 'size': '10', 'order_id': 123456}, 'flags': 0, 'sequence': 1, 'ts_event': 1, 'ts_init': 2}"#;
assert_eq!(dict_string, expected_string);
});
}
diff --git a/nautilus_core/model/src/python/data/ticker.rs b/nautilus_core/model/src/python/data/depth.rs
similarity index 61%
rename from nautilus_core/model/src/python/data/ticker.rs
rename to nautilus_core/model/src/python/data/depth.rs
index 4fdd4ad5f5ea..6e5aaa2a6d59 100644
--- a/nautilus_core/model/src/python/data/ticker.rs
+++ b/nautilus_core/model/src/python/data/depth.rs
@@ -1,5 +1,5 @@
// -------------------------------------------------------------------------------------------------
-// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
+// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
@@ -14,7 +14,7 @@
// -------------------------------------------------------------------------------------------------
use std::{
- collections::hash_map::DefaultHasher,
+ collections::{hash_map::DefaultHasher, HashMap},
hash::{Hash, Hasher},
};
@@ -26,14 +26,40 @@ use nautilus_core::{
use pyo3::{prelude::*, pyclass::CompareOp, types::PyDict};
use crate::{
- data::ticker::Ticker, identifiers::instrument_id::InstrumentId, python::PY_MODULE_MODEL,
+ data::{
+ depth::{OrderBookDepth10, DEPTH10_LEN},
+ order::BookOrder,
+ },
+ identifiers::instrument_id::InstrumentId,
+ python::PY_MODULE_MODEL,
};
#[pymethods]
-impl Ticker {
+impl OrderBookDepth10 {
+ #[allow(clippy::too_many_arguments)]
#[new]
- fn py_new(instrument_id: InstrumentId, ts_event: UnixNanos, ts_init: UnixNanos) -> Self {
- Self::new(instrument_id, ts_event, ts_init)
+ fn py_new(
+ instrument_id: InstrumentId,
+ bids: [BookOrder; DEPTH10_LEN],
+ asks: [BookOrder; DEPTH10_LEN],
+ bid_counts: [u32; DEPTH10_LEN],
+ ask_counts: [u32; DEPTH10_LEN],
+ flags: u8,
+ sequence: u64,
+ ts_event: UnixNanos,
+ ts_init: UnixNanos,
+ ) -> Self {
+ Self::new(
+ instrument_id,
+ bids,
+ asks,
+ bid_counts,
+ ask_counts,
+ flags,
+ sequence,
+ ts_event,
+ ts_init,
+ )
}
fn __richcmp__(&self, other: &Self, op: CompareOp, py: Python<'_>) -> Py {
@@ -63,6 +89,36 @@ impl Ticker {
self.instrument_id
}
+ #[getter]
+ fn bids(&self) -> [BookOrder; DEPTH10_LEN] {
+ self.bids
+ }
+
+ #[getter]
+ fn asks(&self) -> [BookOrder; DEPTH10_LEN] {
+ self.asks
+ }
+
+ #[getter]
+ fn bid_counts(&self) -> [u32; DEPTH10_LEN] {
+ self.bid_counts
+ }
+
+ #[getter]
+ fn ask_counts(&self) -> [u32; DEPTH10_LEN] {
+ self.ask_counts
+ }
+
+ #[getter]
+ fn flags(&self) -> u8 {
+ self.flags
+ }
+
+ #[getter]
+ fn sequence(&self) -> u64 {
+ self.sequence
+ }
+
#[getter]
fn ts_event(&self) -> UnixNanos {
self.ts_event
@@ -76,12 +132,12 @@ impl Ticker {
#[staticmethod]
#[pyo3(name = "fully_qualified_name")]
fn py_fully_qualified_name() -> String {
- format!("{}:{}", PY_MODULE_MODEL, stringify!(Ticker))
+ format!("{}:{}", PY_MODULE_MODEL, stringify!(OrderBookDepth10))
}
/// Return a dictionary representation of the object.
#[pyo3(name = "as_dict")]
- pub fn py_as_dict(&self, py: Python<'_>) -> PyResult> {
+ fn py_as_dict(&self, py: Python<'_>) -> PyResult> {
// Serialize object to JSON bytes
let json_str = serde_json::to_string(self).map_err(to_pyvalue_err)?;
// Parse JSON into a Python dictionary
@@ -94,10 +150,35 @@ impl Ticker {
/// Return a new object from the given dictionary representation.
#[staticmethod]
#[pyo3(name = "from_dict")]
- pub fn py_from_dict(py: Python<'_>, values: Py) -> PyResult {
+ fn py_from_dict(py: Python<'_>, values: Py) -> PyResult {
from_dict_pyo3(py, values)
}
+ #[staticmethod]
+ #[pyo3(name = "get_metadata")]
+ fn py_get_metadata(
+ instrument_id: &InstrumentId,
+ price_precision: u8,
+ size_precision: u8,
+ ) -> PyResult