From 91c8ff9a4631a43a57801c2abbe9503cf847869b Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sat, 25 May 2024 07:28:51 +1000 Subject: [PATCH 01/59] Bump version --- RELEASES.md | 15 +++++++++++++++ nautilus_core/Cargo.lock | 30 +++++++++++++++--------------- nautilus_core/Cargo.toml | 2 +- pyproject.toml | 2 +- version.json | 2 +- 5 files changed, 33 insertions(+), 18 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 98f0e7a6932b..97bcee25b233 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,18 @@ +# NautilusTrader 1.194.0 Beta + +Released on TBD (UTC). + +### Enhancements +None + +### Breaking Changes +None + +### Fixes +None + +--- + # NautilusTrader 1.193.0 Beta Released on 24th May 2024 (UTC). diff --git a/nautilus_core/Cargo.lock b/nautilus_core/Cargo.lock index 79c5f5270daf..8cf8f55d5150 100644 --- a/nautilus_core/Cargo.lock +++ b/nautilus_core/Cargo.lock @@ -2076,9 +2076,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +checksum = "3d8d52be92d09acc2e01dddb7fde3ad983fc6489c7db4837e605bc3fca4cb63e" dependencies = [ "bytes", "futures-channel", @@ -2493,7 +2493,7 @@ dependencies = [ [[package]] name = "nautilus-accounting" -version = "0.23.0" +version = "0.24.0" dependencies = [ "anyhow", "cbindgen", @@ -2509,7 +2509,7 @@ dependencies = [ [[package]] name = "nautilus-adapters" -version = "0.23.0" +version = "0.24.0" dependencies = [ "anyhow", "chrono", @@ -2540,7 +2540,7 @@ dependencies = [ [[package]] name = "nautilus-backtest" -version = "0.23.0" +version = "0.24.0" dependencies = [ "anyhow", "cbindgen", @@ -2557,7 +2557,7 @@ dependencies = [ [[package]] name = "nautilus-cli" -version = "0.23.0" +version = "0.24.0" dependencies = [ "anyhow", "clap 4.5.4", @@ -2574,7 +2574,7 @@ dependencies = [ [[package]] name = "nautilus-common" -version = "0.23.0" +version = "0.24.0" dependencies = [ "anyhow", "cbindgen", @@ -2602,7 +2602,7 @@ dependencies = [ [[package]] name = "nautilus-core" -version = "0.23.0" +version = "0.24.0" dependencies = [ "anyhow", "cbindgen", @@ -2622,7 +2622,7 @@ dependencies = [ [[package]] name = "nautilus-execution" -version = "0.23.0" +version = "0.24.0" dependencies = [ "anyhow", "criterion", @@ -2647,7 +2647,7 @@ dependencies = [ [[package]] name = "nautilus-indicators" -version = "0.23.0" +version = "0.24.0" dependencies = [ "anyhow", "nautilus-core", @@ -2659,7 +2659,7 @@ dependencies = [ [[package]] name = "nautilus-infrastructure" -version = "0.23.0" +version = "0.24.0" dependencies = [ "anyhow", "log", @@ -2682,7 +2682,7 @@ dependencies = [ [[package]] name = "nautilus-model" -version = "0.23.0" +version = "0.24.0" dependencies = [ "anyhow", "cbindgen", @@ -2710,7 +2710,7 @@ dependencies = [ [[package]] name = "nautilus-network" -version = "0.23.0" +version = "0.24.0" dependencies = [ "anyhow", "axum", @@ -2735,7 +2735,7 @@ dependencies = [ [[package]] name = "nautilus-persistence" -version = "0.23.0" +version = "0.24.0" dependencies = [ "anyhow", "binary-heap-plus", @@ -2758,7 +2758,7 @@ dependencies = [ [[package]] name = "nautilus-pyo3" -version = "0.23.0" +version = "0.24.0" dependencies = [ "nautilus-accounting", "nautilus-adapters", diff --git a/nautilus_core/Cargo.toml b/nautilus_core/Cargo.toml index 234c015038ea..2fb525de83c2 100644 --- a/nautilus_core/Cargo.toml +++ b/nautilus_core/Cargo.toml @@ -19,7 +19,7 @@ members = [ [workspace.package] rust-version = "1.78.0" -version = "0.23.0" +version = "0.24.0" edition = "2021" authors = ["Nautech Systems "] description = "A high-performance algorithmic trading platform and event-driven backtester" diff --git a/pyproject.toml b/pyproject.toml index 7de9dc786348..4634cf9ff3fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "nautilus_trader" -version = "1.193.0" +version = "1.194.0" description = "A high-performance algorithmic trading platform and event-driven backtester" authors = ["Nautech Systems "] license = "LGPL-3.0-or-later" diff --git a/version.json b/version.json index 43ccf52343e0..96ed80e58ffb 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, "label": "", - "message": "v1.193.0", + "message": "v1.194.0", "color": "orange" } From 0725b0d6da319a6b5be7a3688e026d3aa796a0d0 Mon Sep 17 00:00:00 2001 From: Pushkar Mishra Date: Sat, 25 May 2024 03:30:22 +0530 Subject: [PATCH 02/59] Port VolumeWeightedAveragePrice indicator (#1665) --- nautilus_core/indicators/src/average/mod.rs | 1 + nautilus_core/indicators/src/average/vidya.rs | 2 +- nautilus_core/indicators/src/average/vwap.rs | 187 ++++++++++++++++++ .../indicators/src/python/average/mod.rs | 2 + .../indicators/src/python/average/vidya.rs | 22 ++- .../indicators/src/python/average/vwap.rs | 81 ++++++++ nautilus_core/indicators/src/python/mod.rs | 1 + nautilus_core/indicators/src/stubs.rs | 9 +- nautilus_trader/core/nautilus_pyo3.pyi | 16 ++ 9 files changed, 308 insertions(+), 13 deletions(-) create mode 100644 nautilus_core/indicators/src/average/vwap.rs create mode 100644 nautilus_core/indicators/src/python/average/vwap.rs diff --git a/nautilus_core/indicators/src/average/mod.rs b/nautilus_core/indicators/src/average/mod.rs index 95db06550fba..1cb9448a77fa 100644 --- a/nautilus_core/indicators/src/average/mod.rs +++ b/nautilus_core/indicators/src/average/mod.rs @@ -33,6 +33,7 @@ pub mod hma; pub mod rma; pub mod sma; pub mod vidya; +pub mod vwap; pub mod wma; #[repr(C)] diff --git a/nautilus_core/indicators/src/average/vidya.rs b/nautilus_core/indicators/src/average/vidya.rs index eb780edb1520..989778fae3f4 100644 --- a/nautilus_core/indicators/src/average/vidya.rs +++ b/nautilus_core/indicators/src/average/vidya.rs @@ -41,7 +41,7 @@ pub struct VariableIndexDynamicAverage { pub initialized: bool, has_inputs: bool, pub cmo: ChandeMomentumOscillator, - cmo_pct: f64, + pub cmo_pct: f64, } impl Display for VariableIndexDynamicAverage { diff --git a/nautilus_core/indicators/src/average/vwap.rs b/nautilus_core/indicators/src/average/vwap.rs new file mode 100644 index 000000000000..3e4a4801015d --- /dev/null +++ b/nautilus_core/indicators/src/average/vwap.rs @@ -0,0 +1,187 @@ +// ------------------------------------------------------------------------------------------------- +// 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 nautilus_model::data::bar::Bar; +use std::fmt::{Display, Formatter}; + +use crate::indicator::Indicator; + +#[repr(C)] +#[derive(Debug)] +#[cfg_attr( + feature = "python", + pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.indicators") +)] +pub struct VolumeWeightedAveragePrice { + pub value: f64, + pub initialized: bool, + has_inputs: bool, + price_volume: f64, + volume_total: f64, + day: f64, +} + +impl Display for VolumeWeightedAveragePrice { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.name()) + } +} + +impl Indicator for VolumeWeightedAveragePrice { + fn name(&self) -> String { + stringify!(VolumeWeightedAveragePrice).to_string() + } + + fn has_inputs(&self) -> bool { + self.has_inputs + } + + fn initialized(&self) -> bool { + self.initialized + } + + fn handle_bar(&mut self, bar: &Bar) { + self.update_raw( + (&bar.close).into(), + (&bar.volume).into(), + bar.ts_init.as_f64(), + ); + } + + fn reset(&mut self) { + self.value = 0.0; + self.has_inputs = false; + self.initialized = false; + self.day = 0.0; + self.price_volume = 0.0; + self.volume_total = 0.0; + self.value = 0.0; + } +} + +impl VolumeWeightedAveragePrice { + pub fn new() -> anyhow::Result { + Ok(Self { + value: 0.0, + has_inputs: false, + initialized: false, + price_volume: 0.0, + volume_total: 0.0, + day: 0.0, + }) + } + + pub fn update_raw(&mut self, price: f64, volume: f64, timestamp: f64) { + if timestamp != self.day { + self.reset(); + self.day = timestamp; + self.value = price; + } + + if !self.initialized { + self.has_inputs = true; + self.initialized = true; + } + + if volume == 0.0 { + return; + } + + self.price_volume += price * volume; + self.volume_total += volume; + self.value = self.price_volume / self.volume_total; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////////////// +#[cfg(test)] +mod tests { + use nautilus_model::data::bar::Bar; + use rstest::rstest; + + use crate::{average::vwap::VolumeWeightedAveragePrice, indicator::Indicator, stubs::*}; + + #[rstest] + fn test_vwap_initialized(indicator_vwap: VolumeWeightedAveragePrice) { + let display_st = format!("{indicator_vwap}"); + assert_eq!(display_st, "VolumeWeightedAveragePrice"); + assert!(!indicator_vwap.initialized()); + assert!(!indicator_vwap.has_inputs()); + } + + #[rstest] + fn test_value_with_one_input(mut indicator_vwap: VolumeWeightedAveragePrice) { + indicator_vwap.update_raw(10.0, 10.0, 10.0); + assert_eq!(indicator_vwap.value, 10.0); + } + + #[rstest] + fn test_value_with_three_inputs_on_the_same_day( + mut indicator_vwap: VolumeWeightedAveragePrice, + ) { + indicator_vwap.update_raw(10.0, 10.0, 10.0); + indicator_vwap.update_raw(20.0, 20.0, 10.0); + indicator_vwap.update_raw(30.0, 30.0, 10.0); + assert_eq!(indicator_vwap.value, 23.333333333333332); + } + + #[rstest] + fn test_value_with_three_inputs_on_different_days( + mut indicator_vwap: VolumeWeightedAveragePrice, + ) { + indicator_vwap.update_raw(10.0, 10.0, 10.0); + indicator_vwap.update_raw(20.0, 20.0, 20.0); + indicator_vwap.update_raw(30.0, 30.0, 10.0); + assert_eq!(indicator_vwap.value, 30.0); + } + + #[rstest] + fn test_value_with_ten_inputs(mut indicator_vwap: VolumeWeightedAveragePrice) { + indicator_vwap.update_raw(1.00000, 1.00000, 10.0); + indicator_vwap.update_raw(1.00010, 2.00000, 10.0); + indicator_vwap.update_raw(1.00020, 3.00000, 10.0); + indicator_vwap.update_raw(1.00030, 1.00000, 10.0); + indicator_vwap.update_raw(1.00040, 2.00000, 10.0); + indicator_vwap.update_raw(1.00050, 3.00000, 10.0); + indicator_vwap.update_raw(1.00040, 1.00000, 10.0); + indicator_vwap.update_raw(1.00030, 2.00000, 10.0); + indicator_vwap.update_raw(1.00020, 3.00000, 10.0); + indicator_vwap.update_raw(1.00010, 1.00000, 10.0); + indicator_vwap.update_raw(1.00000, 2.00000, 10.0); + assert_eq!(indicator_vwap.value, 1.000242857142857); + } + + #[rstest] + fn test_handle_bar( + mut indicator_vwap: VolumeWeightedAveragePrice, + bar_ethusdt_binance_minute_bid: Bar, + ) { + indicator_vwap.handle_bar(&bar_ethusdt_binance_minute_bid); + assert_eq!(indicator_vwap.value, 1522.0); + assert!(indicator_vwap.initialized); + } + + #[rstest] + fn test_reset(mut indicator_vwap: VolumeWeightedAveragePrice) { + indicator_vwap.update_raw(10.0, 10.0, 10.0); + assert_eq!(indicator_vwap.value, 10.0); + indicator_vwap.reset(); + assert_eq!(indicator_vwap.value, 0.0); + assert!(!indicator_vwap.has_inputs); + assert!(!indicator_vwap.initialized); + } +} diff --git a/nautilus_core/indicators/src/python/average/mod.rs b/nautilus_core/indicators/src/python/average/mod.rs index dc1c7f2f8bf9..16fc0a9f2bf8 100644 --- a/nautilus_core/indicators/src/python/average/mod.rs +++ b/nautilus_core/indicators/src/python/average/mod.rs @@ -19,4 +19,6 @@ pub mod ema; pub mod hma; pub mod rma; pub mod sma; +pub mod vidya; +pub mod vwap; pub mod wma; diff --git a/nautilus_core/indicators/src/python/average/vidya.rs b/nautilus_core/indicators/src/python/average/vidya.rs index 443cadf8fc01..a66b859926f3 100644 --- a/nautilus_core/indicators/src/python/average/vidya.rs +++ b/nautilus_core/indicators/src/python/average/vidya.rs @@ -21,7 +21,7 @@ use nautilus_model::{ use pyo3::prelude::*; use crate::{ - average::vidya::VariableIndexDynamicAverage, + average::{vidya::VariableIndexDynamicAverage, MovingAverageType}, indicator::{Indicator, MovingAverage}, }; @@ -30,14 +30,17 @@ impl VariableIndexDynamicAverage { #[new] pub fn py_new( period: usize, - weights: Vec, price_type: Option, + cmo_ma_type: Option, ) -> PyResult { - Self::new(period, weights, price_type).map_err(to_pyvalue_err) + Self::new(period, price_type, cmo_ma_type).map_err(to_pyvalue_err) } fn __repr__(&self) -> String { - format!("VariableIndexDynamicAverage({},{:?})", self.period, self.weights) + format!( + "VariableIndexDynamicAverage({},{:?})", + self.period, self.price_type + ) } #[getter] @@ -60,8 +63,8 @@ impl VariableIndexDynamicAverage { #[getter] #[pyo3(name = "alpha")] - fn py_alpha(&self) -> usize { - self.alpha() + fn py_alpha(&self) -> f64 { + self.alpha } #[getter] @@ -83,12 +86,11 @@ impl VariableIndexDynamicAverage { } #[getter] - #[pyo3(name = "cmo")] - fn py_cmo(&self) -> ChandeMomentumOscillator { - self.cmo + #[pyo3(name = "value")] + fn py_value(&self) -> f64 { + self.value } - #[pyo3(name = "handle_quote_tick")] fn py_handle_quote_tick(&mut self, tick: &QuoteTick) { self.py_update_raw(tick.extract_price(self.price_type).into()); diff --git a/nautilus_core/indicators/src/python/average/vwap.rs b/nautilus_core/indicators/src/python/average/vwap.rs new file mode 100644 index 000000000000..41df2d4faeeb --- /dev/null +++ b/nautilus_core/indicators/src/python/average/vwap.rs @@ -0,0 +1,81 @@ +// ------------------------------------------------------------------------------------------------- +// 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 nautilus_core::{nanos::UnixNanos, python::to_pyvalue_err}; +use nautilus_model::{ + data::{bar::Bar, quote::QuoteTick, trade::TradeTick}, + enums::PriceType, +}; +use pyo3::prelude::*; + +use crate::{ + average::vwap::VolumeWeightedAveragePrice, + indicator::{Indicator, MovingAverage}, +}; + +#[pymethods] +impl VolumeWeightedAveragePrice { + #[new] + pub fn py_new() -> PyResult { + Self::new().map_err(to_pyvalue_err) + } + + fn __repr__(&self) -> String { + format!("VolumeWeightedAveragePrice") + } + + #[getter] + #[pyo3(name = "name")] + fn py_name(&self) -> String { + self.name() + } + + #[getter] + #[pyo3(name = "has_inputs")] + fn py_has_inputs(&self) -> bool { + self.has_inputs() + } + + #[getter] + #[pyo3(name = "value")] + fn py_value(&self) -> f64 { + self.value + } + + #[getter] + #[pyo3(name = "initialized")] + fn py_initialized(&self) -> bool { + self.initialized + } + + #[pyo3(name = "handle_bar")] + fn py_handle_bar(&mut self, bar: &Bar) { + self.py_update_raw( + (&bar.close).into(), + (&bar.volume).into(), + bar.ts_init.as_f64(), + ); + } + + #[pyo3(name = "reset")] + fn py_reset(&mut self) { + self.reset(); + } + + #[pyo3(name = "update_raw")] + fn py_update_raw(&mut self, value: f64, volume: f64, ts: f64) { + self.update_raw(value, volume, ts); + } +} diff --git a/nautilus_core/indicators/src/python/mod.rs b/nautilus_core/indicators/src/python/mod.rs index ed63431a324e..6bc4002a04e9 100644 --- a/nautilus_core/indicators/src/python/mod.rs +++ b/nautilus_core/indicators/src/python/mod.rs @@ -35,6 +35,7 @@ pub fn indicators(_: Python<'_>, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; + m.add_class::()?; // book m.add_class::()?; // ratio diff --git a/nautilus_core/indicators/src/stubs.rs b/nautilus_core/indicators/src/stubs.rs index 610b90805abe..513903094b43 100644 --- a/nautilus_core/indicators/src/stubs.rs +++ b/nautilus_core/indicators/src/stubs.rs @@ -31,8 +31,8 @@ use crate::{ average::{ ama::AdaptiveMovingAverage, dema::DoubleExponentialMovingAverage, ema::ExponentialMovingAverage, hma::HullMovingAverage, rma::WilderMovingAverage, - sma::SimpleMovingAverage, vidya::VariableIndexDynamicAverage, wma::WeightedMovingAverage, - MovingAverageType, + sma::SimpleMovingAverage, vidya::VariableIndexDynamicAverage, + vwap::VolumeWeightedAveragePrice, wma::WeightedMovingAverage, MovingAverageType, }, momentum::{bias::Bias, cmo::ChandeMomentumOscillator, rsi::RelativeStrengthIndex}, ratio::efficiency_ratio::EfficiencyRatio, @@ -137,6 +137,11 @@ pub fn indicator_vidya_10() -> VariableIndexDynamicAverage { .unwrap() } +#[fixture] +pub fn indicator_vwap() -> VolumeWeightedAveragePrice { + VolumeWeightedAveragePrice::new().unwrap() +} + #[fixture] pub fn indicator_wma_10() -> WeightedMovingAverage { let weights = vec![0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]; diff --git a/nautilus_trader/core/nautilus_pyo3.pyi b/nautilus_trader/core/nautilus_pyo3.pyi index 1fcabc1948fb..55c727f4fa65 100644 --- a/nautilus_trader/core/nautilus_pyo3.pyi +++ b/nautilus_trader/core/nautilus_pyo3.pyi @@ -2664,6 +2664,22 @@ class VariableIndexDynamicAverage: def handle_bar(self, bar: Bar) -> None: ... def reset(self) -> None: ... +class VolumeWeightedAveragePrice: + def __init__( + self + ) -> None: ... + @property + def name(self) -> str: ... + @property + def initialized(self) -> bool: ... + @property + def has_inputs(self) -> bool: ... + @property + def value(self) -> float: ... + def update_raw(self, price: float, volume: float, timestamp: float ) -> None: ... + def handle_bar(self, bar: Bar) -> None: ... + def reset(self) -> None: ... + class ChandeMomentumOscillator: def __init__( self, From 77e8607e38c1a0d7605d5356445d59f4f7bd843f Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sat, 25 May 2024 07:43:58 +1000 Subject: [PATCH 03/59] Update README --- README.md | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 7b4a7a2c1ce8..567d55a841b8 100644 --- a/README.md +++ b/README.md @@ -43,16 +43,16 @@ including FX, Equities, Futures, Options, CFDs, Crypto and Betting - across mult ## Features -- **Fast** - Core written in Rust with asynchronous networking using [tokio](https://crates.io/crates/tokio) -- **Reliable** - Type safety and thread safety through Rust. Redis backed performant state persistence -- **Portable** - OS independent, runs on Linux, macOS, Windows. Deploy using Docker -- **Flexible** - Modular adapters mean any REST, WebSocket, or FIX API can be integrated -- **Advanced** - Time in force `IOC`, `FOK`, `GTD`, `AT_THE_OPEN`, `AT_THE_CLOSE`, advanced order types and conditional triggers. Execution instructions `post-only`, `reduce-only`, and icebergs. Contingency order lists including `OCO`, `OTO` -- **Customizable** - Add user defined custom components, or assemble entire systems from scratch leveraging the cache and message bus -- **Backtesting** - Run with multiple venues, instruments and strategies simultaneously using historical quote tick, trade tick, bar, order book and custom data with nanosecond resolution -- **Live** - Use identical strategy implementations between backtesting and live deployments -- **Multi-venue** - Multiple venue capabilities facilitate market making and statistical arbitrage strategies -- **AI Agent Training** - Backtest engine fast enough to be used to train AI trading agents (RL/ES) +- **Fast:** Core written in Rust with asynchronous networking using [tokio](https://crates.io/crates/tokio) +- **Reliable:** Type safety and thread safety through Rust. Redis backed performant state persistence +- **Portable:** OS independent, runs on Linux, macOS, Windows. Deploy using Docker +- **Flexible:** Modular adapters mean any REST, WebSocket, or FIX API can be integrated +- **Advanced:** Time in force `IOC`, `FOK`, `GTD`, `AT_THE_OPEN`, `AT_THE_CLOSE`, advanced order types and conditional triggers. Execution instructions `post-only`, `reduce-only`, and icebergs. Contingency order lists including `OCO`, `OTO` +- **Customizable:** Add user defined custom components, or assemble entire systems from scratch leveraging the cache and message bus +- **Backtesting:** Run with multiple venues, instruments and strategies simultaneously using historical quote tick, trade tick, bar, order book and custom data with nanosecond resolution +- **Live:** Use identical strategy implementations between backtesting and live deployments +- **Multi-venue:** Multiple venue capabilities facilitate market making and statistical arbitrage strategies +- **AI training:** - Backtest engine fast enough to be used to train AI trading agents (RL/ES) ![Alt text](https://github.com/nautechsystems/nautilus_trader/blob/develop/docs/_images/nautilus-art.png?raw=true "nautilus") @@ -63,10 +63,10 @@ including FX, Equities, Futures, Options, CFDs, Crypto and Betting - across mult ## Why NautilusTrader? -- **Highly performant event-driven Python** - native binary core components -- **Parity between backtesting and live trading** - identical strategy code -- **Reduced operational risk** - risk management functionality, logical correctness and type safety -- **Highly extendable** - message bus, custom components and actors, custom data, custom adapters +- **Highly performant event-driven Python:** Native binary core components +- **Parity between backtesting and live trading:** Identical strategy code +- **Reduced operational risk:** Risk management functionality, logical correctness and type safety +- **Highly extendable:** Message bus, custom components and actors, custom data, custom adapters Traditionally, trading strategy research and backtesting might be conducted in Python (or other suitable language) using vectorized methods, with the strategy then needing to be reimplemented in a more event-drive way @@ -129,8 +129,8 @@ into a unified interface. The following integrations are currently supported: | [Databento](https://databento.com) | `DATABENTO` | Data Provider | ![status](https://img.shields.io/badge/beta-yellow) | [Guide](https://docs.nautilustrader.io/integrations/databento.html) | | [Interactive Brokers](https://www.interactivebrokers.com) | `INTERACTIVE_BROKERS` | Brokerage (multi-venue) | ![status](https://img.shields.io/badge/stable-green) | [Guide](https://docs.nautilustrader.io/integrations/ib.html) | -- `ID:` The default client ID for the integrations adapter clients -- `Type:` The type of integration (often the venue type) +- **ID:** The default client ID for the integrations adapter clients +- **Type:** The type of integration (often the venue type) ### Status - `building` - Under construction and likely not in a usable state @@ -196,19 +196,23 @@ Refer to the [Installation Guide](https://docs.nautilustrader.io/getting_started ## Versioning and releases -NautilusTrader is currently following a bi-weekly beta release schedule. +NautilusTrader is currently targeting a weekly release schedule, occasionally there may be experimental +or larger features which will delay a release by several weeks. + The API is becoming more stable, however breaking changes are still possible between releases. Documentation of these changes in the release notes are made on a best-effort basis. ### Branches +We aim to maintain a stable passing build on all branches. + - `master` branch will always reflect the source code for the latest released version - `nightly` branch may contain experimental features and is generally merged from `develop` branch daily, 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 +- `develop` branch is normally very active with frequent commits and may contain experimental features -The current roadmap has a goal of achieving a stable API for a `2.x` version. From this -point we will follow a formal process for releases, with deprecation periods for any API changes. +The current roadmap has a goal of achieving a stable API for a `2.x` version (likely post Rust port). +From this point we will follow a formal process for releases, with deprecation periods for any API changes. +This allows us to maintain a maximum pace of development for now. ## Makefile From cf85b65326738dde78942ea1c9284b79c6362576 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sat, 25 May 2024 07:46:23 +1000 Subject: [PATCH 04/59] Update release notes --- RELEASES.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 97bcee25b233..995fab0b8a52 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -3,7 +3,7 @@ Released on TBD (UTC). ### Enhancements -None +- Ported `VolumeWeightedAveragePrice` indicator to Rust (#1665), thanks @Pushkarm029 ### Breaking Changes None @@ -87,7 +87,7 @@ Released on 20th April 2024 (UTC). - Improved `modify_order` error logging when order values remain unchanged - Added `RecordFlag` enum for Rust and Python - Interactive Brokers further improvements and fixes, thanks @rsmb7z -- Ported Bias indicator to Rust, thanks @Pushkarm029 +- Ported `Bias` indicator to Rust, thanks @Pushkarm029 ### Breaking Changes - Reordered `OrderBookDelta` params `flags` and `sequence` and removed default 0 values (more explicit and less chance of mismatches) @@ -126,8 +126,8 @@ Released on 22nd March 2024 (UTC). - Improved Binance execution client ping listen key error handling and logging - Improved Redis cache adapter and message bus error handling and logging - Improved Redis port parsing (`DatabaseConfig.port` can now be either a string or integer) -- Ported ChandeMomentumOscillator indicator to Rust, thanks @Pushkarm029 -- Ported VIDYA indicator to Rust, thanks @Pushkarm029 +- Ported `ChandeMomentumOscillator` indicator to Rust, thanks @Pushkarm029 +- Ported `VIDYA` indicator to Rust, thanks @Pushkarm029 - Refactored `InteractiveBrokersEWrapper`, thanks @rsmb7z - Redact Redis passwords in strings and logs - Upgraded `redis` crate to 0.25.2 which bumps up TLS dependencies, and turned on `tls-rustls-webpki-roots` feature flag From 8c2a027b9e28b289573c357d1039a8a986ee0cab Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sat, 25 May 2024 08:06:45 +1000 Subject: [PATCH 05/59] Standardize markdown colon formatting --- README.md | 2 +- docs/api_reference/index.md | 4 ++-- docs/concepts/overview.md | 15 +++++++++------ docs/developer_guide/adapters.md | 8 ++++---- docs/integrations/ib.md | 2 +- docs/tutorials/index.md | 4 ++-- 6 files changed, 19 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 567d55a841b8..a3b2e979ed2f 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ including FX, Equities, Futures, Options, CFDs, Crypto and Betting - across mult - **Backtesting:** Run with multiple venues, instruments and strategies simultaneously using historical quote tick, trade tick, bar, order book and custom data with nanosecond resolution - **Live:** Use identical strategy implementations between backtesting and live deployments - **Multi-venue:** Multiple venue capabilities facilitate market making and statistical arbitrage strategies -- **AI training:** - Backtest engine fast enough to be used to train AI trading agents (RL/ES) +- **AI Training:** Backtest engine fast enough to be used to train AI trading agents (RL/ES) ![Alt text](https://github.com/nautechsystems/nautilus_trader/blob/develop/docs/_images/nautilus-art.png?raw=true "nautilus") diff --git a/docs/api_reference/index.md b/docs/api_reference/index.md index 86e7a057ab98..4aafd5a097bd 100644 --- a/docs/api_reference/index.md +++ b/docs/api_reference/index.md @@ -34,8 +34,8 @@ 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. -- **Nightly**: This API reference is built from the head of the `nightly` branch and represents bleeding edge and experimental changes/features currently in development. +- **Latest:** This API reference is built from the head of the `master` branch and represents the latest stable release. +- **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/overview.md b/docs/concepts/overview.md index 080cd14b66c4..5d163d66397b 100644 --- a/docs/concepts/overview.md +++ b/docs/concepts/overview.md @@ -26,7 +26,7 @@ including FX, Equities, Futures, Options, CFDs, Crypto and Betting - across mult - **Backtesting:** Run with multiple venues, instruments and strategies simultaneously using historical quote tick, trade tick, bar, order book and custom data with nanosecond resolution - **Live:** Use identical strategy implementations between backtesting and live deployments - **Multi-venue:** Multiple venue capabilities facilitate market making and statistical arbitrage strategies -- **AI Agent Training:** Backtest engine fast enough to be used to train AI trading agents (RL/ES) +- **AI Training:** Backtest engine fast enough to be used to train AI trading agents (RL/ES) ![Nautilus](https://github.com/nautechsystems/nautilus_trader/blob/develop/docs/_images/nautilus-art.png?raw=true "nautilus") > *nautilus - from ancient Greek 'sailor' and naus 'ship'.* @@ -36,10 +36,10 @@ including FX, Equities, Futures, Options, CFDs, Crypto and Betting - across mult ## Why NautilusTrader? -- **Highly performant event-driven Python** - native binary core components -- **Parity between backtesting and live trading** - identical strategy code -- **Reduced operational risk** - risk management functionality, logical correctness and type safety -- **Highly extendable** - message bus, custom components and actors, custom data, custom adapters +- **Highly performant event-driven Python:** Native binary core components +- **Parity between backtesting and live trading:** Identical strategy code +- **Reduced operational risk:** Risk management functionality, logical correctness and type safety +- **Highly extendable:** Message bus, custom components and actors, custom data, custom adapters Traditionally, trading strategy research and backtesting might be conducted in Python (or other suitable language) using vectorized methods, with the strategy then needing to be reimplemented in a more event-drive way @@ -95,11 +95,13 @@ Python 3.11 offers improved run-time performance, while Python 3.12 additionally ``` ## Domain model + The platform features a comprehensive trading domain model that includes various value types such as `Price` and `Quantity`, as well as more complex entities such as `Order` and `Position` objects, 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) - `OrderBookDeltas` (container type) @@ -140,6 +142,7 @@ The price types and bar aggregations can be combined with step sizes >= 1 in any This enables maximum flexibility and now allows alternative bars to be aggregated for live trading. ### Account Types + The following account types are available for both live and backtest environments; - `Cash` single-currency (base currency) @@ -149,6 +152,7 @@ The following account types are available for both live and backtest environment - `Betting` single-currency ### Order Types + The following order types are available (when possible on a venue); - `MARKET` @@ -160,4 +164,3 @@ The following order types are available (when possible on a venue); - `LIMIT_IF_TOUCHED` - `TRAILING_STOP_MARKET` - `TRAILING_STOP_LIMIT` - diff --git a/docs/developer_guide/adapters.md b/docs/developer_guide/adapters.md index 335ff2c9595b..9b0f42cdb404 100644 --- a/docs/developer_guide/adapters.md +++ b/docs/developer_guide/adapters.md @@ -9,10 +9,10 @@ into a unified interface. ## Structure of an Adapter An adapter typically consists of several components: -1. **Instrument Provider**: Supplies instrument definitions -2. **Data Client**: Handles live market data feeds and historical data requests -3. **Execution Client**: Handles order execution and management -5. **Configuration**: Configures the client settings +1. **Instrument Provider:** Supplies instrument definitions +2. **Data Client:** Handles live market data feeds and historical data requests +3. **Execution Client:** Handles order execution and management +5. **Configuration:** Configures the client settings ## Steps to Implement a New Adapter diff --git a/docs/integrations/ib.md b/docs/integrations/ib.md index 3a669a17fd51..aa34049b0930 100644 --- a/docs/integrations/ib.md +++ b/docs/integrations/ib.md @@ -56,7 +56,7 @@ print(gateway.is_logged_in(gateway.container)) print(gateway.container.logs()) ``` -**Note**: To supply credentials to the Interactive Brokers Gateway, either pass the `username` and `password` to the config dictionaries, or set the following environment variables: +**Note:** To supply credentials to the Interactive Brokers Gateway, either pass the `username` and `password` to the config dictionaries, or set the following environment variables: - `TWS_USERNAME` - `TWS_PASSWORD` diff --git a/docs/tutorials/index.md b/docs/tutorials/index.md index c34eb49efeb3..046c9fa9b4e5 100644 --- a/docs/tutorials/index.md +++ b/docs/tutorials/index.md @@ -19,8 +19,8 @@ From basic tasks to more advanced operations, these tutorials cater to a wide ra ```{tip} Make sure you are following the tutorial docs which match the version of NautilusTrader you are running: -- **Latest** - These docs are built from the HEAD of the `master` branch and work with the latest stable release. -- **Develop** - These docs are built from the HEAD of the `develop` branch and work with bleeding edge and experimental changes/features currently in development. +- **Latest:** These docs are built from the HEAD of the `master` branch and work with the latest stable release. +- **Develop:** These docs are built from the HEAD of the `develop` branch and work with bleeding edge and experimental changes/features currently in development. ``` ## Backtesting From 43bc38d123dce3ceff603b4f4221bd488427772a Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sat, 25 May 2024 09:05:47 +1000 Subject: [PATCH 06/59] Update Rust docs --- nautilus_core/common/src/cache/core.rs | 2 ++ nautilus_core/common/src/clock.rs | 6 ++++++ nautilus_trader/core/includes/common.h | 10 ++++++++++ nautilus_trader/core/rust/common.pxd | 6 ++++++ 4 files changed, 24 insertions(+) diff --git a/nautilus_core/common/src/cache/core.rs b/nautilus_core/common/src/cache/core.rs index 143995e25706..011bfae44dc3 100644 --- a/nautilus_core/common/src/cache/core.rs +++ b/nautilus_core/common/src/cache/core.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! The core cache in-memory structure. + use std::{ collections::{HashMap, HashSet, VecDeque}, time::{SystemTime, UNIX_EPOCH}, diff --git a/nautilus_core/common/src/clock.rs b/nautilus_core/common/src/clock.rs index 3051268f9616..16b0e0a3a033 100644 --- a/nautilus_core/common/src/clock.rs +++ b/nautilus_core/common/src/clock.rs @@ -71,6 +71,9 @@ pub trait Clock { fn cancel_timers(&mut self); } +/// Provides a static test clock. +/// +/// Stores the current timestamp internally which can be advanced. pub struct TestClock { time: AtomicTime, timers: HashMap, @@ -261,6 +264,9 @@ impl Clock for TestClock { } } +/// Provides a real-time clock which uses system time. +/// +/// Timestamps are guaranteed to be unique and monotonically increasing. pub struct LiveClock { time: &'static AtomicTime, timers: HashMap, diff --git a/nautilus_trader/core/includes/common.h b/nautilus_trader/core/includes/common.h index e6881f4e03e0..f9dd06b6f0f4 100644 --- a/nautilus_trader/core/includes/common.h +++ b/nautilus_trader/core/includes/common.h @@ -193,10 +193,20 @@ typedef enum LogLevel { ERROR = 40, } LogLevel; +/** + * Provides a real-time clock which uses system time. + * + * Timestamps are guaranteed to be unique and monotonically increasing. + */ typedef struct LiveClock LiveClock; typedef struct LogGuard LogGuard; +/** + * Provides a static test clock. + * + * Stores the current timestamp internally which can be advanced. + */ typedef struct TestClock TestClock; /** diff --git a/nautilus_trader/core/rust/common.pxd b/nautilus_trader/core/rust/common.pxd index 899a9273f402..f7584ee5751d 100644 --- a/nautilus_trader/core/rust/common.pxd +++ b/nautilus_trader/core/rust/common.pxd @@ -101,12 +101,18 @@ cdef extern from "../includes/common.h": # The **ERROR** error log level. ERROR # = 40, + # Provides a real-time clock which uses system time. + # + # Timestamps are guaranteed to be unique and monotonically increasing. cdef struct LiveClock: pass cdef struct LogGuard: pass + # Provides a static test clock. + # + # Stores the current timestamp internally which can be advanced. cdef struct TestClock: pass From 26464dc36a909fc0096dbf086509297276eb7ecc Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sat, 25 May 2024 10:07:44 +1000 Subject: [PATCH 07/59] Format imports --- nautilus_core/indicators/src/average/vwap.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nautilus_core/indicators/src/average/vwap.rs b/nautilus_core/indicators/src/average/vwap.rs index 3e4a4801015d..90929804bbe9 100644 --- a/nautilus_core/indicators/src/average/vwap.rs +++ b/nautilus_core/indicators/src/average/vwap.rs @@ -13,9 +13,10 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -use nautilus_model::data::bar::Bar; use std::fmt::{Display, Formatter}; +use nautilus_model::data::bar::Bar; + use crate::indicator::Indicator; #[repr(C)] From d1bbcabfd5ed58d3a6558ee6b152ddb23c6e39af Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sat, 25 May 2024 10:10:37 +1000 Subject: [PATCH 08/59] Standardize new method docs --- nautilus_core/common/src/timer.rs | 4 ++-- nautilus_core/core/src/uuid.rs | 2 +- nautilus_core/model/src/identifiers/account_id.rs | 2 +- nautilus_core/model/src/identifiers/client_id.rs | 2 +- nautilus_core/model/src/identifiers/client_order_id.rs | 2 +- nautilus_core/model/src/identifiers/component_id.rs | 2 +- nautilus_core/model/src/identifiers/exec_algorithm_id.rs | 2 +- nautilus_core/model/src/identifiers/instrument_id.rs | 2 +- nautilus_core/model/src/identifiers/order_list_id.rs | 2 +- nautilus_core/model/src/identifiers/position_id.rs | 2 +- nautilus_core/model/src/identifiers/strategy_id.rs | 2 +- nautilus_core/model/src/identifiers/symbol.rs | 2 +- nautilus_core/model/src/identifiers/trade_id.rs | 2 +- nautilus_core/model/src/identifiers/trader_id.rs | 2 +- nautilus_core/model/src/identifiers/venue.rs | 2 +- nautilus_core/model/src/identifiers/venue_order_id.rs | 2 +- 16 files changed, 17 insertions(+), 17 deletions(-) diff --git a/nautilus_core/common/src/timer.rs b/nautilus_core/common/src/timer.rs index fdea2c94c8c4..8e1b09fde70c 100644 --- a/nautilus_core/common/src/timer.rs +++ b/nautilus_core/common/src/timer.rs @@ -130,7 +130,7 @@ pub struct TestTimer { } impl TestTimer { - /// Creates a new `TestTimer`. + /// Creates a new `TestTimer` instance. pub fn new( name: &str, interval_ns: u64, @@ -233,7 +233,7 @@ pub struct LiveTimer { } impl LiveTimer { - /// Creates a new `LiveTimer`. + /// Creates a new `LiveTimer` instance. pub fn new( name: &str, interval_ns: u64, diff --git a/nautilus_core/core/src/uuid.rs b/nautilus_core/core/src/uuid.rs index e2ebfc1ab6ca..b3b323e404aa 100644 --- a/nautilus_core/core/src/uuid.rs +++ b/nautilus_core/core/src/uuid.rs @@ -43,7 +43,7 @@ pub struct UUID4 { } impl UUID4 { - /// Creates a new `UUID4`. + /// Creates a new `UUID4` instance. #[must_use] pub fn new() -> Self { let uuid = Uuid::new_v4(); diff --git a/nautilus_core/model/src/identifiers/account_id.rs b/nautilus_core/model/src/identifiers/account_id.rs index c1586d0930f7..c6b3ef71ddb5 100644 --- a/nautilus_core/model/src/identifiers/account_id.rs +++ b/nautilus_core/model/src/identifiers/account_id.rs @@ -39,7 +39,7 @@ use super::venue::Venue; pub struct AccountId(Ustr); impl AccountId { - /// Creates a new `AccountId` instance from the given identifier value. + /// Creates a new `AccountId` instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/client_id.rs b/nautilus_core/model/src/identifiers/client_id.rs index a0c680e08d6c..cb78b19f2ac0 100644 --- a/nautilus_core/model/src/identifiers/client_id.rs +++ b/nautilus_core/model/src/identifiers/client_id.rs @@ -31,7 +31,7 @@ use ustr::Ustr; pub struct ClientId(Ustr); impl ClientId { - /// Creates a new `ClientId` instance from the given identifier value. + /// Creates a new `ClientId` instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/client_order_id.rs b/nautilus_core/model/src/identifiers/client_order_id.rs index d99e6f199b36..d54e492db11f 100644 --- a/nautilus_core/model/src/identifiers/client_order_id.rs +++ b/nautilus_core/model/src/identifiers/client_order_id.rs @@ -31,7 +31,7 @@ use ustr::Ustr; pub struct ClientOrderId(Ustr); impl ClientOrderId { - /// Creates a new `ClientOrderId` instance from the given identifier value. + /// Creates a new `ClientOrderId` instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/component_id.rs b/nautilus_core/model/src/identifiers/component_id.rs index 1212783c9eb4..6f8399b39d5b 100644 --- a/nautilus_core/model/src/identifiers/component_id.rs +++ b/nautilus_core/model/src/identifiers/component_id.rs @@ -31,7 +31,7 @@ use ustr::Ustr; pub struct ComponentId(Ustr); impl ComponentId { - /// Creates a new `ComponentId` instance from the given identifier value. + /// Creates a new `ComponentId` instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/exec_algorithm_id.rs b/nautilus_core/model/src/identifiers/exec_algorithm_id.rs index ecd57d6bd383..25c6753ebab4 100644 --- a/nautilus_core/model/src/identifiers/exec_algorithm_id.rs +++ b/nautilus_core/model/src/identifiers/exec_algorithm_id.rs @@ -31,7 +31,7 @@ use ustr::Ustr; pub struct ExecAlgorithmId(Ustr); impl ExecAlgorithmId { - /// Creates a new `ExecAlgorithmId` instance from the given identifier value. + /// Creates a new `ExecAlgorithmId` instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/instrument_id.rs b/nautilus_core/model/src/identifiers/instrument_id.rs index 07fec16c60d2..144008c02aab 100644 --- a/nautilus_core/model/src/identifiers/instrument_id.rs +++ b/nautilus_core/model/src/identifiers/instrument_id.rs @@ -40,7 +40,7 @@ pub struct InstrumentId { } impl InstrumentId { - /// Creates a new `InstrumentId` instance from the given `Symbol` and `Venue`. + /// Creates a new `InstrumentId` instance. #[must_use] pub fn new(symbol: Symbol, venue: Venue) -> Self { Self { symbol, venue } diff --git a/nautilus_core/model/src/identifiers/order_list_id.rs b/nautilus_core/model/src/identifiers/order_list_id.rs index a136eb7b1930..0c722a1ceb53 100644 --- a/nautilus_core/model/src/identifiers/order_list_id.rs +++ b/nautilus_core/model/src/identifiers/order_list_id.rs @@ -31,7 +31,7 @@ use ustr::Ustr; pub struct OrderListId(Ustr); impl OrderListId { - /// Creates a new `OrderListId` instance from the given identifier value. + /// Creates a new `OrderListId` instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/position_id.rs b/nautilus_core/model/src/identifiers/position_id.rs index 618b0ad7a14a..1392128d075a 100644 --- a/nautilus_core/model/src/identifiers/position_id.rs +++ b/nautilus_core/model/src/identifiers/position_id.rs @@ -31,7 +31,7 @@ use ustr::Ustr; pub struct PositionId(Ustr); impl PositionId { - /// Creates a new `PositionId` instance from the given identifier value. + /// Creates a new `PositionId` instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/strategy_id.rs b/nautilus_core/model/src/identifiers/strategy_id.rs index 137a8063e3c9..7f49097ff716 100644 --- a/nautilus_core/model/src/identifiers/strategy_id.rs +++ b/nautilus_core/model/src/identifiers/strategy_id.rs @@ -40,7 +40,7 @@ const EXTERNAL_STRATEGY_ID: &str = "EXTERNAL"; pub struct StrategyId(Ustr); impl StrategyId { - /// Creates a new `StrategyId` instance from the given identifier value. + /// Creates a new `StrategyId` instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/symbol.rs b/nautilus_core/model/src/identifiers/symbol.rs index dacbb5d1051a..0886bdc0d72d 100644 --- a/nautilus_core/model/src/identifiers/symbol.rs +++ b/nautilus_core/model/src/identifiers/symbol.rs @@ -31,7 +31,7 @@ use ustr::Ustr; pub struct Symbol(Ustr); impl Symbol { - /// Creates a new `Symbol` instance from the given identifier value. + /// Creates a new `Symbol` instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/trade_id.rs b/nautilus_core/model/src/identifiers/trade_id.rs index 8ab6adbac517..086a46711191 100644 --- a/nautilus_core/model/src/identifiers/trade_id.rs +++ b/nautilus_core/model/src/identifiers/trade_id.rs @@ -45,7 +45,7 @@ pub struct TradeId { } impl TradeId { - /// Creates a new `TradeId` instance from the given identifier value. + /// Creates a new `TradeId` instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/trader_id.rs b/nautilus_core/model/src/identifiers/trader_id.rs index 2b1de86aa54b..eff8e9c090b4 100644 --- a/nautilus_core/model/src/identifiers/trader_id.rs +++ b/nautilus_core/model/src/identifiers/trader_id.rs @@ -37,7 +37,7 @@ use ustr::Ustr; pub struct TraderId(Ustr); impl TraderId { - /// Creates a new `TraderId` instance from the given identifier value. + /// Creates a new `TraderId` instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/venue.rs b/nautilus_core/model/src/identifiers/venue.rs index 347a2763cc6a..eddf34a10bda 100644 --- a/nautilus_core/model/src/identifiers/venue.rs +++ b/nautilus_core/model/src/identifiers/venue.rs @@ -35,7 +35,7 @@ pub const SYNTHETIC_VENUE: &str = "SYNTH"; pub struct Venue(Ustr); impl Venue { - /// Creates a new `Venue` instance from the given identifier value. + /// Creates a new `Venue` instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/venue_order_id.rs b/nautilus_core/model/src/identifiers/venue_order_id.rs index f1ce60d71524..99991d625243 100644 --- a/nautilus_core/model/src/identifiers/venue_order_id.rs +++ b/nautilus_core/model/src/identifiers/venue_order_id.rs @@ -31,7 +31,7 @@ use ustr::Ustr; pub struct VenueOrderId(Ustr); impl VenueOrderId { - /// Creates a new `VenueOrderId` instance from the given identifier value. + /// Creates a new `VenueOrderId` instance. /// /// # Panics /// From 97514003cdef9e2a605b9e92269550d59ac996e3 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sat, 25 May 2024 10:11:07 +1000 Subject: [PATCH 09/59] Update Cache docs --- nautilus_core/common/src/cache/core.rs | 189 ++++++++++++++++++------- 1 file changed, 139 insertions(+), 50 deletions(-) diff --git a/nautilus_core/common/src/cache/core.rs b/nautilus_core/common/src/cache/core.rs index 011bfae44dc3..f1b880e5671a 100644 --- a/nautilus_core/common/src/cache/core.rs +++ b/nautilus_core/common/src/cache/core.rs @@ -51,7 +51,7 @@ use ustr::Ustr; use super::database::CacheDatabaseAdapter; use crate::{enums::SerializationEncoding, interface::account::Account}; -/// The configuration for `Cache` instances. +/// Configuration for `Cache` instances. pub struct CacheConfig { pub encoding: SerializationEncoding, pub timestamps_as_iso8601: bool, @@ -90,6 +90,7 @@ impl CacheConfig { } impl Default for CacheConfig { + /// Creates a new default `CacheConfig` instance. fn default() -> Self { Self::new( SerializationEncoding::MsgPack, @@ -137,7 +138,7 @@ pub struct CacheIndex { } impl CacheIndex { - /// Clear the index which will clear/reset all internal state. + /// Clears the index which will clear/reset all internal state. pub fn clear(&mut self) { self.venue_account.clear(); self.venue_orders.clear(); @@ -191,12 +192,14 @@ pub struct Cache { } impl Default for Cache { + /// Creates a new default `Cache` instance. fn default() -> Self { Self::new(CacheConfig::default(), None) } } impl Cache { + /// Creates a new `Cache` instance. #[must_use] pub fn new(config: CacheConfig, database: Option) -> Self { let index = CacheIndex { @@ -252,7 +255,7 @@ impl Cache { // -- COMMANDS -------------------------------------------------------------------------------- - /// Clear the current general cache and load the general objects from the cache database. + /// Clears the current general cache and load the general objects from the cache database. pub fn cache_general(&mut self) -> anyhow::Result<()> { self.general = match &self.database { Some(db) => db.load()?, @@ -266,7 +269,7 @@ impl Cache { Ok(()) } - /// Clear the current currencies cache and load currencies from the cache database. + /// Clears the current currencies cache and load currencies from the cache database. pub fn cache_currencies(&mut self) -> anyhow::Result<()> { self.currencies = match &self.database { Some(db) => db.load_currencies()?, @@ -277,7 +280,7 @@ impl Cache { Ok(()) } - /// Clear the current instruments cache and load instruments from the cache database. + /// Clears the current instruments cache and load instruments from the cache database. pub fn cache_instruments(&mut self) -> anyhow::Result<()> { self.instruments = match &self.database { Some(db) => db.load_instruments()?, @@ -288,7 +291,7 @@ impl Cache { Ok(()) } - /// Clear the current synthetic instruments cache and load synthetic instruments from the cache + /// Clears the current synthetic instruments cache and load synthetic instruments from the cache /// database. pub fn cache_synthetics(&mut self) -> anyhow::Result<()> { self.synthetics = match &self.database { @@ -303,7 +306,7 @@ impl Cache { Ok(()) } - /// Clear the current accounts cache and load accounts from the cache database. + /// Clears the current accounts cache and load accounts from the cache database. pub fn cache_accounts(&mut self) -> anyhow::Result<()> { self.accounts = match &self.database { Some(db) => db.load_accounts()?, @@ -317,7 +320,7 @@ impl Cache { Ok(()) } - /// Clear the current orders cache and load orders from the cache database. + /// Clears the current orders cache and load orders from the cache database. pub fn cache_orders(&mut self) -> anyhow::Result<()> { self.orders = match &self.database { Some(db) => db.load_orders()?, @@ -328,7 +331,7 @@ impl Cache { Ok(()) } - /// Clear the current positions cache and load positions from the cache database. + /// Clears the current positions cache and load positions from the cache database. pub fn cache_positions(&mut self) -> anyhow::Result<()> { self.positions = match &self.database { Some(db) => db.load_positions()?, @@ -339,7 +342,7 @@ impl Cache { Ok(()) } - /// Clear the current cache index and re-build. + /// Clears the current cache index and re-build. pub fn build_index(&mut self) { self.index.clear(); debug!("Building index"); @@ -506,7 +509,7 @@ impl Cache { } } - /// Check integrity of data within the cache. + /// Checks integrity of data within the cache. /// /// All data should be loaded from the database prior to this call. /// If an error is found then a log error message will also be produced. @@ -879,7 +882,7 @@ impl Cache { } } - /// Check for any residual open state and log warnings if any are found. + /// Checks for any residual open state and log warnings if any are found. /// ///'Open state' is considered to be open orders and open positions. #[must_use] @@ -903,13 +906,13 @@ impl Cache { residuals } - /// Clear the caches index. + /// Clears the caches index. pub fn clear_index(&mut self) { self.index.clear(); debug!("Cleared index"); } - /// Reset the cache. + /// Resets the cache. /// /// All stateful fields are reset to their initial value. pub fn reset(&mut self) { @@ -942,7 +945,7 @@ impl Cache { Ok(()) } - /// Flush the caches database which permanently removes all persisted data. + /// Flushes the caches database which permanently removes all persisted data. pub fn flush_db(&self) -> anyhow::Result<()> { if let Some(database) = &self.database { // TODO: Log operations in database adapter @@ -951,10 +954,10 @@ impl Cache { Ok(()) } - /// Add the given general object to the cache. + /// Adds a general object `value` (as bytes) to the cache at the given `key`. /// - /// The cache is agnostic to what the object actually is (and how it may be serialized), - /// offering maximum flexibility. + /// The cache is agnostic to what the bytes actually represent (and how it may be serialized), + /// which provides maximum flexibility. pub fn add(&mut self, key: &str, value: Vec) -> anyhow::Result<()> { check_valid_string(key, stringify!(key))?; check_slice_not_empty(value.as_slice(), stringify!(value))?; @@ -968,14 +971,14 @@ impl Cache { Ok(()) } - /// Add the given order `book` to the cache. + /// Adds the given order `book` to the cache. pub fn add_order_book(&mut self, book: OrderBook) -> anyhow::Result<()> { debug!("Adding `OrderBook` {}", book.instrument_id); self.books.insert(book.instrument_id, book); Ok(()) } - /// Add the given `quote` tick to the cache. + /// Adds the given `quote` tick to the cache. pub fn add_quote(&mut self, quote: QuoteTick) -> anyhow::Result<()> { debug!("Adding `QuoteTick` {}", quote.instrument_id); let quotes_deque = self @@ -986,7 +989,7 @@ impl Cache { Ok(()) } - /// Add the given `quotes` to the cache. + /// Adds the given `quotes` to the cache. pub fn add_quotes(&mut self, quotes: &[QuoteTick]) -> anyhow::Result<()> { check_slice_not_empty(quotes, stringify!(quotes))?; @@ -1003,7 +1006,7 @@ impl Cache { Ok(()) } - /// Add the given `trade` tick to the cache. + /// Adds the given `trade` tick to the cache. pub fn add_trade(&mut self, trade: TradeTick) -> anyhow::Result<()> { debug!("Adding `TradeTick` {}", trade.instrument_id); let trades_deque = self @@ -1014,7 +1017,7 @@ impl Cache { Ok(()) } - /// Add the give `trades` to the cache. + /// Adds the give `trades` to the cache. pub fn add_trades(&mut self, trades: &[TradeTick]) -> anyhow::Result<()> { check_slice_not_empty(trades, stringify!(trades))?; @@ -1031,7 +1034,7 @@ impl Cache { Ok(()) } - /// Add the given `bar` to the cache. + /// Adds the given `bar` to the cache. pub fn add_bar(&mut self, bar: Bar) -> anyhow::Result<()> { debug!("Adding `Bar` {}", bar.bar_type); let bars = self @@ -1042,7 +1045,7 @@ impl Cache { Ok(()) } - /// Add the given `bars` to the cache. + /// Adds the given `bars` to the cache. pub fn add_bars(&mut self, bars: &[Bar]) -> anyhow::Result<()> { check_slice_not_empty(bars, stringify!(bars))?; @@ -1059,7 +1062,7 @@ impl Cache { Ok(()) } - /// Add the given `currency` to the cache. + /// Adds the given `currency` to the cache. pub fn add_currency(&mut self, currency: Currency) -> anyhow::Result<()> { debug!("Adding `Currency` {}", currency.code); @@ -1071,7 +1074,7 @@ impl Cache { Ok(()) } - /// Add the given `instrument` to the cache. + /// Adds the given `instrument` to the cache. pub fn add_instrument(&mut self, instrument: InstrumentAny) -> anyhow::Result<()> { debug!("Adding `Instrument` {}", instrument.id()); @@ -1083,7 +1086,7 @@ impl Cache { Ok(()) } - /// Add the given `synthetic` instrument to the cache. + /// Adds the given `synthetic` instrument to the cache. pub fn add_synthetic(&mut self, synthetic: SyntheticInstrument) -> anyhow::Result<()> { debug!("Adding `SyntheticInstrument` {}", synthetic.id); @@ -1095,7 +1098,7 @@ impl Cache { Ok(()) } - /// Add the given `account` to the cache. + /// Adds the given `account` to the cache. pub fn add_account(&mut self, account: Box) -> anyhow::Result<()> { debug!("Adding `Account` {}", account.id()); @@ -1107,7 +1110,9 @@ impl Cache { Ok(()) } - /// Index the given client order ID with the given venue order ID. + /// Indexes the given `client_order_id` with the given `venue_order_id`. + /// + /// The `overwrite` parameter determines whether to overwrite any existing cached identifier. pub fn add_venue_order_id( &mut self, client_order_id: &ClientOrderId, @@ -1135,7 +1140,7 @@ impl Cache { Ok(()) } - /// Add the order to the cache indexed with any given identifiers. + /// Adds the given `order` to the cache indexed with any given identifiers. /// /// # Parameters /// @@ -1273,7 +1278,7 @@ impl Cache { Ok(()) } - /// Index the given `position_id` with the other given IDs. + /// Indexes the given `position_id` with the other given IDs. pub fn add_position_id( &mut self, position_id: &PositionId, @@ -1312,6 +1317,7 @@ impl Cache { Ok(()) } + /// Adds the given `position` to the cache. pub fn add_position(&mut self, position: Position, oms_type: OmsType) -> anyhow::Result<()> { self.positions.insert(position.id, position.clone()); self.index.positions.insert(position.id); @@ -1354,7 +1360,7 @@ impl Cache { Ok(()) } - /// Update the given `account` in the cache. + /// Updates the given `account` in the cache. pub fn update_account(&mut self, account: &dyn Account) -> anyhow::Result<()> { if let Some(database) = &mut self.database { database.update_account(account)?; @@ -1362,7 +1368,7 @@ impl Cache { Ok(()) } - /// Update the given `order` in the cache. + /// Updates the given `order` in the cache. pub fn update_order(&mut self, order: &OrderAny) -> anyhow::Result<()> { let client_order_id = order.client_order_id(); @@ -1412,14 +1418,14 @@ impl Cache { Ok(()) } - /// Update the given `order` as pending cancel locally. + /// Updates the given `order` as pending cancel locally. pub fn update_order_pending_cancel_local(&mut self, order: &OrderAny) { self.index .orders_pending_cancel .insert(order.client_order_id()); } - /// Update the given `position` in the cache. + /// Updates the given `position` in the cache. pub fn update_position(&mut self, position: &Position) -> anyhow::Result<()> { // Update open/closed state if position.is_open() { @@ -1596,6 +1602,7 @@ impl Cache { positions } + /// Returns the `ClientOrderId`s of all orders. #[must_use] pub fn client_order_ids( &self, @@ -1610,6 +1617,7 @@ impl Cache { } } + /// Returns the `ClientOrderId`s of all open orders. #[must_use] pub fn client_order_ids_open( &self, @@ -1629,6 +1637,7 @@ impl Cache { } } + /// Returns the `ClientOrderId`s of all closed orders. #[must_use] pub fn client_order_ids_closed( &self, @@ -1648,6 +1657,7 @@ impl Cache { } } + /// Returns the `ClientOrderId`s of all emulated orders. #[must_use] pub fn client_order_ids_emulated( &self, @@ -1667,6 +1677,7 @@ impl Cache { } } + /// Returns the `ClientOrderId`s of all in-flight orders. #[must_use] pub fn client_order_ids_inflight( &self, @@ -1686,6 +1697,7 @@ impl Cache { } } + /// Returns `PositionId`s of all positions. #[must_use] pub fn position_ids( &self, @@ -1700,6 +1712,7 @@ impl Cache { } } + /// Returns the `PositionId`s of all open positions. #[must_use] pub fn position_open_ids( &self, @@ -1719,6 +1732,7 @@ impl Cache { } } + /// Returns the `PositionId`s of all closed positions. #[must_use] pub fn position_closed_ids( &self, @@ -1738,16 +1752,19 @@ impl Cache { } } + /// Returns the `ComponentId`s of all actors. #[must_use] pub fn actor_ids(&self) -> HashSet { self.index.actors.clone() } + /// Returns the `StrategyId`s of all strategies. #[must_use] pub fn strategy_ids(&self) -> HashSet { self.index.strategies.clone() } + /// Returns the `ExecAlgorithmId`s of all execution algorithms. #[must_use] pub fn exec_algorithm_ids(&self) -> HashSet { self.index.exec_algorithms.clone() @@ -1755,26 +1772,31 @@ impl Cache { // -- ORDER QUERIES --------------------------------------------------------------------------- + /// Gets a reference to the order with the given `client_order_id` (if found). #[must_use] pub fn order(&self, client_order_id: &ClientOrderId) -> Option<&OrderAny> { self.orders.get(client_order_id) } + /// Gets a reference to the client order ID for given `venue_order_id` (if found). #[must_use] pub fn client_order_id(&self, venue_order_id: &VenueOrderId) -> Option<&ClientOrderId> { self.index.venue_order_ids.get(venue_order_id) } + /// Gets a reference to the venue order ID for given `client_order_id` (if found). #[must_use] pub fn venue_order_id(&self, client_order_id: &ClientOrderId) -> Option<&VenueOrderId> { self.index.client_order_ids.get(client_order_id) } + /// Gets a reference to the client ID indexed for given `client_order_id` (if found). #[must_use] pub fn client_id(&self, client_order_id: &ClientOrderId) -> Option<&ClientId> { self.index.order_client.get(client_order_id) } + /// Returns references to all orders matching the given optional filter parameters. #[must_use] pub fn orders( &self, @@ -1787,6 +1809,7 @@ impl Cache { self.get_orders_for_ids(&client_order_ids, side) } + /// Returns references to all open orders matching the given optional filter parameters. #[must_use] pub fn orders_open( &self, @@ -1799,6 +1822,7 @@ impl Cache { self.get_orders_for_ids(&client_order_ids, side) } + /// Returns references to all closed orders matching the given optional filter parameters. #[must_use] pub fn orders_closed( &self, @@ -1811,6 +1835,7 @@ impl Cache { self.get_orders_for_ids(&client_order_ids, side) } + /// Returns references to all emulated orders matching the given optional filter parameters. #[must_use] pub fn orders_emulated( &self, @@ -1823,6 +1848,7 @@ impl Cache { self.get_orders_for_ids(&client_order_ids, side) } + /// Returns references to all in-flight orders matching the given optional filter parameters. #[must_use] pub fn orders_inflight( &self, @@ -1835,6 +1861,7 @@ impl Cache { self.get_orders_for_ids(&client_order_ids, side) } + /// Returns references to all orders for the given `position_id`. #[must_use] pub fn orders_for_position(&self, position_id: &PositionId) -> Vec<&OrderAny> { let client_order_ids = self.index.position_orders.get(position_id); @@ -1846,36 +1873,43 @@ impl Cache { } } + /// Returns whether an order with the given `client_order_id` exists. #[must_use] pub fn order_exists(&self, client_order_id: &ClientOrderId) -> bool { self.index.orders.contains(client_order_id) } + /// Returns whether an order with the given `client_order_id` is open. #[must_use] pub fn is_order_open(&self, client_order_id: &ClientOrderId) -> bool { self.index.orders_open.contains(client_order_id) } + /// Returns whether an order with the given `client_order_id` is closed. #[must_use] pub fn is_order_closed(&self, client_order_id: &ClientOrderId) -> bool { self.index.orders_closed.contains(client_order_id) } + /// Returns whether an order with the given `client_order_id` is emulated. #[must_use] pub fn is_order_emulated(&self, client_order_id: &ClientOrderId) -> bool { self.index.orders_emulated.contains(client_order_id) } + /// Returns whether an order with the given `client_order_id` is in-flight. #[must_use] pub fn is_order_inflight(&self, client_order_id: &ClientOrderId) -> bool { self.index.orders_inflight.contains(client_order_id) } + /// Returns whether an order with the given `client_order_id` is `PENDING_CANCEL` locally. #[must_use] pub fn is_order_pending_cancel_local(&self, client_order_id: &ClientOrderId) -> bool { self.index.orders_pending_cancel.contains(client_order_id) } + /// Returns the count of all open orders. #[must_use] pub fn orders_open_count( &self, @@ -1888,6 +1922,7 @@ impl Cache { .len() } + /// Returns the count of all closed orders. #[must_use] pub fn orders_closed_count( &self, @@ -1900,6 +1935,7 @@ impl Cache { .len() } + /// Returns the count of all emulated orders. #[must_use] pub fn orders_emulated_count( &self, @@ -1912,6 +1948,7 @@ impl Cache { .len() } + /// Returns the count of all in-flight orders. #[must_use] pub fn orders_inflight_count( &self, @@ -1924,6 +1961,7 @@ impl Cache { .len() } + /// Returns the count of all orders. #[must_use] pub fn orders_total_count( &self, @@ -1935,11 +1973,13 @@ impl Cache { self.orders(venue, instrument_id, strategy_id, side).len() } + /// Returns the order list for the given `order_list_id`. #[must_use] pub fn order_list(&self, order_list_id: &OrderListId) -> Option<&OrderList> { self.order_lists.get(order_list_id) } + /// Returns all order lists matching the given optional filter parameters. #[must_use] pub fn order_lists( &self, @@ -1964,6 +2004,7 @@ impl Cache { order_lists } + /// Returns whether an order list with the given `order_list_id` exists. #[must_use] pub fn order_list_exists(&self, order_list_id: &OrderListId) -> bool { self.order_lists.contains_key(order_list_id) @@ -1971,6 +2012,8 @@ impl Cache { // -- EXEC ALGORITHM QUERIES ------------------------------------------------------------------ + /// Returns references to all orders associated with the given `exec_algorithm_id` matching the given + /// optional filter parameters. #[must_use] pub fn orders_for_exec_algorithm( &self, @@ -1996,6 +2039,7 @@ impl Cache { } } + /// Returns references to all orders with the given `exec_spawn_id`. #[must_use] pub fn orders_for_exec_spawn(&self, exec_spawn_id: &ClientOrderId) -> Vec<&OrderAny> { self.get_orders_for_ids( @@ -2007,6 +2051,7 @@ impl Cache { ) } + /// Returns the total order quantity for the given `exec_spawn_id`. #[must_use] pub fn exec_spawn_total_quantity( &self, @@ -2030,6 +2075,7 @@ impl Cache { total_quantity } + /// Returns the total filled quantity for all orders with the given `exec_spawn_id`. #[must_use] pub fn exec_spawn_total_filled_qty( &self, @@ -2053,6 +2099,7 @@ impl Cache { total_quantity } + /// Returns the total leaves quantity for all orders with the given `exec_spawn_id`. #[must_use] pub fn exec_spawn_total_leaves_qty( &self, @@ -2078,11 +2125,13 @@ impl Cache { // -- POSITION QUERIES ------------------------------------------------------------------------ + /// Returns a reference to the position with the given `position_id` (if found). #[must_use] pub fn position(&self, position_id: &PositionId) -> Option<&Position> { self.positions.get(position_id) } + /// Returns a reference to the position for the given `client_order_id` (if found). #[must_use] pub fn position_for_order(&self, client_order_id: &ClientOrderId) -> Option<&Position> { self.index @@ -2091,11 +2140,13 @@ impl Cache { .and_then(|position_id| self.positions.get(position_id)) } + /// Returns a reference to the position ID for the given `client_order_id` (if found). #[must_use] pub fn position_id(&self, client_order_id: &ClientOrderId) -> Option<&PositionId> { self.index.order_position.get(client_order_id) } + /// Returns a reference to all positions matching the given optional filter parameters. #[must_use] pub fn positions( &self, @@ -2108,6 +2159,7 @@ impl Cache { self.get_positions_for_ids(&position_ids, side) } + /// Returns a reference to all open positions matching the given optional filter parameters. #[must_use] pub fn positions_open( &self, @@ -2120,6 +2172,7 @@ impl Cache { self.get_positions_for_ids(&position_ids, side) } + /// Returns a reference to all closed positions matching the given optional filter parameters. #[must_use] pub fn positions_closed( &self, @@ -2132,21 +2185,25 @@ impl Cache { self.get_positions_for_ids(&position_ids, side) } + /// Returns whether a position with the given `position_id` exists. #[must_use] pub fn position_exists(&self, position_id: &PositionId) -> bool { self.index.positions.contains(position_id) } + /// Returns whether a position with the given `position_id` is open. #[must_use] pub fn is_position_open(&self, position_id: &PositionId) -> bool { self.index.positions_open.contains(position_id) } + /// Returns whether a position with the given `position_id` is closed. #[must_use] pub fn is_position_closed(&self, position_id: &PositionId) -> bool { self.index.positions_closed.contains(position_id) } + /// Returns the count of all open positions. #[must_use] pub fn positions_open_count( &self, @@ -2154,11 +2211,12 @@ impl Cache { instrument_id: Option<&InstrumentId>, strategy_id: Option<&StrategyId>, side: Option, - ) -> u64 { + ) -> usize { self.positions_open(venue, instrument_id, strategy_id, side) - .len() as u64 + .len() } + /// Returns the count of all closed positions. #[must_use] pub fn positions_closed_count( &self, @@ -2166,11 +2224,12 @@ impl Cache { instrument_id: Option<&InstrumentId>, strategy_id: Option<&StrategyId>, side: Option, - ) -> u64 { + ) -> usize { self.positions_closed(venue, instrument_id, strategy_id, side) - .len() as u64 + .len() } + /// Returns the count of all positions. #[must_use] pub fn positions_total_count( &self, @@ -2178,18 +2237,20 @@ impl Cache { instrument_id: Option<&InstrumentId>, strategy_id: Option<&StrategyId>, side: Option, - ) -> u64 { + ) -> usize { self.positions(venue, instrument_id, strategy_id, side) - .len() as u64 + .len() } // -- STRATEGY QUERIES ------------------------------------------------------------------------ + /// Gets a reference to the strategy ID for the given `client_order_id` (if found). #[must_use] pub fn strategy_id_for_order(&self, client_order_id: &ClientOrderId) -> Option<&StrategyId> { self.index.order_strategy.get(client_order_id) } + /// Gets a reference to the strategy ID for the given `position_id` (if found). #[must_use] pub fn strategy_id_for_position(&self, position_id: &PositionId) -> Option<&StrategyId> { self.index.position_strategy.get(position_id) @@ -2197,6 +2258,7 @@ impl Cache { // -- GENERAL --------------------------------------------------------------------------------- + /// Gets a reference to the general object value for the given `key` (if found). pub fn get(&self, key: &str) -> anyhow::Result> { check_valid_string(key, stringify!(key))?; @@ -2205,6 +2267,7 @@ impl Cache { // -- DATA QUERIES ---------------------------------------------------------------------------- + /// Returns the price for the given `instrument_id` and `price_type` (if found). #[must_use] pub fn price(&self, instrument_id: &InstrumentId, price_type: PriceType) -> Option { match price_type { @@ -2232,6 +2295,7 @@ impl Cache { } } + /// Gets all quote ticks for the given `instrument_id`. #[must_use] pub fn quote_ticks(&self, instrument_id: &InstrumentId) -> Option> { self.quotes @@ -2239,6 +2303,7 @@ impl Cache { .map(|quotes| quotes.iter().copied().collect()) } + /// Gets all trade ticks for the given `instrument_id`. #[must_use] pub fn trade_ticks(&self, instrument_id: &InstrumentId) -> Option> { self.trades @@ -2246,6 +2311,7 @@ impl Cache { .map(|trades| trades.iter().copied().collect()) } + /// Gets all bars for the given `bar_type`. #[must_use] pub fn bars(&self, bar_type: &BarType) -> Option> { self.bars @@ -2253,11 +2319,13 @@ impl Cache { .map(|bars| bars.iter().copied().collect()) } + /// Gets a reference to the order book for the given `instrument_id`. #[must_use] pub fn order_book(&self, instrument_id: &InstrumentId) -> Option<&OrderBook> { self.books.get(instrument_id) } + /// Gets a reference to the latest quote tick for the given `instrument_id`. #[must_use] pub fn quote_tick(&self, instrument_id: &InstrumentId) -> Option<&QuoteTick> { self.quotes @@ -2265,6 +2333,7 @@ impl Cache { .and_then(|quotes| quotes.front()) } + /// Gets a refernece to the latest trade tick for the given `instrument_id`. #[must_use] pub fn trade_tick(&self, instrument_id: &InstrumentId) -> Option<&TradeTick> { self.trades @@ -2272,52 +2341,61 @@ impl Cache { .and_then(|trades| trades.front()) } + /// Gets a reference to the latest bar for the given `bar_type`. #[must_use] pub fn bar(&self, bar_type: &BarType) -> Option<&Bar> { self.bars.get(bar_type).and_then(|bars| bars.front()) } + /// Gets the order book update count for the given `instrument_id`. #[must_use] - pub fn book_update_count(&self, instrument_id: &InstrumentId) -> u64 { - self.books.get(instrument_id).map_or(0, |book| book.count) + pub fn book_update_count(&self, instrument_id: &InstrumentId) -> usize { + self.books.get(instrument_id).map_or(0, |book| book.count) as usize } + /// Gets the quote tick count for the given `instrument_id`. #[must_use] - pub fn quote_tick_count(&self, instrument_id: &InstrumentId) -> u64 { + pub fn quote_tick_count(&self, instrument_id: &InstrumentId) -> usize { self.quotes .get(instrument_id) - .map_or(0, std::collections::VecDeque::len) as u64 + .map_or(0, std::collections::VecDeque::len) } + /// Gets the trade tick count for the given `instrument_id`. #[must_use] - pub fn trade_tick_count(&self, instrument_id: &InstrumentId) -> u64 { + pub fn trade_tick_count(&self, instrument_id: &InstrumentId) -> usize { self.trades .get(instrument_id) - .map_or(0, std::collections::VecDeque::len) as u64 + .map_or(0, std::collections::VecDeque::len) } + /// Gets the bar count for the given `instrument_id`. #[must_use] - pub fn bar_count(&self, bar_type: &BarType) -> u64 { + pub fn bar_count(&self, bar_type: &BarType) -> usize { self.bars .get(bar_type) - .map_or(0, std::collections::VecDeque::len) as u64 + .map_or(0, std::collections::VecDeque::len) } + /// Returns whether the cache contains an order book for the given `instrument_id`. #[must_use] pub fn has_order_book(&self, instrument_id: &InstrumentId) -> bool { self.books.contains_key(instrument_id) } + /// Returns whether the cache contains quote ticks for the given `instrument_id`. #[must_use] pub fn has_quote_ticks(&self, instrument_id: &InstrumentId) -> bool { self.quote_tick_count(instrument_id) > 0 } + /// Returns whether the cache contains trade ticks for the given `instrument_id`. #[must_use] pub fn has_trade_ticks(&self, instrument_id: &InstrumentId) -> bool { self.trade_tick_count(instrument_id) > 0 } + /// Returns whether the cache contains bars for the given `bar_type`. #[must_use] pub fn has_bars(&self, bar_type: &BarType) -> bool { self.bar_count(bar_type) > 0 @@ -2325,11 +2403,13 @@ impl Cache { // -- INSTRUMENT QUERIES ---------------------------------------------------------------------- + /// Returns a reference to the instrument for the given `instrument_id` (if found). #[must_use] pub fn instrument(&self, instrument_id: &InstrumentId) -> Option<&InstrumentAny> { self.instruments.get(instrument_id) } + /// Returns references to all instrument IDs for the given `venue`. #[must_use] pub fn instrument_ids(&self, venue: &Venue) -> Vec<&InstrumentId> { self.instruments @@ -2338,6 +2418,7 @@ impl Cache { .collect() } + /// Returns references to all instruments for the given `venue`. #[must_use] pub fn instruments(&self, venue: &Venue) -> Vec<&InstrumentAny> { self.instruments @@ -2346,6 +2427,7 @@ impl Cache { .collect() } + /// Returns references to all bar types contained in the cache. #[must_use] pub fn bar_types( &self, @@ -2372,16 +2454,19 @@ impl Cache { // -- SYNTHETIC QUERIES ----------------------------------------------------------------------- + /// Returns a reference to the synthetic instrument for the given `instrument_id` (if found). #[must_use] pub fn synthetic(&self, instrument_id: &InstrumentId) -> Option<&SyntheticInstrument> { self.synthetics.get(instrument_id) } + /// Returns references to instrument IDs for all synthetic instruments contained in the cache. #[must_use] pub fn synthetic_ids(&self) -> Vec<&InstrumentId> { self.synthetics.keys().collect() } + /// Returns references to all synthetic instruments contained in the cache. #[must_use] pub fn synthetics(&self) -> Vec<&SyntheticInstrument> { self.synthetics.values().collect() @@ -2389,6 +2474,7 @@ impl Cache { // -- ACCOUNT QUERIES ----------------------------------------------------------------------- + /// Returns a reference to the account for the given `account_id` (if found). #[must_use] pub fn account(&self, account_id: &AccountId) -> Option<&dyn Account> { self.accounts @@ -2396,6 +2482,7 @@ impl Cache { .map(std::convert::AsRef::as_ref) } + /// Returns a reference to the account for the given `venue` (if found). #[must_use] pub fn account_for_venue(&self, venue: &Venue) -> Option<&dyn Account> { self.index @@ -2405,11 +2492,13 @@ impl Cache { .map(std::convert::AsRef::as_ref) } + /// Returns a reference to the account ID for the given `venue` (if found). #[must_use] pub fn account_id(&self, venue: &Venue) -> Option<&AccountId> { self.index.venue_account.get(venue) } + /// Returns references to all accounts for the given `account_id`. #[must_use] pub fn accounts(&self, account_id: &AccountId) -> Vec<&dyn Account> { self.accounts From a981ae4140a8c744f7ccebdf323dfc9811c4f638 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sat, 25 May 2024 10:51:55 +1000 Subject: [PATCH 10/59] Update Rust docs --- nautilus_core/accounting/src/account/base.rs | 1 + nautilus_core/accounting/src/account/cash.rs | 1 + nautilus_core/accounting/src/account/margin.rs | 1 + nautilus_core/adapters/src/databento/live.rs | 2 +- nautilus_core/adapters/src/databento/loader.rs | 1 + nautilus_core/adapters/src/databento/types.rs | 2 ++ nautilus_core/backtest/src/engine.rs | 3 ++- nautilus_core/backtest/src/matching_engine.rs | 3 ++- nautilus_core/common/src/cache/core.rs | 7 ++++--- nautilus_core/common/src/cache/database.rs | 1 + nautilus_core/common/src/clock.rs | 4 ++++ nautilus_core/common/src/factories.rs | 1 + .../common/src/generators/client_order_id.rs | 1 + .../common/src/generators/order_list_id.rs | 1 + .../common/src/generators/position_id.rs | 1 + nautilus_core/common/src/logging/logger.rs | 5 +++++ nautilus_core/common/src/logging/writer.rs | 4 ++++ nautilus_core/common/src/msgbus/core.rs | 3 ++- nautilus_core/common/src/timer.rs | 10 ++++++---- nautilus_core/core/src/time.rs | 6 +++++- nautilus_core/core/src/uuid.rs | 3 ++- nautilus_core/execution/src/matching_core.rs | 1 + nautilus_core/execution/src/messages/cancel.rs | 1 + .../execution/src/messages/cancel_all.rs | 1 + .../execution/src/messages/cancel_batch.rs | 1 + nautilus_core/execution/src/messages/modify.rs | 1 + nautilus_core/execution/src/messages/query.rs | 1 + nautilus_core/execution/src/messages/submit.rs | 1 + .../execution/src/messages/submit_list.rs | 1 + nautilus_core/indicators/src/average/ama.rs | 1 + nautilus_core/indicators/src/average/dema.rs | 1 + nautilus_core/indicators/src/average/ema.rs | 1 + nautilus_core/indicators/src/average/hma.rs | 1 + nautilus_core/indicators/src/average/rma.rs | 1 + nautilus_core/indicators/src/average/sma.rs | 1 + nautilus_core/indicators/src/average/vidya.rs | 1 + nautilus_core/indicators/src/average/vwap.rs | 1 + nautilus_core/indicators/src/average/wma.rs | 1 + nautilus_core/indicators/src/book/imbalance.rs | 1 + nautilus_core/indicators/src/momentum/aroon.rs | 1 + nautilus_core/indicators/src/momentum/bias.rs | 1 + nautilus_core/indicators/src/momentum/cmo.rs | 1 + nautilus_core/indicators/src/momentum/rsi.rs | 1 + .../indicators/src/ratio/efficiency_ratio.rs | 1 + nautilus_core/indicators/src/volatility/atr.rs | 1 + nautilus_core/infrastructure/src/redis/cache.rs | 1 + nautilus_core/infrastructure/src/sql/pg.rs | 1 + nautilus_core/model/src/data/bar.rs | 3 +++ nautilus_core/model/src/data/delta.rs | 1 + nautilus_core/model/src/data/deltas.rs | 1 + nautilus_core/model/src/data/depth.rs | 1 + nautilus_core/model/src/data/order.rs | 2 ++ nautilus_core/model/src/data/quote.rs | 1 + nautilus_core/model/src/data/stubs.rs | 3 +++ nautilus_core/model/src/data/trade.rs | 1 + nautilus_core/model/src/events/account/state.rs | 1 + .../model/src/events/order/accepted.rs | 1 + .../model/src/events/order/cancel_rejected.rs | 1 + .../model/src/events/order/canceled.rs | 1 + nautilus_core/model/src/events/order/denied.rs | 1 + .../model/src/events/order/emulated.rs | 1 + nautilus_core/model/src/events/order/expired.rs | 1 + nautilus_core/model/src/events/order/filled.rs | 2 ++ .../model/src/events/order/initialized.rs | 2 ++ .../model/src/events/order/modify_rejected.rs | 1 + .../model/src/events/order/pending_cancel.rs | 1 + .../model/src/events/order/pending_update.rs | 1 + .../model/src/events/order/rejected.rs | 1 + .../model/src/events/order/released.rs | 1 + .../model/src/events/order/submitted.rs | 1 + .../model/src/events/order/triggered.rs | 1 + nautilus_core/model/src/events/order/updated.rs | 1 + .../model/src/identifiers/account_id.rs | 2 +- .../model/src/identifiers/client_id.rs | 2 +- .../model/src/identifiers/client_order_id.rs | 2 +- .../model/src/identifiers/component_id.rs | 2 +- .../model/src/identifiers/exec_algorithm_id.rs | 2 +- .../model/src/identifiers/instrument_id.rs | 2 +- .../model/src/identifiers/order_list_id.rs | 2 +- .../model/src/identifiers/position_id.rs | 2 +- .../model/src/identifiers/strategy_id.rs | 2 +- nautilus_core/model/src/identifiers/stubs.rs | 10 ++++++++++ nautilus_core/model/src/identifiers/symbol.rs | 2 +- nautilus_core/model/src/identifiers/trade_id.rs | 2 +- .../model/src/identifiers/trader_id.rs | 2 +- nautilus_core/model/src/identifiers/venue.rs | 2 +- .../model/src/identifiers/venue_order_id.rs | 2 +- .../model/src/instruments/crypto_future.rs | 1 + .../model/src/instruments/crypto_perpetual.rs | 1 + .../model/src/instruments/currency_pair.rs | 1 + nautilus_core/model/src/instruments/equity.rs | 1 + .../model/src/instruments/futures_contract.rs | 1 + .../model/src/instruments/futures_spread.rs | 1 + .../model/src/instruments/options_contract.rs | 1 + .../model/src/instruments/options_spread.rs | 1 + nautilus_core/model/src/instruments/stubs.rs | 1 + .../model/src/instruments/synthetic.rs | 1 + nautilus_core/model/src/orderbook/book.rs | 1 + nautilus_core/model/src/orders/base.rs | 1 + nautilus_core/model/src/orders/default.rs | 17 +++++++++-------- nautilus_core/model/src/orders/limit.rs | 1 + .../model/src/orders/limit_if_touched.rs | 1 + nautilus_core/model/src/orders/list.rs | 1 + nautilus_core/model/src/orders/market.rs | 1 + .../model/src/orders/market_if_touched.rs | 1 + .../model/src/orders/market_to_limit.rs | 1 + nautilus_core/model/src/orders/stop_limit.rs | 1 + nautilus_core/model/src/orders/stop_market.rs | 1 + .../model/src/orders/trailing_stop_limit.rs | 1 + .../model/src/orders/trailing_stop_market.rs | 1 + nautilus_core/model/src/types/currency.rs | 1 + nautilus_core/model/src/types/money.rs | 1 + nautilus_core/model/src/types/quantity.rs | 1 + nautilus_core/network/src/http.rs | 1 + nautilus_core/network/src/ratelimiter/gcra.rs | 1 + .../persistence/src/backend/kmerge_batch.rs | 1 + .../persistence/src/backend/session.rs | 2 ++ nautilus_trader/core/includes/common.h | 2 +- nautilus_trader/core/rust/common.pxd | 2 +- 119 files changed, 171 insertions(+), 37 deletions(-) diff --git a/nautilus_core/accounting/src/account/base.rs b/nautilus_core/accounting/src/account/base.rs index bbb74bf3996d..b7a52d2e4e2d 100644 --- a/nautilus_core/accounting/src/account/base.rs +++ b/nautilus_core/accounting/src/account/base.rs @@ -44,6 +44,7 @@ pub struct BaseAccount { } impl BaseAccount { + /// Creates a new [`BaseAccount`] instance. pub fn new(event: AccountState, calculate_account_state: bool) -> anyhow::Result { let mut balances_starting: HashMap = HashMap::new(); let mut balances: HashMap = HashMap::new(); diff --git a/nautilus_core/accounting/src/account/cash.rs b/nautilus_core/accounting/src/account/cash.rs index 7a7acb51f143..0fc53797b12b 100644 --- a/nautilus_core/accounting/src/account/cash.rs +++ b/nautilus_core/accounting/src/account/cash.rs @@ -43,6 +43,7 @@ pub struct CashAccount { } impl CashAccount { + /// Creates a new [`CashAccount`] instance. pub fn new(event: AccountState, calculate_account_state: bool) -> anyhow::Result { Ok(Self { base: BaseAccount::new(event, calculate_account_state)?, diff --git a/nautilus_core/accounting/src/account/margin.rs b/nautilus_core/accounting/src/account/margin.rs index 4c016b43dc9b..cbad02117d07 100644 --- a/nautilus_core/accounting/src/account/margin.rs +++ b/nautilus_core/accounting/src/account/margin.rs @@ -54,6 +54,7 @@ pub struct MarginAccount { } impl MarginAccount { + /// Creates a new [`MarginAccount`] instance. pub fn new(event: AccountState, calculate_account_state: bool) -> anyhow::Result { Ok(Self { base: BaseAccount::new(event, calculate_account_state)?, diff --git a/nautilus_core/adapters/src/databento/live.rs b/nautilus_core/adapters/src/databento/live.rs index b2d6945e4b4d..ea93e9aa0595 100644 --- a/nautilus_core/adapters/src/databento/live.rs +++ b/nautilus_core/adapters/src/databento/live.rs @@ -83,7 +83,7 @@ pub struct DatabentoFeedHandler { } impl DatabentoFeedHandler { - /// Initialize a new instance of the [`DatabentoFeedHandler`]. + /// Creates a new [`DatabentoFeedHandler`] instance. #[must_use] pub fn new( key: String, diff --git a/nautilus_core/adapters/src/databento/loader.rs b/nautilus_core/adapters/src/databento/loader.rs index 9cc95fde38bf..2a9ed687fae7 100644 --- a/nautilus_core/adapters/src/databento/loader.rs +++ b/nautilus_core/adapters/src/databento/loader.rs @@ -68,6 +68,7 @@ pub struct DatabentoDataLoader { } impl DatabentoDataLoader { + /// Creates a new [`DatabentoDataLoader`] instance. pub fn new(path: Option) -> anyhow::Result { let mut loader = Self { publishers_map: IndexMap::new(), diff --git a/nautilus_core/adapters/src/databento/types.rs b/nautilus_core/adapters/src/databento/types.rs index f7832a0e377f..da3ed8aca2b5 100644 --- a/nautilus_core/adapters/src/databento/types.rs +++ b/nautilus_core/adapters/src/databento/types.rs @@ -85,6 +85,7 @@ pub struct DatabentoImbalance { } impl DatabentoImbalance { + /// Creates a new [`DatabentoImbalance`] instance. #[allow(clippy::too_many_arguments)] pub fn new( instrument_id: InstrumentId, @@ -154,6 +155,7 @@ pub struct DatabentoStatistics { } impl DatabentoStatistics { + /// Creates a new [`DatabentoStatistics`] instance. #[allow(clippy::too_many_arguments)] pub fn new( instrument_id: InstrumentId, diff --git a/nautilus_core/backtest/src/engine.rs b/nautilus_core/backtest/src/engine.rs index 924de33c7b86..669259b12b3d 100644 --- a/nautilus_core/backtest/src/engine.rs +++ b/nautilus_core/backtest/src/engine.rs @@ -29,7 +29,7 @@ pub struct TimeEventAccumulator { } impl TimeEventAccumulator { - /// Creates a new `TimeEventAccumulator` instance. + /// Creates a new [`TimeEventAccumulator`] instance. #[must_use] pub fn new() -> Self { Self { @@ -55,6 +55,7 @@ impl TimeEventAccumulator { } impl Default for TimeEventAccumulator { + /// Creates a new default [`TimeEventAccumulator`] instance. fn default() -> Self { Self::new() } diff --git a/nautilus_core/backtest/src/matching_engine.rs b/nautilus_core/backtest/src/matching_engine.rs index 744a7ab800fa..f196dce29e3c 100644 --- a/nautilus_core/backtest/src/matching_engine.rs +++ b/nautilus_core/backtest/src/matching_engine.rs @@ -91,8 +91,9 @@ pub struct OrderMatchingEngine { execution_count: usize, } -// Note: we'll probably be changing the `FillModel` (don't add for now) +// TODO: we'll probably be changing the `FillModel` (don't add for now) impl OrderMatchingEngine { + /// Creates a new [`OrderMatchingEngine`] instance. #[allow(clippy::too_many_arguments)] pub fn new( instrument: Box, diff --git a/nautilus_core/common/src/cache/core.rs b/nautilus_core/common/src/cache/core.rs index f1b880e5671a..8e7478c27b35 100644 --- a/nautilus_core/common/src/cache/core.rs +++ b/nautilus_core/common/src/cache/core.rs @@ -64,6 +64,7 @@ pub struct CacheConfig { } impl CacheConfig { + /// Creates a new [`CacheConfig`] instance. #[allow(clippy::too_many_arguments)] #[must_use] pub fn new( @@ -90,7 +91,7 @@ impl CacheConfig { } impl Default for CacheConfig { - /// Creates a new default `CacheConfig` instance. + /// Creates a new default [`CacheConfig`] instance. fn default() -> Self { Self::new( SerializationEncoding::MsgPack, @@ -192,14 +193,14 @@ pub struct Cache { } impl Default for Cache { - /// Creates a new default `Cache` instance. + /// Creates a new default [`Cache`] instance. fn default() -> Self { Self::new(CacheConfig::default(), None) } } impl Cache { - /// Creates a new `Cache` instance. + /// Creates a new [`Cache`] instance. #[must_use] pub fn new(config: CacheConfig, database: Option) -> Self { let index = CacheIndex { diff --git a/nautilus_core/common/src/cache/database.rs b/nautilus_core/common/src/cache/database.rs index 8ee5905df020..cbe093be84e3 100644 --- a/nautilus_core/common/src/cache/database.rs +++ b/nautilus_core/common/src/cache/database.rs @@ -58,6 +58,7 @@ pub struct DatabaseCommand { } impl DatabaseCommand { + /// Creates a new [`DatabaseCommand`] instance. #[must_use] pub fn new(op_type: DatabaseOperation, key: String, payload: Option>>) -> Self { Self { diff --git a/nautilus_core/common/src/clock.rs b/nautilus_core/common/src/clock.rs index 16b0e0a3a033..5eae76e8d50f 100644 --- a/nautilus_core/common/src/clock.rs +++ b/nautilus_core/common/src/clock.rs @@ -82,6 +82,7 @@ pub struct TestClock { } impl TestClock { + /// Creates a new [`TestClock`] instance. #[must_use] pub fn new() -> Self { Self { @@ -152,6 +153,7 @@ fn create_time_event_handler(event: TimeEvent, handler: &EventHandler) -> TimeEv } impl Default for TestClock { + /// Creates a new default [`TestClock`] instance. fn default() -> Self { Self::new() } @@ -274,6 +276,7 @@ pub struct LiveClock { } impl LiveClock { + /// Creates a new [`LiveClock`] instance. #[must_use] pub fn new() -> Self { Self { @@ -290,6 +293,7 @@ impl LiveClock { } impl Default for LiveClock { + /// Creates a new default [`LiveClock`] instance. fn default() -> Self { Self::new() } diff --git a/nautilus_core/common/src/factories.rs b/nautilus_core/common/src/factories.rs index 25f0d7a8662c..66b2291d763c 100644 --- a/nautilus_core/common/src/factories.rs +++ b/nautilus_core/common/src/factories.rs @@ -44,6 +44,7 @@ pub struct OrderFactory { } impl OrderFactory { + /// Creates a new [`OrderFactory`] instance. pub fn new( trader_id: TraderId, strategy_id: StrategyId, diff --git a/nautilus_core/common/src/generators/client_order_id.rs b/nautilus_core/common/src/generators/client_order_id.rs index 1063484ac27f..67154218c8cd 100644 --- a/nautilus_core/common/src/generators/client_order_id.rs +++ b/nautilus_core/common/src/generators/client_order_id.rs @@ -29,6 +29,7 @@ pub struct ClientOrderIdGenerator { } impl ClientOrderIdGenerator { + /// Creates a new [`ClientOrderIdGenerator`] instance. #[must_use] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/common/src/generators/order_list_id.rs b/nautilus_core/common/src/generators/order_list_id.rs index a0a3ff8fa983..1c6efc6c8e2a 100644 --- a/nautilus_core/common/src/generators/order_list_id.rs +++ b/nautilus_core/common/src/generators/order_list_id.rs @@ -29,6 +29,7 @@ pub struct OrderListIdGenerator { } impl OrderListIdGenerator { + /// Creates a new [`OrderListIdGenerator`] instance. #[must_use] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/common/src/generators/position_id.rs b/nautilus_core/common/src/generators/position_id.rs index 93f7cde6c89a..24d183b86747 100644 --- a/nautilus_core/common/src/generators/position_id.rs +++ b/nautilus_core/common/src/generators/position_id.rs @@ -30,6 +30,7 @@ pub struct PositionIdGenerator { } impl PositionIdGenerator { + /// Creates a new [`PositionIdGenerator`] instance. #[must_use] pub fn new(trader_id: TraderId, clock: &'static AtomicTime) -> Self { Self { diff --git a/nautilus_core/common/src/logging/logger.rs b/nautilus_core/common/src/logging/logger.rs index 5e1b84ae9c09..c419ddfac9aa 100644 --- a/nautilus_core/common/src/logging/logger.rs +++ b/nautilus_core/common/src/logging/logger.rs @@ -64,6 +64,7 @@ pub struct LoggerConfig { } impl Default for LoggerConfig { + /// Creates a new default [`LoggerConfig`] instance. fn default() -> Self { Self { stdout_level: LevelFilter::Info, @@ -76,6 +77,7 @@ impl Default for LoggerConfig { } impl LoggerConfig { + /// Creates a new [`LoggerConfig`] instance. #[must_use] pub fn new( stdout_level: LevelFilter, @@ -194,6 +196,7 @@ pub struct LogLineWrapper { } impl LogLineWrapper { + /// Creates a new [`LogLineWrapper`] instance. #[must_use] pub fn new(line: LogLine, trader_id: Ustr, timestamp: UnixNanos) -> Self { Self { @@ -479,6 +482,7 @@ pub struct LogGuard { } impl LogGuard { + /// Creates a new [`LogGuard`] instance. #[must_use] pub fn new(handle: Option>) -> Self { Self { handle } @@ -486,6 +490,7 @@ impl LogGuard { } impl Default for LogGuard { + /// Creates a new default [`LogGuard`] instance. fn default() -> Self { Self::new(None) } diff --git a/nautilus_core/common/src/logging/writer.rs b/nautilus_core/common/src/logging/writer.rs index 27bb6732d0bf..11876edddf7a 100644 --- a/nautilus_core/common/src/logging/writer.rs +++ b/nautilus_core/common/src/logging/writer.rs @@ -42,6 +42,7 @@ pub struct StdoutWriter { } impl StdoutWriter { + /// Creates a new [`StdoutWriter`] instance. #[must_use] pub fn new(level: LevelFilter, is_colored: bool) -> Self { Self { @@ -80,6 +81,7 @@ pub struct StderrWriter { } impl StderrWriter { + /// Creates a new [`StderrWriter`] instance. #[must_use] pub fn new(is_colored: bool) -> Self { Self { @@ -121,6 +123,7 @@ pub struct FileWriterConfig { } impl FileWriterConfig { + /// Creates a new [`FileWriterConfig`] instance. #[must_use] pub fn new( directory: Option, @@ -147,6 +150,7 @@ pub struct FileWriter { } impl FileWriter { + /// Creates a new [`FileWriter`] instance. pub fn new( trader_id: String, instance_id: String, diff --git a/nautilus_core/common/src/msgbus/core.rs b/nautilus_core/common/src/msgbus/core.rs index 441a27123de2..31a92742dd6e 100644 --- a/nautilus_core/common/src/msgbus/core.rs +++ b/nautilus_core/common/src/msgbus/core.rs @@ -43,6 +43,7 @@ pub struct Subscription { } impl Subscription { + /// Creates a new [`Subscription`] instance. #[must_use] pub fn new( topic: Ustr, @@ -165,7 +166,7 @@ pub struct MessageBus { } impl MessageBus { - /// Creates a new `MessageBus` instance. + /// Creates a new [`MessageBus`] instance. pub fn new( trader_id: TraderId, instance_id: UUID4, diff --git a/nautilus_core/common/src/timer.rs b/nautilus_core/common/src/timer.rs index 8e1b09fde70c..06b3ca623292 100644 --- a/nautilus_core/common/src/timer.rs +++ b/nautilus_core/common/src/timer.rs @@ -59,8 +59,10 @@ pub struct TimeEvent { pub ts_init: UnixNanos, } -/// Assumes `name` is a valid string. impl TimeEvent { + /// Creates a new [`TimeEvent`] instance. + /// + /// Assumes `name` is a valid string. #[must_use] pub fn new(name: Ustr, event_id: UUID4, ts_event: UnixNanos, ts_init: UnixNanos) -> Self { Self { @@ -92,7 +94,7 @@ impl PartialEq for TimeEvent { #[derive(Clone, Debug)] /// Represents a time event and its associated handler. pub struct TimeEventHandler { - /// The event. + /// The time event. pub event: TimeEvent, /// The callable raw pointer. pub callback_ptr: *mut c_char, @@ -130,7 +132,7 @@ pub struct TestTimer { } impl TestTimer { - /// Creates a new `TestTimer` instance. + /// Creates a new [`TestTimer`] instance. pub fn new( name: &str, interval_ns: u64, @@ -233,7 +235,7 @@ pub struct LiveTimer { } impl LiveTimer { - /// Creates a new `LiveTimer` instance. + /// Creates a new [`LiveTimer`] instance. pub fn new( name: &str, interval_ns: u64, diff --git a/nautilus_core/core/src/time.rs b/nautilus_core/core/src/time.rs index 680ad87a8926..a03f654f8285 100644 --- a/nautilus_core/core/src/time.rs +++ b/nautilus_core/core/src/time.rs @@ -85,13 +85,17 @@ impl Deref for AtomicTime { } impl Default for AtomicTime { + /// Creates a new default [`AtomicTime`] instance. fn default() -> Self { Self::new(true, UnixNanos::default()) } } impl AtomicTime { - /// New atomic clock set with the given UNIX time (nanoseconds). + /// Creates a new [`AtomicTime`] instance. + /// + /// The `realtime` flag will determine whether the atomic time is based off system time. + /// The time will be set to the given UNIX `time` (nanoseconds). #[must_use] pub fn new(realtime: bool, time: UnixNanos) -> Self { Self { diff --git a/nautilus_core/core/src/uuid.rs b/nautilus_core/core/src/uuid.rs index b3b323e404aa..795dc3b1df64 100644 --- a/nautilus_core/core/src/uuid.rs +++ b/nautilus_core/core/src/uuid.rs @@ -43,7 +43,7 @@ pub struct UUID4 { } impl UUID4 { - /// Creates a new `UUID4` instance. + /// Creates a new [`UUID4`] instance. #[must_use] pub fn new() -> Self { let uuid = Uuid::new_v4(); @@ -84,6 +84,7 @@ impl From<&str> for UUID4 { } impl Default for UUID4 { + /// Creates a new default [`UUID4`] instance. fn default() -> Self { Self::new() } diff --git a/nautilus_core/execution/src/matching_core.rs b/nautilus_core/execution/src/matching_core.rs index deac0486b23b..d83965e74425 100644 --- a/nautilus_core/execution/src/matching_core.rs +++ b/nautilus_core/execution/src/matching_core.rs @@ -51,6 +51,7 @@ pub struct OrderMatchingCore { } impl OrderMatchingCore { + // Creates a new [`OrderMatchingCore`] instance. #[must_use] pub fn new( instrument_id: InstrumentId, diff --git a/nautilus_core/execution/src/messages/cancel.rs b/nautilus_core/execution/src/messages/cancel.rs index fa6b9de42ea1..37fd17983827 100644 --- a/nautilus_core/execution/src/messages/cancel.rs +++ b/nautilus_core/execution/src/messages/cancel.rs @@ -38,6 +38,7 @@ pub struct CancelOrder { } impl CancelOrder { + /// Creates a new [`CancelOrder`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/execution/src/messages/cancel_all.rs b/nautilus_core/execution/src/messages/cancel_all.rs index 5b7d1f9d6c3c..58f9a242ab51 100644 --- a/nautilus_core/execution/src/messages/cancel_all.rs +++ b/nautilus_core/execution/src/messages/cancel_all.rs @@ -40,6 +40,7 @@ pub struct CancelAllOrders { } impl CancelAllOrders { + /// Creates a new [`CancelAllOrders`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/execution/src/messages/cancel_batch.rs b/nautilus_core/execution/src/messages/cancel_batch.rs index 6553231ebf9f..35e43e3db526 100644 --- a/nautilus_core/execution/src/messages/cancel_batch.rs +++ b/nautilus_core/execution/src/messages/cancel_batch.rs @@ -38,6 +38,7 @@ pub struct BatchCancelOrders { } impl BatchCancelOrders { + /// Creates a new [`BatchCancelOrders`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/execution/src/messages/modify.rs b/nautilus_core/execution/src/messages/modify.rs index 65796b43ba11..f5ac3d4584b0 100644 --- a/nautilus_core/execution/src/messages/modify.rs +++ b/nautilus_core/execution/src/messages/modify.rs @@ -44,6 +44,7 @@ pub struct ModifyOrder { } impl ModifyOrder { + /// Creates a new [`ModifyOrder`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/execution/src/messages/query.rs b/nautilus_core/execution/src/messages/query.rs index dbf788744a70..e4539eb3694e 100644 --- a/nautilus_core/execution/src/messages/query.rs +++ b/nautilus_core/execution/src/messages/query.rs @@ -38,6 +38,7 @@ pub struct QueryOrder { } impl QueryOrder { + /// Creates a new [`QueryOrder`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/execution/src/messages/submit.rs b/nautilus_core/execution/src/messages/submit.rs index 9117e77641cb..99f4c74445ff 100644 --- a/nautilus_core/execution/src/messages/submit.rs +++ b/nautilus_core/execution/src/messages/submit.rs @@ -42,6 +42,7 @@ pub struct SubmitOrder { } impl SubmitOrder { + /// Creates a new [`SubmitOrder`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/execution/src/messages/submit_list.rs b/nautilus_core/execution/src/messages/submit_list.rs index dc8bf73c49e7..c0f535c5e69c 100644 --- a/nautilus_core/execution/src/messages/submit_list.rs +++ b/nautilus_core/execution/src/messages/submit_list.rs @@ -43,6 +43,7 @@ pub struct SubmitOrderList { } impl SubmitOrderList { + /// Creates a new [`SubmitOrderList`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/indicators/src/average/ama.rs b/nautilus_core/indicators/src/average/ama.rs index 52246dd49491..53b4d6a72965 100644 --- a/nautilus_core/indicators/src/average/ama.rs +++ b/nautilus_core/indicators/src/average/ama.rs @@ -104,6 +104,7 @@ impl Indicator for AdaptiveMovingAverage { } impl AdaptiveMovingAverage { + /// Creates a new [`AdaptiveMovingAverage`] instance. pub fn new( period_efficiency_ratio: usize, period_fast: usize, diff --git a/nautilus_core/indicators/src/average/dema.rs b/nautilus_core/indicators/src/average/dema.rs index 3c465db7f045..05728e1bd80b 100644 --- a/nautilus_core/indicators/src/average/dema.rs +++ b/nautilus_core/indicators/src/average/dema.rs @@ -87,6 +87,7 @@ impl Indicator for DoubleExponentialMovingAverage { } impl DoubleExponentialMovingAverage { + /// Creates a new [`DoubleExponentialMovingAverage`] instance. pub fn new(period: usize, price_type: Option) -> anyhow::Result { Ok(Self { period, diff --git a/nautilus_core/indicators/src/average/ema.rs b/nautilus_core/indicators/src/average/ema.rs index 63829e68036b..e43f57371e48 100644 --- a/nautilus_core/indicators/src/average/ema.rs +++ b/nautilus_core/indicators/src/average/ema.rs @@ -78,6 +78,7 @@ impl Indicator for ExponentialMovingAverage { } impl ExponentialMovingAverage { + /// Creates a new [`ExponentialMovingAverage`] instance. pub fn new(period: usize, price_type: Option) -> anyhow::Result { // Inputs don't require validation, however we return a `Result` // to standardize with other indicators which do need validation. diff --git a/nautilus_core/indicators/src/average/hma.rs b/nautilus_core/indicators/src/average/hma.rs index cc025cf6e1ef..f331328b448d 100644 --- a/nautilus_core/indicators/src/average/hma.rs +++ b/nautilus_core/indicators/src/average/hma.rs @@ -96,6 +96,7 @@ fn _get_weights(size: usize) -> Vec { } impl HullMovingAverage { + /// Creates a new [`HullMovingAverage`] instance. pub fn new(period: usize, price_type: Option) -> anyhow::Result { let period_halved = period / 2; let period_sqrt = (period as f64).sqrt() as usize; diff --git a/nautilus_core/indicators/src/average/rma.rs b/nautilus_core/indicators/src/average/rma.rs index fc43d102a68c..ab735e50e937 100644 --- a/nautilus_core/indicators/src/average/rma.rs +++ b/nautilus_core/indicators/src/average/rma.rs @@ -78,6 +78,7 @@ impl Indicator for WilderMovingAverage { } impl WilderMovingAverage { + /// Creates a new [`WilderMovingAverage`] instance. pub fn new(period: usize, price_type: Option) -> anyhow::Result { // Inputs don't require validation, however we return a `Result` // to standardize with other indicators which do need validation. diff --git a/nautilus_core/indicators/src/average/sma.rs b/nautilus_core/indicators/src/average/sma.rs index 705ace65122b..a8cc57d2e126 100644 --- a/nautilus_core/indicators/src/average/sma.rs +++ b/nautilus_core/indicators/src/average/sma.rs @@ -77,6 +77,7 @@ impl Indicator for SimpleMovingAverage { } impl SimpleMovingAverage { + /// Creates a new [`SimpleMovingAverage`] instance. pub fn new(period: usize, price_type: Option) -> anyhow::Result { // Inputs don't require validation, however we return a `Result` // to standardize with other indicators which do need validation. diff --git a/nautilus_core/indicators/src/average/vidya.rs b/nautilus_core/indicators/src/average/vidya.rs index 989778fae3f4..ee94e76a61df 100644 --- a/nautilus_core/indicators/src/average/vidya.rs +++ b/nautilus_core/indicators/src/average/vidya.rs @@ -86,6 +86,7 @@ impl Indicator for VariableIndexDynamicAverage { } impl VariableIndexDynamicAverage { + /// Creates a new [`VariableIndexDynamicAverage`] instance. pub fn new( period: usize, price_type: Option, diff --git a/nautilus_core/indicators/src/average/vwap.rs b/nautilus_core/indicators/src/average/vwap.rs index 90929804bbe9..1e0d9c096105 100644 --- a/nautilus_core/indicators/src/average/vwap.rs +++ b/nautilus_core/indicators/src/average/vwap.rs @@ -73,6 +73,7 @@ impl Indicator for VolumeWeightedAveragePrice { } impl VolumeWeightedAveragePrice { + /// Creates a new [`VolumeWeightedAveragePrice`] instance. pub fn new() -> anyhow::Result { Ok(Self { value: 0.0, diff --git a/nautilus_core/indicators/src/average/wma.rs b/nautilus_core/indicators/src/average/wma.rs index b2bb5ee94531..c11a0bb23688 100644 --- a/nautilus_core/indicators/src/average/wma.rs +++ b/nautilus_core/indicators/src/average/wma.rs @@ -52,6 +52,7 @@ impl Display for WeightedMovingAverage { } impl WeightedMovingAverage { + /// Creates a new [`WeightedMovingAverage`] instance. pub fn new( period: usize, weights: Vec, diff --git a/nautilus_core/indicators/src/book/imbalance.rs b/nautilus_core/indicators/src/book/imbalance.rs index f2f78151302b..0c8835af4d21 100644 --- a/nautilus_core/indicators/src/book/imbalance.rs +++ b/nautilus_core/indicators/src/book/imbalance.rs @@ -64,6 +64,7 @@ impl Indicator for BookImbalanceRatio { } impl BookImbalanceRatio { + /// Creates a new [`BookImbalanceRatio`] instance. pub fn new() -> anyhow::Result { // Inputs don't require validation, however we return a `Result` // to standardize with other indicators which do need validation. diff --git a/nautilus_core/indicators/src/momentum/aroon.rs b/nautilus_core/indicators/src/momentum/aroon.rs index 365f83f31f7d..51c14d0100ff 100644 --- a/nautilus_core/indicators/src/momentum/aroon.rs +++ b/nautilus_core/indicators/src/momentum/aroon.rs @@ -91,6 +91,7 @@ impl Indicator for AroonOscillator { } impl AroonOscillator { + /// Creates a new [`AroonOscillator`] instance. pub fn new(period: usize) -> anyhow::Result { Ok(Self { period, diff --git a/nautilus_core/indicators/src/momentum/bias.rs b/nautilus_core/indicators/src/momentum/bias.rs index 2101bb79a541..88a90712d45e 100644 --- a/nautilus_core/indicators/src/momentum/bias.rs +++ b/nautilus_core/indicators/src/momentum/bias.rs @@ -73,6 +73,7 @@ impl Indicator for Bias { } impl Bias { + /// Creates a new [`Bias`] instance. pub fn new(period: usize, ma_type: Option) -> anyhow::Result { Ok(Self { period, diff --git a/nautilus_core/indicators/src/momentum/cmo.rs b/nautilus_core/indicators/src/momentum/cmo.rs index d94dc6f57217..d2443548bdeb 100644 --- a/nautilus_core/indicators/src/momentum/cmo.rs +++ b/nautilus_core/indicators/src/momentum/cmo.rs @@ -81,6 +81,7 @@ impl Indicator for ChandeMomentumOscillator { } impl ChandeMomentumOscillator { + /// Creates a new [`ChandeMomentumOscillator`] instance. pub fn new(period: usize, ma_type: Option) -> anyhow::Result { Ok(Self { period, diff --git a/nautilus_core/indicators/src/momentum/rsi.rs b/nautilus_core/indicators/src/momentum/rsi.rs index ea0f2c5ea9b8..3be10825ebcf 100644 --- a/nautilus_core/indicators/src/momentum/rsi.rs +++ b/nautilus_core/indicators/src/momentum/rsi.rs @@ -86,6 +86,7 @@ impl Indicator for RelativeStrengthIndex { } impl RelativeStrengthIndex { + /// Creates a new [`RelativeStrengthIndex`] instance. pub fn new(period: usize, ma_type: Option) -> anyhow::Result { Ok(Self { period, diff --git a/nautilus_core/indicators/src/ratio/efficiency_ratio.rs b/nautilus_core/indicators/src/ratio/efficiency_ratio.rs index 0cda086051d4..c039c060c016 100644 --- a/nautilus_core/indicators/src/ratio/efficiency_ratio.rs +++ b/nautilus_core/indicators/src/ratio/efficiency_ratio.rs @@ -79,6 +79,7 @@ impl Indicator for EfficiencyRatio { } impl EfficiencyRatio { + /// Creates a new [`EfficiencyRatio`] instance. pub fn new(period: usize, price_type: Option) -> anyhow::Result { Ok(Self { period, diff --git a/nautilus_core/indicators/src/volatility/atr.rs b/nautilus_core/indicators/src/volatility/atr.rs index c6d411136872..f8d62f9e8aa0 100644 --- a/nautilus_core/indicators/src/volatility/atr.rs +++ b/nautilus_core/indicators/src/volatility/atr.rs @@ -83,6 +83,7 @@ impl Indicator for AverageTrueRange { } impl AverageTrueRange { + /// Creates a new [`AverageTrueRange`] instance. pub fn new( period: usize, ma_type: Option, diff --git a/nautilus_core/infrastructure/src/redis/cache.rs b/nautilus_core/infrastructure/src/redis/cache.rs index 44b15a9bf0a9..23a5dc373834 100644 --- a/nautilus_core/infrastructure/src/redis/cache.rs +++ b/nautilus_core/infrastructure/src/redis/cache.rs @@ -78,6 +78,7 @@ pub struct RedisCacheDatabase { impl CacheDatabase for RedisCacheDatabase { type DatabaseType = RedisCacheDatabase; + /// Creates a new [`RedisCacheDatabase`] instance. fn new( trader_id: TraderId, instance_id: UUID4, diff --git a/nautilus_core/infrastructure/src/sql/pg.rs b/nautilus_core/infrastructure/src/sql/pg.rs index 3a68c871cad2..639bf6249cac 100644 --- a/nautilus_core/infrastructure/src/sql/pg.rs +++ b/nautilus_core/infrastructure/src/sql/pg.rs @@ -28,6 +28,7 @@ pub struct PostgresConnectOptions { } impl PostgresConnectOptions { + /// Creates a new [`PostgresConnectOptions`] instance. pub fn new( host: String, port: u16, diff --git a/nautilus_core/model/src/data/bar.rs b/nautilus_core/model/src/data/bar.rs index 44c8d4d997f0..dd91adc35e8d 100644 --- a/nautilus_core/model/src/data/bar.rs +++ b/nautilus_core/model/src/data/bar.rs @@ -53,6 +53,7 @@ pub struct BarSpecification { } impl BarSpecification { + /// Creates a new [`BarSpecification`] instance. #[must_use] pub fn new(step: usize, aggregation: BarAggregation, price_type: PriceType) -> Self { Self { @@ -87,6 +88,7 @@ pub struct BarType { } impl BarType { + /// Creates a new [`BarType`] instance. #[must_use] pub fn new( instrument_id: InstrumentId, @@ -230,6 +232,7 @@ pub struct Bar { } impl Bar { + /// Creates a new [`Bar`] instance. #[must_use] #[allow(clippy::too_many_arguments)] pub fn new( diff --git a/nautilus_core/model/src/data/delta.rs b/nautilus_core/model/src/data/delta.rs index a86113b96e50..8380f5403a07 100644 --- a/nautilus_core/model/src/data/delta.rs +++ b/nautilus_core/model/src/data/delta.rs @@ -59,6 +59,7 @@ pub struct OrderBookDelta { } impl OrderBookDelta { + /// Creates a new [`OrderBookDelta`] instance. #[allow(clippy::too_many_arguments)] #[must_use] pub fn new( diff --git a/nautilus_core/model/src/data/deltas.rs b/nautilus_core/model/src/data/deltas.rs index 7f7d478e5317..b0b34dea5cab 100644 --- a/nautilus_core/model/src/data/deltas.rs +++ b/nautilus_core/model/src/data/deltas.rs @@ -50,6 +50,7 @@ pub struct OrderBookDeltas { } impl OrderBookDeltas { + /// Creates a new [`OrderBookDeltas`] instance. #[allow(clippy::too_many_arguments)] #[must_use] pub fn new(instrument_id: InstrumentId, deltas: Vec) -> Self { diff --git a/nautilus_core/model/src/data/depth.rs b/nautilus_core/model/src/data/depth.rs index fbc85ced4f5a..490c18800780 100644 --- a/nautilus_core/model/src/data/depth.rs +++ b/nautilus_core/model/src/data/depth.rs @@ -67,6 +67,7 @@ pub struct OrderBookDepth10 { } impl OrderBookDepth10 { + /// Creates a new [`OrderBookDepth10`] instance. #[allow(clippy::too_many_arguments)] #[must_use] pub fn new( diff --git a/nautilus_core/model/src/data/order.rs b/nautilus_core/model/src/data/order.rs index cc10eee94fb3..6854cec2b23c 100644 --- a/nautilus_core/model/src/data/order.rs +++ b/nautilus_core/model/src/data/order.rs @@ -64,6 +64,7 @@ pub struct BookOrder { } impl BookOrder { + /// Creates a new [`BookOrder`] instance. #[must_use] pub fn new(side: OrderSide, price: Price, size: Quantity, order_id: u64) -> Self { Self { @@ -95,6 +96,7 @@ impl BookOrder { } impl Default for BookOrder { + /// Creates a NULL [`BookOrder`] instance. fn default() -> Self { NULL_ORDER } diff --git a/nautilus_core/model/src/data/quote.rs b/nautilus_core/model/src/data/quote.rs index 64a283bcb7c6..3bd2c63a924c 100644 --- a/nautilus_core/model/src/data/quote.rs +++ b/nautilus_core/model/src/data/quote.rs @@ -61,6 +61,7 @@ pub struct QuoteTick { } impl QuoteTick { + /// Creates a new [`QuoteTick`] instance. pub fn new( instrument_id: InstrumentId, bid_price: Price, diff --git a/nautilus_core/model/src/data/stubs.rs b/nautilus_core/model/src/data/stubs.rs index d38b62f925d1..8fe0cba852a5 100644 --- a/nautilus_core/model/src/data/stubs.rs +++ b/nautilus_core/model/src/data/stubs.rs @@ -34,6 +34,7 @@ use crate::{ }; impl Default for QuoteTick { + /// Creates a new default [`QuoteTick`] instance for testing. fn default() -> Self { Self { instrument_id: InstrumentId::from("AUDUSD.SIM"), @@ -48,6 +49,7 @@ impl Default for QuoteTick { } impl Default for TradeTick { + /// Creates a new default [`TradeTick`] instance for testing. fn default() -> Self { TradeTick { instrument_id: InstrumentId::from("AUDUSD.SIM"), @@ -62,6 +64,7 @@ impl Default for TradeTick { } impl Default for Bar { + /// Creates a new default [`Bar`] instance for testing. fn default() -> Self { Self { bar_type: BarType::from("AUDUSD.SIM-1-MINUTE-LAST-INTERNAL"), diff --git a/nautilus_core/model/src/data/trade.rs b/nautilus_core/model/src/data/trade.rs index f7f419d995b2..5a995d06a09e 100644 --- a/nautilus_core/model/src/data/trade.rs +++ b/nautilus_core/model/src/data/trade.rs @@ -60,6 +60,7 @@ pub struct TradeTick { } impl TradeTick { + /// Creates a new [`TradeTick`] instance. #[must_use] pub fn new( instrument_id: InstrumentId, diff --git a/nautilus_core/model/src/events/account/state.rs b/nautilus_core/model/src/events/account/state.rs index 2366b8f0f0f9..6a2c540d5f65 100644 --- a/nautilus_core/model/src/events/account/state.rs +++ b/nautilus_core/model/src/events/account/state.rs @@ -46,6 +46,7 @@ pub struct AccountState { } impl AccountState { + /// Creates a new [`AccountState`] instance. #[allow(clippy::too_many_arguments)] pub fn new( account_id: AccountId, diff --git a/nautilus_core/model/src/events/order/accepted.rs b/nautilus_core/model/src/events/order/accepted.rs index 60403674a778..76fa62d30c0f 100644 --- a/nautilus_core/model/src/events/order/accepted.rs +++ b/nautilus_core/model/src/events/order/accepted.rs @@ -58,6 +58,7 @@ pub struct OrderAccepted { } impl OrderAccepted { + /// Creates a new [`OrderAccepted`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/events/order/cancel_rejected.rs b/nautilus_core/model/src/events/order/cancel_rejected.rs index 6bb0c7e832b9..87e4dbd0a8c1 100644 --- a/nautilus_core/model/src/events/order/cancel_rejected.rs +++ b/nautilus_core/model/src/events/order/cancel_rejected.rs @@ -59,6 +59,7 @@ pub struct OrderCancelRejected { } impl OrderCancelRejected { + /// Creates a new [`OrderCancelRejected`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/events/order/canceled.rs b/nautilus_core/model/src/events/order/canceled.rs index 4aef943ab447..a1e5e9f1793a 100644 --- a/nautilus_core/model/src/events/order/canceled.rs +++ b/nautilus_core/model/src/events/order/canceled.rs @@ -58,6 +58,7 @@ pub struct OrderCanceled { } impl OrderCanceled { + /// Creates a new [`OrderCanceled`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/events/order/denied.rs b/nautilus_core/model/src/events/order/denied.rs index 7819a1196a3c..d545408fd493 100644 --- a/nautilus_core/model/src/events/order/denied.rs +++ b/nautilus_core/model/src/events/order/denied.rs @@ -55,6 +55,7 @@ pub struct OrderDenied { } impl OrderDenied { + /// Creates a new [`OrderDenied`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/events/order/emulated.rs b/nautilus_core/model/src/events/order/emulated.rs index e17a5eb185af..5e720e8abed7 100644 --- a/nautilus_core/model/src/events/order/emulated.rs +++ b/nautilus_core/model/src/events/order/emulated.rs @@ -54,6 +54,7 @@ pub struct OrderEmulated { } impl OrderEmulated { + /// Creates a new [`OrderEmulated`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/events/order/expired.rs b/nautilus_core/model/src/events/order/expired.rs index 9671d87ce4da..712a2ebe7b03 100644 --- a/nautilus_core/model/src/events/order/expired.rs +++ b/nautilus_core/model/src/events/order/expired.rs @@ -58,6 +58,7 @@ pub struct OrderExpired { } impl OrderExpired { + /// Creates a new [`OrderExpired`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/events/order/filled.rs b/nautilus_core/model/src/events/order/filled.rs index 251a58ceb469..2768b622f437 100644 --- a/nautilus_core/model/src/events/order/filled.rs +++ b/nautilus_core/model/src/events/order/filled.rs @@ -66,6 +66,7 @@ pub struct OrderFilled { } impl OrderFilled { + /// Creates a new [`OrderFilled`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, @@ -123,6 +124,7 @@ impl OrderFilled { } impl Default for OrderFilled { + /// Creates a new default [`OrderFilled`] instance for testing. fn default() -> Self { Self { trader_id: TraderId::default(), diff --git a/nautilus_core/model/src/events/order/initialized.rs b/nautilus_core/model/src/events/order/initialized.rs index e52e41afa90b..9bc19c687b54 100644 --- a/nautilus_core/model/src/events/order/initialized.rs +++ b/nautilus_core/model/src/events/order/initialized.rs @@ -84,6 +84,7 @@ pub struct OrderInitialized { } impl Default for OrderInitialized { + /// Creates a new default [`OrderInitialized`] instance for testing. fn default() -> Self { Self { trader_id: TraderId::default(), @@ -124,6 +125,7 @@ impl Default for OrderInitialized { } impl OrderInitialized { + /// Creates a new [`OrderInitialized`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/events/order/modify_rejected.rs b/nautilus_core/model/src/events/order/modify_rejected.rs index a59a3d741903..27c5b41d7af1 100644 --- a/nautilus_core/model/src/events/order/modify_rejected.rs +++ b/nautilus_core/model/src/events/order/modify_rejected.rs @@ -59,6 +59,7 @@ pub struct OrderModifyRejected { } impl OrderModifyRejected { + /// Creates a new [`OrderModifyRejected`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/events/order/pending_cancel.rs b/nautilus_core/model/src/events/order/pending_cancel.rs index 1ce6f4dc0b6f..3ba03fdb9972 100644 --- a/nautilus_core/model/src/events/order/pending_cancel.rs +++ b/nautilus_core/model/src/events/order/pending_cancel.rs @@ -58,6 +58,7 @@ pub struct OrderPendingCancel { } impl OrderPendingCancel { + /// Creates a new [`OrderPendingCancel`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/events/order/pending_update.rs b/nautilus_core/model/src/events/order/pending_update.rs index e1926644161d..53d1fffc4967 100644 --- a/nautilus_core/model/src/events/order/pending_update.rs +++ b/nautilus_core/model/src/events/order/pending_update.rs @@ -58,6 +58,7 @@ pub struct OrderPendingUpdate { } impl OrderPendingUpdate { + /// Creates a new [`OrderPendingUpdate`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/events/order/rejected.rs b/nautilus_core/model/src/events/order/rejected.rs index 9c276f3babb7..835737527a0f 100644 --- a/nautilus_core/model/src/events/order/rejected.rs +++ b/nautilus_core/model/src/events/order/rejected.rs @@ -58,6 +58,7 @@ pub struct OrderRejected { } impl OrderRejected { + /// Creates a new [`OrderRejected`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/events/order/released.rs b/nautilus_core/model/src/events/order/released.rs index 1f23481e44f7..11af4b0427fa 100644 --- a/nautilus_core/model/src/events/order/released.rs +++ b/nautilus_core/model/src/events/order/released.rs @@ -55,6 +55,7 @@ pub struct OrderReleased { } impl OrderReleased { + /// Creates a new [`OrderReleased`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/events/order/submitted.rs b/nautilus_core/model/src/events/order/submitted.rs index 2b2566a9238c..38a8618ea7d4 100644 --- a/nautilus_core/model/src/events/order/submitted.rs +++ b/nautilus_core/model/src/events/order/submitted.rs @@ -55,6 +55,7 @@ pub struct OrderSubmitted { } impl OrderSubmitted { + /// Creates a new [`OrderSubmitted`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/events/order/triggered.rs b/nautilus_core/model/src/events/order/triggered.rs index 6d98b85a2a2b..3b777f3586da 100644 --- a/nautilus_core/model/src/events/order/triggered.rs +++ b/nautilus_core/model/src/events/order/triggered.rs @@ -58,6 +58,7 @@ pub struct OrderTriggered { } impl OrderTriggered { + /// Creates a new [`OrderTriggered`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/events/order/updated.rs b/nautilus_core/model/src/events/order/updated.rs index d0eaba1e25a7..986a38964986 100644 --- a/nautilus_core/model/src/events/order/updated.rs +++ b/nautilus_core/model/src/events/order/updated.rs @@ -61,6 +61,7 @@ pub struct OrderUpdated { } impl OrderUpdated { + /// Creates a new [`OrderUpdated`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/identifiers/account_id.rs b/nautilus_core/model/src/identifiers/account_id.rs index c6b3ef71ddb5..ae97d791b90b 100644 --- a/nautilus_core/model/src/identifiers/account_id.rs +++ b/nautilus_core/model/src/identifiers/account_id.rs @@ -39,7 +39,7 @@ use super::venue::Venue; pub struct AccountId(Ustr); impl AccountId { - /// Creates a new `AccountId` instance. + /// Creates a new [`AccountId`] instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/client_id.rs b/nautilus_core/model/src/identifiers/client_id.rs index cb78b19f2ac0..fd194d70f284 100644 --- a/nautilus_core/model/src/identifiers/client_id.rs +++ b/nautilus_core/model/src/identifiers/client_id.rs @@ -31,7 +31,7 @@ use ustr::Ustr; pub struct ClientId(Ustr); impl ClientId { - /// Creates a new `ClientId` instance. + /// Creates a new [`ClientId`] instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/client_order_id.rs b/nautilus_core/model/src/identifiers/client_order_id.rs index d54e492db11f..4d6096841d7a 100644 --- a/nautilus_core/model/src/identifiers/client_order_id.rs +++ b/nautilus_core/model/src/identifiers/client_order_id.rs @@ -31,7 +31,7 @@ use ustr::Ustr; pub struct ClientOrderId(Ustr); impl ClientOrderId { - /// Creates a new `ClientOrderId` instance. + /// Creates a new [`ClientOrderId`] instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/component_id.rs b/nautilus_core/model/src/identifiers/component_id.rs index 6f8399b39d5b..f8d656041b74 100644 --- a/nautilus_core/model/src/identifiers/component_id.rs +++ b/nautilus_core/model/src/identifiers/component_id.rs @@ -31,7 +31,7 @@ use ustr::Ustr; pub struct ComponentId(Ustr); impl ComponentId { - /// Creates a new `ComponentId` instance. + /// Creates a new [`ComponentId`] instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/exec_algorithm_id.rs b/nautilus_core/model/src/identifiers/exec_algorithm_id.rs index 25c6753ebab4..ae6ac94d9951 100644 --- a/nautilus_core/model/src/identifiers/exec_algorithm_id.rs +++ b/nautilus_core/model/src/identifiers/exec_algorithm_id.rs @@ -31,7 +31,7 @@ use ustr::Ustr; pub struct ExecAlgorithmId(Ustr); impl ExecAlgorithmId { - /// Creates a new `ExecAlgorithmId` instance. + /// Creates a new [`ExecAlgorithmId`] instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/instrument_id.rs b/nautilus_core/model/src/identifiers/instrument_id.rs index 144008c02aab..2fe7f632a226 100644 --- a/nautilus_core/model/src/identifiers/instrument_id.rs +++ b/nautilus_core/model/src/identifiers/instrument_id.rs @@ -40,7 +40,7 @@ pub struct InstrumentId { } impl InstrumentId { - /// Creates a new `InstrumentId` instance. + /// Creates a new [`InstrumentId`] instance. #[must_use] pub fn new(symbol: Symbol, venue: Venue) -> Self { Self { symbol, venue } diff --git a/nautilus_core/model/src/identifiers/order_list_id.rs b/nautilus_core/model/src/identifiers/order_list_id.rs index 0c722a1ceb53..18b686d80c5b 100644 --- a/nautilus_core/model/src/identifiers/order_list_id.rs +++ b/nautilus_core/model/src/identifiers/order_list_id.rs @@ -31,7 +31,7 @@ use ustr::Ustr; pub struct OrderListId(Ustr); impl OrderListId { - /// Creates a new `OrderListId` instance. + /// Creates a new [`OrderListId`] instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/position_id.rs b/nautilus_core/model/src/identifiers/position_id.rs index 1392128d075a..eb24836e1778 100644 --- a/nautilus_core/model/src/identifiers/position_id.rs +++ b/nautilus_core/model/src/identifiers/position_id.rs @@ -31,7 +31,7 @@ use ustr::Ustr; pub struct PositionId(Ustr); impl PositionId { - /// Creates a new `PositionId` instance. + /// Creates a new [`PositionId`] instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/strategy_id.rs b/nautilus_core/model/src/identifiers/strategy_id.rs index 7f49097ff716..a105bb2579a5 100644 --- a/nautilus_core/model/src/identifiers/strategy_id.rs +++ b/nautilus_core/model/src/identifiers/strategy_id.rs @@ -40,7 +40,7 @@ const EXTERNAL_STRATEGY_ID: &str = "EXTERNAL"; pub struct StrategyId(Ustr); impl StrategyId { - /// Creates a new `StrategyId` instance. + /// Creates a new [`StrategyId`] instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/stubs.rs b/nautilus_core/model/src/identifiers/stubs.rs index bc45115fa253..1d295eb26871 100644 --- a/nautilus_core/model/src/identifiers/stubs.rs +++ b/nautilus_core/model/src/identifiers/stubs.rs @@ -24,60 +24,70 @@ use crate::identifiers::{ }; impl Default for AccountId { + /// Creates a new default [`AccountId`] instance for testing. fn default() -> Self { Self::from("SIM-001") } } impl Default for ClientId { + /// Creates a new default [`ClientId`] instance for testing. fn default() -> Self { Self::from("SIM") } } impl Default for ClientOrderId { + /// Creates a new default [`ClientOrderId`] instance for testing. fn default() -> Self { Self::from("O-19700101-0000-000-001-1") } } impl Default for PositionId { + /// Creates a new default [`PositionId`] instance for testing. fn default() -> Self { Self::from("P-001") } } impl Default for StrategyId { + /// Creates a new default [`StrategyId`] instance for testing. fn default() -> Self { Self::from("S-001") } } impl Default for Symbol { + /// Creates a new default [`Symbol`] instance for testing. fn default() -> Self { Self::from("AUD/USD") } } impl Default for TradeId { + /// Creates a new default [`TradeId`] instance for testing. fn default() -> Self { Self::from("1") } } impl Default for TraderId { + /// Creates a new default [`TraderId`] instance for testing. fn default() -> Self { Self::from("TRADER-000") } } impl Default for Venue { + /// Creates a new default [`Venue`] instance for testing. fn default() -> Self { Self::from("SIM") } } impl Default for VenueOrderId { + /// Creates a new default [`VenueOrderId`] instance for testing. fn default() -> Self { Self::from("001") } diff --git a/nautilus_core/model/src/identifiers/symbol.rs b/nautilus_core/model/src/identifiers/symbol.rs index 0886bdc0d72d..29f2593450fd 100644 --- a/nautilus_core/model/src/identifiers/symbol.rs +++ b/nautilus_core/model/src/identifiers/symbol.rs @@ -31,7 +31,7 @@ use ustr::Ustr; pub struct Symbol(Ustr); impl Symbol { - /// Creates a new `Symbol` instance. + /// Creates a new [`Symbol`] instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/trade_id.rs b/nautilus_core/model/src/identifiers/trade_id.rs index 086a46711191..16412d21c50c 100644 --- a/nautilus_core/model/src/identifiers/trade_id.rs +++ b/nautilus_core/model/src/identifiers/trade_id.rs @@ -45,7 +45,7 @@ pub struct TradeId { } impl TradeId { - /// Creates a new `TradeId` instance. + /// Creates a new [`TradeId`] instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/trader_id.rs b/nautilus_core/model/src/identifiers/trader_id.rs index eff8e9c090b4..e811bac84c60 100644 --- a/nautilus_core/model/src/identifiers/trader_id.rs +++ b/nautilus_core/model/src/identifiers/trader_id.rs @@ -37,7 +37,7 @@ use ustr::Ustr; pub struct TraderId(Ustr); impl TraderId { - /// Creates a new `TraderId` instance. + /// Creates a new [`TraderId`] instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/venue.rs b/nautilus_core/model/src/identifiers/venue.rs index eddf34a10bda..372028c13039 100644 --- a/nautilus_core/model/src/identifiers/venue.rs +++ b/nautilus_core/model/src/identifiers/venue.rs @@ -35,7 +35,7 @@ pub const SYNTHETIC_VENUE: &str = "SYNTH"; pub struct Venue(Ustr); impl Venue { - /// Creates a new `Venue` instance. + /// Creates a new [`Venue`] instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/identifiers/venue_order_id.rs b/nautilus_core/model/src/identifiers/venue_order_id.rs index 99991d625243..c1d4dbb59e7d 100644 --- a/nautilus_core/model/src/identifiers/venue_order_id.rs +++ b/nautilus_core/model/src/identifiers/venue_order_id.rs @@ -31,7 +31,7 @@ use ustr::Ustr; pub struct VenueOrderId(Ustr); impl VenueOrderId { - /// Creates a new `VenueOrderId` instance. + /// Creates a new [`VenueOrderId`] instance. /// /// # Panics /// diff --git a/nautilus_core/model/src/instruments/crypto_future.rs b/nautilus_core/model/src/instruments/crypto_future.rs index 1759275c315f..2647b06a9133 100644 --- a/nautilus_core/model/src/instruments/crypto_future.rs +++ b/nautilus_core/model/src/instruments/crypto_future.rs @@ -66,6 +66,7 @@ pub struct CryptoFuture { } impl CryptoFuture { + /// Creates a new [`CryptoFuture`] instance. #[allow(clippy::too_many_arguments)] pub fn new( id: InstrumentId, diff --git a/nautilus_core/model/src/instruments/crypto_perpetual.rs b/nautilus_core/model/src/instruments/crypto_perpetual.rs index 14996cfc8134..0b033d73a101 100644 --- a/nautilus_core/model/src/instruments/crypto_perpetual.rs +++ b/nautilus_core/model/src/instruments/crypto_perpetual.rs @@ -65,6 +65,7 @@ pub struct CryptoPerpetual { } impl CryptoPerpetual { + /// Creates a new [`CryptoPerpetual`] instance. #[allow(clippy::too_many_arguments)] pub fn new( id: InstrumentId, diff --git a/nautilus_core/model/src/instruments/currency_pair.rs b/nautilus_core/model/src/instruments/currency_pair.rs index 46dec3a40668..10f5e70e58f7 100644 --- a/nautilus_core/model/src/instruments/currency_pair.rs +++ b/nautilus_core/model/src/instruments/currency_pair.rs @@ -62,6 +62,7 @@ pub struct CurrencyPair { } impl CurrencyPair { + /// Creates a new [`CurrencyPair`] instance. #[allow(clippy::too_many_arguments)] pub fn new( id: InstrumentId, diff --git a/nautilus_core/model/src/instruments/equity.rs b/nautilus_core/model/src/instruments/equity.rs index f3365f6e0157..2815636e8c55 100644 --- a/nautilus_core/model/src/instruments/equity.rs +++ b/nautilus_core/model/src/instruments/equity.rs @@ -59,6 +59,7 @@ pub struct Equity { } impl Equity { + /// Creates a new [`Equity`] instance. #[allow(clippy::too_many_arguments)] pub fn new( id: InstrumentId, diff --git a/nautilus_core/model/src/instruments/futures_contract.rs b/nautilus_core/model/src/instruments/futures_contract.rs index c4204fede104..75682413a95e 100644 --- a/nautilus_core/model/src/instruments/futures_contract.rs +++ b/nautilus_core/model/src/instruments/futures_contract.rs @@ -66,6 +66,7 @@ pub struct FuturesContract { } impl FuturesContract { + /// Creates a new [`FuturesContract`] instance. #[allow(clippy::too_many_arguments)] pub fn new( id: InstrumentId, diff --git a/nautilus_core/model/src/instruments/futures_spread.rs b/nautilus_core/model/src/instruments/futures_spread.rs index f8f8940c9f3f..ef90a3159e39 100644 --- a/nautilus_core/model/src/instruments/futures_spread.rs +++ b/nautilus_core/model/src/instruments/futures_spread.rs @@ -67,6 +67,7 @@ pub struct FuturesSpread { } impl FuturesSpread { + /// Creates a new [`FuturesSpread`] instance. #[allow(clippy::too_many_arguments)] pub fn new( id: InstrumentId, diff --git a/nautilus_core/model/src/instruments/options_contract.rs b/nautilus_core/model/src/instruments/options_contract.rs index 8233fe5d2142..667fb5221e40 100644 --- a/nautilus_core/model/src/instruments/options_contract.rs +++ b/nautilus_core/model/src/instruments/options_contract.rs @@ -68,6 +68,7 @@ pub struct OptionsContract { } impl OptionsContract { + /// Creates a new [`OptionsContract`] instance. #[allow(clippy::too_many_arguments)] pub fn new( id: InstrumentId, diff --git a/nautilus_core/model/src/instruments/options_spread.rs b/nautilus_core/model/src/instruments/options_spread.rs index 3db07bb87441..e235bdda2d05 100644 --- a/nautilus_core/model/src/instruments/options_spread.rs +++ b/nautilus_core/model/src/instruments/options_spread.rs @@ -67,6 +67,7 @@ pub struct OptionsSpread { } impl OptionsSpread { + /// Creates a new [`OptionsSpread`] instance. #[allow(clippy::too_many_arguments)] pub fn new( id: InstrumentId, diff --git a/nautilus_core/model/src/instruments/stubs.rs b/nautilus_core/model/src/instruments/stubs.rs index 8bdb5ae42e71..dcf46e25d844 100644 --- a/nautilus_core/model/src/instruments/stubs.rs +++ b/nautilus_core/model/src/instruments/stubs.rs @@ -34,6 +34,7 @@ use crate::{ }; impl Default for SyntheticInstrument { + /// Creates a new default [`SyntheticInstrument`] instance for testing. fn default() -> Self { let btc_binance = InstrumentId::from("BTC.BINANCE"); let ltc_binance = InstrumentId::from("LTC.BINANCE"); diff --git a/nautilus_core/model/src/instruments/synthetic.rs b/nautilus_core/model/src/instruments/synthetic.rs index 139ec8945891..cb2e48782e18 100644 --- a/nautilus_core/model/src/instruments/synthetic.rs +++ b/nautilus_core/model/src/instruments/synthetic.rs @@ -48,6 +48,7 @@ pub struct SyntheticInstrument { } impl SyntheticInstrument { + /// Creates a new [`SyntheticInstrument`] instance. pub fn new( symbol: Symbol, price_precision: u8, diff --git a/nautilus_core/model/src/orderbook/book.rs b/nautilus_core/model/src/orderbook/book.rs index b7056d0bcc3d..0f4bd003a3ba 100644 --- a/nautilus_core/model/src/orderbook/book.rs +++ b/nautilus_core/model/src/orderbook/book.rs @@ -53,6 +53,7 @@ pub struct OrderBook { } impl OrderBook { + /// Creates a new [`OrderBook`] instance. #[must_use] pub fn new(book_type: BookType, instrument_id: InstrumentId) -> Self { Self { diff --git a/nautilus_core/model/src/orders/base.rs b/nautilus_core/model/src/orders/base.rs index 533a4fc43fd4..090baa61d239 100644 --- a/nautilus_core/model/src/orders/base.rs +++ b/nautilus_core/model/src/orders/base.rs @@ -421,6 +421,7 @@ pub struct OrderCore { } impl OrderCore { + /// Creates a new [`OrderCore`] instance. pub fn new(init: OrderInitialized) -> anyhow::Result { let events: Vec = vec![OrderEventAny::Initialized(init.clone())]; Ok(Self { diff --git a/nautilus_core/model/src/orders/default.rs b/nautilus_core/model/src/orders/default.rs index 40900286e4aa..244deb988d01 100644 --- a/nautilus_core/model/src/orders/default.rs +++ b/nautilus_core/model/src/orders/default.rs @@ -30,8 +30,8 @@ use crate::{ types::{price::Price, quantity::Quantity}, }; -/// Provides a default [`LimitOrder`] used for testing. impl Default for LimitOrder { + /// Creates a new default [`LimitOrder`] instance for testing. fn default() -> Self { Self::new( TraderId::default(), @@ -64,8 +64,8 @@ impl Default for LimitOrder { } } -/// Provides a default [`LimitIfTouchedOrder`] used for testing. impl Default for LimitIfTouchedOrder { + /// Creates a new default [`LimitIfTouchedOrder`] instance for testing. fn default() -> Self { Self::new( TraderId::default(), @@ -102,6 +102,7 @@ impl Default for LimitIfTouchedOrder { /// Provides a default [`MarketOrder`] used for testing. impl Default for MarketOrder { + /// Creates a new default [`MarketOrder`] instance for testing. fn default() -> Self { Self::new( TraderId::default(), @@ -128,8 +129,8 @@ impl Default for MarketOrder { } } -/// Provides a default [`MarketIfTouchedOrder`] used for testing. impl Default for MarketIfTouchedOrder { + /// Creates a new default [`MarketIfTouchedOrder`] instance for testing. fn default() -> Self { Self::new( TraderId::default(), @@ -162,8 +163,8 @@ impl Default for MarketIfTouchedOrder { } } -/// Provides a default [`MarketToLimitOrder`] used for testing. impl Default for MarketToLimitOrder { + /// Creates a new default [`MarketToLimitOrder`] instance for testing. fn default() -> Self { Self::new( TraderId::default(), @@ -193,8 +194,8 @@ impl Default for MarketToLimitOrder { } } -/// Provides a default [`StopLimitOrder`] used for testing. impl Default for StopLimitOrder { + /// Creates a new default [`StopLimitOrder`] instance for testing. fn default() -> Self { Self::new( TraderId::default(), @@ -229,8 +230,8 @@ impl Default for StopLimitOrder { } } -/// Provides a default [`StopMarketOrder`] used for testing. impl Default for StopMarketOrder { + /// Creates a new default [`StopMarketOrder`] instance for testing. fn default() -> Self { Self::new( TraderId::default(), @@ -263,8 +264,8 @@ impl Default for StopMarketOrder { } } -/// Provides a default [`TrailingStopLimitOrder`] used for testing. impl Default for TrailingStopLimitOrder { + /// Creates a new default [`TrailingStopLimitOrder`] instance for testing. fn default() -> Self { Self::new( TraderId::default(), @@ -302,8 +303,8 @@ impl Default for TrailingStopLimitOrder { } } -/// Provides a default [`TrailingStopMarketOrder`] used for testing. impl Default for TrailingStopMarketOrder { + /// Creates a new default [`TrailingStopMarketOrder`] instance for testing. fn default() -> Self { Self::new( TraderId::default(), diff --git a/nautilus_core/model/src/orders/limit.rs b/nautilus_core/model/src/orders/limit.rs index 84d0d325a53e..b0924d8bb6ea 100644 --- a/nautilus_core/model/src/orders/limit.rs +++ b/nautilus_core/model/src/orders/limit.rs @@ -61,6 +61,7 @@ pub struct LimitOrder { } impl LimitOrder { + /// Creates a new [`LimitOrder`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/orders/limit_if_touched.rs b/nautilus_core/model/src/orders/limit_if_touched.rs index e9a1a3120173..67aaf493d7cb 100644 --- a/nautilus_core/model/src/orders/limit_if_touched.rs +++ b/nautilus_core/model/src/orders/limit_if_touched.rs @@ -60,6 +60,7 @@ pub struct LimitIfTouchedOrder { } impl LimitIfTouchedOrder { + /// Creates a new [`LimitIfTouchedOrder`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/orders/list.rs b/nautilus_core/model/src/orders/list.rs index b98722b5a0ac..cb114fe6a578 100644 --- a/nautilus_core/model/src/orders/list.rs +++ b/nautilus_core/model/src/orders/list.rs @@ -40,6 +40,7 @@ pub struct OrderList { } impl OrderList { + /// Creates a new [`OrderList`] instance. pub fn new( order_list_id: OrderListId, instrument_id: InstrumentId, diff --git a/nautilus_core/model/src/orders/market.rs b/nautilus_core/model/src/orders/market.rs index 456312f159b3..788e45b7c579 100644 --- a/nautilus_core/model/src/orders/market.rs +++ b/nautilus_core/model/src/orders/market.rs @@ -56,6 +56,7 @@ pub struct MarketOrder { } impl MarketOrder { + /// Creates a new [`MarketOrder`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/orders/market_if_touched.rs b/nautilus_core/model/src/orders/market_if_touched.rs index b7bc900f9843..7b70b919ff73 100644 --- a/nautilus_core/model/src/orders/market_if_touched.rs +++ b/nautilus_core/model/src/orders/market_if_touched.rs @@ -58,6 +58,7 @@ pub struct MarketIfTouchedOrder { } impl MarketIfTouchedOrder { + /// Creates a new [`MarketIfTouchedOrder`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/orders/market_to_limit.rs b/nautilus_core/model/src/orders/market_to_limit.rs index 2342c71f14d7..263eafd6fd52 100644 --- a/nautilus_core/model/src/orders/market_to_limit.rs +++ b/nautilus_core/model/src/orders/market_to_limit.rs @@ -56,6 +56,7 @@ pub struct MarketToLimitOrder { } impl MarketToLimitOrder { + /// Creates a new [`MarketToLimitOrder`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/orders/stop_limit.rs b/nautilus_core/model/src/orders/stop_limit.rs index ba13d0167ebc..3ef9ff222001 100644 --- a/nautilus_core/model/src/orders/stop_limit.rs +++ b/nautilus_core/model/src/orders/stop_limit.rs @@ -61,6 +61,7 @@ pub struct StopLimitOrder { } impl StopLimitOrder { + /// Creates a new [`StopLimitOrder`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/orders/stop_market.rs b/nautilus_core/model/src/orders/stop_market.rs index 4b9c91402d53..241de2acbd55 100644 --- a/nautilus_core/model/src/orders/stop_market.rs +++ b/nautilus_core/model/src/orders/stop_market.rs @@ -59,6 +59,7 @@ pub struct StopMarketOrder { } impl StopMarketOrder { + /// Creates a new [`StopMarketOrder`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/orders/trailing_stop_limit.rs b/nautilus_core/model/src/orders/trailing_stop_limit.rs index d51bd02dd688..99aa108e6e10 100644 --- a/nautilus_core/model/src/orders/trailing_stop_limit.rs +++ b/nautilus_core/model/src/orders/trailing_stop_limit.rs @@ -63,6 +63,7 @@ pub struct TrailingStopLimitOrder { } impl TrailingStopLimitOrder { + /// Creates a new [`TrailingStopLimitOrder`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/orders/trailing_stop_market.rs b/nautilus_core/model/src/orders/trailing_stop_market.rs index d7dbf5ba46f0..ed9748cb4d62 100644 --- a/nautilus_core/model/src/orders/trailing_stop_market.rs +++ b/nautilus_core/model/src/orders/trailing_stop_market.rs @@ -61,6 +61,7 @@ pub struct TrailingStopMarketOrder { } impl TrailingStopMarketOrder { + /// Creates a new [`TrailingStopMarketOrder`] instance. #[allow(clippy::too_many_arguments)] pub fn new( trader_id: TraderId, diff --git a/nautilus_core/model/src/types/currency.rs b/nautilus_core/model/src/types/currency.rs index 257ae565204f..04e1a4272b28 100644 --- a/nautilus_core/model/src/types/currency.rs +++ b/nautilus_core/model/src/types/currency.rs @@ -41,6 +41,7 @@ pub struct Currency { } impl Currency { + /// Creates a new [`Currency`] instance. pub fn new( code: &str, precision: u8, diff --git a/nautilus_core/model/src/types/money.rs b/nautilus_core/model/src/types/money.rs index e9f07ccd0941..fdc28944f763 100644 --- a/nautilus_core/model/src/types/money.rs +++ b/nautilus_core/model/src/types/money.rs @@ -47,6 +47,7 @@ pub struct Money { } impl Money { + /// Creates a new [`Money`] instance. pub fn new(amount: f64, currency: Currency) -> anyhow::Result { check_in_range_inclusive_f64(amount, MONEY_MIN, MONEY_MAX, "amount")?; diff --git a/nautilus_core/model/src/types/quantity.rs b/nautilus_core/model/src/types/quantity.rs index 00665c17c6b1..cf67272ceb95 100644 --- a/nautilus_core/model/src/types/quantity.rs +++ b/nautilus_core/model/src/types/quantity.rs @@ -44,6 +44,7 @@ pub struct Quantity { } impl Quantity { + /// Creates a new [`Quantity`] instance. pub fn new(value: f64, precision: u8) -> anyhow::Result { check_in_range_inclusive_f64(value, QUANTITY_MIN, QUANTITY_MAX, "value")?; check_fixed_precision(precision)?; diff --git a/nautilus_core/network/src/http.rs b/nautilus_core/network/src/http.rs index 2116c7d60de7..8382b7cee640 100644 --- a/nautilus_core/network/src/http.rs +++ b/nautilus_core/network/src/http.rs @@ -148,6 +148,7 @@ pub struct HttpResponse { } impl Default for InnerHttpClient { + /// Creates a new default [`InnerHttpClient`] instance. fn default() -> Self { let client = reqwest::Client::new(); Self { diff --git a/nautilus_core/network/src/ratelimiter/gcra.rs b/nautilus_core/network/src/ratelimiter/gcra.rs index dd014dafbe5a..601f8d61bc0f 100644 --- a/nautilus_core/network/src/ratelimiter/gcra.rs +++ b/nautilus_core/network/src/ratelimiter/gcra.rs @@ -34,6 +34,7 @@ pub struct StateSnapshot { } impl StateSnapshot { + /// Creates a new [`StateSnapshot`] instance. #[inline] pub(crate) fn new(t: Nanos, tau: Nanos, time_of_measurement: Nanos, tat: Nanos) -> Self { Self { diff --git a/nautilus_core/persistence/src/backend/kmerge_batch.rs b/nautilus_core/persistence/src/backend/kmerge_batch.rs index 7e9834e76259..bfc14d55e200 100644 --- a/nautilus_core/persistence/src/backend/kmerge_batch.rs +++ b/nautilus_core/persistence/src/backend/kmerge_batch.rs @@ -107,6 +107,7 @@ where I: Iterator>, C: Compare>, { + /// Creates a new [`KMerge`] instance. pub fn new(cmp: C) -> Self { Self { heap: BinaryHeap::from_vec_cmp(Vec::new(), cmp), diff --git a/nautilus_core/persistence/src/backend/session.rs b/nautilus_core/persistence/src/backend/session.rs index 5ec6ba3a2436..8360877e593e 100644 --- a/nautilus_core/persistence/src/backend/session.rs +++ b/nautilus_core/persistence/src/backend/session.rs @@ -64,6 +64,7 @@ pub struct DataBackendSession { } impl DataBackendSession { + /// Creates a new [`DataBackendSession`] instance. #[must_use] pub fn new(chunk_size: usize) -> Self { let runtime = tokio::runtime::Builder::new_multi_thread() @@ -183,6 +184,7 @@ pub struct DataQueryResult { } impl DataQueryResult { + /// Creates a new [`DataQueryResult`] instance. #[must_use] pub fn new(result: QueryResult, size: usize) -> Self { Self { diff --git a/nautilus_trader/core/includes/common.h b/nautilus_trader/core/includes/common.h index f9dd06b6f0f4..c88fb06ed657 100644 --- a/nautilus_trader/core/includes/common.h +++ b/nautilus_trader/core/includes/common.h @@ -279,7 +279,7 @@ typedef struct TimeEvent_t { */ typedef struct TimeEventHandler_t { /** - * The event. + * The time event. */ struct TimeEvent_t event; /** diff --git a/nautilus_trader/core/rust/common.pxd b/nautilus_trader/core/rust/common.pxd index f7584ee5751d..e6abcab2c987 100644 --- a/nautilus_trader/core/rust/common.pxd +++ b/nautilus_trader/core/rust/common.pxd @@ -163,7 +163,7 @@ cdef extern from "../includes/common.h": # Represents a time event and its associated handler. cdef struct TimeEventHandler_t: - # The event. + # The time event. TimeEvent_t event; # The callable raw pointer. char *callback_ptr; From 8bdd93670fdefdd0f346742f0c886f96daa8fa5e Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sat, 25 May 2024 14:25:17 +1000 Subject: [PATCH 11/59] Update GitHub macos runner arch --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ede213706e08..1092b9be56b7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -238,7 +238,7 @@ jobs: strategy: fail-fast: false matrix: - arch: [x64] + arch: [arm64] os: [macos-latest] python-version: ["3.10", "3.11", "3.12"] defaults: From 5a8fc5b9d3cd2105801a7c09869eacc98863278a Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sat, 25 May 2024 14:26:13 +1000 Subject: [PATCH 12/59] Update Rust docs --- nautilus_core/common/src/cache/core.rs | 14 ++++----- .../model/src/identifiers/account_id.rs | 15 +++++----- .../model/src/identifiers/client_id.rs | 4 ++- .../model/src/identifiers/client_order_id.rs | 4 ++- .../model/src/identifiers/component_id.rs | 4 ++- .../src/identifiers/exec_algorithm_id.rs | 4 ++- .../model/src/identifiers/instrument_id.rs | 2 ++ nautilus_core/model/src/identifiers/macros.rs | 2 ++ .../model/src/identifiers/order_list_id.rs | 4 ++- .../model/src/identifiers/position_id.rs | 4 ++- .../model/src/identifiers/strategy_id.rs | 22 +++++++------- nautilus_core/model/src/identifiers/symbol.rs | 4 ++- .../model/src/identifiers/trade_id.rs | 14 +++++---- .../model/src/identifiers/trader_id.rs | 22 +++++++------- nautilus_core/model/src/identifiers/venue.rs | 4 ++- .../model/src/identifiers/venue_order_id.rs | 4 ++- .../model/src/orderbook/aggregation.rs | 2 ++ nautilus_core/model/src/orderbook/analysis.rs | 2 ++ nautilus_core/model/src/orderbook/book.rs | 4 ++- nautilus_core/model/src/orderbook/display.rs | 2 ++ nautilus_core/model/src/orderbook/error.rs | 2 ++ nautilus_core/model/src/orderbook/ladder.rs | 4 ++- nautilus_core/model/src/orderbook/level.rs | 2 ++ nautilus_core/model/src/orderbook/mod.rs | 2 +- nautilus_trader/core/includes/model.h | 30 +------------------ nautilus_trader/core/rust/model.pxd | 30 +------------------ 26 files changed, 97 insertions(+), 110 deletions(-) diff --git a/nautilus_core/common/src/cache/core.rs b/nautilus_core/common/src/cache/core.rs index 8e7478c27b35..81898d696431 100644 --- a/nautilus_core/common/src/cache/core.rs +++ b/nautilus_core/common/src/cache/core.rs @@ -256,7 +256,7 @@ impl Cache { // -- COMMANDS -------------------------------------------------------------------------------- - /// Clears the current general cache and load the general objects from the cache database. + /// Clears the current general cache and loads the general objects from the cache database. pub fn cache_general(&mut self) -> anyhow::Result<()> { self.general = match &self.database { Some(db) => db.load()?, @@ -270,7 +270,7 @@ impl Cache { Ok(()) } - /// Clears the current currencies cache and load currencies from the cache database. + /// Clears the current currencies cache and loads currencies from the cache database. pub fn cache_currencies(&mut self) -> anyhow::Result<()> { self.currencies = match &self.database { Some(db) => db.load_currencies()?, @@ -281,7 +281,7 @@ impl Cache { Ok(()) } - /// Clears the current instruments cache and load instruments from the cache database. + /// Clears the current instruments cache and loads instruments from the cache database. pub fn cache_instruments(&mut self) -> anyhow::Result<()> { self.instruments = match &self.database { Some(db) => db.load_instruments()?, @@ -292,7 +292,7 @@ impl Cache { Ok(()) } - /// Clears the current synthetic instruments cache and load synthetic instruments from the cache + /// Clears the current synthetic instruments cache and loads synthetic instruments from the cache /// database. pub fn cache_synthetics(&mut self) -> anyhow::Result<()> { self.synthetics = match &self.database { @@ -307,7 +307,7 @@ impl Cache { Ok(()) } - /// Clears the current accounts cache and load accounts from the cache database. + /// Clears the current accounts cache and loads accounts from the cache database. pub fn cache_accounts(&mut self) -> anyhow::Result<()> { self.accounts = match &self.database { Some(db) => db.load_accounts()?, @@ -321,7 +321,7 @@ impl Cache { Ok(()) } - /// Clears the current orders cache and load orders from the cache database. + /// Clears the current orders cache and loads orders from the cache database. pub fn cache_orders(&mut self) -> anyhow::Result<()> { self.orders = match &self.database { Some(db) => db.load_orders()?, @@ -332,7 +332,7 @@ impl Cache { Ok(()) } - /// Clears the current positions cache and load positions from the cache database. + /// Clears the current positions cache and loads positions from the cache database. pub fn cache_positions(&mut self) -> anyhow::Result<()> { self.positions = match &self.database { Some(db) => db.load_positions()?, diff --git a/nautilus_core/model/src/identifiers/account_id.rs b/nautilus_core/model/src/identifiers/account_id.rs index ae97d791b90b..e2dcf75dbae7 100644 --- a/nautilus_core/model/src/identifiers/account_id.rs +++ b/nautilus_core/model/src/identifiers/account_id.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Represents a valid account ID. + use std::{ fmt::{Debug, Display, Formatter}, hash::Hash, @@ -24,12 +26,6 @@ use ustr::Ustr; use super::venue::Venue; /// Represents a valid account ID. -/// -/// Must be correctly formatted with two valid strings either side of a hyphen '-'. -/// It is expected an account ID is the name of the issuer with an account number -/// separated by a hyphen. -/// -/// Example: "IB-D02851908". #[repr(C)] #[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr( @@ -41,9 +37,14 @@ pub struct AccountId(Ustr); impl AccountId { /// Creates a new [`AccountId`] instance. /// + /// Must be correctly formatted with two valid strings either side of a hyphen '-'. + /// It is expected an account ID is the name of the issuer with an account number + /// separated by a hyphen. + /// + /// Example: "IB-D02851908". /// # Panics /// - /// Panics if the value is not a valid string, or does not contain a hyphen '-' separator. + /// Panics if `value` is not a valid string, or does not contain a hyphen '-' separator. pub fn new(value: &str) -> anyhow::Result { check_valid_string(value, stringify!(value))?; check_string_contains(value, "-", stringify!(value))?; diff --git a/nautilus_core/model/src/identifiers/client_id.rs b/nautilus_core/model/src/identifiers/client_id.rs index fd194d70f284..0cb742ec52c5 100644 --- a/nautilus_core/model/src/identifiers/client_id.rs +++ b/nautilus_core/model/src/identifiers/client_id.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Represents a system client ID. + use std::{ fmt::{Debug, Display, Formatter}, hash::Hash, @@ -35,7 +37,7 @@ impl ClientId { /// /// # Panics /// - /// Panics if the value is not a valid string. + /// Panics if `value` is not a valid string. pub fn new(value: &str) -> anyhow::Result { check_valid_string(value, stringify!(value))?; diff --git a/nautilus_core/model/src/identifiers/client_order_id.rs b/nautilus_core/model/src/identifiers/client_order_id.rs index 4d6096841d7a..2f323fbfa4c1 100644 --- a/nautilus_core/model/src/identifiers/client_order_id.rs +++ b/nautilus_core/model/src/identifiers/client_order_id.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Represents a valid client order ID (assigned by the Nautilus system). + use std::{ fmt::{Debug, Display, Formatter}, hash::Hash, @@ -35,7 +37,7 @@ impl ClientOrderId { /// /// # Panics /// - /// Panics if the value is not a valid string. + /// Panics if `value` is not a valid string. pub fn new(value: &str) -> anyhow::Result { check_valid_string(value, stringify!(value))?; diff --git a/nautilus_core/model/src/identifiers/component_id.rs b/nautilus_core/model/src/identifiers/component_id.rs index f8d656041b74..119c25d5cf56 100644 --- a/nautilus_core/model/src/identifiers/component_id.rs +++ b/nautilus_core/model/src/identifiers/component_id.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Represents a valid component ID. + use std::{ fmt::{Debug, Display, Formatter}, hash::Hash, @@ -35,7 +37,7 @@ impl ComponentId { /// /// # Panics /// - /// Panics if the value is not a valid string. + /// Panics if `value` is not a valid string. pub fn new(value: &str) -> anyhow::Result { check_valid_string(value, stringify!(value))?; diff --git a/nautilus_core/model/src/identifiers/exec_algorithm_id.rs b/nautilus_core/model/src/identifiers/exec_algorithm_id.rs index ae6ac94d9951..de79b2fbb69a 100644 --- a/nautilus_core/model/src/identifiers/exec_algorithm_id.rs +++ b/nautilus_core/model/src/identifiers/exec_algorithm_id.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Represents a valid execution algorithm ID. + use std::{ fmt::{Debug, Display, Formatter}, hash::Hash, @@ -35,7 +37,7 @@ impl ExecAlgorithmId { /// /// # Panics /// - /// Panics if the value is not a valid string. + /// Panics if `value` is not a valid string. pub fn new(value: &str) -> anyhow::Result { check_valid_string(value, stringify!(value))?; diff --git a/nautilus_core/model/src/identifiers/instrument_id.rs b/nautilus_core/model/src/identifiers/instrument_id.rs index 2fe7f632a226..34789b9a8e85 100644 --- a/nautilus_core/model/src/identifiers/instrument_id.rs +++ b/nautilus_core/model/src/identifiers/instrument_id.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Represents a valid instrument ID. + use std::{ fmt::{Debug, Display, Formatter}, hash::Hash, diff --git a/nautilus_core/model/src/identifiers/macros.rs b/nautilus_core/model/src/identifiers/macros.rs index 9b9194d15a76..a9372b4f8f9d 100644 --- a/nautilus_core/model/src/identifiers/macros.rs +++ b/nautilus_core/model/src/identifiers/macros.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Provides macros for generating identifier functionality. + macro_rules! impl_serialization_for_identifier { ($ty:ty) => { impl Serialize for $ty { diff --git a/nautilus_core/model/src/identifiers/order_list_id.rs b/nautilus_core/model/src/identifiers/order_list_id.rs index 18b686d80c5b..d59eb228c2eb 100644 --- a/nautilus_core/model/src/identifiers/order_list_id.rs +++ b/nautilus_core/model/src/identifiers/order_list_id.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Represents a valid order list ID (assigned by the Nautilus system). + use std::{ fmt::{Debug, Display, Formatter}, hash::Hash, @@ -35,7 +37,7 @@ impl OrderListId { /// /// # Panics /// - /// Panics if the value is not a valid string. + /// Panics if `value` is not a valid string. pub fn new(value: &str) -> anyhow::Result { check_valid_string(value, stringify!(value))?; diff --git a/nautilus_core/model/src/identifiers/position_id.rs b/nautilus_core/model/src/identifiers/position_id.rs index eb24836e1778..3beeca15e3f2 100644 --- a/nautilus_core/model/src/identifiers/position_id.rs +++ b/nautilus_core/model/src/identifiers/position_id.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Represents a valid position ID. + use std::{ fmt::{Debug, Display, Formatter}, hash::Hash, @@ -35,7 +37,7 @@ impl PositionId { /// /// # Panics /// - /// Panics if the value is not a valid string. + /// Panics if `value` is not a valid string. pub fn new(value: &str) -> anyhow::Result { check_valid_string(value, stringify!(value))?; diff --git a/nautilus_core/model/src/identifiers/strategy_id.rs b/nautilus_core/model/src/identifiers/strategy_id.rs index a105bb2579a5..c062cd26156c 100644 --- a/nautilus_core/model/src/identifiers/strategy_id.rs +++ b/nautilus_core/model/src/identifiers/strategy_id.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Represents a valid strategy ID. + use std::fmt::{Debug, Display, Formatter}; use nautilus_core::correctness::{check_string_contains, check_valid_string}; @@ -22,15 +24,6 @@ use ustr::Ustr; const EXTERNAL_STRATEGY_ID: &str = "EXTERNAL"; /// Represents a valid strategy ID. -/// -/// Must be correctly formatted with two valid strings either side of a hyphen. -/// It is expected a strategy ID is the class name of the strategy, -/// with an order ID tag number separated by a hyphen. -/// -/// Example: "EMACross-001". -/// -/// The reason for the numerical component of the ID is so that order and position IDs -/// do not collide with those from another strategy within the node instance. #[repr(C)] #[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr( @@ -42,9 +35,18 @@ pub struct StrategyId(Ustr); impl StrategyId { /// Creates a new [`StrategyId`] instance. /// + /// Must be correctly formatted with two valid strings either side of a hyphen. + /// It is expected a strategy ID is the class name of the strategy, + /// with an order ID tag number separated by a hyphen. + /// + /// Example: "EMACross-001". + /// + /// The reason for the numerical component of the ID is so that order and position IDs + /// do not collide with those from another strategy within the node instance. + /// /// # Panics /// - /// Panics if the value is not a valid string, or does not contain a hyphen '-' separator. + /// Panics if `value` is not a valid string, or does not contain a hyphen '-' separator. pub fn new(value: &str) -> anyhow::Result { check_valid_string(value, stringify!(value))?; if value != EXTERNAL_STRATEGY_ID { diff --git a/nautilus_core/model/src/identifiers/symbol.rs b/nautilus_core/model/src/identifiers/symbol.rs index 29f2593450fd..2ad075483d95 100644 --- a/nautilus_core/model/src/identifiers/symbol.rs +++ b/nautilus_core/model/src/identifiers/symbol.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Represents a valid ticker symbol ID for a tradable instrument. + use std::{ fmt::{Debug, Display, Formatter}, hash::Hash, @@ -35,7 +37,7 @@ impl Symbol { /// /// # Panics /// - /// Panics if the value is not a valid string. + /// Panics if `value` is not a valid string. pub fn new(value: &str) -> anyhow::Result { check_valid_string(value, stringify!(value))?; diff --git a/nautilus_core/model/src/identifiers/trade_id.rs b/nautilus_core/model/src/identifiers/trade_id.rs index 16412d21c50c..1f6ff7d48054 100644 --- a/nautilus_core/model/src/identifiers/trade_id.rs +++ b/nautilus_core/model/src/identifiers/trade_id.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Represents a valid trade match ID (assigned by a trading venue). + use std::{ ffi::{CStr, CString}, fmt::{Debug, Display, Formatter}, @@ -27,11 +29,6 @@ const TRADE_ID_LEN: usize = 37; /// Represents a valid trade match ID (assigned by a trading venue). /// -/// Maximum length is 36 characters. -/// -/// The unique ID assigned to the trade entity once it is received or matched by -/// the exchange or central counterparty. -/// /// Can correspond to the `TradeID <1003> field` of the FIX protocol. #[repr(C)] #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] @@ -47,9 +44,14 @@ pub struct TradeId { impl TradeId { /// Creates a new [`TradeId`] instance. /// + /// Maximum length is 36 characters. + /// + /// The unique ID assigned to the trade entity once it is received or matched by + /// the exchange or central counterparty. + /// /// # Panics /// - /// Panics if the value is not a valid string, or value length is greater than 36. + /// Panics if `value` is not a valid string, or value length is greater than 36. pub fn new(value: &str) -> anyhow::Result { let cstr = CString::new(value).expect("`CString` conversion failed"); Self::from_cstr(cstr) diff --git a/nautilus_core/model/src/identifiers/trader_id.rs b/nautilus_core/model/src/identifiers/trader_id.rs index e811bac84c60..5568fd22e2fe 100644 --- a/nautilus_core/model/src/identifiers/trader_id.rs +++ b/nautilus_core/model/src/identifiers/trader_id.rs @@ -13,21 +13,14 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Represents a valid trader ID. + use std::fmt::{Debug, Display, Formatter}; use nautilus_core::correctness::{check_string_contains, check_valid_string}; use ustr::Ustr; /// Represents a valid trader ID. -/// -/// Must be correctly formatted with two valid strings either side of a hyphen. -/// It is expected a trader ID is the abbreviated name of the trader -/// with an order ID tag number separated by a hyphen. -/// -/// Example: "TESTER-001". - -/// The reason for the numerical component of the ID is so that order and position IDs -/// do not collide with those from another node instance. #[repr(C)] #[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr( @@ -39,9 +32,18 @@ pub struct TraderId(Ustr); impl TraderId { /// Creates a new [`TraderId`] instance. /// + /// Must be correctly formatted with two valid strings either side of a hyphen. + /// It is expected a trader ID is the abbreviated name of the trader + /// with an order ID tag number separated by a hyphen. + /// + /// Example: "TESTER-001". + /// + /// The reason for the numerical component of the ID is so that order and position IDs + /// do not collide with those from another node instance. + /// /// # Panics /// - /// Panics if the value is not a valid string, or does not contain a hyphen '-' separator. + /// Panics if `value` is not a valid string, or does not contain a hyphen '-' separator. pub fn new(value: &str) -> anyhow::Result { check_valid_string(value, stringify!(value))?; check_string_contains(value, "-", stringify!(value))?; diff --git a/nautilus_core/model/src/identifiers/venue.rs b/nautilus_core/model/src/identifiers/venue.rs index 372028c13039..b0c7e882bdbe 100644 --- a/nautilus_core/model/src/identifiers/venue.rs +++ b/nautilus_core/model/src/identifiers/venue.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Represents a valid trading venue ID. + use std::{ fmt::{Debug, Display, Formatter}, hash::Hash, @@ -39,7 +41,7 @@ impl Venue { /// /// # Panics /// - /// Panics if the value is not a valid string. + /// Panics if `value` is not a valid string. pub fn new(value: &str) -> anyhow::Result { check_valid_string(value, stringify!(value))?; diff --git a/nautilus_core/model/src/identifiers/venue_order_id.rs b/nautilus_core/model/src/identifiers/venue_order_id.rs index c1d4dbb59e7d..ad3189dea78c 100644 --- a/nautilus_core/model/src/identifiers/venue_order_id.rs +++ b/nautilus_core/model/src/identifiers/venue_order_id.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Represents a valid venue order ID (assigned by a trading venue). + use std::{ fmt::{Debug, Display, Formatter}, hash::Hash, @@ -35,7 +37,7 @@ impl VenueOrderId { /// /// # Panics /// - /// Panics if the value is not a valid string. + /// Panics if `value` is not a valid string. pub fn new(value: &str) -> anyhow::Result { check_valid_string(value, stringify!(value))?; diff --git a/nautilus_core/model/src/orderbook/aggregation.rs b/nautilus_core/model/src/orderbook/aggregation.rs index aa85d7b49ca2..dfa59d795fe9 100644 --- a/nautilus_core/model/src/orderbook/aggregation.rs +++ b/nautilus_core/model/src/orderbook/aggregation.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Functions related to normalizing and processing top-of-book events. + use nautilus_core::nanos::UnixNanos; use super::{book::OrderBook, error::InvalidBookOperation}; diff --git a/nautilus_core/model/src/orderbook/analysis.rs b/nautilus_core/model/src/orderbook/analysis.rs index e411cce7486b..250c7fed91c2 100644 --- a/nautilus_core/model/src/orderbook/analysis.rs +++ b/nautilus_core/model/src/orderbook/analysis.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Functions related to order book analysis. + use std::collections::BTreeMap; use super::{book::OrderBook, ladder::BookPrice, level::Level}; diff --git a/nautilus_core/model/src/orderbook/book.rs b/nautilus_core/model/src/orderbook/book.rs index 0f4bd003a3ba..7bf3e749865a 100644 --- a/nautilus_core/model/src/orderbook/book.rs +++ b/nautilus_core/model/src/orderbook/book.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Provides a performant, generic, multi-purpose order book. + use nautilus_core::nanos::UnixNanos; use super::{aggregation::pre_process_order, analysis, display::pprint_book, level::Level}; @@ -26,7 +28,7 @@ use crate::{ types::{price::Price, quantity::Quantity}, }; -/// Provides an order book. +/// Provides a performant, generic, multi-purpose order book. /// /// Can handle the following granularity data: /// - MBO (market by order) / L3 diff --git a/nautilus_core/model/src/orderbook/display.rs b/nautilus_core/model/src/orderbook/display.rs index c84734ee92dc..61bf6eb31e7f 100644 --- a/nautilus_core/model/src/orderbook/display.rs +++ b/nautilus_core/model/src/orderbook/display.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Functions related to order book display. + use tabled::{settings::Style, Table, Tabled}; use super::{ladder::BookPrice, level::Level}; diff --git a/nautilus_core/model/src/orderbook/error.rs b/nautilus_core/model/src/orderbook/error.rs index 8bc78b163ec9..316228229f69 100644 --- a/nautilus_core/model/src/orderbook/error.rs +++ b/nautilus_core/model/src/orderbook/error.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Errors associated with order book operations and integrity. + use nautilus_core::nanos::UnixNanos; use super::ladder::BookPrice; diff --git a/nautilus_core/model/src/orderbook/ladder.rs b/nautilus_core/model/src/orderbook/ladder.rs index 1bf717cea899..034aa97a12e9 100644 --- a/nautilus_core/model/src/orderbook/ladder.rs +++ b/nautilus_core/model/src/orderbook/ladder.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Represents a ladder of price levels for one side of an order book. + use std::{ cmp::Ordering, collections::{BTreeMap, HashMap}, @@ -75,7 +77,7 @@ impl Display for BookPrice { } } -/// Represents one side of an order book as a ladder of price levels. +/// Represents a ladder of price levels for one side of an order book. #[derive(Clone, Debug)] pub struct Ladder { pub side: OrderSide, diff --git a/nautilus_core/model/src/orderbook/level.rs b/nautilus_core/model/src/orderbook/level.rs index 77e12ddf5845..79e5a39d40a6 100644 --- a/nautilus_core/model/src/orderbook/level.rs +++ b/nautilus_core/model/src/orderbook/level.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Represents a discrete price level in an order book. + use std::{cmp::Ordering, collections::BTreeMap}; use nautilus_core::nanos::UnixNanos; diff --git a/nautilus_core/model/src/orderbook/mod.rs b/nautilus_core/model/src/orderbook/mod.rs index d42aff96dbea..3e630c374242 100644 --- a/nautilus_core/model/src/orderbook/mod.rs +++ b/nautilus_core/model/src/orderbook/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Provides a generic order book which can handle L1/L2/L3 data. +//! Provides order book components which can handle L1/L2/L3 data. pub mod aggregation; pub mod analysis; diff --git a/nautilus_trader/core/includes/model.h b/nautilus_trader/core/includes/model.h index dec7b94fcc23..e1ab012f3162 100644 --- a/nautilus_trader/core/includes/model.h +++ b/nautilus_trader/core/includes/model.h @@ -705,7 +705,7 @@ typedef enum TriggerType { typedef struct Level Level; /** - * Provides an order book. + * Provides a performant, generic, multi-purpose order book. * * Can handle the following granularity data: * - MBO (market by order) / L3 @@ -924,11 +924,6 @@ typedef struct QuoteTick_t { /** * Represents a valid trade match ID (assigned by a trading venue). * - * Maximum length is 36 characters. - * - * The unique ID assigned to the trade entity once it is received or matched by - * the exchange or central counterparty. - * * Can correspond to the `TradeID <1003> field` of the FIX protocol. */ typedef struct TradeId_t { @@ -1089,14 +1084,6 @@ typedef struct Data_t { /** * Represents a valid trader ID. - * - * Must be correctly formatted with two valid strings either side of a hyphen. - * It is expected a trader ID is the abbreviated name of the trader - * with an order ID tag number separated by a hyphen. - * - * Example: "TESTER-001". - * The reason for the numerical component of the ID is so that order and position IDs - * do not collide with those from another node instance. */ typedef struct TraderId_t { char* _0; @@ -1104,15 +1091,6 @@ typedef struct TraderId_t { /** * Represents a valid strategy ID. - * - * Must be correctly formatted with two valid strings either side of a hyphen. - * It is expected a strategy ID is the class name of the strategy, - * with an order ID tag number separated by a hyphen. - * - * Example: "EMACross-001". - * - * The reason for the numerical component of the ID is so that order and position IDs - * do not collide with those from another strategy within the node instance. */ typedef struct StrategyId_t { char* _0; @@ -1159,12 +1137,6 @@ typedef struct OrderReleased_t { /** * Represents a valid account ID. - * - * Must be correctly formatted with two valid strings either side of a hyphen '-'. - * It is expected an account ID is the name of the issuer with an account number - * separated by a hyphen. - * - * Example: "IB-D02851908". */ typedef struct AccountId_t { char* _0; diff --git a/nautilus_trader/core/rust/model.pxd b/nautilus_trader/core/rust/model.pxd index 1663a62ee6ab..16776f952621 100644 --- a/nautilus_trader/core/rust/model.pxd +++ b/nautilus_trader/core/rust/model.pxd @@ -378,7 +378,7 @@ cdef extern from "../includes/model.h": cdef struct Level: pass - # Provides an order book. + # Provides a performant, generic, multi-purpose order book. # # Can handle the following granularity data: # - MBO (market by order) / L3 @@ -510,11 +510,6 @@ cdef extern from "../includes/model.h": # Represents a valid trade match ID (assigned by a trading venue). # - # Maximum length is 36 characters. - # - # The unique ID assigned to the trade entity once it is received or matched by - # the exchange or central counterparty. - # # Can correspond to the `TradeID <1003> field` of the FIX protocol. cdef struct TradeId_t: # The trade match ID value as a fixed-length C string byte array (includes null terminator). @@ -598,27 +593,10 @@ cdef extern from "../includes/model.h": Bar_t bar; # Represents a valid trader ID. - # - # Must be correctly formatted with two valid strings either side of a hyphen. - # It is expected a trader ID is the abbreviated name of the trader - # with an order ID tag number separated by a hyphen. - # - # Example: "TESTER-001". - # The reason for the numerical component of the ID is so that order and position IDs - # do not collide with those from another node instance. cdef struct TraderId_t: char* _0; # Represents a valid strategy ID. - # - # Must be correctly formatted with two valid strings either side of a hyphen. - # It is expected a strategy ID is the class name of the strategy, - # with an order ID tag number separated by a hyphen. - # - # Example: "EMACross-001". - # - # The reason for the numerical component of the ID is so that order and position IDs - # do not collide with those from another strategy within the node instance. cdef struct StrategyId_t: char* _0; @@ -656,12 +634,6 @@ cdef extern from "../includes/model.h": uint64_t ts_init; # Represents a valid account ID. - # - # Must be correctly formatted with two valid strings either side of a hyphen '-'. - # It is expected an account ID is the name of the issuer with an account number - # separated by a hyphen. - # - # Example: "IB-D02851908". cdef struct AccountId_t: char* _0; From 7710385604209f2cc47b92988e5ed05d45ca4bdd Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sat, 25 May 2024 14:41:14 +1000 Subject: [PATCH 13/59] Fix clippy lints --- nautilus_core/indicators/src/average/vwap.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nautilus_core/indicators/src/average/vwap.rs b/nautilus_core/indicators/src/average/vwap.rs index 1e0d9c096105..ea4f52330edf 100644 --- a/nautilus_core/indicators/src/average/vwap.rs +++ b/nautilus_core/indicators/src/average/vwap.rs @@ -138,7 +138,7 @@ mod tests { indicator_vwap.update_raw(10.0, 10.0, 10.0); indicator_vwap.update_raw(20.0, 20.0, 10.0); indicator_vwap.update_raw(30.0, 30.0, 10.0); - assert_eq!(indicator_vwap.value, 23.333333333333332); + assert_eq!(indicator_vwap.value, 23.333_333_333_333_332); } #[rstest] @@ -164,7 +164,7 @@ mod tests { indicator_vwap.update_raw(1.00020, 3.00000, 10.0); indicator_vwap.update_raw(1.00010, 1.00000, 10.0); indicator_vwap.update_raw(1.00000, 2.00000, 10.0); - assert_eq!(indicator_vwap.value, 1.000242857142857); + assert_eq!(indicator_vwap.value, 1.000_242_857_142_857); } #[rstest] From 9fc085a0084ecf42b2bd94ed9d7e384bebe94b37 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sat, 25 May 2024 15:54:46 +1000 Subject: [PATCH 14/59] Fix example base currency --- examples/notebooks/backtest_fx_usdjpy.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/notebooks/backtest_fx_usdjpy.ipynb b/examples/notebooks/backtest_fx_usdjpy.ipynb index d2c3242d840c..ff98d2817119 100644 --- a/examples/notebooks/backtest_fx_usdjpy.ipynb +++ b/examples/notebooks/backtest_fx_usdjpy.ipynb @@ -96,7 +96,7 @@ " venue=SIM,\n", " oms_type=OmsType.HEDGING, # Venue will generate position IDs\n", " account_type=AccountType.MARGIN,\n", - " base_currency=None, # Standard single-currency account\n", + " base_currency=USD, # Standard single-currency account\n", " starting_balances=[Money(1_000_000, USD), Money(10_000_000, JPY)], # Single-currency or multi-currency accounts\n", " fill_model=fill_model,\n", " modules=[fx_rollover_interest],\n", From a0971edc5729d600161c1037336f2108d2660a59 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sat, 25 May 2024 16:04:08 +1000 Subject: [PATCH 15/59] Standardize hyphenated terminology --- docs/integrations/databento.md | 4 ++-- nautilus_core/model/src/enums.rs | 4 ++-- nautilus_core/model/src/orderbook/book.rs | 6 +++--- nautilus_core/model/src/stubs.rs | 4 ++-- nautilus_trader/core/includes/model.h | 10 +++++----- nautilus_trader/core/rust/model.pxd | 10 +++++----- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/integrations/databento.md b/docs/integrations/databento.md index 103fb175f3df..49d00a787ffa 100644 --- a/docs/integrations/databento.md +++ b/docs/integrations/databento.md @@ -161,7 +161,7 @@ The following Databento instrument classes are supported by NautilusTrader: | FX spot | `X` | `CurrencyPair` | | Bond | `B` | Not yet available | -### MBO (market by order) +### MBO (market-by-order) This schema is the highest granularity data offered by Databento, and represents full order book depth. Some messages also provide trade information, and so when @@ -175,7 +175,7 @@ registered handler. Order book snapshots are also buffered into a discrete `OrderBookDeltas` container object, which occurs during the replay startup sequence. -### MBP-1 (market by price, top-of-book) +### MBP-1 (market-by-price, top-of-book) This schema represents the top-of-book only (quotes *and* trades). Like with MBO messages, some messages carry trade information, and so when decoding MBP-1 messages Nautilus diff --git a/nautilus_core/model/src/enums.rs b/nautilus_core/model/src/enums.rs index 2c861edfca0b..7a856482659f 100644 --- a/nautilus_core/model/src/enums.rs +++ b/nautilus_core/model/src/enums.rs @@ -355,9 +355,9 @@ impl FromU8 for BookAction { pub enum BookType { /// Top-of-book best bid/ask, one level per side. L1_MBP = 1, - /// Market by price, one order per level (aggregated). + /// Market-by-price, one order per level (aggregated). L2_MBP = 2, - /// Market by order, multiple orders per level (full granularity). + /// Market-by-order, multiple orders per level (full granularity). L3_MBO = 3, } diff --git a/nautilus_core/model/src/orderbook/book.rs b/nautilus_core/model/src/orderbook/book.rs index 7bf3e749865a..7c990de3ab01 100644 --- a/nautilus_core/model/src/orderbook/book.rs +++ b/nautilus_core/model/src/orderbook/book.rs @@ -31,9 +31,9 @@ use crate::{ /// Provides a performant, generic, multi-purpose order book. /// /// Can handle the following granularity data: -/// - MBO (market by order) / L3 -/// - MBP (market by price) / L2 aggregated order per level -/// - MBP (market by price) / L1 top-of-book only +/// - MBO (market-by-order) / L3 +/// - MBP (market-by-price) / L2 aggregated order per level +/// - MBP (market-by-price) / L1 top-of-book only #[derive(Clone, Debug)] #[cfg_attr( feature = "python", diff --git a/nautilus_core/model/src/stubs.rs b/nautilus_core/model/src/stubs.rs index 107498b9b566..43e619a27e54 100644 --- a/nautilus_core/model/src/stubs.rs +++ b/nautilus_core/model/src/stubs.rs @@ -154,7 +154,7 @@ pub fn stub_order_book_mbp( OrderSide::Buy, price, size, - 0, // order_id not applicable for MBP (market by price) books + 0, // order_id not applicable for MBP (market-by-price) books ); book.add(order, 0, 1, 2.into()); } @@ -175,7 +175,7 @@ pub fn stub_order_book_mbp( OrderSide::Sell, price, size, - 0, // order_id not applicable for MBP (market by price) books + 0, // order_id not applicable for MBP (market-by-price) books ); book.add(order, 0, 1, 2.into()); } diff --git a/nautilus_trader/core/includes/model.h b/nautilus_trader/core/includes/model.h index e1ab012f3162..833b8c2da79e 100644 --- a/nautilus_trader/core/includes/model.h +++ b/nautilus_trader/core/includes/model.h @@ -138,11 +138,11 @@ typedef enum BookType { */ L1_MBP = 1, /** - * Market by price, one order per level (aggregated). + * Market-by-price, one order per level (aggregated). */ L2_MBP = 2, /** - * Market by order, multiple orders per level (full granularity). + * Market-by-order, multiple orders per level (full granularity). */ L3_MBO = 3, } BookType; @@ -708,9 +708,9 @@ typedef struct Level Level; * Provides a performant, generic, multi-purpose order book. * * Can handle the following granularity data: - * - MBO (market by order) / L3 - * - MBP (market by price) / L2 aggregated order per level - * - MBP (market by price) / L1 top-of-book only + * - MBO (market-by-order) / L3 + * - MBP (market-by-price) / L2 aggregated order per level + * - MBP (market-by-price) / L1 top-of-book only */ typedef struct OrderBook OrderBook; diff --git a/nautilus_trader/core/rust/model.pxd b/nautilus_trader/core/rust/model.pxd index 16776f952621..a2b70b8e88da 100644 --- a/nautilus_trader/core/rust/model.pxd +++ b/nautilus_trader/core/rust/model.pxd @@ -80,9 +80,9 @@ cdef extern from "../includes/model.h": cpdef enum BookType: # Top-of-book best bid/ask, one level per side. L1_MBP # = 1, - # Market by price, one order per level (aggregated). + # Market-by-price, one order per level (aggregated). L2_MBP # = 2, - # Market by order, multiple orders per level (full granularity). + # Market-by-order, multiple orders per level (full granularity). L3_MBO # = 3, # The order contigency type which specifies the behavior of linked orders. @@ -381,9 +381,9 @@ cdef extern from "../includes/model.h": # Provides a performant, generic, multi-purpose order book. # # Can handle the following granularity data: - # - MBO (market by order) / L3 - # - MBP (market by price) / L2 aggregated order per level - # - MBP (market by price) / L1 top-of-book only + # - MBO (market-by-order) / L3 + # - MBP (market-by-price) / L2 aggregated order per level + # - MBP (market-by-price) / L1 top-of-book only cdef struct OrderBook: pass From bbeee3646488554d190dfd2850dc88439dbffdc0 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sat, 25 May 2024 16:14:38 +1000 Subject: [PATCH 16/59] Fix notebook account comments --- examples/notebooks/backtest_fx_usdjpy.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/notebooks/backtest_fx_usdjpy.ipynb b/examples/notebooks/backtest_fx_usdjpy.ipynb index ff98d2817119..5b3d02704cc8 100644 --- a/examples/notebooks/backtest_fx_usdjpy.ipynb +++ b/examples/notebooks/backtest_fx_usdjpy.ipynb @@ -96,8 +96,8 @@ " venue=SIM,\n", " oms_type=OmsType.HEDGING, # Venue will generate position IDs\n", " account_type=AccountType.MARGIN,\n", - " base_currency=USD, # Standard single-currency account\n", - " starting_balances=[Money(1_000_000, USD), Money(10_000_000, JPY)], # Single-currency or multi-currency accounts\n", + " base_currency=None, # Multi-currency account\n", + " starting_balances=[Money(1_000_000, USD), Money(10_000_000, JPY)],\n", " fill_model=fill_model,\n", " modules=[fx_rollover_interest],\n", ")" From f65fb85f2667aacdd7cad33bcd95088afb734a61 Mon Sep 17 00:00:00 2001 From: Pushkar Mishra Date: Sat, 25 May 2024 13:58:23 +0530 Subject: [PATCH 17/59] Port VerticalHorizontalFilter indicator (#1666) --- nautilus_core/indicators/src/momentum/bias.rs | 2 +- nautilus_core/indicators/src/momentum/mod.rs | 1 + nautilus_core/indicators/src/momentum/vhf.rs | 201 ++++++++++++++++++ nautilus_core/indicators/src/python/mod.rs | 1 + .../indicators/src/python/momentum/mod.rs | 1 + .../indicators/src/python/momentum/vhf.rs | 89 ++++++++ nautilus_core/indicators/src/stubs.rs | 10 +- nautilus_trader/core/nautilus_pyo3.pyi | 20 ++ 8 files changed, 323 insertions(+), 2 deletions(-) create mode 100644 nautilus_core/indicators/src/momentum/vhf.rs create mode 100644 nautilus_core/indicators/src/python/momentum/vhf.rs diff --git a/nautilus_core/indicators/src/momentum/bias.rs b/nautilus_core/indicators/src/momentum/bias.rs index 88a90712d45e..0969ee9a1932 100644 --- a/nautilus_core/indicators/src/momentum/bias.rs +++ b/nautilus_core/indicators/src/momentum/bias.rs @@ -81,7 +81,7 @@ impl Bias { value: 0.0, count: 0, previous_close: 0.0, - ma: MovingAverageFactory::create(MovingAverageType::Simple, period), + ma: MovingAverageFactory::create(ma_type.unwrap_or(MovingAverageType::Simple), period), has_inputs: false, initialized: false, }) diff --git a/nautilus_core/indicators/src/momentum/mod.rs b/nautilus_core/indicators/src/momentum/mod.rs index 6a11fd173c08..2c50d306a533 100644 --- a/nautilus_core/indicators/src/momentum/mod.rs +++ b/nautilus_core/indicators/src/momentum/mod.rs @@ -19,3 +19,4 @@ pub mod aroon; pub mod bias; pub mod cmo; pub mod rsi; +pub mod vhf; diff --git a/nautilus_core/indicators/src/momentum/vhf.rs b/nautilus_core/indicators/src/momentum/vhf.rs new file mode 100644 index 000000000000..888e6089080e --- /dev/null +++ b/nautilus_core/indicators/src/momentum/vhf.rs @@ -0,0 +1,201 @@ +// ------------------------------------------------------------------------------------------------- +// 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::{Debug, Display}; + +use nautilus_model::data::bar::Bar; +use std::collections::VecDeque; + +use crate::{ + average::{MovingAverageFactory, MovingAverageType}, + indicator::{Indicator, MovingAverage}, +}; + +#[repr(C)] +#[derive(Debug)] +#[cfg_attr( + feature = "python", + pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.indicators") +)] + +pub struct VerticalHorizontalFilter { + pub period: usize, + pub ma_type: MovingAverageType, + pub value: f64, + pub initialized: bool, + ma: Box, + has_inputs: bool, + previous_close: f64, + prices: VecDeque, +} + +impl Display for VerticalHorizontalFilter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}({},{})", self.name(), self.period, self.ma_type,) + } +} + +impl Indicator for VerticalHorizontalFilter { + fn name(&self) -> String { + stringify!(VerticalHorizontalFilter).to_string() + } + + fn has_inputs(&self) -> bool { + self.has_inputs + } + + fn initialized(&self) -> bool { + self.initialized + } + + fn handle_bar(&mut self, bar: &Bar) { + self.update_raw((&bar.close).into()); + } + + fn reset(&mut self) { + self.prices.clear(); + self.ma.reset(); + self.previous_close = 0.0; + self.value = 0.0; + self.has_inputs = false; + self.initialized = false; + } +} + +impl VerticalHorizontalFilter { + /// Creates a new [`VerticalHorizontalFilter`] instance. + pub fn new(period: usize, ma_type: Option) -> anyhow::Result { + Ok(Self { + period, + ma_type: ma_type.unwrap_or(MovingAverageType::Simple), + value: 0.0, + previous_close: 0.0, + ma: MovingAverageFactory::create(ma_type.unwrap_or(MovingAverageType::Simple), period), + has_inputs: false, + initialized: false, + prices: VecDeque::with_capacity(period), + }) + } + + pub fn update_raw(&mut self, close: f64) { + if !self.has_inputs { + self.previous_close = close; + } + self.prices.push_back(close); + + let max_price = self + .prices + .iter() + .cloned() + .fold(f64::NEG_INFINITY, f64::max); + + let min_price = self.prices.iter().cloned().fold(f64::INFINITY, f64::min); + + self.ma.update_raw(f64::abs(close - self.previous_close)); + if self.initialized { + self.value = f64::abs(max_price - min_price) / self.period as f64 / self.ma.value() + } + self.previous_close = close; + + self._check_initialized(); + } + + pub fn _check_initialized(&mut self) { + if !self.initialized { + self.has_inputs = true; + if self.ma.initialized() { + self.initialized = true; + } + } + } +} +//////////////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////////////// +#[cfg(test)] +mod tests { + use nautilus_model::data::bar::Bar; + use rstest::rstest; + + use crate::{indicator::Indicator, momentum::vhf::VerticalHorizontalFilter, stubs::*}; + + #[rstest] + fn test_dema_initialized(vhf_10: VerticalHorizontalFilter) { + let display_str = format!("{vhf_10}"); + assert_eq!(display_str, "VerticalHorizontalFilter(10,SIMPLE)"); + assert_eq!(vhf_10.period, 10); + assert!(!vhf_10.initialized); + assert!(!vhf_10.has_inputs); + } + + #[rstest] + fn test_value_with_one_input(mut vhf_10: VerticalHorizontalFilter) { + vhf_10.update_raw(1.0); + assert_eq!(vhf_10.value, 0.0); + } + + #[rstest] + fn test_value_with_three_inputs(mut vhf_10: VerticalHorizontalFilter) { + vhf_10.update_raw(1.0); + vhf_10.update_raw(2.0); + vhf_10.update_raw(3.0); + assert_eq!(vhf_10.value, 0.0); + } + + #[rstest] + fn test_value_with_ten_inputs(mut vhf_10: VerticalHorizontalFilter) { + vhf_10.update_raw(1.00000); + vhf_10.update_raw(1.00010); + vhf_10.update_raw(1.00020); + vhf_10.update_raw(1.00030); + vhf_10.update_raw(1.00040); + vhf_10.update_raw(1.00050); + vhf_10.update_raw(1.00040); + vhf_10.update_raw(1.00030); + vhf_10.update_raw(1.00020); + vhf_10.update_raw(1.00010); + vhf_10.update_raw(1.00000); + assert_eq!(vhf_10.value, 0.5); + } + + #[rstest] + fn test_initialized_with_required_input(mut vhf_10: VerticalHorizontalFilter) { + for i in 1..10 { + vhf_10.update_raw(f64::from(i)); + } + assert!(!vhf_10.initialized); + vhf_10.update_raw(10.0); + assert!(vhf_10.initialized); + } + + #[rstest] + fn test_handle_bar(mut vhf_10: VerticalHorizontalFilter, bar_ethusdt_binance_minute_bid: Bar) { + vhf_10.handle_bar(&bar_ethusdt_binance_minute_bid); + assert_eq!(vhf_10.value, 0.0); + assert!(vhf_10.has_inputs); + assert!(!vhf_10.initialized); + } + + #[rstest] + fn test_reset(mut vhf_10: VerticalHorizontalFilter) { + vhf_10.update_raw(1.0); + assert_eq!(vhf_10.prices.len(), 1); + vhf_10.reset(); + assert_eq!(vhf_10.value, 0.0); + assert_eq!(vhf_10.prices.len(), 0); + assert!(!vhf_10.has_inputs); + assert!(!vhf_10.initialized); + } +} diff --git a/nautilus_core/indicators/src/python/mod.rs b/nautilus_core/indicators/src/python/mod.rs index 6bc4002a04e9..4a30d1f69196 100644 --- a/nautilus_core/indicators/src/python/mod.rs +++ b/nautilus_core/indicators/src/python/mod.rs @@ -45,6 +45,7 @@ pub fn indicators(_: Python<'_>, m: &PyModule) -> PyResult<()> { m.add_class::()?; m.add_class::()?; m.add_class::()?; + m.add_class::()?; // volatility m.add_class::()?; Ok(()) diff --git a/nautilus_core/indicators/src/python/momentum/mod.rs b/nautilus_core/indicators/src/python/momentum/mod.rs index 7ace632bf2db..b89c07bf626b 100644 --- a/nautilus_core/indicators/src/python/momentum/mod.rs +++ b/nautilus_core/indicators/src/python/momentum/mod.rs @@ -17,3 +17,4 @@ pub mod aroon; pub mod bias; pub mod cmo; pub mod rsi; +pub mod vhf; diff --git a/nautilus_core/indicators/src/python/momentum/vhf.rs b/nautilus_core/indicators/src/python/momentum/vhf.rs new file mode 100644 index 000000000000..0a6b512302fd --- /dev/null +++ b/nautilus_core/indicators/src/python/momentum/vhf.rs @@ -0,0 +1,89 @@ +// ------------------------------------------------------------------------------------------------- +// 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 nautilus_core::python::to_pyvalue_err; +use nautilus_model::data::{bar::Bar, quote::QuoteTick, trade::TradeTick}; +use pyo3::prelude::*; + +use crate::{ + average::MovingAverageType, indicator::Indicator, momentum::vhf::VerticalHorizontalFilter, +}; + +#[pymethods] +impl VerticalHorizontalFilter { + #[new] + pub fn py_new(period: usize, ma_type: Option) -> PyResult { + Self::new(period, ma_type).map_err(to_pyvalue_err) + } + + fn __repr__(&self) -> String { + format!("VerticalHorizontalFilter({})", self.period) + } + + #[getter] + #[pyo3(name = "name")] + fn py_name(&self) -> String { + self.name() + } + + #[getter] + #[pyo3(name = "period")] + fn py_period(&self) -> usize { + self.period + } + + #[getter] + #[pyo3(name = "has_inputs")] + fn py_has_inputs(&self) -> bool { + self.has_inputs() + } + + #[getter] + #[pyo3(name = "value")] + fn py_value(&self) -> f64 { + self.value + } + + #[getter] + #[pyo3(name = "initialized")] + fn py_initialized(&self) -> bool { + self.initialized + } + + #[pyo3(name = "update_raw")] + fn py_update_raw(&mut self, close: f64) { + self.update_raw(close); + } + + #[pyo3(name = "handle_quote_tick")] + fn py_handle_quote_tick(&mut self, _tick: &QuoteTick) { + // Function body intentionally left blank. + } + + #[pyo3(name = "handle_trade_tick")] + fn py_handle_trade_tick(&mut self, _tick: &TradeTick) { + // Function body intentionally left blank. + } + + #[pyo3(name = "handle_bar")] + fn py_handle_bar(&mut self, bar: &Bar) { + self.update_raw((&bar.close).into()); + } + + #[pyo3(name = "reset")] + fn py_reset(&mut self) { + self.reset(); + } +} diff --git a/nautilus_core/indicators/src/stubs.rs b/nautilus_core/indicators/src/stubs.rs index 513903094b43..0d4a87692f34 100644 --- a/nautilus_core/indicators/src/stubs.rs +++ b/nautilus_core/indicators/src/stubs.rs @@ -34,7 +34,10 @@ use crate::{ sma::SimpleMovingAverage, vidya::VariableIndexDynamicAverage, vwap::VolumeWeightedAveragePrice, wma::WeightedMovingAverage, MovingAverageType, }, - momentum::{bias::Bias, cmo::ChandeMomentumOscillator, rsi::RelativeStrengthIndex}, + momentum::{ + bias::Bias, cmo::ChandeMomentumOscillator, rsi::RelativeStrengthIndex, + vhf::VerticalHorizontalFilter, + }, ratio::efficiency_ratio::EfficiencyRatio, }; @@ -173,3 +176,8 @@ pub fn cmo_10() -> ChandeMomentumOscillator { pub fn bias_10() -> Bias { Bias::new(10, Some(MovingAverageType::Wilder)).unwrap() } + +#[fixture] +pub fn vhf_10() -> VerticalHorizontalFilter { + VerticalHorizontalFilter::new(10, Some(MovingAverageType::Simple)).unwrap() +} diff --git a/nautilus_trader/core/nautilus_pyo3.pyi b/nautilus_trader/core/nautilus_pyo3.pyi index 55c727f4fa65..2cfd70c9f71b 100644 --- a/nautilus_trader/core/nautilus_pyo3.pyi +++ b/nautilus_trader/core/nautilus_pyo3.pyi @@ -2680,6 +2680,26 @@ class VolumeWeightedAveragePrice: def handle_bar(self, bar: Bar) -> None: ... def reset(self) -> None: ... +class VerticalHorizontalFilter: + def __init__( + self, + period: int, + ma_type: MovingAverageType = ..., + ) -> None: ... + @property + def name(self) -> str: ... + @property + def period(self) -> int: ... + @property + def initialized(self) -> bool: ... + @property + def has_inputs(self) -> bool: ... + @property + def value(self) -> float: ... + def update_raw(self, close: float) -> None: ... + def handle_bar(self, bar: Bar) -> None: ... + def reset(self) -> None: ... + class ChandeMomentumOscillator: def __init__( self, From 6062a43f5717e2e156d16aa6acc7fd7144b87d16 Mon Sep 17 00:00:00 2001 From: rsmb7z <105105941+rsmb7z@users.noreply.github.com> Date: Sun, 26 May 2024 01:11:22 +0300 Subject: [PATCH 18/59] Refactor mocks in Interactive Brokers integration tests (#1669) --- .../adapters/interactive_brokers/conftest.py | 72 +++++---- .../interactive_brokers/mock_client.py | 146 ++++++++++++++++++ .../test_execution_order_transform.py | 91 +++++++++++ 3 files changed, 279 insertions(+), 30 deletions(-) create mode 100644 tests/integration_tests/adapters/interactive_brokers/mock_client.py create mode 100644 tests/integration_tests/adapters/interactive_brokers/test_execution_order_transform.py diff --git a/tests/integration_tests/adapters/interactive_brokers/conftest.py b/tests/integration_tests/adapters/interactive_brokers/conftest.py index 9b2c9d714c5f..a6f4d806b52c 100644 --- a/tests/integration_tests/adapters/interactive_brokers/conftest.py +++ b/tests/integration_tests/adapters/interactive_brokers/conftest.py @@ -15,6 +15,7 @@ import asyncio from unittest.mock import AsyncMock from unittest.mock import MagicMock +from unittest.mock import patch import pytest @@ -33,12 +34,35 @@ from nautilus_trader.model.identifiers import Venue from nautilus_trader.test_kit.functions import ensure_all_tasks_completed from nautilus_trader.test_kit.stubs.events import TestEventStubs +from tests.integration_tests.adapters.interactive_brokers.mock_client import MockInteractiveBrokersClient from tests.integration_tests.adapters.interactive_brokers.test_kit import IBTestContractStubs # fmt: on +def mocked_ib_client( + loop, + msgbus, + cache, + clock, + host, + port, + client_id, + **kwargs, +) -> MockInteractiveBrokersClient: + client = MockInteractiveBrokersClient( + loop=loop, + msgbus=msgbus, + cache=cache, + clock=clock, + host=host, + port=port, + client_id=client_id, + ) + return client + + @pytest.fixture() def event_loop(): loop = asyncio.get_event_loop() @@ -120,19 +144,15 @@ def instrument_provider(ib_client): @pytest.fixture() -def data_client(mocker, data_client_config, venue, event_loop, msgbus, cache, clock): - mocker.patch( - "nautilus_trader.adapters.interactive_brokers.factories.get_cached_ib_client", - return_value=InteractiveBrokersClient( - loop=event_loop, - msgbus=msgbus, - cache=cache, - clock=clock, - host=data_client_config.ibg_host, - port=data_client_config.ibg_port, - client_id=data_client_config.ibg_client_id, - ), - ) +@patch( + "nautilus_trader.adapters.interactive_brokers.factories.get_cached_ib_client", + new=mocked_ib_client, +) +@patch( + "nautilus_trader.adapters.interactive_brokers.factories.get_cached_interactive_brokers_instrument_provider", + new=InteractiveBrokersInstrumentProvider, +) +def data_client(data_client_config, venue, event_loop, msgbus, cache, clock): client = InteractiveBrokersLiveDataClientFactory.create( loop=event_loop, name=venue.value, @@ -143,26 +163,20 @@ def data_client(mocker, data_client_config, venue, event_loop, msgbus, cache, cl ) client._client._is_ib_connected.set() client._client._connect = AsyncMock() - client._client._eclient = MagicMock() client._client._account_ids = {"DU123456,"} - # client._client.start() return client @pytest.fixture() -def exec_client(mocker, exec_client_config, venue, event_loop, msgbus, cache, clock): - mocker.patch( - "nautilus_trader.adapters.interactive_brokers.factories.get_cached_ib_client", - return_value=InteractiveBrokersClient( - loop=event_loop, - msgbus=msgbus, - cache=cache, - clock=clock, - host=exec_client_config.ibg_host, - port=exec_client_config.ibg_port, - client_id=exec_client_config.ibg_client_id, - ), - ) +@patch( + "nautilus_trader.adapters.interactive_brokers.factories.get_cached_ib_client", + new=mocked_ib_client, +) +@patch( + "nautilus_trader.adapters.interactive_brokers.factories.get_cached_interactive_brokers_instrument_provider", + new=InteractiveBrokersInstrumentProvider, +) +def exec_client(exec_client_config, venue, event_loop, msgbus, cache, clock): client = InteractiveBrokersLiveExecClientFactory.create( loop=event_loop, name=venue.value, @@ -173,9 +187,7 @@ def exec_client(mocker, exec_client_config, venue, event_loop, msgbus, cache, cl ) client._client._is_ib_connected.set() client._client._connect = AsyncMock() - client._client._eclient = MagicMock() client._client._account_ids = {"DU123456,"} - # client._client.start() return client diff --git a/tests/integration_tests/adapters/interactive_brokers/mock_client.py b/tests/integration_tests/adapters/interactive_brokers/mock_client.py new file mode 100644 index 000000000000..5c956e6d3e23 --- /dev/null +++ b/tests/integration_tests/adapters/interactive_brokers/mock_client.py @@ -0,0 +1,146 @@ +import asyncio +from collections.abc import Callable +from unittest.mock import MagicMock + +from ibapi.client import EClient + +# fmt: off +from nautilus_trader.adapters.interactive_brokers.client.client import InteractiveBrokersClient +from nautilus_trader.adapters.interactive_brokers.client.wrapper import InteractiveBrokersEWrapper +from nautilus_trader.adapters.interactive_brokers.common import IBContract +from nautilus_trader.adapters.interactive_brokers.parsing.instruments import ib_contract_to_instrument_id +from tests.integration_tests.adapters.interactive_brokers.test_kit import IBTestContractStubs + + +class MockEClient(EClient): + """ + MockEClient is a subclass of EClient which is used for simulating Interactive + Brokers' client operations. + + This class overloads a few methods of the parent class to better accommodate testing + needs. More methods can be added as and when needed, depending on the testing + requirements. + + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._next_valid_counter = 0 + + def _handle_task(self, handler: Callable, **kwargs): + loop = asyncio.get_event_loop() + if loop.is_running(): + loop.create_task(handler(**kwargs)) # noqa: RUF006 + else: + loop.run_until_complete(handler(**kwargs)) + + ######################################################################### + ################## Market Data + ######################################################################### + + ######################################################################### + ################## Options + ######################################################################### + + ######################################################################### + ################## Orders + ######################################################################### + + ######################################################################### + ################## Account and Portfolio + ######################################################################### + + ######################################################################### + ################## Daily PnL + ######################################################################### + + ######################################################################### + ################## Executions + ######################################################################### + + ######################################################################### + ################## Contract Details + ######################################################################### + + def reqContractDetails(self, reqId: int, contract: IBContract): + instrument_id = ib_contract_to_instrument_id(contract) + match instrument_id.value: + case "AAPL.NASDAQ": + self._handle_task( + self.wrapper._client.process_contract_details, + req_id=reqId, + contract_details=IBTestContractStubs.aapl_equity_contract_details(), + ) + case "EUR/USD.IDEALPRO": + self._handle_task( + self.wrapper._client.process_contract_details, + req_id=reqId, + contract_details=IBTestContractStubs.eurusd_forex_contract_details(), + ) + + self._handle_task( + self.wrapper._client.process_contract_details_end, + req_id=reqId, + ) + + ######################################################################### + ################## Market Depth + ######################################################################### + + ######################################################################### + ################## News Bulletins + ######################################################################### + + ######################################################################### + ################## Financial Advisors + ######################################################################### + def reqManagedAccts(self): + self._handle_task( + self.wrapper._client.process_managed_accounts, + accounts_list="DU1234567,", + ) + + ######################################################################### + ################## Historical Data + ######################################################################### + + ######################################################################### + ################## Market Scanners + ######################################################################### + + ######################################################################### + ################## Real Time Bars + ######################################################################### + + ######################################################################### + ################## Fundamental Data + ######################################################################### + + ######################################################################## + ################## News + ######################################################################### + + ######################################################################### + ################## Display Groups + ######################################################################### + + +class MockInteractiveBrokersClient(InteractiveBrokersClient): + """ + MockInteractiveBrokersClient is a subclass of InteractiveBrokersClient used for + simulating client operations. + + This class initializes the EClient with a mocked version for testing purposes. + + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + self._eclient = MockEClient( + wrapper=InteractiveBrokersEWrapper( + nautilus_logger=self._log, + client=self, + ), + ) + self._start = MagicMock() diff --git a/tests/integration_tests/adapters/interactive_brokers/test_execution_order_transform.py b/tests/integration_tests/adapters/interactive_brokers/test_execution_order_transform.py new file mode 100644 index 000000000000..3639c50c1e24 --- /dev/null +++ b/tests/integration_tests/adapters/interactive_brokers/test_execution_order_transform.py @@ -0,0 +1,91 @@ +# ------------------------------------------------------------------------------------------------- +# 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. +# ------------------------------------------------------------------------------------------------- + +import pytest + +from nautilus_trader.model.enums import TimeInForce +from nautilus_trader.model.identifiers import Venue +from nautilus_trader.test_kit.stubs.data import TestInstrumentProvider +from nautilus_trader.test_kit.stubs.execution import TestExecStubs + + +_AAPL = TestInstrumentProvider.equity("AAPL", "NASDAQ") +_EURUSD = TestInstrumentProvider.default_fx_ccy("EUR/USD", Venue("IDEALPRO")) + + +@pytest.mark.parametrize( + "expected_order_type, expected_tif, nautilus_order", + [ + # fmt: off + ("MKT", "GTC", TestExecStubs.market_order(instrument=_EURUSD, time_in_force=TimeInForce.GTC)), + ("MKT", "DAY", TestExecStubs.market_order(instrument=_EURUSD, time_in_force=TimeInForce.DAY)), + ("MKT", "IOC", TestExecStubs.market_order(instrument=_EURUSD, time_in_force=TimeInForce.IOC)), + ("MKT", "FOK", TestExecStubs.market_order(instrument=_EURUSD, time_in_force=TimeInForce.FOK)), + ("MKT", "OPG", TestExecStubs.market_order(instrument=_EURUSD, time_in_force=TimeInForce.AT_THE_OPEN)), + ("MOC", "DAY", TestExecStubs.market_order(instrument=_EURUSD, time_in_force=TimeInForce.AT_THE_CLOSE)), + # fmt: on + ], +) +@pytest.mark.asyncio +async def test_transform_order_to_ib_order_market( + expected_order_type, + expected_tif, + nautilus_order, + exec_client, +): + # Arrange + await exec_client._instrument_provider.load_async(nautilus_order.instrument_id) + + # Act + ib_order = exec_client._transform_order_to_ib_order(nautilus_order) + + # Assert + assert ( + ib_order.orderType == expected_order_type + ), f"{expected_order_type=}, but got {ib_order.orderType=}" + assert ib_order.tif == expected_tif, f"{expected_tif=}, but got {ib_order.tif=}" + + +@pytest.mark.parametrize( + "expected_order_type, expected_tif, nautilus_order", + [ + # fmt: off + ("LMT", "GTC", TestExecStubs.limit_order(instrument=_EURUSD, time_in_force=TimeInForce.GTC)), + ("LMT", "DAY", TestExecStubs.limit_order(instrument=_EURUSD, time_in_force=TimeInForce.DAY)), + ("LMT", "IOC", TestExecStubs.limit_order(instrument=_EURUSD, time_in_force=TimeInForce.IOC)), + ("LMT", "FOK", TestExecStubs.limit_order(instrument=_EURUSD, time_in_force=TimeInForce.FOK)), + ("LMT", "OPG", TestExecStubs.limit_order(instrument=_EURUSD, time_in_force=TimeInForce.AT_THE_OPEN)), + ("LOC", "DAY", TestExecStubs.limit_order(instrument=_EURUSD, time_in_force=TimeInForce.AT_THE_CLOSE)), + # fmt: on + ], +) +@pytest.mark.asyncio +async def test_transform_order_to_ib_order_limit( + expected_order_type, + expected_tif, + nautilus_order, + exec_client, +): + # Arrange + await exec_client._instrument_provider.load_async(nautilus_order.instrument_id) + + # Act + ib_order = exec_client._transform_order_to_ib_order(nautilus_order) + + # Assert + assert ( + ib_order.orderType == expected_order_type + ), f"{expected_order_type=}, but got {ib_order.orderType=}" + assert ib_order.tif == expected_tif, f"{expected_tif=}, but got {ib_order.tif=}" From c746c498d18b98b6d92e799092039bb02cedb253 Mon Sep 17 00:00:00 2001 From: David Blom Date: Sun, 26 May 2024 00:12:51 +0200 Subject: [PATCH 19/59] Fix parsing Bybit deltas (#1668) --- nautilus_trader/adapters/bybit/schemas/ws.py | 1 - .../adapters/bybit/test_ws_decoders.py | 25 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/nautilus_trader/adapters/bybit/schemas/ws.py b/nautilus_trader/adapters/bybit/schemas/ws.py index 4e5d11c02fa5..fc07dc622a3d 100644 --- a/nautilus_trader/adapters/bybit/schemas/ws.py +++ b/nautilus_trader/adapters/bybit/schemas/ws.py @@ -237,7 +237,6 @@ def parse_to_deltas( is_snapshot=False, ) deltas.append(delta) - deltas.append(delta) for ask in asks_raw: delta = parse_bybit_delta( diff --git a/tests/integration_tests/adapters/bybit/test_ws_decoders.py b/tests/integration_tests/adapters/bybit/test_ws_decoders.py index d8483cd20572..7f8cf9044e0a 100644 --- a/tests/integration_tests/adapters/bybit/test_ws_decoders.py +++ b/tests/integration_tests/adapters/bybit/test_ws_decoders.py @@ -49,6 +49,9 @@ from nautilus_trader.adapters.bybit.schemas.ws import BybitWsTickerSpotMsg from nautilus_trader.adapters.bybit.schemas.ws import BybitWsTrade from nautilus_trader.adapters.bybit.schemas.ws import BybitWsTradeMsg +from nautilus_trader.model.identifiers import InstrumentId +from nautilus_trader.model.identifiers import Symbol +from nautilus_trader.model.identifiers import Venue class TestBybitWsDecoders: @@ -132,6 +135,28 @@ def test_ws_public_orderbook_delta(self): assert result.ts == 1687940967466 assert result.type == "delta" + def test_ws_public_orderbook_delta_parse_to_deltas(self): + # Prepare + item = pkgutil.get_data( + "tests.integration_tests.adapters.bybit.resources.ws_messages.public", + "ws_orderbook_delta.json", + ) + assert item is not None + instrument_id = InstrumentId(Symbol("BTCUSDT-LINEAR"), Venue("BYBIT")) + decoder = msgspec.json.Decoder(BybitWsOrderbookDepthMsg) + + # Act + result = decoder.decode(item).data.parse_to_deltas( + instrument_id=instrument_id, + price_precision=2, + size_precision=2, + ts_event=0, + ts_init=0, + ) + + # Assert + assert len(result.deltas) == 12 + def test_ws_public_orderbook_snapshot(self): item = pkgutil.get_data( "tests.integration_tests.adapters.bybit.resources.ws_messages.public", From 87d5b1c200506c926c169dddb4b939d5d6d90138 Mon Sep 17 00:00:00 2001 From: David Blom Date: Sun, 26 May 2024 00:18:40 +0200 Subject: [PATCH 20/59] Set F_LAST flag for Bybit deltas (#1670) --- .../adapters/bybit/common/parsing.py | 3 +- nautilus_trader/adapters/bybit/schemas/ws.py | 44 ++++++++- .../public/ws_orderbook_delta_no_asks.json | 33 +++++++ .../public/ws_orderbook_snapshot_no_asks.json | 21 ++++ .../adapters/bybit/test_ws_decoders.py | 98 +++++++++++++++++++ 5 files changed, 194 insertions(+), 5 deletions(-) create mode 100644 tests/integration_tests/adapters/bybit/resources/ws_messages/public/ws_orderbook_delta_no_asks.json create mode 100644 tests/integration_tests/adapters/bybit/resources/ws_messages/public/ws_orderbook_snapshot_no_asks.json diff --git a/nautilus_trader/adapters/bybit/common/parsing.py b/nautilus_trader/adapters/bybit/common/parsing.py index 5e5eef82eaa1..0fcdb932c556 100644 --- a/nautilus_trader/adapters/bybit/common/parsing.py +++ b/nautilus_trader/adapters/bybit/common/parsing.py @@ -47,6 +47,7 @@ def parse_bybit_delta( ts_event: int, ts_init: int, is_snapshot: bool, + flags: int = 0, ) -> OrderBookDelta: price = values[0] size = values[1] @@ -64,7 +65,7 @@ def parse_bybit_delta( size=size, order_id=update_id, ), - flags=0, # Not applicable + flags=flags, sequence=sequence, ts_event=ts_event, ts_init=ts_init, diff --git a/nautilus_trader/adapters/bybit/schemas/ws.py b/nautilus_trader/adapters/bybit/schemas/ws.py index fc07dc622a3d..d6f0ab90a433 100644 --- a/nautilus_trader/adapters/bybit/schemas/ws.py +++ b/nautilus_trader/adapters/bybit/schemas/ws.py @@ -172,8 +172,17 @@ def parse_to_snapshot( ts_init=ts_init, ) deltas.append(clear) + num_bids_raw = len(bids_raw) + num_asks_raw = len(asks_raw) + + for bid_id, bid in enumerate(bids_raw): + flags = 0 + + if bid_id == num_bids_raw - 1 and num_asks_raw == 0: + # F_LAST, 1 << 7 + # Last message in the packet from the venue for a given `instrument_id` + flags = 128 - for bid in bids_raw: delta = parse_bybit_delta( instrument_id=instrument_id, values=bid, @@ -183,10 +192,18 @@ def parse_to_snapshot( ts_event=ts_event, ts_init=ts_init, is_snapshot=True, + flags=flags, ) deltas.append(delta) - for ask in asks_raw: + for ask_id, ask in enumerate(asks_raw): + flags = 0 + + if ask_id == num_asks_raw - 1: + # F_LAST, 1 << 7 + # Last message in the packet from the venue for a given `instrument_id` + flags = 128 + delta = parse_bybit_delta( instrument_id=instrument_id, values=ask, @@ -196,6 +213,7 @@ def parse_to_snapshot( ts_event=ts_event, ts_init=ts_init, is_snapshot=True, + flags=flags, ) deltas.append(delta) @@ -224,8 +242,17 @@ def parse_to_deltas( for d in self.a ] deltas: list[OrderBookDelta] = [] + num_bids_raw = len(bids_raw) + num_asks_raw = len(asks_raw) + + for bid_id, bid in enumerate(bids_raw): + flags = 0 + + if bid_id == num_bids_raw - 1 and num_asks_raw == 0: + # F_LAST, 1 << 7 + # Last message in the packet from the venue for a given `instrument_id` + flags = 128 - for bid in bids_raw: delta = parse_bybit_delta( instrument_id=instrument_id, values=bid, @@ -235,10 +262,18 @@ def parse_to_deltas( ts_event=ts_event, ts_init=ts_init, is_snapshot=False, + flags=flags, ) deltas.append(delta) - for ask in asks_raw: + for ask_id, ask in enumerate(asks_raw): + flags = 0 + + if ask_id == num_asks_raw - 1: + # F_LAST, 1 << 7 + # Last message in the packet from the venue for a given `instrument_id` + flags = 128 + delta = parse_bybit_delta( instrument_id=instrument_id, values=ask, @@ -248,6 +283,7 @@ def parse_to_deltas( ts_event=ts_event, ts_init=ts_init, is_snapshot=False, + flags=flags, ) deltas.append(delta) diff --git a/tests/integration_tests/adapters/bybit/resources/ws_messages/public/ws_orderbook_delta_no_asks.json b/tests/integration_tests/adapters/bybit/resources/ws_messages/public/ws_orderbook_delta_no_asks.json new file mode 100644 index 000000000000..71d380c14a0d --- /dev/null +++ b/tests/integration_tests/adapters/bybit/resources/ws_messages/public/ws_orderbook_delta_no_asks.json @@ -0,0 +1,33 @@ +{ + "topic": "orderbook.50.BTCUSDT", + "type": "delta", + "ts": 1687940967466, + "data": { + "s": "BTCUSDT", + "b": [ + [ + "30247.20", + "30.028" + ], + [ + "30245.40", + "0.224" + ], + [ + "30242.10", + "1.593" + ], + [ + "30240.30", + "1.305" + ], + [ + "30240.00", + "0" + ] + ], + "a": [], + "u": 177400507, + "seq": 66544703342 + } +} \ No newline at end of file diff --git a/tests/integration_tests/adapters/bybit/resources/ws_messages/public/ws_orderbook_snapshot_no_asks.json b/tests/integration_tests/adapters/bybit/resources/ws_messages/public/ws_orderbook_snapshot_no_asks.json new file mode 100644 index 000000000000..2a51bd596f3b --- /dev/null +++ b/tests/integration_tests/adapters/bybit/resources/ws_messages/public/ws_orderbook_snapshot_no_asks.json @@ -0,0 +1,21 @@ +{ + "topic": "orderbook.50.BTCUSDT", + "type": "snapshot", + "ts": 1672304484978, + "data": { + "s": "BTCUSDT", + "b": [ + [ + "16493.50", + "0.006" + ], + [ + "16493.00", + "0.100" + ] + ], + "a": [], + "u": 18521288, + "seq": 7961638724 + } +} \ No newline at end of file diff --git a/tests/integration_tests/adapters/bybit/test_ws_decoders.py b/tests/integration_tests/adapters/bybit/test_ws_decoders.py index 7f8cf9044e0a..f712610a9611 100644 --- a/tests/integration_tests/adapters/bybit/test_ws_decoders.py +++ b/tests/integration_tests/adapters/bybit/test_ws_decoders.py @@ -156,6 +156,44 @@ def test_ws_public_orderbook_delta_parse_to_deltas(self): # Assert assert len(result.deltas) == 12 + assert result.is_snapshot is False + + # Test that only the last delta has a F_LAST flag + for delta_id, delta in enumerate(result.deltas): + if delta_id < len(result.deltas) - 1: + assert delta.flags == 0 + else: + assert delta.flags == 128 + + def test_ws_public_orderbook_delta_parse_to_deltas_no_asks(self): + # Prepare + item = pkgutil.get_data( + "tests.integration_tests.adapters.bybit.resources.ws_messages.public", + "ws_orderbook_delta_no_asks.json", + ) + assert item is not None + instrument_id = InstrumentId(Symbol("BTCUSDT-LINEAR"), Venue("BYBIT")) + decoder = msgspec.json.Decoder(BybitWsOrderbookDepthMsg) + + # Act + result = decoder.decode(item).data.parse_to_deltas( + instrument_id=instrument_id, + price_precision=2, + size_precision=2, + ts_event=0, + ts_init=0, + ) + + # Assert + assert len(result.deltas) == 5 + assert result.is_snapshot is False + + # Test that only the last delta has a F_LAST flag + for delta_id, delta in enumerate(result.deltas): + if delta_id < len(result.deltas) - 1: + assert delta.flags == 0 + else: + assert delta.flags == 128 def test_ws_public_orderbook_snapshot(self): item = pkgutil.get_data( @@ -183,6 +221,66 @@ def test_ws_public_orderbook_snapshot(self): assert result.type == "snapshot" assert result.ts == 1672304484978 + def test_ws_public_orderbook_snapshot_flags(self): + # Prepare + item = pkgutil.get_data( + "tests.integration_tests.adapters.bybit.resources.ws_messages.public", + "ws_orderbook_snapshot.json", + ) + assert item is not None + instrument_id = InstrumentId(Symbol("BTCUSDT-LINEAR"), Venue("BYBIT")) + decoder = msgspec.json.Decoder(BybitWsOrderbookDepthMsg) + + # Act + result = decoder.decode(item).data.parse_to_snapshot( + instrument_id=instrument_id, + price_precision=2, + size_precision=2, + ts_event=0, + ts_init=0, + ) + + # Assert + assert len(result.deltas) == 5 + assert result.is_snapshot + + # Test that only the last delta has a F_LAST flag + for delta_id, delta in enumerate(result.deltas): + if delta_id < len(result.deltas) - 1: + assert delta.flags == 0 + else: + assert delta.flags == 128 + + def test_ws_public_orderbook_snapshot_flags_no_asks(self): + # Prepare + item = pkgutil.get_data( + "tests.integration_tests.adapters.bybit.resources.ws_messages.public", + "ws_orderbook_snapshot_no_asks.json", + ) + assert item is not None + instrument_id = InstrumentId(Symbol("BTCUSDT-LINEAR"), Venue("BYBIT")) + decoder = msgspec.json.Decoder(BybitWsOrderbookDepthMsg) + + # Act + result = decoder.decode(item).data.parse_to_snapshot( + instrument_id=instrument_id, + price_precision=2, + size_precision=2, + ts_event=0, + ts_init=0, + ) + + # Assert + assert len(result.deltas) == 3 + assert result.is_snapshot + + # Test that only the last delta has a F_LAST flag + for delta_id, delta in enumerate(result.deltas): + if delta_id < len(result.deltas) - 1: + assert delta.flags == 0 + else: + assert delta.flags == 128 + def test_ws_public_ticker_linear(self): item = pkgutil.get_data( "tests.integration_tests.adapters.bybit.resources.ws_messages.public", From 0f6fbf12df06638eaf5bfc843d308c7f42e6535e Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sun, 26 May 2024 08:20:20 +1000 Subject: [PATCH 21/59] Update Rust dependencies --- nautilus_core/Cargo.lock | 38 +++++++++++++++++++------------------- nautilus_core/Cargo.toml | 2 +- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/nautilus_core/Cargo.lock b/nautilus_core/Cargo.lock index 8cf8f55d5150..9947d623c4de 100644 --- a/nautilus_core/Cargo.lock +++ b/nautilus_core/Cargo.lock @@ -1637,12 +1637,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" -[[package]] -name = "finl_unicode" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" - [[package]] name = "fixedbitset" version = "0.4.2" @@ -3074,9 +3068,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -3358,9 +3352,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.83" +version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" +checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" dependencies = [ "unicode-ident", ] @@ -4055,18 +4049,18 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" [[package]] name = "serde" -version = "1.0.202" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.202" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", @@ -4523,13 +4517,13 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stringprep" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" dependencies = [ - "finl_unicode", "unicode-bidi", "unicode-normalization", + "unicode-properties", ] [[package]] @@ -5158,6 +5152,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-properties" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" + [[package]] name = "unicode-segmentation" version = "1.11.0" @@ -5656,9 +5656,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] name = "zstd" diff --git a/nautilus_core/Cargo.toml b/nautilus_core/Cargo.toml index 2fb525de83c2..d4d25459a273 100644 --- a/nautilus_core/Cargo.toml +++ b/nautilus_core/Cargo.toml @@ -42,7 +42,7 @@ rmp-serde = "1.3.0" rust_decimal = "1.35.0" rust_decimal_macros = "1.34.2" semver = "1.0.23" -serde = { version = "1.0.202", features = ["derive"] } +serde = { version = "1.0.203", features = ["derive"] } serde_json = "1.0.117" strum = { version = "0.26.2", features = ["derive"] } thiserror = "1.0.61" From e33acd468658340b54818308d32594541cca8c26 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sun, 26 May 2024 08:25:23 +1000 Subject: [PATCH 22/59] Update release notes --- RELEASES.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 995fab0b8a52..75c4029a9a95 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -3,13 +3,16 @@ Released on TBD (UTC). ### Enhancements +- Improved Bybit order book deltas parsing to set `F_LAST` flag (#1670), thanks @davidsblom +- Improved Interactive Brokers integration test mocks (#1669), thanks @rsmb7z - Ported `VolumeWeightedAveragePrice` indicator to Rust (#1665), thanks @Pushkarm029 +- Ported `VerticalHorizontalFilter` indicator to Rust (#1666), thanks @Pushkarm029 ### Breaking Changes None ### Fixes -None +- Fixed Bybit order book deltas parsing (was appending bid side twice) (#1668), thanks @davidsblom --- From 734d3c37ed49c6ccf22581965582b199fefa0d75 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sun, 26 May 2024 08:39:07 +1000 Subject: [PATCH 23/59] Minor formatting --- nautilus_core/indicators/src/momentum/vhf.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/nautilus_core/indicators/src/momentum/vhf.rs b/nautilus_core/indicators/src/momentum/vhf.rs index 888e6089080e..dfe245d9620b 100644 --- a/nautilus_core/indicators/src/momentum/vhf.rs +++ b/nautilus_core/indicators/src/momentum/vhf.rs @@ -121,6 +121,7 @@ impl VerticalHorizontalFilter { } } } + //////////////////////////////////////////////////////////////////////////////// // Tests //////////////////////////////////////////////////////////////////////////////// From 2654ff35eec3366fe9f6bed786744ee4eff139a6 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sun, 26 May 2024 08:48:48 +1000 Subject: [PATCH 24/59] Minor formatting --- nautilus_core/indicators/src/momentum/vhf.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nautilus_core/indicators/src/momentum/vhf.rs b/nautilus_core/indicators/src/momentum/vhf.rs index dfe245d9620b..63ac85dec78f 100644 --- a/nautilus_core/indicators/src/momentum/vhf.rs +++ b/nautilus_core/indicators/src/momentum/vhf.rs @@ -13,10 +13,12 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -use std::fmt::{Debug, Display}; +use std::{ + collections::VecDeque, + fmt::{Debug, Display}, +}; use nautilus_model::data::bar::Bar; -use std::collections::VecDeque; use crate::{ average::{MovingAverageFactory, MovingAverageType}, From 89080b8ba1a12d0b2541f79ca9e917ce6b2af4b1 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sun, 26 May 2024 08:49:51 +1000 Subject: [PATCH 25/59] Consolidate serialization modules --- nautilus_core/core/src/deserialization.rs | 91 ------------------- nautilus_core/core/src/equality.rs | 5 + nautilus_core/core/src/lib.rs | 1 - nautilus_core/core/src/serialization.rs | 81 ++++++++++++++++- .../model/src/events/order/accepted.rs | 2 +- .../model/src/events/order/cancel_rejected.rs | 2 +- .../model/src/events/order/canceled.rs | 2 +- .../model/src/events/order/expired.rs | 2 +- .../model/src/events/order/modify_rejected.rs | 2 +- .../model/src/events/order/pending_cancel.rs | 2 +- .../model/src/events/order/pending_update.rs | 2 +- .../model/src/events/order/rejected.rs | 2 +- .../model/src/events/order/triggered.rs | 2 +- .../model/src/events/order/updated.rs | 2 +- nautilus_core/model/src/identifiers/stubs.rs | 2 + 15 files changed, 97 insertions(+), 103 deletions(-) delete mode 100644 nautilus_core/core/src/deserialization.rs diff --git a/nautilus_core/core/src/deserialization.rs b/nautilus_core/core/src/deserialization.rs deleted file mode 100644 index 0658b551db5b..000000000000 --- a/nautilus_core/core/src/deserialization.rs +++ /dev/null @@ -1,91 +0,0 @@ -// ------------------------------------------------------------------------------------------------- -// 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; - -use serde::{ - de::{Unexpected, Visitor}, - Deserializer, -}; - -struct BoolVisitor; - -impl<'de> Visitor<'de> for BoolVisitor { - type Value = u8; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a boolean as u8") - } - - fn visit_bool(self, value: bool) -> Result - where - E: serde::de::Error, - { - Ok(u8::from(value)) - } - - fn visit_u64(self, value: u64) -> Result - where - E: serde::de::Error, - { - if value > u64::from(u8::MAX) { - Err(E::invalid_value(Unexpected::Unsigned(value), &self)) - } else { - Ok(value as u8) - } - } -} - -pub fn from_bool_as_u8<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - deserializer.deserialize_any(BoolVisitor) -} - -#[cfg(test)] -mod tests { - use serde::Deserialize; - - use super::from_bool_as_u8; - - #[derive(Deserialize)] - pub struct TestStruct { - #[serde(deserialize_with = "from_bool_as_u8")] - pub value: u8, - } - - #[test] - fn test_deserialize_bool_as_u8_with_boolean() { - let json_true = r#"{"value": true}"#; - let test_struct: TestStruct = serde_json::from_str(json_true).unwrap(); - assert_eq!(test_struct.value, 1); - - let json_false = r#"{"value": false}"#; - let test_struct: TestStruct = serde_json::from_str(json_false).unwrap(); - assert_eq!(test_struct.value, 0); - } - - #[test] - fn test_deserialize_bool_as_u8_with_u64() { - let json_true = r#"{"value": 1}"#; - let test_struct: TestStruct = serde_json::from_str(json_true).unwrap(); - assert_eq!(test_struct.value, 1); - - let json_false = r#"{"value": 0}"#; - let test_struct: TestStruct = serde_json::from_str(json_false).unwrap(); - assert_eq!(test_struct.value, 0); - } -} diff --git a/nautilus_core/core/src/equality.rs b/nautilus_core/core/src/equality.rs index 49a078d79d5f..243607704256 100644 --- a/nautilus_core/core/src/equality.rs +++ b/nautilus_core/core/src/equality.rs @@ -13,9 +13,14 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Functions to evaluation total equality of two instances. + use pretty_assertions::assert_eq; use serde::Serialize; +/// Return whether `a` and `b` are entirely equal (all fields). +/// +/// Serializes the input values to JSON and compares the resulting strings. pub fn entirely_equal(a: T, b: T) { let a_serialized = serde_json::to_string(&a).unwrap(); let b_serialized = serde_json::to_string(&b).unwrap(); diff --git a/nautilus_core/core/src/lib.rs b/nautilus_core/core/src/lib.rs index 11c7d9026f6f..62a04051182a 100644 --- a/nautilus_core/core/src/lib.rs +++ b/nautilus_core/core/src/lib.rs @@ -29,7 +29,6 @@ pub mod correctness; pub mod datetime; -pub mod deserialization; pub mod equality; pub mod message; pub mod nanos; diff --git a/nautilus_core/core/src/serialization.rs b/nautilus_core/core/src/serialization.rs index fc1af358a15f..58b8b6211e24 100644 --- a/nautilus_core/core/src/serialization.rs +++ b/nautilus_core/core/src/serialization.rs @@ -13,8 +13,16 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines common serialization traits. +//! Defines common serialization traits and functions. +use std::fmt; + +use serde::{ + de::{Unexpected, Visitor}, + Deserializer, +}; + +struct BoolVisitor; use serde::{Deserialize, Serialize}; /// Represents types which are serializable for JSON and `MsgPack` specifications. @@ -39,3 +47,74 @@ pub trait Serializable: Serialize + for<'de> Deserialize<'de> { rmp_serde::to_vec_named(self) } } + +impl<'de> Visitor<'de> for BoolVisitor { + type Value = u8; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a boolean as u8") + } + + fn visit_bool(self, value: bool) -> Result + where + E: serde::de::Error, + { + Ok(u8::from(value)) + } + + fn visit_u64(self, value: u64) -> Result + where + E: serde::de::Error, + { + if value > u64::from(u8::MAX) { + Err(E::invalid_value(Unexpected::Unsigned(value), &self)) + } else { + Ok(value as u8) + } + } +} + +pub fn from_bool_as_u8<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + deserializer.deserialize_any(BoolVisitor) +} + +//////////////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////////////// +#[cfg(test)] +mod tests { + use serde::Deserialize; + + use super::from_bool_as_u8; + + #[derive(Deserialize)] + pub struct TestStruct { + #[serde(deserialize_with = "from_bool_as_u8")] + pub value: u8, + } + + #[test] + fn test_deserialize_bool_as_u8_with_boolean() { + let json_true = r#"{"value": true}"#; + let test_struct: TestStruct = serde_json::from_str(json_true).unwrap(); + assert_eq!(test_struct.value, 1); + + let json_false = r#"{"value": false}"#; + let test_struct: TestStruct = serde_json::from_str(json_false).unwrap(); + assert_eq!(test_struct.value, 0); + } + + #[test] + fn test_deserialize_bool_as_u8_with_u64() { + let json_true = r#"{"value": 1}"#; + let test_struct: TestStruct = serde_json::from_str(json_true).unwrap(); + assert_eq!(test_struct.value, 1); + + let json_false = r#"{"value": 0}"#; + let test_struct: TestStruct = serde_json::from_str(json_false).unwrap(); + assert_eq!(test_struct.value, 0); + } +} diff --git a/nautilus_core/model/src/events/order/accepted.rs b/nautilus_core/model/src/events/order/accepted.rs index 76fa62d30c0f..1e387a3556e8 100644 --- a/nautilus_core/model/src/events/order/accepted.rs +++ b/nautilus_core/model/src/events/order/accepted.rs @@ -16,7 +16,7 @@ use std::fmt::{Debug, Display}; use derive_builder::Builder; -use nautilus_core::{deserialization::from_bool_as_u8, nanos::UnixNanos, uuid::UUID4}; +use nautilus_core::{nanos::UnixNanos, serialization::from_bool_as_u8, uuid::UUID4}; use serde::{Deserialize, Serialize}; use ustr::Ustr; diff --git a/nautilus_core/model/src/events/order/cancel_rejected.rs b/nautilus_core/model/src/events/order/cancel_rejected.rs index 87e4dbd0a8c1..2fe30fe716d0 100644 --- a/nautilus_core/model/src/events/order/cancel_rejected.rs +++ b/nautilus_core/model/src/events/order/cancel_rejected.rs @@ -16,7 +16,7 @@ use std::fmt::{Debug, Display}; use derive_builder::Builder; -use nautilus_core::{deserialization::from_bool_as_u8, nanos::UnixNanos, uuid::UUID4}; +use nautilus_core::{nanos::UnixNanos, serialization::from_bool_as_u8, uuid::UUID4}; use serde::{Deserialize, Serialize}; use ustr::Ustr; diff --git a/nautilus_core/model/src/events/order/canceled.rs b/nautilus_core/model/src/events/order/canceled.rs index a1e5e9f1793a..8dcd693bb731 100644 --- a/nautilus_core/model/src/events/order/canceled.rs +++ b/nautilus_core/model/src/events/order/canceled.rs @@ -16,7 +16,7 @@ use std::fmt::{Debug, Display}; use derive_builder::Builder; -use nautilus_core::{deserialization::from_bool_as_u8, nanos::UnixNanos, uuid::UUID4}; +use nautilus_core::{nanos::UnixNanos, serialization::from_bool_as_u8, uuid::UUID4}; use serde::{Deserialize, Serialize}; use ustr::Ustr; diff --git a/nautilus_core/model/src/events/order/expired.rs b/nautilus_core/model/src/events/order/expired.rs index 712a2ebe7b03..36c6921f6451 100644 --- a/nautilus_core/model/src/events/order/expired.rs +++ b/nautilus_core/model/src/events/order/expired.rs @@ -16,7 +16,7 @@ use std::fmt::{Debug, Display}; use derive_builder::Builder; -use nautilus_core::{deserialization::from_bool_as_u8, nanos::UnixNanos, uuid::UUID4}; +use nautilus_core::{nanos::UnixNanos, serialization::from_bool_as_u8, uuid::UUID4}; use serde::{Deserialize, Serialize}; use ustr::Ustr; diff --git a/nautilus_core/model/src/events/order/modify_rejected.rs b/nautilus_core/model/src/events/order/modify_rejected.rs index 27c5b41d7af1..2b91df165935 100644 --- a/nautilus_core/model/src/events/order/modify_rejected.rs +++ b/nautilus_core/model/src/events/order/modify_rejected.rs @@ -16,7 +16,7 @@ use std::fmt::{Debug, Display}; use derive_builder::Builder; -use nautilus_core::{deserialization::from_bool_as_u8, nanos::UnixNanos, uuid::UUID4}; +use nautilus_core::{nanos::UnixNanos, serialization::from_bool_as_u8, uuid::UUID4}; use serde::{Deserialize, Serialize}; use ustr::Ustr; diff --git a/nautilus_core/model/src/events/order/pending_cancel.rs b/nautilus_core/model/src/events/order/pending_cancel.rs index 3ba03fdb9972..ebb8a58c6f28 100644 --- a/nautilus_core/model/src/events/order/pending_cancel.rs +++ b/nautilus_core/model/src/events/order/pending_cancel.rs @@ -16,7 +16,7 @@ use std::fmt::{Debug, Display}; use derive_builder::Builder; -use nautilus_core::{deserialization::from_bool_as_u8, nanos::UnixNanos, uuid::UUID4}; +use nautilus_core::{nanos::UnixNanos, serialization::from_bool_as_u8, uuid::UUID4}; use serde::{Deserialize, Serialize}; use ustr::Ustr; diff --git a/nautilus_core/model/src/events/order/pending_update.rs b/nautilus_core/model/src/events/order/pending_update.rs index 53d1fffc4967..8b3e50b00d1a 100644 --- a/nautilus_core/model/src/events/order/pending_update.rs +++ b/nautilus_core/model/src/events/order/pending_update.rs @@ -16,7 +16,7 @@ use std::fmt::{Debug, Display}; use derive_builder::Builder; -use nautilus_core::{deserialization::from_bool_as_u8, nanos::UnixNanos, uuid::UUID4}; +use nautilus_core::{nanos::UnixNanos, serialization::from_bool_as_u8, uuid::UUID4}; use serde::{Deserialize, Serialize}; use ustr::Ustr; diff --git a/nautilus_core/model/src/events/order/rejected.rs b/nautilus_core/model/src/events/order/rejected.rs index 835737527a0f..b7151f31a3cb 100644 --- a/nautilus_core/model/src/events/order/rejected.rs +++ b/nautilus_core/model/src/events/order/rejected.rs @@ -16,7 +16,7 @@ use std::fmt::{Debug, Display}; use derive_builder::Builder; -use nautilus_core::{deserialization::from_bool_as_u8, nanos::UnixNanos, uuid::UUID4}; +use nautilus_core::{nanos::UnixNanos, serialization::from_bool_as_u8, uuid::UUID4}; use serde::{Deserialize, Serialize}; use ustr::Ustr; diff --git a/nautilus_core/model/src/events/order/triggered.rs b/nautilus_core/model/src/events/order/triggered.rs index 3b777f3586da..5b0ddae85140 100644 --- a/nautilus_core/model/src/events/order/triggered.rs +++ b/nautilus_core/model/src/events/order/triggered.rs @@ -16,7 +16,7 @@ use std::fmt::{Debug, Display}; use derive_builder::Builder; -use nautilus_core::{deserialization::from_bool_as_u8, nanos::UnixNanos, uuid::UUID4}; +use nautilus_core::{nanos::UnixNanos, serialization::from_bool_as_u8, uuid::UUID4}; use serde::{Deserialize, Serialize}; use ustr::Ustr; diff --git a/nautilus_core/model/src/events/order/updated.rs b/nautilus_core/model/src/events/order/updated.rs index 986a38964986..f6830cc92c2e 100644 --- a/nautilus_core/model/src/events/order/updated.rs +++ b/nautilus_core/model/src/events/order/updated.rs @@ -16,7 +16,7 @@ use std::fmt::{Debug, Display}; use derive_builder::Builder; -use nautilus_core::{deserialization::from_bool_as_u8, nanos::UnixNanos, uuid::UUID4}; +use nautilus_core::{nanos::UnixNanos, serialization::from_bool_as_u8, uuid::UUID4}; use serde::{Deserialize, Serialize}; use ustr::Ustr; diff --git a/nautilus_core/model/src/identifiers/stubs.rs b/nautilus_core/model/src/identifiers/stubs.rs index 1d295eb26871..bdf30048558a 100644 --- a/nautilus_core/model/src/identifiers/stubs.rs +++ b/nautilus_core/model/src/identifiers/stubs.rs @@ -13,6 +13,8 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- +//! Default implementations and fixture functions to provide stub identifiers for testing. + use nautilus_core::uuid::UUID4; use rstest::fixture; From 8125598275b30a7a107d05e0b3856519b4322c47 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sun, 26 May 2024 08:57:21 +1000 Subject: [PATCH 26/59] Standardize tests --- nautilus_core/core/src/serialization.rs | 31 +++++++++++-------------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/nautilus_core/core/src/serialization.rs b/nautilus_core/core/src/serialization.rs index 58b8b6211e24..ae3e59a875fb 100644 --- a/nautilus_core/core/src/serialization.rs +++ b/nautilus_core/core/src/serialization.rs @@ -86,6 +86,7 @@ where //////////////////////////////////////////////////////////////////////////////// #[cfg(test)] mod tests { + use rstest::*; use serde::Deserialize; use super::from_bool_as_u8; @@ -96,25 +97,19 @@ mod tests { pub value: u8, } - #[test] - fn test_deserialize_bool_as_u8_with_boolean() { - let json_true = r#"{"value": true}"#; - let test_struct: TestStruct = serde_json::from_str(json_true).unwrap(); - assert_eq!(test_struct.value, 1); - - let json_false = r#"{"value": false}"#; - let test_struct: TestStruct = serde_json::from_str(json_false).unwrap(); - assert_eq!(test_struct.value, 0); + #[rstest] + #[case(r#"{"value": true}"#, 1)] + #[case(r#"{"value": false}"#, 0)] + fn test_deserialize_bool_as_u8_with_boolean(#[case] json_str: &str, #[case] expected: u8) { + let test_struct: TestStruct = serde_json::from_str(json_str).unwrap(); + assert_eq!(test_struct.value, expected); } - #[test] - fn test_deserialize_bool_as_u8_with_u64() { - let json_true = r#"{"value": 1}"#; - let test_struct: TestStruct = serde_json::from_str(json_true).unwrap(); - assert_eq!(test_struct.value, 1); - - let json_false = r#"{"value": 0}"#; - let test_struct: TestStruct = serde_json::from_str(json_false).unwrap(); - assert_eq!(test_struct.value, 0); + #[rstest] + #[case(r#"{"value": 1}"#, 1)] + #[case(r#"{"value": 0}"#, 0)] + fn test_deserialize_bool_as_u8_with_u64(#[case] json_str: &str, #[case] expected: u8) { + let test_struct: TestStruct = serde_json::from_str(json_str).unwrap(); + assert_eq!(test_struct.value, expected); } } From cdd8b59b5df221771d5716b7e3a87e183fbcc4a9 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sun, 26 May 2024 08:59:20 +1000 Subject: [PATCH 27/59] Standardize emphasis --- nautilus_core/core/src/correctness.rs | 4 ++-- nautilus_trader/common/config.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/nautilus_core/core/src/correctness.rs b/nautilus_core/core/src/correctness.rs index e298f813d43f..a6a275e4b656 100644 --- a/nautilus_core/core/src/correctness.rs +++ b/nautilus_core/core/src/correctness.rs @@ -196,7 +196,7 @@ pub fn check_slice_empty(slice: &[T], param: &str) -> anyhow::Result<()> { Ok(()) } -/// Checks the slice is *not* empty. +/// Checks the slice is **not** empty. pub fn check_slice_not_empty(slice: &[T], param: &str) -> anyhow::Result<()> { if slice.is_empty() { anyhow::bail!( @@ -219,7 +219,7 @@ pub fn check_map_empty(map: &HashMap, param: &str) -> anyhow::Result Ok(()) } -/// Checks the map is *not* empty. +/// Checks the map is **not** empty. pub fn check_map_not_empty(map: &HashMap, param: &str) -> anyhow::Result<()> { if map.is_empty() { anyhow::bail!( diff --git a/nautilus_trader/common/config.py b/nautilus_trader/common/config.py index df7bc5a2cf05..eb0dfc9750fe 100644 --- a/nautilus_trader/common/config.py +++ b/nautilus_trader/common/config.py @@ -320,7 +320,7 @@ class MessageBusConfig(NautilusConfig, frozen=True): If `use_trader_id` and `use_instance_id` are *both* false, then it becomes possible for many traders to be configured to write to the same streams. types_filter : list[type], optional - A list of serializable types *not* to publish externally. + A list of serializable types **not** to publish externally. """ From d1a49a7318f154d68883e170a5310eacc82abbc5 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sun, 26 May 2024 09:12:12 +1000 Subject: [PATCH 28/59] Standardize Rust docs --- nautilus_core/accounting/src/account/mod.rs | 2 +- nautilus_core/adapters/src/databento/loader.rs | 2 +- nautilus_core/adapters/src/databento/mod.rs | 2 +- nautilus_core/backtest/src/matching_engine.rs | 4 ++-- nautilus_core/common/src/cache/database.rs | 2 +- nautilus_core/common/src/clock.rs | 6 +++--- nautilus_core/common/src/factories.rs | 2 +- nautilus_core/common/src/ffi/clock.rs | 4 ++-- nautilus_core/common/src/ffi/logging.rs | 2 +- nautilus_core/common/src/ffi/mod.rs | 2 +- nautilus_core/common/src/handlers.rs | 2 +- nautilus_core/common/src/logging/logger.rs | 2 +- nautilus_core/common/src/msgbus/core.rs | 2 +- nautilus_core/common/src/msgbus/database.rs | 2 +- nautilus_core/common/src/timer.rs | 6 +++--- nautilus_core/common/src/xrate.rs | 2 +- nautilus_core/core/src/correctness.rs | 2 +- nautilus_core/core/src/datetime.rs | 2 +- nautilus_core/core/src/ffi/mod.rs | 2 +- nautilus_core/core/src/parsing.rs | 2 +- nautilus_core/core/src/time.rs | 6 +++--- nautilus_core/execution/src/client.rs | 2 +- nautilus_core/execution/src/matching_core.rs | 2 +- nautilus_core/model/src/currencies.rs | 2 +- nautilus_core/model/src/data/deltas.rs | 2 +- nautilus_core/model/src/ffi/instruments/synthetic.rs | 2 +- nautilus_core/model/src/ffi/mod.rs | 2 +- nautilus_core/model/src/ffi/orderbook/book.rs | 2 +- nautilus_core/model/src/ffi/orderbook/level.rs | 2 +- nautilus_core/model/src/orderbook/book.rs | 2 +- nautilus_core/model/src/orderbook/mod.rs | 2 +- nautilus_core/model/src/orders/default.rs | 1 - nautilus_core/network/src/http.rs | 4 ++-- nautilus_core/network/src/socket.rs | 2 +- nautilus_core/network/src/websocket.rs | 2 +- nautilus_trader/core/includes/common.h | 10 +++++----- nautilus_trader/core/includes/model.h | 8 ++++---- nautilus_trader/core/rust/common.pxd | 10 +++++----- nautilus_trader/core/rust/model.pxd | 8 ++++---- 39 files changed, 61 insertions(+), 62 deletions(-) diff --git a/nautilus_core/accounting/src/account/mod.rs b/nautilus_core/accounting/src/account/mod.rs index 3aee414fe5f6..62b28ed7a536 100644 --- a/nautilus_core/accounting/src/account/mod.rs +++ b/nautilus_core/accounting/src/account/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Provides account types and accounting functionality. +//! Account implementations and accounting functionality. pub mod base; pub mod cash; diff --git a/nautilus_core/adapters/src/databento/loader.rs b/nautilus_core/adapters/src/databento/loader.rs index 2a9ed687fae7..d4a95ea0dabb 100644 --- a/nautilus_core/adapters/src/databento/loader.rs +++ b/nautilus_core/adapters/src/databento/loader.rs @@ -39,7 +39,7 @@ use super::{ types::{DatabentoImbalance, DatabentoPublisher, DatabentoStatistics, Dataset, PublisherId}, }; -/// Provides a Nautilus data loader for Databento Binary Encoding (DBN) format data. +/// A Nautilus data loader for Databento Binary Encoding (DBN) format data. /// /// # Supported schemas: /// - MBO -> `OrderBookDelta` diff --git a/nautilus_core/adapters/src/databento/mod.rs b/nautilus_core/adapters/src/databento/mod.rs index 70880f3c5581..bf8a79cb1408 100644 --- a/nautilus_core/adapters/src/databento/mod.rs +++ b/nautilus_core/adapters/src/databento/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Provides the [Databento](https://databento.com) integration adapter. +//! The [Databento](https://databento.com) integration adapter. pub mod common; pub mod decode; diff --git a/nautilus_core/backtest/src/matching_engine.rs b/nautilus_core/backtest/src/matching_engine.rs index f196dce29e3c..10da49cc3bb8 100644 --- a/nautilus_core/backtest/src/matching_engine.rs +++ b/nautilus_core/backtest/src/matching_engine.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Provides an `OrderMatchingEngine` for use in research, backtesting and sandbox environments. +//! An `OrderMatchingEngine` for use in research, backtesting and sandbox environments. // Under development #![allow(dead_code)] @@ -55,7 +55,7 @@ pub struct OrderMatchingEngineConfig { pub use_reduce_only: bool, } -/// Provides an order matching engine for a single market. +/// An order matching engine for a single market. pub struct OrderMatchingEngine { /// The venue for the matching engine. pub venue: Venue, diff --git a/nautilus_core/common/src/cache/database.rs b/nautilus_core/common/src/cache/database.rs index cbe093be84e3..d53671b526cf 100644 --- a/nautilus_core/common/src/cache/database.rs +++ b/nautilus_core/common/src/cache/database.rs @@ -79,7 +79,7 @@ impl DatabaseCommand { } } -/// Provides a generic cache database facade. +/// A generic cache database facade. /// /// The main operations take a consistent `key` and `payload` which should provide enough /// information to implement the cache database in many different technologies. diff --git a/nautilus_core/common/src/clock.rs b/nautilus_core/common/src/clock.rs index 5eae76e8d50f..4837d786bffc 100644 --- a/nautilus_core/common/src/clock.rs +++ b/nautilus_core/common/src/clock.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Provides real-time and static test `Clock` implementations. +//! Real-time and static test `Clock` implementations. use std::{collections::HashMap, ops::Deref}; @@ -71,7 +71,7 @@ pub trait Clock { fn cancel_timers(&mut self); } -/// Provides a static test clock. +/// A static test clock. /// /// Stores the current timestamp internally which can be advanced. pub struct TestClock { @@ -266,7 +266,7 @@ impl Clock for TestClock { } } -/// Provides a real-time clock which uses system time. +/// A real-time clock which uses system time. /// /// Timestamps are guaranteed to be unique and monotonically increasing. pub struct LiveClock { diff --git a/nautilus_core/common/src/factories.rs b/nautilus_core/common/src/factories.rs index 66b2291d763c..d63fab6f6ae4 100644 --- a/nautilus_core/common/src/factories.rs +++ b/nautilus_core/common/src/factories.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Provides factories for constructing domain objects such as orders. +//! Factories for constructing domain objects such as orders. use std::collections::HashMap; diff --git a/nautilus_core/common/src/ffi/clock.rs b/nautilus_core/common/src/ffi/clock.rs index adef67b72e1d..2c7af904b57d 100644 --- a/nautilus_core/common/src/ffi/clock.rs +++ b/nautilus_core/common/src/ffi/clock.rs @@ -34,7 +34,7 @@ use crate::{ timer::{TimeEvent, TimeEventHandler}, }; -/// Provides a C compatible Foreign Function Interface (FFI) for an underlying [`TestClock`]. +/// C compatible Foreign Function Interface (FFI) for an underlying [`TestClock`]. /// /// This struct wraps `TestClock` in a way that makes it compatible with C function /// calls, enabling interaction with `TestClock` in a C environment. @@ -243,7 +243,7 @@ pub extern "C" fn test_clock_cancel_timers(clock: &mut TestClock_API) { clock.cancel_timers(); } -/// Provides a C compatible Foreign Function Interface (FFI) for an underlying [`LiveClock`]. +/// C compatible Foreign Function Interface (FFI) for an underlying [`LiveClock`]. /// /// This struct wraps `LiveClock` in a way that makes it compatible with C function /// calls, enabling interaction with `LiveClock` in a C environment. diff --git a/nautilus_core/common/src/ffi/logging.rs b/nautilus_core/common/src/ffi/logging.rs index ea0153155656..ec413a4dee1c 100644 --- a/nautilus_core/common/src/ffi/logging.rs +++ b/nautilus_core/common/src/ffi/logging.rs @@ -37,7 +37,7 @@ use crate::{ }, }; -/// Provides a C compatible Foreign Function Interface (FFI) for an underlying [`LogGuard`]. +/// C compatible Foreign Function Interface (FFI) for an underlying [`LogGuard`]. /// /// This struct wraps `LogGuard` in a way that makes it compatible with C function /// calls, enabling interaction with `LogGuard` in a C environment. diff --git a/nautilus_core/common/src/ffi/mod.rs b/nautilus_core/common/src/ffi/mod.rs index 6ef20a9758be..b8a88091d317 100644 --- a/nautilus_core/common/src/ffi/mod.rs +++ b/nautilus_core/common/src/ffi/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Provides a C foreign function interface (FFI) from `cbindgen`. +//! C foreign function interface (FFI) from `cbindgen`. pub mod clock; pub mod enums; diff --git a/nautilus_core/common/src/handlers.rs b/nautilus_core/common/src/handlers.rs index 1ebbcd39f38d..7ffb7b6e7a42 100644 --- a/nautilus_core/common/src/handlers.rs +++ b/nautilus_core/common/src/handlers.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Provides common message handlers. +//! Common message handlers. #[cfg(not(feature = "python"))] use std::ffi::c_char; diff --git a/nautilus_core/common/src/logging/logger.rs b/nautilus_core/common/src/logging/logger.rs index c419ddfac9aa..a1ff6364a043 100644 --- a/nautilus_core/common/src/logging/logger.rs +++ b/nautilus_core/common/src/logging/logger.rs @@ -148,7 +148,7 @@ impl LoggerConfig { /// configured to filter modules and write up to a specific level only using /// by passing a configuration using the `RUST_LOG` environment variable. -/// Provides a high-performance logger utilizing a MPSC channel under the hood. +/// 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. diff --git a/nautilus_core/common/src/msgbus/core.rs b/nautilus_core/common/src/msgbus/core.rs index 31a92742dd6e..dc4b65e5dd5e 100644 --- a/nautilus_core/common/src/msgbus/core.rs +++ b/nautilus_core/common/src/msgbus/core.rs @@ -110,7 +110,7 @@ impl fmt::Display for BusMessage { } } -/// Provides a generic message bus to facilitate various messaging patterns. +/// A generic message bus to facilitate various messaging patterns. /// /// The bus provides both a producer and consumer API for Pub/Sub, Req/Rep, as /// well as direct point-to-point messaging to registered endpoints. diff --git a/nautilus_core/common/src/msgbus/database.rs b/nautilus_core/common/src/msgbus/database.rs index 2e81e6a18cba..b8a82e091340 100644 --- a/nautilus_core/common/src/msgbus/database.rs +++ b/nautilus_core/common/src/msgbus/database.rs @@ -18,7 +18,7 @@ use std::collections::HashMap; use nautilus_core::uuid::UUID4; use nautilus_model::identifiers::trader_id::TraderId; -/// Provides a generic message bus database facade. +/// A generic message bus database facade. /// /// The main operations take a consistent `key` and `payload` which should provide enough /// information to implement the message bus database in many different technologies. diff --git a/nautilus_core/common/src/timer.rs b/nautilus_core/common/src/timer.rs index 06b3ca623292..9a68f9e9276d 100644 --- a/nautilus_core/common/src/timer.rs +++ b/nautilus_core/common/src/timer.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Provides real-time and test timers for use with `Clock` implementations. +//! Real-time and test timers for use with `Clock` implementations. use std::{ cmp::Ordering, @@ -120,7 +120,7 @@ impl Ord for TimeEventHandler { } } -/// Provides a test timer for user with a `TestClock`. +/// A test timer for user with a `TestClock`. #[derive(Clone, Copy, Debug)] pub struct TestTimer { pub name: Ustr, @@ -222,7 +222,7 @@ impl Iterator for TestTimer { } } -/// Provides a live timer for use with a `LiveClock`. +/// A live timer for use with a `LiveClock`. pub struct LiveTimer { pub name: Ustr, pub interval_ns: NonZeroU64, diff --git a/nautilus_core/common/src/xrate.rs b/nautilus_core/common/src/xrate.rs index 3dade8021426..9d47037673e4 100644 --- a/nautilus_core/common/src/xrate.rs +++ b/nautilus_core/common/src/xrate.rs @@ -18,7 +18,7 @@ // as its not efficient to be allocating so many structures and doing so many recalculations" // **************************************************************************** -//! Provides exchange rate calculations between currencies. +//! Exchange rate calculations between currencies. //! //! An exchange rate is the value of one asset versus that of another. use std::collections::{HashMap, HashSet}; diff --git a/nautilus_core/core/src/correctness.rs b/nautilus_core/core/src/correctness.rs index a6a275e4b656..cbd822f6ddd9 100644 --- a/nautilus_core/core/src/correctness.rs +++ b/nautilus_core/core/src/correctness.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Provides static condition checks similar to the *design by contract* philosophy +//! Functions for static condition checks similar to the *design by contract* philosophy //! to help ensure logical correctness. //! //! This module provides validation checking of function or method conditions. diff --git a/nautilus_core/core/src/datetime.rs b/nautilus_core/core/src/datetime.rs index 9ceb2e75d890..7aaf322a4a45 100644 --- a/nautilus_core/core/src/datetime.rs +++ b/nautilus_core/core/src/datetime.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Provides common data and time functions. +//! Common data and time functions. use std::time::{Duration, UNIX_EPOCH}; diff --git a/nautilus_core/core/src/ffi/mod.rs b/nautilus_core/core/src/ffi/mod.rs index dcd5dcba6e5b..a3f4389f9ae8 100644 --- a/nautilus_core/core/src/ffi/mod.rs +++ b/nautilus_core/core/src/ffi/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Provides a C foreign function interface (FFI) from `cbindgen`. +//! C foreign function interface (FFI) from `cbindgen`. pub mod cvec; pub mod datetime; diff --git a/nautilus_core/core/src/parsing.rs b/nautilus_core/core/src/parsing.rs index 46fe3e8094ac..7e5d55eacb9e 100644 --- a/nautilus_core/core/src/parsing.rs +++ b/nautilus_core/core/src/parsing.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Provides core parsing functions. +//! Core parsing functions. /// Returns the decimal precision inferred from the given string. #[must_use] diff --git a/nautilus_core/core/src/time.rs b/nautilus_core/core/src/time.rs index a03f654f8285..d2b06d3c69fe 100644 --- a/nautilus_core/core/src/time.rs +++ b/nautilus_core/core/src/time.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Provides the core `AtomicTime` real-time and static clocks. +//! The core `AtomicTime` real-time and static clocks. use std::{ ops::Deref, @@ -29,10 +29,10 @@ use crate::{ nanos::UnixNanos, }; -/// Provides a global atomic time in real-time mode for use across the system. +/// 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. +/// 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. diff --git a/nautilus_core/execution/src/client.rs b/nautilus_core/execution/src/client.rs index 396157104811..d4f7135fbb5b 100644 --- a/nautilus_core/execution/src/client.rs +++ b/nautilus_core/execution/src/client.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Provides execution client base functionality. +//! Base execution client functionality. // Under development #![allow(dead_code)] diff --git a/nautilus_core/execution/src/matching_core.rs b/nautilus_core/execution/src/matching_core.rs index d83965e74425..17eaa1db705e 100644 --- a/nautilus_core/execution/src/matching_core.rs +++ b/nautilus_core/execution/src/matching_core.rs @@ -31,7 +31,7 @@ use nautilus_model::{ types::price::Price, }; -/// Provides a generic order matching core. +/// A generic order matching core. pub struct OrderMatchingCore { /// The instrument ID for the matching core. pub instrument_id: InstrumentId, diff --git a/nautilus_core/model/src/currencies.rs b/nautilus_core/model/src/currencies.rs index 19ddc0bae4fe..f7c3ba614b40 100644 --- a/nautilus_core/model/src/currencies.rs +++ b/nautilus_core/model/src/currencies.rs @@ -988,7 +988,7 @@ impl Currency { } } -/// Provides a map of built-in `Currency` constants. +/// A map of built-in `Currency` constants. pub static CURRENCY_MAP: Lazy>> = Lazy::new(|| { let mut map = HashMap::new(); /////////////////////////////////////////////////////////////////////////// diff --git a/nautilus_core/model/src/data/deltas.rs b/nautilus_core/model/src/data/deltas.rs index b0b34dea5cab..702bd80fec68 100644 --- a/nautilus_core/model/src/data/deltas.rs +++ b/nautilus_core/model/src/data/deltas.rs @@ -112,7 +112,7 @@ impl GetTsInit for OrderBookDeltas { } } -/// Provides a C compatible Foreign Function Interface (FFI) for an underlying [`OrderBookDeltas`]. +/// C compatible Foreign Function Interface (FFI) for an underlying [`OrderBookDeltas`]. /// /// This struct wraps `OrderBookDeltas` in a way that makes it compatible with C function /// calls, enabling interaction with `OrderBookDeltas` in a C environment. diff --git a/nautilus_core/model/src/ffi/instruments/synthetic.rs b/nautilus_core/model/src/ffi/instruments/synthetic.rs index 19bef1a27a86..c9a9d28a2967 100644 --- a/nautilus_core/model/src/ffi/instruments/synthetic.rs +++ b/nautilus_core/model/src/ffi/instruments/synthetic.rs @@ -33,7 +33,7 @@ use crate::{ types::price::{Price, ERROR_PRICE}, }; -/// Provides a C compatible Foreign Function Interface (FFI) for an underlying +/// C compatible Foreign Function Interface (FFI) for an underlying /// [`SyntheticInstrument`]. /// /// This struct wraps `SyntheticInstrument` in a way that makes it compatible with C function diff --git a/nautilus_core/model/src/ffi/mod.rs b/nautilus_core/model/src/ffi/mod.rs index c0a94de1cfcb..49f3e6caa9d2 100644 --- a/nautilus_core/model/src/ffi/mod.rs +++ b/nautilus_core/model/src/ffi/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Provides a C foreign function interface (FFI) from `cbindgen`. +//! C foreign function interface (FFI) from `cbindgen`. pub mod data; pub mod enums; diff --git a/nautilus_core/model/src/ffi/orderbook/book.rs b/nautilus_core/model/src/ffi/orderbook/book.rs index f7726e024d92..96a1372f9aa5 100644 --- a/nautilus_core/model/src/ffi/orderbook/book.rs +++ b/nautilus_core/model/src/ffi/orderbook/book.rs @@ -36,7 +36,7 @@ use crate::{ types::{price::Price, quantity::Quantity}, }; -/// Provides a C compatible Foreign Function Interface (FFI) for an underlying `OrderBook`. +/// C compatible Foreign Function Interface (FFI) for an underlying `OrderBook`. /// /// This struct wraps `OrderBook` in a way that makes it compatible with C function /// calls, enabling interaction with `OrderBook` in a C environment. diff --git a/nautilus_core/model/src/ffi/orderbook/level.rs b/nautilus_core/model/src/ffi/orderbook/level.rs index 35cc3e322989..fba03259fef4 100644 --- a/nautilus_core/model/src/ffi/orderbook/level.rs +++ b/nautilus_core/model/src/ffi/orderbook/level.rs @@ -24,7 +24,7 @@ use crate::{ types::price::Price, }; -/// Provides a C compatible Foreign Function Interface (FFI) for an underlying order book[`Level`]. +/// C compatible Foreign Function Interface (FFI) for an underlying order book[`Level`]. /// /// This struct wraps `Level` in a way that makes it compatible with C function /// calls, enabling interaction with `Level` in a C environment. diff --git a/nautilus_core/model/src/orderbook/book.rs b/nautilus_core/model/src/orderbook/book.rs index 7c990de3ab01..d1e86971b4f9 100644 --- a/nautilus_core/model/src/orderbook/book.rs +++ b/nautilus_core/model/src/orderbook/book.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Provides a performant, generic, multi-purpose order book. +//! A performant, generic, multi-purpose order book. use nautilus_core::nanos::UnixNanos; diff --git a/nautilus_core/model/src/orderbook/mod.rs b/nautilus_core/model/src/orderbook/mod.rs index 3e630c374242..fe246463180e 100644 --- a/nautilus_core/model/src/orderbook/mod.rs +++ b/nautilus_core/model/src/orderbook/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Provides order book components which can handle L1/L2/L3 data. +//! Order book components which can handle L1/L2/L3 data. pub mod aggregation; pub mod analysis; diff --git a/nautilus_core/model/src/orders/default.rs b/nautilus_core/model/src/orders/default.rs index 244deb988d01..2a93e51dbb32 100644 --- a/nautilus_core/model/src/orders/default.rs +++ b/nautilus_core/model/src/orders/default.rs @@ -100,7 +100,6 @@ impl Default for LimitIfTouchedOrder { } } -/// Provides a default [`MarketOrder`] used for testing. impl Default for MarketOrder { /// Creates a new default [`MarketOrder`] instance for testing. fn default() -> Self { diff --git a/nautilus_core/network/src/http.rs b/nautilus_core/network/src/http.rs index 8382b7cee640..dc0ea9db1a4a 100644 --- a/nautilus_core/network/src/http.rs +++ b/nautilus_core/network/src/http.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Provides a high-performance HTTP client implementation. +//! A high-performance HTTP client implementation. use std::{ collections::{hash_map::DefaultHasher, HashMap}, @@ -31,7 +31,7 @@ use tracing::trace; use crate::ratelimiter::{clock::MonotonicClock, quota::Quota, RateLimiter}; -/// Provides a high-performance `HttpClient` for HTTP requests. +/// A high-performance `HttpClient` for HTTP requests. /// /// The client is backed by a hyper Client which keeps connections alive and /// can be cloned cheaply. The client also has a list of header fields to diff --git a/nautilus_core/network/src/socket.rs b/nautilus_core/network/src/socket.rs index 9db1bfbde901..ce804e4eba75 100644 --- a/nautilus_core/network/src/socket.rs +++ b/nautilus_core/network/src/socket.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Provides a high-performance raw TCP client implementation with TLS capability. +//! A high-performance raw TCP client implementation with TLS capability. use std::{sync::Arc, time::Duration}; diff --git a/nautilus_core/network/src/websocket.rs b/nautilus_core/network/src/websocket.rs index a0dda5dca218..11c95572e0f8 100644 --- a/nautilus_core/network/src/websocket.rs +++ b/nautilus_core/network/src/websocket.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Provides a high-performance WebSocket client implementation. +//! A high-performance WebSocket client implementation. use std::{str::FromStr, sync::Arc, time::Duration}; diff --git a/nautilus_trader/core/includes/common.h b/nautilus_trader/core/includes/common.h index c88fb06ed657..bec0ae985e2e 100644 --- a/nautilus_trader/core/includes/common.h +++ b/nautilus_trader/core/includes/common.h @@ -194,7 +194,7 @@ typedef enum LogLevel { } LogLevel; /** - * Provides a real-time clock which uses system time. + * A real-time clock which uses system time. * * Timestamps are guaranteed to be unique and monotonically increasing. */ @@ -203,14 +203,14 @@ typedef struct LiveClock LiveClock; typedef struct LogGuard LogGuard; /** - * Provides a static test clock. + * A static test clock. * * Stores the current timestamp internally which can be advanced. */ typedef struct TestClock TestClock; /** - * Provides a C compatible Foreign Function Interface (FFI) for an underlying [`TestClock`]. + * C compatible Foreign Function Interface (FFI) for an underlying [`TestClock`]. * * This struct wraps `TestClock` in a way that makes it compatible with C function * calls, enabling interaction with `TestClock` in a C environment. @@ -224,7 +224,7 @@ typedef struct TestClock_API { } TestClock_API; /** - * Provides a C compatible Foreign Function Interface (FFI) for an underlying [`LiveClock`]. + * C compatible Foreign Function Interface (FFI) for an underlying [`LiveClock`]. * * This struct wraps `LiveClock` in a way that makes it compatible with C function * calls, enabling interaction with `LiveClock` in a C environment. @@ -239,7 +239,7 @@ typedef struct LiveClock_API { } LiveClock_API; /** - * Provides a C compatible Foreign Function Interface (FFI) for an underlying [`LogGuard`]. + * C compatible Foreign Function Interface (FFI) for an underlying [`LogGuard`]. * * This struct wraps `LogGuard` in a way that makes it compatible with C function * calls, enabling interaction with `LogGuard` in a C environment. diff --git a/nautilus_trader/core/includes/model.h b/nautilus_trader/core/includes/model.h index 833b8c2da79e..af999e31a22d 100644 --- a/nautilus_trader/core/includes/model.h +++ b/nautilus_trader/core/includes/model.h @@ -824,7 +824,7 @@ typedef struct OrderBookDelta_t { } OrderBookDelta_t; /** - * Provides a C compatible Foreign Function Interface (FFI) for an underlying [`OrderBookDeltas`]. + * C compatible Foreign Function Interface (FFI) for an underlying [`OrderBookDeltas`]. * * This struct wraps `OrderBookDeltas` in a way that makes it compatible with C function * calls, enabling interaction with `OrderBookDeltas` in a C environment. @@ -1222,7 +1222,7 @@ typedef struct PositionId_t { } PositionId_t; /** - * Provides a C compatible Foreign Function Interface (FFI) for an underlying + * C compatible Foreign Function Interface (FFI) for an underlying * [`SyntheticInstrument`]. * * This struct wraps `SyntheticInstrument` in a way that makes it compatible with C function @@ -1237,7 +1237,7 @@ typedef struct SyntheticInstrument_API { } SyntheticInstrument_API; /** - * Provides a C compatible Foreign Function Interface (FFI) for an underlying `OrderBook`. + * C compatible Foreign Function Interface (FFI) for an underlying `OrderBook`. * * This struct wraps `OrderBook` in a way that makes it compatible with C function * calls, enabling interaction with `OrderBook` in a C environment. @@ -1251,7 +1251,7 @@ typedef struct OrderBook_API { } OrderBook_API; /** - * Provides a C compatible Foreign Function Interface (FFI) for an underlying order book[`Level`]. + * C compatible Foreign Function Interface (FFI) for an underlying order book[`Level`]. * * This struct wraps `Level` in a way that makes it compatible with C function * calls, enabling interaction with `Level` in a C environment. diff --git a/nautilus_trader/core/rust/common.pxd b/nautilus_trader/core/rust/common.pxd index e6abcab2c987..56c506634233 100644 --- a/nautilus_trader/core/rust/common.pxd +++ b/nautilus_trader/core/rust/common.pxd @@ -101,7 +101,7 @@ cdef extern from "../includes/common.h": # The **ERROR** error log level. ERROR # = 40, - # Provides a real-time clock which uses system time. + # A real-time clock which uses system time. # # Timestamps are guaranteed to be unique and monotonically increasing. cdef struct LiveClock: @@ -110,13 +110,13 @@ cdef extern from "../includes/common.h": cdef struct LogGuard: pass - # Provides a static test clock. + # A static test clock. # # Stores the current timestamp internally which can be advanced. cdef struct TestClock: pass - # Provides a C compatible Foreign Function Interface (FFI) for an underlying [`TestClock`]. + # C compatible Foreign Function Interface (FFI) for an underlying [`TestClock`]. # # This struct wraps `TestClock` in a way that makes it compatible with C function # calls, enabling interaction with `TestClock` in a C environment. @@ -127,7 +127,7 @@ cdef extern from "../includes/common.h": cdef struct TestClock_API: TestClock *_0; - # Provides a C compatible Foreign Function Interface (FFI) for an underlying [`LiveClock`]. + # C compatible Foreign Function Interface (FFI) for an underlying [`LiveClock`]. # # This struct wraps `LiveClock` in a way that makes it compatible with C function # calls, enabling interaction with `LiveClock` in a C environment. @@ -139,7 +139,7 @@ cdef extern from "../includes/common.h": cdef struct LiveClock_API: LiveClock *_0; - # Provides a C compatible Foreign Function Interface (FFI) for an underlying [`LogGuard`]. + # C compatible Foreign Function Interface (FFI) for an underlying [`LogGuard`]. # # This struct wraps `LogGuard` in a way that makes it compatible with C function # calls, enabling interaction with `LogGuard` in a C environment. diff --git a/nautilus_trader/core/rust/model.pxd b/nautilus_trader/core/rust/model.pxd index a2b70b8e88da..80dfc24662f3 100644 --- a/nautilus_trader/core/rust/model.pxd +++ b/nautilus_trader/core/rust/model.pxd @@ -451,7 +451,7 @@ cdef extern from "../includes/model.h": # The UNIX timestamp (nanoseconds) when the struct was initialized. uint64_t ts_init; - # Provides a C compatible Foreign Function Interface (FFI) for an underlying [`OrderBookDeltas`]. + # C compatible Foreign Function Interface (FFI) for an underlying [`OrderBookDeltas`]. # # This struct wraps `OrderBookDeltas` in a way that makes it compatible with C function # calls, enabling interaction with `OrderBookDeltas` in a C environment. @@ -695,7 +695,7 @@ cdef extern from "../includes/model.h": cdef struct PositionId_t: char* _0; - # Provides a C compatible Foreign Function Interface (FFI) for an underlying + # C compatible Foreign Function Interface (FFI) for an underlying # [`SyntheticInstrument`]. # # This struct wraps `SyntheticInstrument` in a way that makes it compatible with C function @@ -707,7 +707,7 @@ cdef extern from "../includes/model.h": cdef struct SyntheticInstrument_API: SyntheticInstrument *_0; - # Provides a C compatible Foreign Function Interface (FFI) for an underlying `OrderBook`. + # C compatible Foreign Function Interface (FFI) for an underlying `OrderBook`. # # This struct wraps `OrderBook` in a way that makes it compatible with C function # calls, enabling interaction with `OrderBook` in a C environment. @@ -718,7 +718,7 @@ cdef extern from "../includes/model.h": cdef struct OrderBook_API: OrderBook *_0; - # Provides a C compatible Foreign Function Interface (FFI) for an underlying order book[`Level`]. + # C compatible Foreign Function Interface (FFI) for an underlying order book[`Level`]. # # This struct wraps `Level` in a way that makes it compatible with C function # calls, enabling interaction with `Level` in a C environment. From 4d263440bb7c7f7798d31b3536fc39709112b8f6 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sun, 26 May 2024 09:57:10 +1000 Subject: [PATCH 29/59] Fix BinanceBar streaming feather writing --- RELEASES.md | 1 + .../binance_futures_testnet_market_maker.py | 1 + nautilus_trader/persistence/config.py | 2 +- nautilus_trader/persistence/writer.py | 17 ++++++++++++----- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 75c4029a9a95..0747950a234f 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -13,6 +13,7 @@ None ### Fixes - Fixed Bybit order book deltas parsing (was appending bid side twice) (#1668), thanks @davidsblom +- Fixed `BinanceBar` streaming feather writing (was not setting up writer) --- diff --git a/examples/live/binance/binance_futures_testnet_market_maker.py b/examples/live/binance/binance_futures_testnet_market_maker.py index 0b422259cbac..0621afedbd39 100644 --- a/examples/live/binance/binance_futures_testnet_market_maker.py +++ b/examples/live/binance/binance_futures_testnet_market_maker.py @@ -71,6 +71,7 @@ # snapshot_orders=True, # snapshot_positions=True, # snapshot_positions_interval=5.0, + # streaming=StreamingConfig(catalog_path="catalog"), data_clients={ "BINANCE": BinanceDataClientConfig( api_key=None, # 'BINANCE_API_KEY' env var diff --git a/nautilus_trader/persistence/config.py b/nautilus_trader/persistence/config.py index 67eeb157c8e7..7fb5c4ee188c 100644 --- a/nautilus_trader/persistence/config.py +++ b/nautilus_trader/persistence/config.py @@ -38,7 +38,7 @@ class StreamingConfig(NautilusConfig, frozen=True): If any existing feather files should be replaced. include_types : list[type], optional A list of Arrow serializable types to write. - If this is specified then *only* the included types will be written. + If this is specified then **only** the included types will be written. """ diff --git a/nautilus_trader/persistence/writer.py b/nautilus_trader/persistence/writer.py index c57997268274..ea75d66a1b7e 100644 --- a/nautilus_trader/persistence/writer.py +++ b/nautilus_trader/persistence/writer.py @@ -56,7 +56,7 @@ class StreamingFeatherWriter: If existing files at the given `path` should be replaced. include_types : list[type], optional A list of Arrow serializable types to write. - If this is specified then *only* the included types will be written. + If this is specified then **only** the included types will be written. """ @@ -89,10 +89,9 @@ def __init__( self._writers: dict[str, RecordBatchStreamWriter] = {} self._instrument_writers: dict[tuple[str, str], RecordBatchStreamWriter] = {} self._per_instrument_writers = { - "trade_tick", - "quote_tick", "order_book_delta", - "ticker", + "quote_tick", + "trade_tick", } self._instruments: dict[InstrumentId, Instrument] = {} self._create_writers() @@ -133,6 +132,8 @@ def _create_writer(self, cls: type, table_name: str | None = None) -> None: self._files[table_name] = f self._writers[table_name] = pa.ipc.new_stream(f, schema) + self.logger.info(f"Created writer for table '{table_name}'") + def _create_writers(self) -> None: for cls in self._schemas: self._create_writer(cls=cls) @@ -156,6 +157,8 @@ def _create_instrument_writer(self, cls: type, obj: Any) -> None: self._files[key] = f self._instrument_writers[key] = pa.ipc.new_stream(f, schema) + self.logger.info(f"Created writer for table '{table_name}'") + def _extract_obj_metadata( self, obj: TradeTick | QuoteTick | Bar | OrderBookDelta, @@ -228,9 +231,10 @@ def write(self, obj: object) -> None: # noqa: C901 table += f"_{str(bar.bar_type).lower()}" if table not in self._writers: + self.logger.debug(f"Writer not setup for table '{table}'") if table.startswith("custom_signal"): self._create_writer(cls=cls) - elif table.startswith("bar"): + elif table.startswith(("bar", "binance_bar")): self._create_writer(cls=cls, table_name=table) elif table in self._per_instrument_writers: key = (table, obj.instrument_id.value) # type: ignore @@ -242,13 +246,16 @@ def write(self, obj: object) -> None: # noqa: C901 return else: return + if table in self._per_instrument_writers: writer: RecordBatchStreamWriter = self._instrument_writers[(table, obj.instrument_id.value)] # type: ignore else: writer: RecordBatchStreamWriter = self._writers[table] # type: ignore + serialized = ArrowSerializer.serialize_batch([obj], data_cls=cls) if not serialized: return + try: writer.write_table(serialized) self.check_flush() From d78f17e0648932809b960b15cb844db7d8159b75 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sun, 26 May 2024 10:28:40 +1000 Subject: [PATCH 30/59] Improve error message when no tick scheme --- RELEASES.md | 1 + nautilus_trader/model/instruments/base.pyx | 16 ++++++++++++---- tests/unit_tests/model/test_instrument.py | 22 ++++++++++++++++++++++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 0747950a234f..26cb109a9bf6 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -5,6 +5,7 @@ Released on TBD (UTC). ### Enhancements - Improved Bybit order book deltas parsing to set `F_LAST` flag (#1670), thanks @davidsblom - Improved Interactive Brokers integration test mocks (#1669), thanks @rsmb7z +- Improved error message when no tick scheme initialized for an instrument, thanks for reporting @VeraLyu - Ported `VolumeWeightedAveragePrice` indicator to Rust (#1665), thanks @Pushkarm029 - Ported `VerticalHorizontalFilter` indicator to Rust (#1666), thanks @Pushkarm029 diff --git a/nautilus_trader/model/instruments/base.pyx b/nautilus_trader/model/instruments/base.pyx index 982799a57d24..1d84167f9d3a 100644 --- a/nautilus_trader/model/instruments/base.pyx +++ b/nautilus_trader/model/instruments/base.pyx @@ -450,10 +450,14 @@ cdef class Instrument(Data): Raises ------ ValueError - If tick scheme is not registered. + If a tick scheme is not initialized. """ - Condition.not_none(self._tick_scheme, "self._tick_scheme") + if self._tick_scheme is None: + raise ValueError( + f"No tick scheme for instrument {self.id.to_str()}. " + "You can specify a tick scheme by passing a `tick_scheme_name` at initialization." + ) return self._tick_scheme.next_bid_price(value=value, n=num_ticks) @@ -477,10 +481,14 @@ cdef class Instrument(Data): Raises ------ ValueError - If tick scheme is not registered. + If a tick scheme is not initialized. """ - Condition.not_none(self._tick_scheme, "self._tick_scheme") + if self._tick_scheme is None: + raise ValueError( + f"No tick scheme for instrument {self.id.to_str()}. " + "You can specify a tick scheme by passing a `tick_scheme_name` at initialization." + ) return self._tick_scheme.next_ask_price(value=value, n=num_ticks) diff --git a/tests/unit_tests/model/test_instrument.py b/tests/unit_tests/model/test_instrument.py index f62b935a0997..bf91a7e3991d 100644 --- a/tests/unit_tests/model/test_instrument.py +++ b/tests/unit_tests/model/test_instrument.py @@ -443,6 +443,28 @@ def test_calculate_base_quantity_audusd(self): # Assert assert result == Quantity.from_str("1250") + def test_next_bid_price_when_no_tick_scheme(self): + # Arrange, Act + with pytest.raises(ValueError) as exc_info: + BTCUSDT_BINANCE.next_bid_price(100_000.0) + + # Assert + assert ( + str(exc_info.value) + == "No tick scheme for instrument BTCUSDT.BINANCE. You can specify a tick scheme by passing a `tick_scheme_name` at initialization." + ) + + def test_next_ask_price_when_no_tick_scheme(self): + # Arrange, Act + with pytest.raises(ValueError) as exc_info: + BTCUSDT_BINANCE.next_ask_price(100_000.0) + + # Assert + assert ( + str(exc_info.value) + == "No tick scheme for instrument BTCUSDT.BINANCE. You can specify a tick scheme by passing a `tick_scheme_name` at initialization." + ) + @pytest.mark.parametrize( ("instrument", "value", "n", "expected"), [ From fa100cdd9a526d0e156449aae79afce8475c3157 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sun, 26 May 2024 11:23:30 +1000 Subject: [PATCH 31/59] Use RecordFlag enum for F_LAST --- nautilus_trader/adapters/bybit/schemas/ws.py | 9 +++++---- .../integration_tests/adapters/bybit/test_ws_decoders.py | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/nautilus_trader/adapters/bybit/schemas/ws.py b/nautilus_trader/adapters/bybit/schemas/ws.py index d6f0ab90a433..b180a7559738 100644 --- a/nautilus_trader/adapters/bybit/schemas/ws.py +++ b/nautilus_trader/adapters/bybit/schemas/ws.py @@ -40,6 +40,7 @@ from nautilus_trader.model.data import TradeTick from nautilus_trader.model.enums import AggressorSide from nautilus_trader.model.enums import OrderSide +from nautilus_trader.model.enums import RecordFlag from nautilus_trader.model.identifiers import AccountId from nautilus_trader.model.identifiers import ClientOrderId from nautilus_trader.model.identifiers import InstrumentId @@ -181,7 +182,7 @@ def parse_to_snapshot( if bid_id == num_bids_raw - 1 and num_asks_raw == 0: # F_LAST, 1 << 7 # Last message in the packet from the venue for a given `instrument_id` - flags = 128 + flags = RecordFlag.F_LAST delta = parse_bybit_delta( instrument_id=instrument_id, @@ -202,7 +203,7 @@ def parse_to_snapshot( if ask_id == num_asks_raw - 1: # F_LAST, 1 << 7 # Last message in the packet from the venue for a given `instrument_id` - flags = 128 + flags = RecordFlag.F_LAST delta = parse_bybit_delta( instrument_id=instrument_id, @@ -251,7 +252,7 @@ def parse_to_deltas( if bid_id == num_bids_raw - 1 and num_asks_raw == 0: # F_LAST, 1 << 7 # Last message in the packet from the venue for a given `instrument_id` - flags = 128 + flags = RecordFlag.F_LAST delta = parse_bybit_delta( instrument_id=instrument_id, @@ -272,7 +273,7 @@ def parse_to_deltas( if ask_id == num_asks_raw - 1: # F_LAST, 1 << 7 # Last message in the packet from the venue for a given `instrument_id` - flags = 128 + flags = RecordFlag.F_LAST delta = parse_bybit_delta( instrument_id=instrument_id, diff --git a/tests/integration_tests/adapters/bybit/test_ws_decoders.py b/tests/integration_tests/adapters/bybit/test_ws_decoders.py index f712610a9611..7e4046fdb4bf 100644 --- a/tests/integration_tests/adapters/bybit/test_ws_decoders.py +++ b/tests/integration_tests/adapters/bybit/test_ws_decoders.py @@ -49,6 +49,7 @@ from nautilus_trader.adapters.bybit.schemas.ws import BybitWsTickerSpotMsg from nautilus_trader.adapters.bybit.schemas.ws import BybitWsTrade from nautilus_trader.adapters.bybit.schemas.ws import BybitWsTradeMsg +from nautilus_trader.model.enums import RecordFlag from nautilus_trader.model.identifiers import InstrumentId from nautilus_trader.model.identifiers import Symbol from nautilus_trader.model.identifiers import Venue @@ -163,7 +164,7 @@ def test_ws_public_orderbook_delta_parse_to_deltas(self): if delta_id < len(result.deltas) - 1: assert delta.flags == 0 else: - assert delta.flags == 128 + assert delta.flags == RecordFlag.F_LAST def test_ws_public_orderbook_delta_parse_to_deltas_no_asks(self): # Prepare @@ -193,7 +194,7 @@ def test_ws_public_orderbook_delta_parse_to_deltas_no_asks(self): if delta_id < len(result.deltas) - 1: assert delta.flags == 0 else: - assert delta.flags == 128 + assert delta.flags == RecordFlag.F_LAST def test_ws_public_orderbook_snapshot(self): item = pkgutil.get_data( @@ -249,7 +250,7 @@ def test_ws_public_orderbook_snapshot_flags(self): if delta_id < len(result.deltas) - 1: assert delta.flags == 0 else: - assert delta.flags == 128 + assert delta.flags == RecordFlag.F_LAST def test_ws_public_orderbook_snapshot_flags_no_asks(self): # Prepare @@ -279,7 +280,7 @@ def test_ws_public_orderbook_snapshot_flags_no_asks(self): if delta_id < len(result.deltas) - 1: assert delta.flags == 0 else: - assert delta.flags == 128 + assert delta.flags == RecordFlag.F_LAST def test_ws_public_ticker_linear(self): item = pkgutil.get_data( From 6519e1d1af4cb43f3a77476760ee98f07599c912 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sun, 26 May 2024 11:30:13 +1000 Subject: [PATCH 32/59] Clarify F_LAST description --- nautilus_core/adapters/src/databento/live.rs | 2 +- nautilus_core/model/src/enums.rs | 2 +- nautilus_trader/core/includes/model.h | 2 +- nautilus_trader/core/rust/model.pxd | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/nautilus_core/adapters/src/databento/live.rs b/nautilus_core/adapters/src/databento/live.rs index ea93e9aa0595..da2700b4e090 100644 --- a/nautilus_core/adapters/src/databento/live.rs +++ b/nautilus_core/adapters/src/databento/live.rs @@ -265,7 +265,7 @@ impl DatabentoFeedHandler { msg.flags.raw(), ); - // Check if last message in the packet + // Check if last message in the book event if !RecordFlag::F_LAST.matches(msg.flags.raw()) { continue; // NOT last message } diff --git a/nautilus_core/model/src/enums.rs b/nautilus_core/model/src/enums.rs index 7a856482659f..cd37253da023 100644 --- a/nautilus_core/model/src/enums.rs +++ b/nautilus_core/model/src/enums.rs @@ -932,7 +932,7 @@ pub enum PriceType { )] #[allow(non_camel_case_types)] pub enum RecordFlag { - /// Last message in the packet from the venue for a given `instrument_id`. + /// Last message in the book event or packet from the venue for a given `instrument_id`. F_LAST = 1 << 7, // 128 /// Top-of-book message, not an individual order. F_TOB = 1 << 6, // 64 diff --git a/nautilus_trader/core/includes/model.h b/nautilus_trader/core/includes/model.h index af999e31a22d..2e89d962a401 100644 --- a/nautilus_trader/core/includes/model.h +++ b/nautilus_trader/core/includes/model.h @@ -547,7 +547,7 @@ typedef enum PriceType { */ typedef enum RecordFlag { /** - * Last message in the packet from the venue for a given `instrument_id`. + * Last message in the book event or packet from the venue for a given `instrument_id`. */ F_LAST = (1 << 7), /** diff --git a/nautilus_trader/core/rust/model.pxd b/nautilus_trader/core/rust/model.pxd index 80dfc24662f3..e7e4a353703f 100644 --- a/nautilus_trader/core/rust/model.pxd +++ b/nautilus_trader/core/rust/model.pxd @@ -296,7 +296,7 @@ cdef extern from "../includes/model.h": # A record flag bit field, indicating event end and data information. cpdef enum RecordFlag: - # Last message in the packet from the venue for a given `instrument_id`. + # Last message in the book event or packet from the venue for a given `instrument_id`. F_LAST # = (1 << 7), # Top-of-book message, not an individual order. F_TOB # = (1 << 6), From 6aa204600d5f00b25537abbc4b1c0b94768732f1 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sun, 26 May 2024 16:26:05 +1000 Subject: [PATCH 33/59] Update Rust docs --- nautilus_core/adapters/src/databento/enums.rs | 2 +- nautilus_core/common/src/enums.rs | 2 +- nautilus_core/core/src/message.rs | 2 +- nautilus_core/core/src/nanos.rs | 2 +- nautilus_core/core/src/serialization.rs | 2 +- nautilus_core/core/src/uuid.rs | 2 +- nautilus_core/execution/src/messages/mod.rs | 2 +- nautilus_core/indicators/src/indicator.rs | 2 +- nautilus_core/model/src/data/mod.rs | 2 +- nautilus_core/model/src/enums.rs | 2 +- nautilus_core/model/src/events/mod.rs | 2 +- nautilus_core/model/src/identifiers/mod.rs | 2 +- nautilus_core/model/src/instruments/mod.rs | 2 +- nautilus_core/model/src/orders/mod.rs | 2 +- nautilus_core/model/src/polymorphism.rs | 2 +- nautilus_core/model/src/position.rs | 2 +- nautilus_core/model/src/python/data/mod.rs | 2 +- nautilus_core/model/src/python/enums.rs | 2 +- nautilus_core/model/src/python/events/mod.rs | 2 +- nautilus_core/model/src/python/identifiers/mod.rs | 2 +- nautilus_core/model/src/python/instruments/mod.rs | 2 +- nautilus_core/model/src/python/types/mod.rs | 2 +- nautilus_core/model/src/types/mod.rs | 2 +- 23 files changed, 23 insertions(+), 23 deletions(-) diff --git a/nautilus_core/adapters/src/databento/enums.rs b/nautilus_core/adapters/src/databento/enums.rs index 0e560533f2c4..5cc16dc275e5 100644 --- a/nautilus_core/adapters/src/databento/enums.rs +++ b/nautilus_core/adapters/src/databento/enums.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines enumerations for the Databento integration. +//! Enumerations for the Databento integration. use std::str::FromStr; diff --git a/nautilus_core/common/src/enums.rs b/nautilus_core/common/src/enums.rs index 4a302e786bcc..677502e3a889 100644 --- a/nautilus_core/common/src/enums.rs +++ b/nautilus_core/common/src/enums.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines common enumerations. +//! Enumerations for common components. use std::fmt::Debug; diff --git a/nautilus_core/core/src/message.rs b/nautilus_core/core/src/message.rs index 97f5a89d7489..9d2d5163470d 100644 --- a/nautilus_core/core/src/message.rs +++ b/nautilus_core/core/src/message.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines common message types. +//! Common message types. use crate::{nanos::UnixNanos, uuid::UUID4}; diff --git a/nautilus_core/core/src/nanos.rs b/nautilus_core/core/src/nanos.rs index fb32ba6ef406..54980302535d 100644 --- a/nautilus_core/core/src/nanos.rs +++ b/nautilus_core/core/src/nanos.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines `UnixNanos` type for working with UNIX epoch (nanoseconds). +//! A `UnixNanos` type for working with UNIX epoch (nanoseconds). use std::{ cmp::Ordering, diff --git a/nautilus_core/core/src/serialization.rs b/nautilus_core/core/src/serialization.rs index ae3e59a875fb..07483c2e4197 100644 --- a/nautilus_core/core/src/serialization.rs +++ b/nautilus_core/core/src/serialization.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines common serialization traits and functions. +//! Common serialization traits and functions. use std::fmt; diff --git a/nautilus_core/core/src/uuid.rs b/nautilus_core/core/src/uuid.rs index 795dc3b1df64..3e9ede04fc7c 100644 --- a/nautilus_core/core/src/uuid.rs +++ b/nautilus_core/core/src/uuid.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! A core `UUID4` universally unique identifier (UUID) version 4 based on a 128-bit +//! A `UUID4` universally unique identifier (UUID) version 4 based on a 128-bit //! label (RFC 4122). use std::{ diff --git a/nautilus_core/execution/src/messages/mod.rs b/nautilus_core/execution/src/messages/mod.rs index c4f34ebd76d4..96dec2e4c7cb 100644 --- a/nautilus_core/execution/src/messages/mod.rs +++ b/nautilus_core/execution/src/messages/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines execution specific messages such as order commands. +//! Execution specific messages such as order commands. use nautilus_model::identifiers::{client_id::ClientId, instrument_id::InstrumentId}; use strum::Display; diff --git a/nautilus_core/indicators/src/indicator.rs b/nautilus_core/indicators/src/indicator.rs index 99b6d73020b8..dabc96a6a9b5 100644 --- a/nautilus_core/indicators/src/indicator.rs +++ b/nautilus_core/indicators/src/indicator.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines a common `Indicator` trait. +//! A common `Indicator` trait. use std::{fmt, fmt::Debug}; diff --git a/nautilus_core/model/src/data/mod.rs b/nautilus_core/model/src/data/mod.rs index 451abf6aae71..7d4936f772a4 100644 --- a/nautilus_core/model/src/data/mod.rs +++ b/nautilus_core/model/src/data/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines `Data` types for the trading domain model. +//! Data types for the trading domain model. pub mod bar; pub mod delta; diff --git a/nautilus_core/model/src/enums.rs b/nautilus_core/model/src/enums.rs index cd37253da023..802ccb9b242e 100644 --- a/nautilus_core/model/src/enums.rs +++ b/nautilus_core/model/src/enums.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines enumerations for the trading domain model. +//! Enumerations for the trading domain model. use std::str::FromStr; diff --git a/nautilus_core/model/src/events/mod.rs b/nautilus_core/model/src/events/mod.rs index bc27a5283ee9..8ebb3e74093f 100644 --- a/nautilus_core/model/src/events/mod.rs +++ b/nautilus_core/model/src/events/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines order, position and account events for the trading domain model. +//! Events for the trading domain model. pub mod account; pub mod order; diff --git a/nautilus_core/model/src/identifiers/mod.rs b/nautilus_core/model/src/identifiers/mod.rs index 2439bb019933..52b0a2fd2d8e 100644 --- a/nautilus_core/model/src/identifiers/mod.rs +++ b/nautilus_core/model/src/identifiers/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines identifiers for the trading domain models. +//! Identifiers for the trading domain model. use std::str::FromStr; diff --git a/nautilus_core/model/src/instruments/mod.rs b/nautilus_core/model/src/instruments/mod.rs index 1e0dd6340097..64b8ca02dedf 100644 --- a/nautilus_core/model/src/instruments/mod.rs +++ b/nautilus_core/model/src/instruments/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines instrument definitions for the trading domain models. +//! Instrument definitions for the trading domain model. pub mod any; pub mod crypto_future; diff --git a/nautilus_core/model/src/orders/mod.rs b/nautilus_core/model/src/orders/mod.rs index b0b48809ff43..6d1746a0478e 100644 --- a/nautilus_core/model/src/orders/mod.rs +++ b/nautilus_core/model/src/orders/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines order types for the trading domain model. +//! Order types for the trading domain model. #![allow(dead_code)] diff --git a/nautilus_core/model/src/polymorphism.rs b/nautilus_core/model/src/polymorphism.rs index 61ff2972fbf5..9089c9b8013d 100644 --- a/nautilus_core/model/src/polymorphism.rs +++ b/nautilus_core/model/src/polymorphism.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines traits to faciliate polymorphism. +//! Traits to faciliate polymorphism. use nautilus_core::nanos::UnixNanos; diff --git a/nautilus_core/model/src/position.rs b/nautilus_core/model/src/position.rs index efc51bec4015..ccedca93238d 100644 --- a/nautilus_core/model/src/position.rs +++ b/nautilus_core/model/src/position.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines a `Position` for the trading domain model. +//! A `Position` for the trading domain model. use std::{ collections::{HashMap, HashSet}, diff --git a/nautilus_core/model/src/python/data/mod.rs b/nautilus_core/model/src/python/data/mod.rs index a9293ad58f9e..61a77d3eb0ff 100644 --- a/nautilus_core/model/src/python/data/mod.rs +++ b/nautilus_core/model/src/python/data/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines data types for the trading domain model. +//! Data types for the trading domain model. pub mod bar; pub mod delta; diff --git a/nautilus_core/model/src/python/enums.rs b/nautilus_core/model/src/python/enums.rs index 0fbce073b174..fbca1f2fdfdb 100644 --- a/nautilus_core/model/src/python/enums.rs +++ b/nautilus_core/model/src/python/enums.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines enumerations for the trading domain model. +//! Enumerations for the trading domain model. use std::str::FromStr; diff --git a/nautilus_core/model/src/python/events/mod.rs b/nautilus_core/model/src/python/events/mod.rs index 816b152aa56f..1e86eda35b56 100644 --- a/nautilus_core/model/src/python/events/mod.rs +++ b/nautilus_core/model/src/python/events/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines events for the trading domain model. +//! Events for the trading domain model. pub mod account; pub mod order; diff --git a/nautilus_core/model/src/python/identifiers/mod.rs b/nautilus_core/model/src/python/identifiers/mod.rs index d652eceaa48e..f891e5e96b01 100644 --- a/nautilus_core/model/src/python/identifiers/mod.rs +++ b/nautilus_core/model/src/python/identifiers/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines identifiers the trading domain model. +//! Identifiers for the trading domain model. use std::str::FromStr; diff --git a/nautilus_core/model/src/python/instruments/mod.rs b/nautilus_core/model/src/python/instruments/mod.rs index a3dc1560ddff..9a1c6f05d499 100644 --- a/nautilus_core/model/src/python/instruments/mod.rs +++ b/nautilus_core/model/src/python/instruments/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines instrument definitions the trading domain model. +//! Instrument definitions the trading domain model. use nautilus_core::python::to_pyvalue_err; use pyo3::{IntoPy, PyObject, PyResult, Python}; diff --git a/nautilus_core/model/src/python/types/mod.rs b/nautilus_core/model/src/python/types/mod.rs index 1fa3ba707554..8310e7fde6c2 100644 --- a/nautilus_core/model/src/python/types/mod.rs +++ b/nautilus_core/model/src/python/types/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines value types such as `Price`, `Quantity` and `Money` for the trading domain model. +//! Value types such as `Price`, `Quantity` and `Money` for the trading domain model. pub mod balance; pub mod currency; diff --git a/nautilus_core/model/src/types/mod.rs b/nautilus_core/model/src/types/mod.rs index 3d34dffa31c5..830d7e86e549 100644 --- a/nautilus_core/model/src/types/mod.rs +++ b/nautilus_core/model/src/types/mod.rs @@ -13,7 +13,7 @@ // limitations under the License. // ------------------------------------------------------------------------------------------------- -//! Defines value types for the trading domain model such as `Price`, `Quantity` and `Money`. +//! Value types for the trading domain model such as `Price`, `Quantity` and `Money`. pub mod balance; pub mod currency; From 3e9f65f589b57c9fce476c357c5c049aedef715e Mon Sep 17 00:00:00 2001 From: David Blom Date: Sun, 26 May 2024 22:21:32 +0200 Subject: [PATCH 34/59] Improve Bybit handling for top-of-book quotes and deltas (#1672) --- nautilus_trader/adapters/bybit/data.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/nautilus_trader/adapters/bybit/data.py b/nautilus_trader/adapters/bybit/data.py index c4e07b0f8d8d..9af05a415d4d 100644 --- a/nautilus_trader/adapters/bybit/data.py +++ b/nautilus_trader/adapters/bybit/data.py @@ -291,14 +291,15 @@ async def _subscribe_order_book_deltas( if instrument_id in self._tob_quotes: if depth == 1: - self._log.debug( + self._log.warning( f"Already subscribed to {instrument_id} top-of-book", LogColor.MAGENTA, ) return # Already subscribed - raise RuntimeError( - "Cannot subscribe to both top-of-book quotes and order book", - ) + + if instrument_id in self._depths: + self._log.warning(f"Already subscribed to {instrument_id} order book deltas") + return self._depths[instrument_id] = depth ws_client = self._ws_clients[bybit_symbol.product_type] @@ -576,7 +577,7 @@ def _handle_ws_message(self, product_type: BybitProductType, raw: bytes) -> None return if "orderbook" in ws_message.topic: - self._handle_orderbook(product_type, raw) + self._handle_orderbook(product_type, raw, ws_message.topic) elif "publicTrade" in ws_message.topic: self._handle_trade(product_type, raw) elif "tickers" in ws_message.topic: @@ -588,7 +589,7 @@ def _handle_ws_message(self, product_type: BybitProductType, raw: bytes) -> None except Exception as e: self._log.error(f"Failed to parse websocket message: {raw.decode()} with error {e}") - def _handle_orderbook(self, product_type: BybitProductType, raw: bytes) -> None: + def _handle_orderbook(self, product_type: BybitProductType, raw: bytes, topic: str) -> None: msg = self._decoder_ws_orderbook.decode(raw) symbol = msg.data.s + f"-{product_type.value.upper()}" instrument_id: InstrumentId = self._get_cached_instrument_id(symbol) @@ -598,7 +599,7 @@ def _handle_orderbook(self, product_type: BybitProductType, raw: bytes) -> None: self._log.error(f"Cannot parse order book data: no instrument for {instrument_id}") return - if instrument_id in self._tob_quotes: + if instrument_id in self._tob_quotes and topic.startswith("orderbook.1."): quote = msg.data.parse_to_quote_tick( instrument_id=instrument_id, last_quote=self._last_quotes.get(instrument_id), From 29d7212871658caf73e28f33fba0f09efc3d0efb Mon Sep 17 00:00:00 2001 From: David Blom Date: Sun, 26 May 2024 22:26:44 +0200 Subject: [PATCH 35/59] Add DataEngine deltas buffering until F_LAST flag (#1673) --- nautilus_trader/data/config.py | 3 + nautilus_trader/data/engine.pxd | 2 + nautilus_trader/data/engine.pyx | 90 ++++++-- nautilus_trader/test_kit/stubs/data.py | 3 +- tests/unit_tests/backtest/test_config.py | 2 +- tests/unit_tests/data/test_engine.py | 267 +++++++++++++++++++++++ 6 files changed, 349 insertions(+), 18 deletions(-) diff --git a/nautilus_trader/data/config.py b/nautilus_trader/data/config.py index c7ca7da07e85..593e8bb9f034 100644 --- a/nautilus_trader/data/config.py +++ b/nautilus_trader/data/config.py @@ -35,6 +35,8 @@ class DataEngineConfig(NautilusConfig, frozen=True): - 'right-open': start time is included and end time is excluded. validate_data_sequence : bool, default False If data objects timestamp sequencing will be validated and handled. + buffer_deltas : bool, default False + Buffer deltas until the F_LAST flag is set for a delta debug : bool, default False If debug mode is active (will provide extra debug logging). @@ -44,4 +46,5 @@ class DataEngineConfig(NautilusConfig, frozen=True): time_bars_timestamp_on_close: bool = True time_bars_interval_type: str = "left-open" validate_data_sequence: bool = False + buffer_deltas: bool = False debug: bool = False diff --git a/nautilus_trader/data/engine.pxd b/nautilus_trader/data/engine.pxd index 6f350633bfd1..7410ba08caf9 100644 --- a/nautilus_trader/data/engine.pxd +++ b/nautilus_trader/data/engine.pxd @@ -57,10 +57,12 @@ cdef class DataEngine(Component): cdef readonly dict[InstrumentId, list[SyntheticInstrument]] _synthetic_trade_feeds cdef readonly list[InstrumentId] _subscribed_synthetic_quotes cdef readonly list[InstrumentId] _subscribed_synthetic_trades + cdef readonly dict[InstrumentId, list[OrderBookDelta]] _buffer_deltas_map cdef readonly bint _time_bars_build_with_no_updates cdef readonly bint _time_bars_timestamp_on_close cdef readonly str _time_bars_interval_type cdef readonly bint _validate_data_sequence + cdef readonly bint _buffer_deltas cdef readonly bint debug """If debug mode is active (will provide extra debug logging).\n\n:returns: `bool`""" diff --git a/nautilus_trader/data/engine.pyx b/nautilus_trader/data/engine.pyx index 5762d901c569..ac1ea67c4f85 100644 --- a/nautilus_trader/data/engine.pyx +++ b/nautilus_trader/data/engine.pyx @@ -89,6 +89,7 @@ from nautilus_trader.model.instruments.base cimport Instrument from nautilus_trader.model.instruments.synthetic cimport SyntheticInstrument from nautilus_trader.model.objects cimport Price from nautilus_trader.model.objects cimport Quantity +from nautilus_trader.model.enums import RecordFlag cdef class DataEngine(Component): @@ -137,6 +138,7 @@ cdef class DataEngine(Component): self._synthetic_trade_feeds: dict[InstrumentId, list[SyntheticInstrument]] = {} self._subscribed_synthetic_quotes: list[InstrumentId] = [] self._subscribed_synthetic_trades: list[InstrumentId] = [] + self._buffer_deltas_map: dict[InstrumentId, list[OrderBookDelta]] = {} # Settings self.debug = config.debug @@ -144,6 +146,7 @@ cdef class DataEngine(Component): self._time_bars_timestamp_on_close = config.time_bars_timestamp_on_close self._time_bars_interval_type = config.time_bars_interval_type self._validate_data_sequence = config.validate_data_sequence + self._buffer_deltas = config.buffer_deltas # Counters self.command_count = 0 @@ -1374,24 +1377,79 @@ cdef class DataEngine(Component): ) cpdef void _handle_order_book_delta(self, OrderBookDelta delta): - cdef OrderBookDeltas deltas = OrderBookDeltas( - instrument_id=delta.instrument_id, - deltas=[delta] - ) - self._msgbus.publish_c( - topic=f"data.book.deltas" - f".{deltas.instrument_id.venue}" - f".{deltas.instrument_id.symbol}", - msg=deltas, - ) + cdef OrderBookDeltas deltas = None + cdef list[OrderBookDelta] buffer_deltas = None + cdef bint is_last_delta = False + + if self._buffer_deltas: + buffer_deltas = self._buffer_deltas_map.get(delta.instrument_id) + + if buffer_deltas is None: + buffer_deltas = [] + self._buffer_deltas_map[delta.instrument_id] = buffer_deltas + + buffer_deltas.append(delta) + is_last_delta = delta.flags == RecordFlag.F_LAST + + if is_last_delta: + deltas = OrderBookDeltas( + instrument_id=delta.instrument_id, + deltas=buffer_deltas + ) + self._msgbus.publish_c( + topic=f"data.book.deltas" + f".{deltas.instrument_id.venue}" + f".{deltas.instrument_id.symbol}", + msg=deltas, + ) + buffer_deltas.clear() + else: + deltas = OrderBookDeltas( + instrument_id=delta.instrument_id, + deltas=[delta] + ) + self._msgbus.publish_c( + topic=f"data.book.deltas" + f".{deltas.instrument_id.venue}" + f".{deltas.instrument_id.symbol}", + msg=deltas, + ) cpdef void _handle_order_book_deltas(self, OrderBookDeltas deltas): - self._msgbus.publish_c( - topic=f"data.book.deltas" - f".{deltas.instrument_id.venue}" - f".{deltas.instrument_id.symbol}", - msg=deltas, - ) + cdef OrderBookDeltas deltas_to_publish = None + cdef list[OrderBookDelta] buffer_deltas = None + cdef bint is_last_delta = False + + if self._buffer_deltas: + buffer_deltas = self._buffer_deltas_map.get(deltas.instrument_id) + + if buffer_deltas is None: + buffer_deltas = [] + self._buffer_deltas_map[deltas.instrument_id] = buffer_deltas + + for delta in deltas.deltas: + buffer_deltas.append(delta) + is_last_delta = delta.flags == RecordFlag.F_LAST + + if is_last_delta: + deltas_to_publish = OrderBookDeltas( + instrument_id=deltas.instrument_id, + deltas=buffer_deltas + ) + self._msgbus.publish_c( + topic=f"data.book.deltas" + f".{deltas.instrument_id.venue}" + f".{deltas.instrument_id.symbol}", + msg=deltas_to_publish, + ) + buffer_deltas.clear() + else: + self._msgbus.publish_c( + topic=f"data.book.deltas" + f".{deltas.instrument_id.venue}" + f".{deltas.instrument_id.symbol}", + msg=deltas, + ) cpdef void _handle_order_book_depth(self, OrderBookDepth10 depth): self._msgbus.publish_c( diff --git a/nautilus_trader/test_kit/stubs/data.py b/nautilus_trader/test_kit/stubs/data.py index 94f70bc4d930..c79f9e535d27 100644 --- a/nautilus_trader/test_kit/stubs/data.py +++ b/nautilus_trader/test_kit/stubs/data.py @@ -427,10 +427,11 @@ def order_book_delta_clear( def order_book_deltas( instrument_id: InstrumentId | None = None, deltas: list[OrderBookDelta] | None = None, + flags: int = 0, ) -> OrderBookDeltas: return OrderBookDeltas( instrument_id=instrument_id or TestIdStubs.audusd_id(), - deltas=deltas or [TestDataStubs.order_book_delta()], + deltas=deltas or [TestDataStubs.order_book_delta(flags=flags)], ) @staticmethod diff --git a/tests/unit_tests/backtest/test_config.py b/tests/unit_tests/backtest/test_config.py index ceeb4dfdf189..cb39a83c6b04 100644 --- a/tests/unit_tests/backtest/test_config.py +++ b/tests/unit_tests/backtest/test_config.py @@ -285,7 +285,7 @@ def test_backtest_run_config_id(self) -> None: TestConfigStubs.backtest_engine_config, ("catalog",), {"persist": True}, - ("ec9a8febb3af4a4b3f7155b9a2c6f9e86597d97a3d62e568a01cd40565a2a7a3",), + ("882b85369baccfa23783b986b9b62a0cf396a9488d8e2e3057bc851c812ee6f6",), ), ( TestConfigStubs.risk_engine_config, diff --git a/tests/unit_tests/data/test_engine.py b/tests/unit_tests/data/test_engine.py index ebbf308d6058..42c314b83088 100644 --- a/tests/unit_tests/data/test_engine.py +++ b/tests/unit_tests/data/test_engine.py @@ -41,6 +41,7 @@ from nautilus_trader.model.enums import BarAggregation from nautilus_trader.model.enums import BookType from nautilus_trader.model.enums import PriceType +from nautilus_trader.model.enums import RecordFlag from nautilus_trader.model.identifiers import ClientId from nautilus_trader.model.identifiers import InstrumentId from nautilus_trader.model.identifiers import Symbol @@ -1035,6 +1036,43 @@ def test_process_order_book_snapshot_when_one_subscriber_then_sends_to_registere # Assert assert isinstance(handler[0], OrderBook) + def test_process_order_book_delta_then_sends_to_registered_handler(self): + # Arrange + self.data_engine.register_client(self.binance_client) + self.binance_client.start() + + self.data_engine.process(ETHUSDT_BINANCE) # <-- add necessary instrument for test + + handler = [] + self.msgbus.subscribe(topic="data.book.deltas.BINANCE.ETHUSDT", handler=handler.append) + + subscribe = Subscribe( + client_id=ClientId(BINANCE.value), + venue=BINANCE, + data_type=DataType( + OrderBookDelta, + { + "instrument_id": ETHUSDT_BINANCE.id, + "book_type": BookType.L3_MBO, + "depth": 5, + "managed": True, + }, + ), + command_id=UUID4(), + ts_init=self.clock.timestamp_ns(), + ) + + self.data_engine.execute(subscribe) + + deltas = TestDataStubs.order_book_delta(ETHUSDT_BINANCE.id) + + # Act + self.data_engine.process(deltas) + + # Assert + assert handler[0].instrument_id == ETHUSDT_BINANCE.id + assert isinstance(handler[0], OrderBookDeltas) + def test_process_order_book_deltas_then_sends_to_registered_handler(self): # Arrange self.data_engine.register_client(self.binance_client) @@ -2376,3 +2414,232 @@ def test_request_instruments_for_venue_when_catalog_registered(self): # assert len(handler[0].data) == 21 # assert handler[0].data[0].ts_init == 1637971200000000000 # assert handler[0].data[-1].ts_init == 1638058200000000000 + + +class TestDataBufferEngine: + def setup(self): + # Fixture Setup + self.clock = TestClock() + self.trader_id = TestIdStubs.trader_id() + + self.msgbus = MessageBus( + trader_id=self.trader_id, + clock=self.clock, + ) + + self.cache = TestComponentStubs.cache() + + self.portfolio = Portfolio( + msgbus=self.msgbus, + cache=self.cache, + clock=self.clock, + ) + + config = DataEngineConfig( + validate_data_sequence=True, + debug=True, + buffer_deltas=True, + ) + self.data_engine = DataEngine( + msgbus=self.msgbus, + cache=self.cache, + clock=self.clock, + config=config, + ) + + self.binance_client = BacktestMarketDataClient( + client_id=ClientId(BINANCE.value), + msgbus=self.msgbus, + cache=self.cache, + clock=self.clock, + ) + + self.bitmex_client = BacktestMarketDataClient( + client_id=ClientId(BITMEX.value), + msgbus=self.msgbus, + cache=self.cache, + clock=self.clock, + ) + + self.quandl = BacktestMarketDataClient( + client_id=ClientId("QUANDL"), + msgbus=self.msgbus, + cache=self.cache, + clock=self.clock, + ) + + self.betfair = BacktestMarketDataClient( + client_id=ClientId("BETFAIR"), + msgbus=self.msgbus, + cache=self.cache, + clock=self.clock, + ) + + self.data_engine.process(BTCUSDT_BINANCE) + self.data_engine.process(ETHUSDT_BINANCE) + self.data_engine.process(XBTUSD_BITMEX) + + def test_process_order_book_delta_buffering_then_sends_to_registered_handler(self): + # Arrange + self.data_engine.register_client(self.binance_client) + self.binance_client.start() + + self.data_engine.process(ETHUSDT_BINANCE) # <-- add necessary instrument for test + + handler = [] + self.msgbus.subscribe(topic="data.book.deltas.BINANCE.ETHUSDT", handler=handler.append) + + subscribe = Subscribe( + client_id=ClientId(BINANCE.value), + venue=BINANCE, + data_type=DataType( + OrderBookDelta, + { + "instrument_id": ETHUSDT_BINANCE.id, + "book_type": BookType.L3_MBO, + "depth": 5, + "managed": True, + }, + ), + command_id=UUID4(), + ts_init=self.clock.timestamp_ns(), + ) + + self.data_engine.execute(subscribe) + + delta = TestDataStubs.order_book_delta(ETHUSDT_BINANCE.id) + last_delta = TestDataStubs.order_book_delta(ETHUSDT_BINANCE.id, flags=RecordFlag.F_LAST) + + self.data_engine.process(delta) + + assert handler == [] + + # Act + self.data_engine.process(last_delta) + + # Assert + assert handler[0].instrument_id == ETHUSDT_BINANCE.id + assert isinstance(handler[0], OrderBookDeltas) + assert len(handler) == 1 + assert handler[0].deltas == [delta, last_delta] + + def test_process_order_book_delta_buffers_are_cleared(self): + # Arrange + self.data_engine.register_client(self.binance_client) + self.binance_client.start() + + self.data_engine.process(ETHUSDT_BINANCE) # <-- add necessary instrument for test + + handler = [] + self.msgbus.subscribe(topic="data.book.deltas.BINANCE.ETHUSDT", handler=handler.append) + + subscribe = Subscribe( + client_id=ClientId(BINANCE.value), + venue=BINANCE, + data_type=DataType( + OrderBookDelta, + { + "instrument_id": ETHUSDT_BINANCE.id, + "book_type": BookType.L3_MBO, + "depth": 5, + "managed": True, + }, + ), + command_id=UUID4(), + ts_init=self.clock.timestamp_ns(), + ) + + self.data_engine.execute(subscribe) + + delta = TestDataStubs.order_book_delta(ETHUSDT_BINANCE.id, flags=RecordFlag.F_LAST) + + # Act + self.data_engine.process(delta) + self.data_engine.process(delta) + + # Assert + assert len(handler) == 2 + assert len(handler[0].deltas) == 1 + assert len(handler[1].deltas) == 1 + + def test_process_order_book_deltas_then_sends_to_registered_handler(self): + # Arrange + self.data_engine.register_client(self.binance_client) + self.binance_client.start() + + self.data_engine.process(ETHUSDT_BINANCE) # <-- add necessary instrument for test + + handler = [] + self.msgbus.subscribe(topic="data.book.deltas.BINANCE.ETHUSDT", handler=handler.append) + + subscribe = Subscribe( + client_id=ClientId(BINANCE.value), + venue=BINANCE, + data_type=DataType( + OrderBookDelta, + { + "instrument_id": ETHUSDT_BINANCE.id, + "book_type": BookType.L3_MBO, + "depth": 5, + "managed": True, + }, + ), + command_id=UUID4(), + ts_init=self.clock.timestamp_ns(), + ) + + self.data_engine.execute(subscribe) + + deltas = TestDataStubs.order_book_deltas(ETHUSDT_BINANCE.id) + last_deltas = TestDataStubs.order_book_deltas(ETHUSDT_BINANCE.id, flags=RecordFlag.F_LAST) + + self.data_engine.process(deltas) + + assert handler == [] + + # Act + self.data_engine.process(last_deltas) + + # Assert + assert handler[0].instrument_id == ETHUSDT_BINANCE.id + assert isinstance(handler[0], OrderBookDeltas) + assert len(handler[0].deltas) == 2 + + def test_process_order_book_deltas_buffers_are_cleared(self): + # Arrange + self.data_engine.register_client(self.binance_client) + self.binance_client.start() + + self.data_engine.process(ETHUSDT_BINANCE) # <-- add necessary instrument for test + + handler = [] + self.msgbus.subscribe(topic="data.book.deltas.BINANCE.ETHUSDT", handler=handler.append) + + subscribe = Subscribe( + client_id=ClientId(BINANCE.value), + venue=BINANCE, + data_type=DataType( + OrderBookDelta, + { + "instrument_id": ETHUSDT_BINANCE.id, + "book_type": BookType.L3_MBO, + "depth": 5, + "managed": True, + }, + ), + command_id=UUID4(), + ts_init=self.clock.timestamp_ns(), + ) + + self.data_engine.execute(subscribe) + + deltas = TestDataStubs.order_book_deltas(ETHUSDT_BINANCE.id, flags=RecordFlag.F_LAST) + + # Act + self.data_engine.process(deltas) + self.data_engine.process(deltas) + + # Assert + assert len(handler) == 2 + assert len(handler[0].deltas) == 1 + assert len(handler[1].deltas) == 1 From e6cf07ec295cff6b747a621b95280b70b1f01420 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Mon, 27 May 2024 06:36:37 +1000 Subject: [PATCH 36/59] Improve buffer_deltas param description --- nautilus_trader/data/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nautilus_trader/data/config.py b/nautilus_trader/data/config.py index 593e8bb9f034..1aa7f5af8246 100644 --- a/nautilus_trader/data/config.py +++ b/nautilus_trader/data/config.py @@ -36,7 +36,7 @@ class DataEngineConfig(NautilusConfig, frozen=True): validate_data_sequence : bool, default False If data objects timestamp sequencing will be validated and handled. buffer_deltas : bool, default False - Buffer deltas until the F_LAST flag is set for a delta + If order book deltas should be buffered until the F_LAST flag is set for a delta. debug : bool, default False If debug mode is active (will provide extra debug logging). From 4f4426ec9192f491c1b0828f8b4f61e179632785 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Mon, 27 May 2024 06:37:16 +1000 Subject: [PATCH 37/59] Update release notes --- RELEASES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 26cb109a9bf6..50e20457d3de 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -3,7 +3,10 @@ Released on TBD (UTC). ### Enhancements +- Added `DataEngine` order book deltas buffering to `F_LAST` flag (#1673), thanks @davidsblom +- Added `DataEngineConfig.buffer_deltas` config option for the above (#1670), thanks @davidsblom - Improved Bybit order book deltas parsing to set `F_LAST` flag (#1670), thanks @davidsblom +- Improved Bybit handling for top-of-book quotes and order book deltas (#1672), thanks @davidsblom - Improved Interactive Brokers integration test mocks (#1669), thanks @rsmb7z - Improved error message when no tick scheme initialized for an instrument, thanks for reporting @VeraLyu - Ported `VolumeWeightedAveragePrice` indicator to Rust (#1665), thanks @Pushkarm029 From e2c0d431731114f17e4b30452d4cdb0cfea7b810 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Mon, 27 May 2024 07:12:19 +1000 Subject: [PATCH 38/59] Add GetOrderStatus trait and impl --- nautilus_core/model/src/orders/any.rs | 23 ++++++++++++++++++++--- nautilus_core/model/src/orders/base.rs | 20 +++++++++++++------- nautilus_core/model/src/polymorphism.rs | 6 +++++- 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/nautilus_core/model/src/orders/any.rs b/nautilus_core/model/src/orders/any.rs index 8caba5116456..982a7dcd8136 100644 --- a/nautilus_core/model/src/orders/any.rs +++ b/nautilus_core/model/src/orders/any.rs @@ -29,7 +29,7 @@ use super::{ trailing_stop_market::TrailingStopMarketOrder, }; use crate::{ - enums::{OrderSide, OrderSideSpecified, TriggerType}, + enums::{OrderSide, OrderSideSpecified, OrderStatus, TriggerType}, events::order::event::OrderEventAny, identifiers::{ account_id::AccountId, client_order_id::ClientOrderId, exec_algorithm_id::ExecAlgorithmId, @@ -39,8 +39,9 @@ use crate::{ polymorphism::{ ApplyOrderEventAny, GetAccountId, GetClientOrderId, GetEmulationTrigger, GetExecAlgorithmId, GetExecSpawnId, GetInstrumentId, GetLimitPrice, GetOrderFilledQty, - GetOrderLeavesQty, GetOrderQuantity, GetOrderSide, GetOrderSideSpecified, GetPositionId, - GetStopPrice, GetStrategyId, GetTraderId, GetVenueOrderId, IsClosed, IsInflight, IsOpen, + GetOrderLeavesQty, GetOrderQuantity, GetOrderSide, GetOrderSideSpecified, GetOrderStatus, + GetPositionId, GetStopPrice, GetStrategyId, GetTraderId, GetVenueOrderId, IsClosed, + IsInflight, IsOpen, }, types::{price::Price, quantity::Quantity}, }; @@ -312,6 +313,22 @@ impl GetOrderQuantity for OrderAny { } } +impl GetOrderStatus for OrderAny { + fn status(&self) -> OrderStatus { + match self { + Self::Limit(order) => order.status, + Self::LimitIfTouched(order) => order.status, + Self::Market(order) => order.status, + Self::MarketIfTouched(order) => order.status, + Self::MarketToLimit(order) => order.status, + Self::StopLimit(order) => order.status, + Self::StopMarket(order) => order.status, + Self::TrailingStopLimit(order) => order.status, + Self::TrailingStopMarket(order) => order.status, + } + } +} + impl GetOrderFilledQty for OrderAny { fn filled_qty(&self) -> Quantity { match self { diff --git a/nautilus_core/model/src/orders/base.rs b/nautilus_core/model/src/orders/base.rs index 090baa61d239..a1d2ec5c70e9 100644 --- a/nautilus_core/model/src/orders/base.rs +++ b/nautilus_core/model/src/orders/base.rs @@ -315,11 +315,16 @@ pub trait Order: 'static + Send { } fn is_inflight(&self) -> bool { - self.emulation_trigger().is_none() - && matches!( - self.status(), - OrderStatus::Submitted | OrderStatus::PendingCancel | OrderStatus::PendingUpdate - ) + if let Some(emulation_trigger) = self.emulation_trigger() { + if emulation_trigger != TriggerType::NoTrigger { + return false; + } + } + + matches!( + self.status(), + OrderStatus::Submitted | OrderStatus::PendingCancel | OrderStatus::PendingUpdate + ) } fn is_pending_update(&self) -> bool { @@ -331,8 +336,9 @@ pub trait Order: 'static + Send { } fn is_spawned(&self) -> bool { - self.exec_algorithm_id().is_some() - && self.exec_spawn_id().unwrap() != self.client_order_id() + self.exec_spawn_id().map_or(false, |exec_spawn_id| { + exec_spawn_id != self.client_order_id() + }) } } diff --git a/nautilus_core/model/src/polymorphism.rs b/nautilus_core/model/src/polymorphism.rs index 9089c9b8013d..19b303e47066 100644 --- a/nautilus_core/model/src/polymorphism.rs +++ b/nautilus_core/model/src/polymorphism.rs @@ -18,7 +18,7 @@ use nautilus_core::nanos::UnixNanos; use crate::{ - enums::{OrderSide, OrderSideSpecified, TriggerType}, + enums::{OrderSide, OrderSideSpecified, OrderStatus, TriggerType}, events::order::event::OrderEventAny, identifiers::{ account_id::AccountId, client_order_id::ClientOrderId, exec_algorithm_id::ExecAlgorithmId, @@ -77,6 +77,10 @@ pub trait GetOrderQuantity { fn quantity(&self) -> Quantity; } +pub trait GetOrderStatus { + fn status(&self) -> OrderStatus; +} + pub trait GetOrderFilledQty { fn filled_qty(&self) -> Quantity; } From db3210522ee8aa44cc3c40dc40f1cba7ff045910 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Mon, 27 May 2024 07:12:58 +1000 Subject: [PATCH 39/59] Continue Cache in Rust --- nautilus_core/common/src/cache/core.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/nautilus_core/common/src/cache/core.rs b/nautilus_core/common/src/cache/core.rs index 81898d696431..570ddbd4d4ff 100644 --- a/nautilus_core/common/src/cache/core.rs +++ b/nautilus_core/common/src/cache/core.rs @@ -2517,7 +2517,7 @@ mod tests { use nautilus_core::{nanos::UnixNanos, uuid::UUID4}; use nautilus_model::{ data::{bar::Bar, quote::QuoteTick, trade::TradeTick}, - enums::OrderSide, + enums::{OrderSide, OrderStatus}, events::order::{accepted::OrderAccepted, event::OrderEventAny, submitted::OrderSubmitted}, identifiers::{ account_id::AccountId, client_order_id::ClientOrderId, position_id::PositionId, @@ -2529,8 +2529,8 @@ mod tests { }, orders::{any::OrderAny, stubs::TestOrderStubs}, polymorphism::{ - ApplyOrderEventAny, GetAccountId, GetClientOrderId, GetInstrumentId, GetStrategyId, - GetTraderId, GetVenueOrderId, IsOpen, + ApplyOrderEventAny, GetAccountId, GetClientOrderId, GetInstrumentId, GetOrderStatus, + GetStrategyId, GetTraderId, GetVenueOrderId, IsOpen, }, types::{price::Price, quantity::Quantity}, }; @@ -2691,22 +2691,23 @@ mod tests { let result = cache.order(&order.client_order_id()).unwrap(); + assert_eq!(order.status(), OrderStatus::Submitted); assert_eq!(result, &order); assert_eq!(cache.orders(None, None, None, None), vec![&order]); assert!(cache.orders_open(None, None, None, None).is_empty()); assert!(cache.orders_closed(None, None, None, None).is_empty()); assert!(cache.orders_emulated(None, None, None, None).is_empty()); - assert!(cache.orders_inflight(None, None, None, None).is_empty()); + assert!(!cache.orders_inflight(None, None, None, None).is_empty()); assert!(cache.order_exists(&order.client_order_id())); assert!(!cache.is_order_open(&order.client_order_id())); assert!(!cache.is_order_closed(&order.client_order_id())); assert!(!cache.is_order_emulated(&order.client_order_id())); - assert!(!cache.is_order_inflight(&order.client_order_id())); + assert!(cache.is_order_inflight(&order.client_order_id())); assert!(!cache.is_order_pending_cancel_local(&order.client_order_id())); assert_eq!(cache.orders_open_count(None, None, None, None), 0); assert_eq!(cache.orders_closed_count(None, None, None, None), 0); assert_eq!(cache.orders_emulated_count(None, None, None, None), 0); - assert_eq!(cache.orders_inflight_count(None, None, None, None), 0); + assert_eq!(cache.orders_inflight_count(None, None, None, None), 1); assert_eq!(cache.orders_total_count(None, None, None, None), 1); assert_eq!(cache.venue_order_id(&order.client_order_id()), None); } From c7150091fa9bd117f33dd402066b834749b5d1e8 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Mon, 27 May 2024 17:33:32 +1000 Subject: [PATCH 40/59] Clear buffered deltas on DataEngine reset --- nautilus_trader/data/engine.pxd | 2 +- nautilus_trader/data/engine.pyx | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/nautilus_trader/data/engine.pxd b/nautilus_trader/data/engine.pxd index 7410ba08caf9..e153dcaf50f5 100644 --- a/nautilus_trader/data/engine.pxd +++ b/nautilus_trader/data/engine.pxd @@ -57,7 +57,7 @@ cdef class DataEngine(Component): cdef readonly dict[InstrumentId, list[SyntheticInstrument]] _synthetic_trade_feeds cdef readonly list[InstrumentId] _subscribed_synthetic_quotes cdef readonly list[InstrumentId] _subscribed_synthetic_trades - cdef readonly dict[InstrumentId, list[OrderBookDelta]] _buffer_deltas_map + cdef readonly dict[InstrumentId, list[OrderBookDelta]] _buffered_deltas_map cdef readonly bint _time_bars_build_with_no_updates cdef readonly bint _time_bars_timestamp_on_close cdef readonly str _time_bars_interval_type diff --git a/nautilus_trader/data/engine.pyx b/nautilus_trader/data/engine.pyx index ac1ea67c4f85..6d04e9fd2b0e 100644 --- a/nautilus_trader/data/engine.pyx +++ b/nautilus_trader/data/engine.pyx @@ -138,7 +138,7 @@ cdef class DataEngine(Component): self._synthetic_trade_feeds: dict[InstrumentId, list[SyntheticInstrument]] = {} self._subscribed_synthetic_quotes: list[InstrumentId] = [] self._subscribed_synthetic_trades: list[InstrumentId] = [] - self._buffer_deltas_map: dict[InstrumentId, list[OrderBookDelta]] = {} + self._buffered_deltas_map: dict[InstrumentId, list[OrderBookDelta]] = {} # Settings self.debug = config.debug @@ -534,6 +534,7 @@ cdef class DataEngine(Component): self._synthetic_trade_feeds.clear() self._subscribed_synthetic_quotes.clear() self._subscribed_synthetic_trades.clear() + self._buffered_deltas_map.clear() self._clock.cancel_timers() self.command_count = 0 @@ -1382,11 +1383,11 @@ cdef class DataEngine(Component): cdef bint is_last_delta = False if self._buffer_deltas: - buffer_deltas = self._buffer_deltas_map.get(delta.instrument_id) + buffer_deltas = self._buffered_deltas_map.get(delta.instrument_id) if buffer_deltas is None: buffer_deltas = [] - self._buffer_deltas_map[delta.instrument_id] = buffer_deltas + self._buffered_deltas_map[delta.instrument_id] = buffer_deltas buffer_deltas.append(delta) is_last_delta = delta.flags == RecordFlag.F_LAST @@ -1421,11 +1422,11 @@ cdef class DataEngine(Component): cdef bint is_last_delta = False if self._buffer_deltas: - buffer_deltas = self._buffer_deltas_map.get(deltas.instrument_id) + buffer_deltas = self._buffered_deltas_map.get(deltas.instrument_id) if buffer_deltas is None: buffer_deltas = [] - self._buffer_deltas_map[deltas.instrument_id] = buffer_deltas + self._buffered_deltas_map[deltas.instrument_id] = buffer_deltas for delta in deltas.deltas: buffer_deltas.append(delta) @@ -1434,7 +1435,7 @@ cdef class DataEngine(Component): if is_last_delta: deltas_to_publish = OrderBookDeltas( instrument_id=deltas.instrument_id, - deltas=buffer_deltas + deltas=buffer_deltas, ) self._msgbus.publish_c( topic=f"data.book.deltas" From 94ca7af415730ae795ab6acb4a9bbaec251d9194 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Tue, 28 May 2024 19:49:34 +1000 Subject: [PATCH 41/59] Update Sandbox examples execution client names --- examples/sandbox/betfair_sandbox.py | 4 ++-- examples/sandbox/binance_futures_testnet_sandbox.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/sandbox/betfair_sandbox.py b/examples/sandbox/betfair_sandbox.py index 3ac80c811698..2360c42f8477 100644 --- a/examples/sandbox/betfair_sandbox.py +++ b/examples/sandbox/betfair_sandbox.py @@ -72,7 +72,7 @@ async def main(instrument_config: BetfairInstrumentProviderConfig) -> TradingNod ), }, exec_clients={ - "SANDBOX": SandboxExecutionClientConfig( + "BETFAIR": SandboxExecutionClientConfig( venue="BETFAIR", base_currency="AUD", starting_balances=["10_000 AUD"], @@ -96,7 +96,7 @@ async def main(instrument_config: BetfairInstrumentProviderConfig) -> TradingNod # Register your client factories with the node (can take user defined factories) node.add_data_client_factory("BETFAIR", BetfairLiveDataClientFactory) - node.add_exec_client_factory("SANDBOX", SandboxLiveExecClientFactory) + node.add_exec_client_factory("BETFAIR", SandboxLiveExecClientFactory) node.build() try: diff --git a/examples/sandbox/binance_futures_testnet_sandbox.py b/examples/sandbox/binance_futures_testnet_sandbox.py index 6e00bf3f8967..db985a5df535 100644 --- a/examples/sandbox/binance_futures_testnet_sandbox.py +++ b/examples/sandbox/binance_futures_testnet_sandbox.py @@ -118,7 +118,7 @@ async def main(): ), }, exec_clients={ - "SANDBOX": SandboxExecutionClientConfig( + "BINANCE": SandboxExecutionClientConfig( venue="BINANCE", starting_balances=["10_000 USDT", "10 ETH"], ), @@ -151,7 +151,7 @@ async def main(): # Register your client factories with the node (can take user defined factories) node.add_data_client_factory("BINANCE", BinanceLiveDataClientFactory) - node.add_exec_client_factory("SANDBOX", SandboxLiveExecClientFactory) + node.add_exec_client_factory("BINANCE", SandboxLiveExecClientFactory) node.build() try: From b8b1e6eaeccbca7f30fa9724f4f6bc7795cf814f Mon Sep 17 00:00:00 2001 From: fred monroe Date: Wed, 29 May 2024 00:59:24 -0400 Subject: [PATCH 42/59] Filter SANDBOX_INSTRUMENTS to match the execution venue (#1675) --- nautilus_trader/adapters/sandbox/execution.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nautilus_trader/adapters/sandbox/execution.py b/nautilus_trader/adapters/sandbox/execution.py index 47145542ad19..382722a5381c 100644 --- a/nautilus_trader/adapters/sandbox/execution.py +++ b/nautilus_trader/adapters/sandbox/execution.py @@ -111,7 +111,7 @@ def __init__( base_currency=base_currency, default_leverage=config.default_leverage, leverages=config.leverages or {}, - instruments=self.INSTRUMENTS, + instruments=[i for i in self.INSTRUMENTS if i.venue == sandbox_venue], modules=[], portfolio=portfolio, msgbus=self._msgbus, From 07f73a239a94e8766ddebc367eda910c39272cb4 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Wed, 29 May 2024 15:10:19 +1000 Subject: [PATCH 43/59] Update pre-commit --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 40eafdd92e32..405859704224 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -31,12 +31,12 @@ repos: # - id: gitlint-ci - repo: https://github.com/codespell-project/codespell - rev: v2.2.6 + rev: v2.3.0 hooks: - id: codespell description: Checks for common misspellings. types_or: [python, cython, rst, markdown] - args: ["-L", "crate,ot,zar"] + args: ["-L", "crate,ot,socio-economic,zar"] ############################################################################## # Rust formatting and linting @@ -82,7 +82,7 @@ repos: exclude: "docs/_pygments/monokai.py" - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.5 + rev: v0.4.6 hooks: - id: ruff args: ["--fix"] From 07840c9857ef185f258986a557da401ce8205f07 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Wed, 29 May 2024 15:10:30 +1000 Subject: [PATCH 44/59] Update release notes --- RELEASES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 50e20457d3de..080e461d136b 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -9,6 +9,7 @@ Released on TBD (UTC). - Improved Bybit handling for top-of-book quotes and order book deltas (#1672), thanks @davidsblom - Improved Interactive Brokers integration test mocks (#1669), thanks @rsmb7z - Improved error message when no tick scheme initialized for an instrument, thanks for reporting @VeraLyu +- Improved `SandboxExecutionClient` instruments filtering to match the venue, thanks @fredmonroe - Ported `VolumeWeightedAveragePrice` indicator to Rust (#1665), thanks @Pushkarm029 - Ported `VerticalHorizontalFilter` indicator to Rust (#1666), thanks @Pushkarm029 @@ -378,7 +379,7 @@ Released on 23rd December 2023 (UTC). - Changed `StrategyConfig.strategy_id` to type `StrategyId | None` - Changed `Instrument`, `OrderFilled` and `AccountState` `info` field serialization due below fix (you'll need to flush your cache) - Changed `CacheConfig` to take a `DatabaseConfig` (better symmetry with `MessageBusConfig`) -- Changed `RedisCacheDatabase` data structure for currencies from hashset to simpler key-value (you'll need to clear cache or delete all curreny keys) +- Changed `RedisCacheDatabase` data structure for currencies from hashset to simpler key-value (you'll need to clear cache or delete all currency keys) - Changed `Actor` state loading to now use the standard `Serializer` - Renamed `register_json_encoding` to `register_config_encoding` - Renamed `register_json_decoding` to `register_config_decoding` From d591cbd1e2eb32951c410b94403d32ddae6473a7 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Wed, 29 May 2024 15:11:13 +1000 Subject: [PATCH 45/59] Update dependencies --- nautilus_core/Cargo.lock | 29 ++-- nautilus_core/infrastructure/Cargo.toml | 2 +- .../network/tokio-tungstenite/CHANGELOG.md | 2 +- .../serialization/arrow/serializer.py | 5 +- nautilus_trader/test_kit/providers.py | 2 +- poetry.lock | 155 +++++++++--------- pyproject.toml | 4 +- 7 files changed, 99 insertions(+), 100 deletions(-) diff --git a/nautilus_core/Cargo.lock b/nautilus_core/Cargo.lock index 9947d623c4de..543dc3597384 100644 --- a/nautilus_core/Cargo.lock +++ b/nautilus_core/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -500,9 +500,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" dependencies = [ "addr2line", "cc", @@ -1848,9 +1848,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "glob" @@ -2070,9 +2070,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d8d52be92d09acc2e01dddb7fde3ad983fc6489c7db4837e605bc3fca4cb63e" +checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56" dependencies = [ "bytes", "futures-channel", @@ -2469,11 +2469,10 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -2940,9 +2939,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" dependencies = [ "memchr", ] @@ -3583,9 +3582,9 @@ dependencies = [ [[package]] name = "redis" -version = "0.25.3" +version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6472825949c09872e8f2c50bde59fcefc17748b6be5c90fd67cd8b4daca73bfd" +checksum = "e0d7a6955c7511f60f3ba9e86c6d02b3c3f144f8c24b288d1f4e18074ab8bbec" dependencies = [ "arc-swap", "async-trait", diff --git a/nautilus_core/infrastructure/Cargo.toml b/nautilus_core/infrastructure/Cargo.toml index b523ae138214..2b0cedbd0250 100644 --- a/nautilus_core/infrastructure/Cargo.toml +++ b/nautilus_core/infrastructure/Cargo.toml @@ -24,7 +24,7 @@ serde_json = { workspace = true } tokio = { workspace = true } tracing = {workspace = true } ustr = { workspace = true } -redis = { version = "0.25.3", features = [ +redis = { version = "0.25.4", features = [ "connection-manager", "keep-alive", "tls-rustls", diff --git a/nautilus_core/network/tokio-tungstenite/CHANGELOG.md b/nautilus_core/network/tokio-tungstenite/CHANGELOG.md index 9002abbafcad..a07463c87668 100644 --- a/nautilus_core/network/tokio-tungstenite/CHANGELOG.md +++ b/nautilus_core/network/tokio-tungstenite/CHANGELOG.md @@ -13,7 +13,7 @@ - Make `Origin` header case-sensitive (to keep compatibility with poorely-written servers that don't accept lowercase `Origin` header). - Make semantics of the reading form the `WebSocketStream` more reasonable (return `None` instead of an error when the stream is normally closed). -- Imrpove the way `poll_close()` works by properly driving the close of the stream till completion. +- Improve the way `poll_close()` works by properly driving the close of the stream till completion. # 0.17.1 diff --git a/nautilus_trader/serialization/arrow/serializer.py b/nautilus_trader/serialization/arrow/serializer.py index 638b0dcdabda..438a109dd921 100644 --- a/nautilus_trader/serialization/arrow/serializer.py +++ b/nautilus_trader/serialization/arrow/serializer.py @@ -113,7 +113,10 @@ def _unpack_container_objects(data_cls: type, data: list[Any]) -> list[Data]: return data @staticmethod - def rust_defined_to_record_batch(data: list[Data], data_cls: type) -> pa.Table | pa.RecordBatch: + def rust_defined_to_record_batch( # noqa: C901 (too complex) + data: list[Data], + data_cls: type, + ) -> pa.Table | pa.RecordBatch: data = sorted(data, key=lambda x: x.ts_init) data = ArrowSerializer._unpack_container_objects(data_cls, data) diff --git a/nautilus_trader/test_kit/providers.py b/nautilus_trader/test_kit/providers.py index aa302bd138a8..a2a3351b6f7f 100644 --- a/nautilus_trader/test_kit/providers.py +++ b/nautilus_trader/test_kit/providers.py @@ -650,7 +650,7 @@ def third_friday_of_month(year: int, month: int) -> dt.date: return third_friday -def get_contract_month_code(expiry_month: int) -> str: +def get_contract_month_code(expiry_month: int) -> str: # noqa: C901 (too complex) match expiry_month: case 1: return "F" diff --git a/poetry.lock b/poetry.lock index 4d184f5a2a90..c4fa670036f5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -394,63 +394,63 @@ files = [ [[package]] name = "coverage" -version = "7.5.1" +version = "7.5.3" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0884920835a033b78d1c73b6d3bbcda8161a900f38a488829a83982925f6c2e"}, - {file = "coverage-7.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:39afcd3d4339329c5f58de48a52f6e4e50f6578dd6099961cf22228feb25f38f"}, - {file = "coverage-7.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b0ceee8147444347da6a66be737c9d78f3353b0681715b668b72e79203e4a"}, - {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a9ca3f2fae0088c3c71d743d85404cec8df9be818a005ea065495bedc33da35"}, - {file = "coverage-7.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd215c0c7d7aab005221608a3c2b46f58c0285a819565887ee0b718c052aa4e"}, - {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4bf0655ab60d754491004a5efd7f9cccefcc1081a74c9ef2da4735d6ee4a6223"}, - {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61c4bf1ba021817de12b813338c9be9f0ad5b1e781b9b340a6d29fc13e7c1b5e"}, - {file = "coverage-7.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:db66fc317a046556a96b453a58eced5024af4582a8dbdc0c23ca4dbc0d5b3146"}, - {file = "coverage-7.5.1-cp310-cp310-win32.whl", hash = "sha256:b016ea6b959d3b9556cb401c55a37547135a587db0115635a443b2ce8f1c7228"}, - {file = "coverage-7.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:df4e745a81c110e7446b1cc8131bf986157770fa405fe90e15e850aaf7619bc8"}, - {file = "coverage-7.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:796a79f63eca8814ca3317a1ea443645c9ff0d18b188de470ed7ccd45ae79428"}, - {file = "coverage-7.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fc84a37bfd98db31beae3c2748811a3fa72bf2007ff7902f68746d9757f3746"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6175d1a0559986c6ee3f7fccfc4a90ecd12ba0a383dcc2da30c2b9918d67d8a3"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fc81d5878cd6274ce971e0a3a18a8803c3fe25457165314271cf78e3aae3aa2"}, - {file = "coverage-7.5.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:556cf1a7cbc8028cb60e1ff0be806be2eded2daf8129b8811c63e2b9a6c43bca"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9981706d300c18d8b220995ad22627647be11a4276721c10911e0e9fa44c83e8"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d7fed867ee50edf1a0b4a11e8e5d0895150e572af1cd6d315d557758bfa9c057"}, - {file = "coverage-7.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef48e2707fb320c8f139424a596f5b69955a85b178f15af261bab871873bb987"}, - {file = "coverage-7.5.1-cp311-cp311-win32.whl", hash = "sha256:9314d5678dcc665330df5b69c1e726a0e49b27df0461c08ca12674bcc19ef136"}, - {file = "coverage-7.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:5fa567e99765fe98f4e7d7394ce623e794d7cabb170f2ca2ac5a4174437e90dd"}, - {file = "coverage-7.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b6cf3764c030e5338e7f61f95bd21147963cf6aa16e09d2f74f1fa52013c1206"}, - {file = "coverage-7.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ec92012fefebee89a6b9c79bc39051a6cb3891d562b9270ab10ecfdadbc0c34"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16db7f26000a07efcf6aea00316f6ac57e7d9a96501e990a36f40c965ec7a95d"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beccf7b8a10b09c4ae543582c1319c6df47d78fd732f854ac68d518ee1fb97fa"}, - {file = "coverage-7.5.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8748731ad392d736cc9ccac03c9845b13bb07d020a33423fa5b3a36521ac6e4e"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7352b9161b33fd0b643ccd1f21f3a3908daaddf414f1c6cb9d3a2fd618bf2572"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7a588d39e0925f6a2bff87154752481273cdb1736270642aeb3635cb9b4cad07"}, - {file = "coverage-7.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:68f962d9b72ce69ea8621f57551b2fa9c70509af757ee3b8105d4f51b92b41a7"}, - {file = "coverage-7.5.1-cp312-cp312-win32.whl", hash = "sha256:f152cbf5b88aaeb836127d920dd0f5e7edff5a66f10c079157306c4343d86c19"}, - {file = "coverage-7.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:5a5740d1fb60ddf268a3811bcd353de34eb56dc24e8f52a7f05ee513b2d4f596"}, - {file = "coverage-7.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e2213def81a50519d7cc56ed643c9e93e0247f5bbe0d1247d15fa520814a7cd7"}, - {file = "coverage-7.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5037f8fcc2a95b1f0e80585bd9d1ec31068a9bcb157d9750a172836e98bc7a90"}, - {file = "coverage-7.5.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3721c2c9e4c4953a41a26c14f4cef64330392a6d2d675c8b1db3b645e31f0e"}, - {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca498687ca46a62ae590253fba634a1fe9836bc56f626852fb2720f334c9e4e5"}, - {file = "coverage-7.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cdcbc320b14c3e5877ee79e649677cb7d89ef588852e9583e6b24c2e5072661"}, - {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:57e0204b5b745594e5bc14b9b50006da722827f0b8c776949f1135677e88d0b8"}, - {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fe7502616b67b234482c3ce276ff26f39ffe88adca2acf0261df4b8454668b4"}, - {file = "coverage-7.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9e78295f4144f9dacfed4f92935fbe1780021247c2fabf73a819b17f0ccfff8d"}, - {file = "coverage-7.5.1-cp38-cp38-win32.whl", hash = "sha256:1434e088b41594baa71188a17533083eabf5609e8e72f16ce8c186001e6b8c41"}, - {file = "coverage-7.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:0646599e9b139988b63704d704af8e8df7fa4cbc4a1f33df69d97f36cb0a38de"}, - {file = "coverage-7.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4cc37def103a2725bc672f84bd939a6fe4522310503207aae4d56351644682f1"}, - {file = "coverage-7.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc0b4d8bfeabd25ea75e94632f5b6e047eef8adaed0c2161ada1e922e7f7cece"}, - {file = "coverage-7.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d0a0f5e06881ecedfe6f3dd2f56dcb057b6dbeb3327fd32d4b12854df36bf26"}, - {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9735317685ba6ec7e3754798c8871c2f49aa5e687cc794a0b1d284b2389d1bd5"}, - {file = "coverage-7.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d21918e9ef11edf36764b93101e2ae8cc82aa5efdc7c5a4e9c6c35a48496d601"}, - {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c3e757949f268364b96ca894b4c342b41dc6f8f8b66c37878aacef5930db61be"}, - {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:79afb6197e2f7f60c4824dd4b2d4c2ec5801ceb6ba9ce5d2c3080e5660d51a4f"}, - {file = "coverage-7.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d1d0d98d95dd18fe29dc66808e1accf59f037d5716f86a501fc0256455219668"}, - {file = "coverage-7.5.1-cp39-cp39-win32.whl", hash = "sha256:1cc0fe9b0b3a8364093c53b0b4c0c2dd4bb23acbec4c9240b5f284095ccf7981"}, - {file = "coverage-7.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:dde0070c40ea8bb3641e811c1cfbf18e265d024deff6de52c5950677a8fb1e0f"}, - {file = "coverage-7.5.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:6537e7c10cc47c595828b8a8be04c72144725c383c4702703ff4e42e44577312"}, - {file = "coverage-7.5.1.tar.gz", hash = "sha256:54de9ef3a9da981f7af93eafde4ede199e0846cd819eb27c88e2b712aae9708c"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, + {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, + {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, + {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, + {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, + {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, + {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, + {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, + {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, + {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, + {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, + {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, + {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, + {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, + {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, + {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, + {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, + {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, + {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, + {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, + {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, + {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, + {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, + {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, + {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, + {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, + {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, + {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, ] [package.dependencies] @@ -1384,18 +1384,15 @@ files = [ [[package]] name = "nodeenv" -version = "1.8.0" +version = "1.9.0" description = "Node.js virtual environment builder" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ - {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, - {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, + {file = "nodeenv-1.9.0-py2.py3-none-any.whl", hash = "sha256:508ecec98f9f3330b636d4448c0f1a56fc68017c68f1e7857ebc52acf0eb879a"}, + {file = "nodeenv-1.9.0.tar.gz", hash = "sha256:07f144e90dae547bf0d4ee8da0ee42664a42a04e02ed68e06324348dafe4bdb1"}, ] -[package.dependencies] -setuptools = "*" - [[package]] name = "numpy" version = "1.26.4" @@ -2004,28 +2001,28 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "ruff" -version = "0.4.5" +version = "0.4.6" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.4.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8f58e615dec58b1a6b291769b559e12fdffb53cc4187160a2fc83250eaf54e96"}, - {file = "ruff-0.4.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:84dd157474e16e3a82745d2afa1016c17d27cb5d52b12e3d45d418bcc6d49264"}, - {file = "ruff-0.4.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25f483ad9d50b00e7fd577f6d0305aa18494c6af139bce7319c68a17180087f4"}, - {file = "ruff-0.4.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:63fde3bf6f3ad4e990357af1d30e8ba2730860a954ea9282c95fc0846f5f64af"}, - {file = "ruff-0.4.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78e3ba4620dee27f76bbcad97067766026c918ba0f2d035c2fc25cbdd04d9c97"}, - {file = "ruff-0.4.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:441dab55c568e38d02bbda68a926a3d0b54f5510095c9de7f95e47a39e0168aa"}, - {file = "ruff-0.4.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1169e47e9c4136c997f08f9857ae889d614c5035d87d38fda9b44b4338909cdf"}, - {file = "ruff-0.4.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:755ac9ac2598a941512fc36a9070a13c88d72ff874a9781493eb237ab02d75df"}, - {file = "ruff-0.4.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4b02a65985be2b34b170025a8b92449088ce61e33e69956ce4d316c0fe7cce0"}, - {file = "ruff-0.4.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:75a426506a183d9201e7e5664de3f6b414ad3850d7625764106f7b6d0486f0a1"}, - {file = "ruff-0.4.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6e1b139b45e2911419044237d90b60e472f57285950e1492c757dfc88259bb06"}, - {file = "ruff-0.4.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a6f29a8221d2e3d85ff0c7b4371c0e37b39c87732c969b4d90f3dad2e721c5b1"}, - {file = "ruff-0.4.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d6ef817124d72b54cc923f3444828ba24fa45c3164bc9e8f1813db2f3d3a8a11"}, - {file = "ruff-0.4.5-py3-none-win32.whl", hash = "sha256:aed8166c18b1a169a5d3ec28a49b43340949e400665555b51ee06f22813ef062"}, - {file = "ruff-0.4.5-py3-none-win_amd64.whl", hash = "sha256:b0b03c619d2b4350b4a27e34fd2ac64d0dabe1afbf43de57d0f9d8a05ecffa45"}, - {file = "ruff-0.4.5-py3-none-win_arm64.whl", hash = "sha256:9d15de3425f53161b3f5a5658d4522e4eee5ea002bf2ac7aa380743dd9ad5fba"}, - {file = "ruff-0.4.5.tar.gz", hash = "sha256:286eabd47e7d4d521d199cab84deca135557e6d1e0f0d01c29e757c3cb151b54"}, + {file = "ruff-0.4.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ef995583a038cd4a7edf1422c9e19118e2511b8ba0b015861b4abd26ec5367c5"}, + {file = "ruff-0.4.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:602ebd7ad909eab6e7da65d3c091547781bb06f5f826974a53dbe563d357e53c"}, + {file = "ruff-0.4.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f9ced5cbb7510fd7525448eeb204e0a22cabb6e99a3cb160272262817d49786"}, + {file = "ruff-0.4.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04a80acfc862e0e1630c8b738e70dcca03f350bad9e106968a8108379e12b31f"}, + {file = "ruff-0.4.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be47700ecb004dfa3fd4dcdddf7322d4e632de3c06cd05329d69c45c0280e618"}, + {file = "ruff-0.4.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1ff930d6e05f444090a0139e4e13e1e2e1f02bd51bb4547734823c760c621e79"}, + {file = "ruff-0.4.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f13410aabd3b5776f9c5699f42b37a3a348d65498c4310589bc6e5c548dc8a2f"}, + {file = "ruff-0.4.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0cf5cc02d3ae52dfb0c8a946eb7a1d6ffe4d91846ffc8ce388baa8f627e3bd50"}, + {file = "ruff-0.4.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea3424793c29906407e3cf417f28fc33f689dacbbadfb52b7e9a809dd535dcef"}, + {file = "ruff-0.4.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1fa8561489fadf483ffbb091ea94b9c39a00ed63efacd426aae2f197a45e67fc"}, + {file = "ruff-0.4.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:4d5b914818d8047270308fe3e85d9d7f4a31ec86c6475c9f418fbd1624d198e0"}, + {file = "ruff-0.4.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:4f02284335c766678778475e7698b7ab83abaf2f9ff0554a07b6f28df3b5c259"}, + {file = "ruff-0.4.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:3a6a0a4f4b5f54fff7c860010ab3dd81425445e37d35701a965c0248819dde7a"}, + {file = "ruff-0.4.6-py3-none-win32.whl", hash = "sha256:9018bf59b3aa8ad4fba2b1dc0299a6e4e60a4c3bc62bbeaea222679865453062"}, + {file = "ruff-0.4.6-py3-none-win_amd64.whl", hash = "sha256:a769ae07ac74ff1a019d6bd529426427c3e30d75bdf1e08bb3d46ac8f417326a"}, + {file = "ruff-0.4.6-py3-none-win_arm64.whl", hash = "sha256:735a16407a1a8f58e4c5b913ad6102722e80b562dd17acb88887685ff6f20cf6"}, + {file = "ruff-0.4.6.tar.gz", hash = "sha256:a797a87da50603f71e6d0765282098245aca6e3b94b7c17473115167d8dfb0b7"}, ] [[package]] @@ -2669,4 +2666,4 @@ ib = ["async-timeout", "defusedxml", "nautilus_ibapi"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "dea2c5c5e50ed163007e0a92b7d0f66abbcc0335513d0ce953902de8512cf074" +content-hash = "fb19037b99a7d95caaf44c0a244c4c22ca2acbd924c5c315232b96e3b5a2fcaa" diff --git a/pyproject.toml b/pyproject.toml index 4634cf9ff3fe..0cc9955d02c6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -83,7 +83,7 @@ docformatter = "^1.7.5" mypy = "^1.10.0" pandas-stubs = "^2.2.2" pre-commit = "^3.7.1" -ruff = "^0.4.5" +ruff = "^0.4.6" types-pytz = "^2023.3" types-requests = "^2.31" types-toml = "^0.10.2" @@ -92,7 +92,7 @@ types-toml = "^0.10.2" optional = true [tool.poetry.group.test.dependencies] -coverage = "^7.5.1" +coverage = "^7.5.3" pytest = "^7.4.4" pytest-aiohttp = "^1.0.5" pytest-asyncio = "==0.21.1" # Pinned due Cython: cannot set '__pytest_asyncio_scoped_event_loop' attribute of immutable type From cbfc7035aa62df6cdf6c45cf3621cf3d0eea4faf Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Wed, 29 May 2024 15:30:30 +1000 Subject: [PATCH 46/59] Fix SimulatedExchange command processing for sandbox --- nautilus_trader/adapters/sandbox/execution.py | 1 + nautilus_trader/backtest/exchange.pxd | 4 ++ nautilus_trader/backtest/exchange.pyx | 55 +++++++++++-------- 3 files changed, 38 insertions(+), 22 deletions(-) diff --git a/nautilus_trader/adapters/sandbox/execution.py b/nautilus_trader/adapters/sandbox/execution.py index 382722a5381c..7e4abddce546 100644 --- a/nautilus_trader/adapters/sandbox/execution.py +++ b/nautilus_trader/adapters/sandbox/execution.py @@ -129,6 +129,7 @@ def __init__( use_position_ids=config.use_position_ids, use_random_ids=config.use_random_ids, use_reduce_only=config.use_reduce_only, + use_message_queue=False, # Do not use internal message queue for real-time ) self._client = BacktestExecClient( exchange=self.exchange, diff --git a/nautilus_trader/backtest/exchange.pxd b/nautilus_trader/backtest/exchange.pxd index e9049dc3c0cb..a1221699dd71 100644 --- a/nautilus_trader/backtest/exchange.pxd +++ b/nautilus_trader/backtest/exchange.pxd @@ -95,6 +95,8 @@ cdef class SimulatedExchange: """If venue order and position IDs will be randomly generated UUID4s.\n\n:returns: `bool`""" cdef readonly bint use_reduce_only """If the `reduce_only` option on orders will be honored.\n\n:returns: `bool`""" + cdef readonly bint use_message_queue + """If an internal message queue is being used to sequentially process incoming trading commands.\n\n:returns: `bool`""" cdef readonly list modules """The simulation modules registered with the exchange.\n\n:returns: `list[SimulationModule]`""" cdef readonly dict instruments @@ -141,6 +143,8 @@ cdef class SimulatedExchange: cpdef void process(self, uint64_t ts_now) cpdef void reset(self) + cdef void _process_trading_command(self, TradingCommand command) + # -- EVENT GENERATORS ----------------------------------------------------------------------------- cdef void _generate_fresh_account_state(self) diff --git a/nautilus_trader/backtest/exchange.pyx b/nautilus_trader/backtest/exchange.pyx index 1b27f5a576d4..60d8273f1921 100644 --- a/nautilus_trader/backtest/exchange.pyx +++ b/nautilus_trader/backtest/exchange.pyx @@ -115,6 +115,11 @@ cdef class SimulatedExchange: If all venue generated identifiers will be random UUID4's. use_reduce_only : bool, default True If the `reduce_only` execution instruction on orders will be honored. + use_message_queue : bool, default True + If an internal message queue should be used to process trading commands in sequence after + they have initially arrived. Setting this to False would be appropriate for real-time + sandbox environments, where we don't want to introduce additional latency of waiting for + the next data event before processing the trading command. Raises ------ @@ -130,6 +135,7 @@ cdef class SimulatedExchange: If `base_currency` and multiple starting balances. ValueError If `modules` contains a type other than `SimulationModule`. + """ def __init__( @@ -159,6 +165,7 @@ cdef class SimulatedExchange: bint use_position_ids = True, bint use_random_ids = False, bint use_reduce_only = True, + bint use_message_queue = True, ) -> None: Condition.list_type(instruments, Instrument, "instruments", "Instrument") Condition.not_empty(starting_balances, "starting_balances") @@ -197,6 +204,7 @@ cdef class SimulatedExchange: self.use_position_ids = use_position_ids self.use_random_ids = use_random_ids self.use_reduce_only = use_reduce_only + self.use_message_queue = use_message_queue self.fill_model = fill_model self.fee_model = fee_model self.latency_model = latency_model @@ -225,7 +233,7 @@ cdef class SimulatedExchange: self._message_queue = deque() self._inflight_queue: list[tuple[(uint64_t, uint64_t), TradingCommand]] = [] - self._inflight_counter: dict[uint64_t, int] = {} + self._inflight_counter: dict[uint64_t, uint64_t] = {} def __repr__(self) -> str: return ( @@ -301,8 +309,6 @@ cdef class SimulatedExchange: """ Add the given instrument to the venue. - A random and unique 32-bit unsigned integer raw ID will be generated. - Parameters ---------- instrument : Instrument @@ -621,7 +627,9 @@ cdef class SimulatedExchange: """ Condition.not_none(command, "command") - if self.latency_model is None: + if not self.use_message_queue: + self._process_trading_command(command) + elif self.latency_model is None: self._message_queue.appendleft(command) else: heappush(self._inflight_queue, self.generate_inflight_command(command)) @@ -802,7 +810,7 @@ cdef class SimulatedExchange: cpdef void process(self, uint64_t ts_now): """ - Process the exchange to the gives time. + Process the exchange to the given time. All pending commands will be processed along with all simulation modules. @@ -826,25 +834,10 @@ cdef class SimulatedExchange: else: break - cdef: - TradingCommand command - Order order - list orders + cdef TradingCommand command while self._message_queue: command = self._message_queue.pop() - if isinstance(command, SubmitOrder): - self._matching_engines[command.instrument_id].process_order(command.order, self.exec_client.account_id) - elif isinstance(command, SubmitOrderList): - for order in command.order_list.orders: - self._matching_engines[command.instrument_id].process_order(order, self.exec_client.account_id) - elif isinstance(command, ModifyOrder): - self._matching_engines[command.instrument_id].process_modify(command, self.exec_client.account_id) - elif isinstance(command, CancelOrder): - self._matching_engines[command.instrument_id].process_cancel(command, self.exec_client.account_id) - elif isinstance(command, CancelAllOrders): - self._matching_engines[command.instrument_id].process_cancel_all(command, self.exec_client.account_id) - elif isinstance(command, BatchCancelOrders): - self._matching_engines[command.instrument_id].process_batch_cancel(command, self.exec_client.account_id) + self._process_trading_command(command) # Iterate over modules cdef SimulationModule module @@ -873,6 +866,24 @@ cdef class SimulatedExchange: self._log.info("Reset") + cdef void _process_trading_command(self, TradingCommand command): + cdef: + Order order + list[Order] orders + if isinstance(command, SubmitOrder): + self._matching_engines[command.instrument_id].process_order(command.order, self.exec_client.account_id) + elif isinstance(command, SubmitOrderList): + for order in command.order_list.orders: + self._matching_engines[command.instrument_id].process_order(order, self.exec_client.account_id) + elif isinstance(command, ModifyOrder): + self._matching_engines[command.instrument_id].process_modify(command, self.exec_client.account_id) + elif isinstance(command, CancelOrder): + self._matching_engines[command.instrument_id].process_cancel(command, self.exec_client.account_id) + elif isinstance(command, CancelAllOrders): + self._matching_engines[command.instrument_id].process_cancel_all(command, self.exec_client.account_id) + elif isinstance(command, BatchCancelOrders): + self._matching_engines[command.instrument_id].process_batch_cancel(command, self.exec_client.account_id) + # -- EVENT GENERATORS ----------------------------------------------------------------------------- cdef void _generate_fresh_account_state(self): From 4d4a3a13b9ad29a34e85f921f21f8b8ab5c3186f Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Wed, 29 May 2024 17:07:52 +1000 Subject: [PATCH 47/59] Improve SimulatedExchange instrument handling --- nautilus_trader/adapters/sandbox/execution.py | 6 +- nautilus_trader/backtest/engine.pyx | 1 - nautilus_trader/backtest/exchange.pyx | 69 ++++++++++++------- .../backtest/test_exchange_bitmex.py | 2 +- ...est_exchange_bracket_if_touched_entries.py | 2 +- .../unit_tests/backtest/test_exchange_cash.py | 2 +- .../backtest/test_exchange_contingencies.py | 2 +- .../unit_tests/backtest/test_exchange_glbx.py | 2 +- .../backtest/test_exchange_l2_mbp.py | 2 +- .../backtest/test_exchange_margin.py | 6 +- .../backtest/test_exchange_stop_limits.py | 2 +- .../backtest/test_exchange_trailing_stops.py | 2 +- tests/unit_tests/execution/test_algorithm.py | 2 +- .../execution/test_emulator_list.py | 2 +- tests/unit_tests/trading/test_strategy.py | 2 +- tests/unit_tests/trading/test_trader.py | 2 +- 16 files changed, 64 insertions(+), 42 deletions(-) diff --git a/nautilus_trader/adapters/sandbox/execution.py b/nautilus_trader/adapters/sandbox/execution.py index 7e4abddce546..1986a872ffe8 100644 --- a/nautilus_trader/adapters/sandbox/execution.py +++ b/nautilus_trader/adapters/sandbox/execution.py @@ -111,7 +111,6 @@ def __init__( base_currency=base_currency, default_leverage=config.default_leverage, leverages=config.leverages or {}, - instruments=[i for i in self.INSTRUMENTS if i.venue == sandbox_venue], modules=[], portfolio=portfolio, msgbus=self._msgbus, @@ -131,6 +130,11 @@ def __init__( use_reduce_only=config.use_reduce_only, use_message_queue=False, # Do not use internal message queue for real-time ) + for instrument in self.INSTRUMENTS: + if instrument.venue != sandbox_venue: + continue # Not for this venue + self.exchange.add_instrument(instrument) + self._client = BacktestExecClient( exchange=self.exchange, msgbus=msgbus, diff --git a/nautilus_trader/backtest/engine.pyx b/nautilus_trader/backtest/engine.pyx index 96ccc1b5bebb..b836d8534975 100644 --- a/nautilus_trader/backtest/engine.pyx +++ b/nautilus_trader/backtest/engine.pyx @@ -465,7 +465,6 @@ cdef class BacktestEngine: base_currency=base_currency, default_leverage=default_leverage, leverages=leverages or {}, - instruments=[], modules=modules, portfolio=self.kernel.portfolio, msgbus=self.kernel.msgbus, diff --git a/nautilus_trader/backtest/exchange.pyx b/nautilus_trader/backtest/exchange.pyx index 60d8273f1921..2937841e6c45 100644 --- a/nautilus_trader/backtest/exchange.pyx +++ b/nautilus_trader/backtest/exchange.pyx @@ -82,20 +82,22 @@ cdef class SimulatedExchange: The account default leverage (for margin accounts). leverages : dict[InstrumentId, Decimal] The instrument specific leverage configuration (for margin accounts). + modules : list[SimulatedModule] + The simulation modules for the exchange. portfolio : PortfolioFacade The read-only portfolio for the exchange. msgbus : MessageBus The message bus for the exchange. cache : CacheFacade The read-only cache for the exchange. + clock : TestClock + The clock for the exchange. fill_model : FillModel The fill model for the exchange. fee_model : FeeModel The fee model for the exchange. latency_model : LatencyModel, optional The latency model for the exchange. - clock : TestClock - The clock for the exchange. book_type : BookType The order book type for the exchange. frozen_account : bool, default False @@ -105,14 +107,14 @@ cdef class SimulatedExchange: reject_stop_orders : bool, default True If stop orders are rejected on submission if in the market. support_gtd_orders : bool, default True - If orders with GTD time in force will be supported by the venue. + If orders with GTD time in force will be supported by the exchange. support_contingent_orders : bool, default True - If contingent orders will be supported/respected by the venue. + If contingent orders will be supported/respected by the exchange. If False then its expected the strategy will be managing any contingent orders. use_position_ids : bool, default True If venue position IDs will be generated on order fills. use_random_ids : bool, default False - If all venue generated identifiers will be random UUID4's. + If all exchange generated identifiers will be random UUID4's. use_reduce_only : bool, default True If the `reduce_only` execution instruction on orders will be honored. use_message_queue : bool, default True @@ -147,7 +149,6 @@ cdef class SimulatedExchange: Currency base_currency: Currency | None, default_leverage not None: Decimal, leverages not None: dict[InstrumentId, Decimal], - list instruments not None, list modules not None, PortfolioFacade portfolio not None, MessageBus msgbus not None, @@ -167,7 +168,6 @@ cdef class SimulatedExchange: bint use_reduce_only = True, bint use_message_queue = True, ) -> None: - Condition.list_type(instruments, Instrument, "instruments", "Instrument") Condition.not_empty(starting_balances, "starting_balances") Condition.list_type(starting_balances, Money, "starting_balances") Condition.list_type(modules, SimulationModule, "modules", "SimulationModule") @@ -224,12 +224,8 @@ cdef class SimulatedExchange: self._log.info(f"Loaded {module}") # Markets - self._matching_engines: dict[InstrumentId, OrderMatchingEngine] = {} - - # Load instruments self.instruments: dict[InstrumentId, Instrument] = {} - for instrument in instruments: - self.add_instrument(instrument) + self._matching_engines: dict[InstrumentId, OrderMatchingEngine] = {} self._message_queue = deque() self._inflight_queue: list[tuple[(uint64_t, uint64_t), TradingCommand]] = [] @@ -302,12 +298,13 @@ cdef class SimulatedExchange: cpdef void initialize_account(self): """ Initialize the account to the starting balances. + """ self._generate_fresh_account_state() cpdef void add_instrument(self, Instrument instrument): """ - Add the given instrument to the venue. + Add the given instrument to the exchange. Parameters ---------- @@ -668,7 +665,10 @@ cdef class SimulatedExchange: cdef OrderMatchingEngine matching_engine = self._matching_engines.get(delta.instrument_id) if matching_engine is None: - raise RuntimeError(f"No matching engine found for {delta.instrument_id}") + instrument = self.cache.instrument(delta.instrument_id) + if instrument is None: + raise RuntimeError(f"No matching engine found for {delta.instrument_id}") + self.add_instrument(instrument) matching_engine.process_order_book_delta(delta) @@ -690,7 +690,10 @@ cdef class SimulatedExchange: cdef OrderMatchingEngine matching_engine = self._matching_engines.get(deltas.instrument_id) if matching_engine is None: - raise RuntimeError(f"No matching engine found for {deltas.instrument_id}") + instrument = self.cache.instrument(deltas.instrument_id) + if instrument is None: + raise RuntimeError(f"No matching engine found for {deltas.instrument_id}") + self.add_instrument(instrument) matching_engine.process_order_book_deltas(deltas) @@ -714,7 +717,10 @@ cdef class SimulatedExchange: cdef OrderMatchingEngine matching_engine = self._matching_engines.get(tick.instrument_id) if matching_engine is None: - raise RuntimeError(f"No matching engine found for {tick.instrument_id}") + instrument = self.cache.instrument(tick.instrument_id) + if instrument is None: + raise RuntimeError(f"No matching engine found for {tick.instrument_id}") + self.add_instrument(instrument) matching_engine.process_quote_tick(tick) @@ -738,7 +744,10 @@ cdef class SimulatedExchange: cdef OrderMatchingEngine matching_engine = self._matching_engines.get(tick.instrument_id) if matching_engine is None: - raise RuntimeError(f"No matching engine found for {tick.instrument_id}") + instrument = self.cache.instrument(tick.instrument_id) + if instrument is None: + raise RuntimeError(f"No matching engine found for {tick.instrument_id}") + self.add_instrument(instrument) matching_engine.process_trade_tick(tick) @@ -762,7 +771,10 @@ cdef class SimulatedExchange: cdef OrderMatchingEngine matching_engine = self._matching_engines.get(bar.bar_type.instrument_id) if matching_engine is None: - raise RuntimeError(f"No matching engine found for {bar.bar_type.instrument_id}") + instrument = self.cache.instrument(bar.bar_type.instrument_id) + if instrument is None: + raise RuntimeError(f"No matching engine found for {bar.bar_type.instrument_id}") + self.add_instrument(instrument) matching_engine.process_bar(bar) @@ -804,7 +816,10 @@ cdef class SimulatedExchange: cdef OrderMatchingEngine matching_engine = self._matching_engines.get(data.instrument_id) if matching_engine is None: - raise RuntimeError(f"No matching engine found for {data.instrument_id}") + instrument = self.cache.instrument(data.instrument_id) + if instrument is None: + raise RuntimeError(f"No matching engine found for {data.instrument_id}") + self.add_instrument(instrument) matching_engine.process_status(data.status) @@ -867,22 +882,26 @@ cdef class SimulatedExchange: self._log.info("Reset") cdef void _process_trading_command(self, TradingCommand command): + cdef OrderMatchingEngine matching_engine = self._matching_engines.get(command.instrument_id) + if matching_engine is None: + raise RuntimeError(f"Cannot process command: no matching engine for {command.instrument_id}") + cdef: Order order list[Order] orders if isinstance(command, SubmitOrder): - self._matching_engines[command.instrument_id].process_order(command.order, self.exec_client.account_id) + matching_engine.process_order(command.order, self.exec_client.account_id) elif isinstance(command, SubmitOrderList): for order in command.order_list.orders: - self._matching_engines[command.instrument_id].process_order(order, self.exec_client.account_id) + matching_engine.process_order(order, self.exec_client.account_id) elif isinstance(command, ModifyOrder): - self._matching_engines[command.instrument_id].process_modify(command, self.exec_client.account_id) + matching_engine.process_modify(command, self.exec_client.account_id) elif isinstance(command, CancelOrder): - self._matching_engines[command.instrument_id].process_cancel(command, self.exec_client.account_id) + matching_engine.process_cancel(command, self.exec_client.account_id) elif isinstance(command, CancelAllOrders): - self._matching_engines[command.instrument_id].process_cancel_all(command, self.exec_client.account_id) + matching_engine.process_cancel_all(command, self.exec_client.account_id) elif isinstance(command, BatchCancelOrders): - self._matching_engines[command.instrument_id].process_batch_cancel(command, self.exec_client.account_id) + matching_engine.process_batch_cancel(command, self.exec_client.account_id) # -- EVENT GENERATORS ----------------------------------------------------------------------------- diff --git a/tests/unit_tests/backtest/test_exchange_bitmex.py b/tests/unit_tests/backtest/test_exchange_bitmex.py index 71fc9f5ae9bc..fe319b1d797f 100644 --- a/tests/unit_tests/backtest/test_exchange_bitmex.py +++ b/tests/unit_tests/backtest/test_exchange_bitmex.py @@ -101,13 +101,13 @@ def setup(self): portfolio=self.portfolio, msgbus=self.msgbus, cache=self.cache, - instruments=[XBTUSD_BITMEX], modules=[], fill_model=FillModel(), fee_model=MakerTakerFeeModel(), clock=self.clock, latency_model=LatencyModel(0), ) + self.exchange.add_instrument(XBTUSD_BITMEX) self.exec_client = BacktestExecClient( exchange=self.exchange, diff --git a/tests/unit_tests/backtest/test_exchange_bracket_if_touched_entries.py b/tests/unit_tests/backtest/test_exchange_bracket_if_touched_entries.py index d138e74983af..1a6f5872d506 100644 --- a/tests/unit_tests/backtest/test_exchange_bracket_if_touched_entries.py +++ b/tests/unit_tests/backtest/test_exchange_bracket_if_touched_entries.py @@ -105,7 +105,6 @@ def setup(self): starting_balances=[Money(200, ETH), Money(1_000_000, USDT)], default_leverage=Decimal(10), leverages={}, - instruments=[ETHUSDT_PERP_BINANCE], modules=[], fill_model=FillModel(), fee_model=MakerTakerFeeModel(), @@ -116,6 +115,7 @@ def setup(self): latency_model=LatencyModel(0), reject_stop_orders=False, ) + self.exchange.add_instrument(ETHUSDT_PERP_BINANCE) self.exec_client = BacktestExecClient( exchange=self.exchange, diff --git a/tests/unit_tests/backtest/test_exchange_cash.py b/tests/unit_tests/backtest/test_exchange_cash.py index c1174f82d0fb..3541440064f2 100644 --- a/tests/unit_tests/backtest/test_exchange_cash.py +++ b/tests/unit_tests/backtest/test_exchange_cash.py @@ -97,7 +97,6 @@ def setup(self) -> None: starting_balances=[Money(1_000_000, USD)], default_leverage=Decimal(0), leverages={}, - instruments=[_AAPL_XNAS], modules=[], fill_model=FillModel(), fee_model=MakerTakerFeeModel(), @@ -107,6 +106,7 @@ def setup(self) -> None: clock=self.clock, latency_model=LatencyModel(0), ) + self.exchange.add_instrument(_AAPL_XNAS) self.exec_client = BacktestExecClient( exchange=self.exchange, diff --git a/tests/unit_tests/backtest/test_exchange_contingencies.py b/tests/unit_tests/backtest/test_exchange_contingencies.py index de2ecb3ff483..202c516094af 100644 --- a/tests/unit_tests/backtest/test_exchange_contingencies.py +++ b/tests/unit_tests/backtest/test_exchange_contingencies.py @@ -94,7 +94,6 @@ def setup(self): starting_balances=[Money(200, ETH), Money(1_000_000, USDT)], default_leverage=Decimal(10), leverages={}, - instruments=[ETHUSDT_PERP_BINANCE], modules=[], fill_model=FillModel(), fee_model=MakerTakerFeeModel(), @@ -104,6 +103,7 @@ def setup(self): clock=self.clock, latency_model=LatencyModel(0), ) + self.exchange.add_instrument(ETHUSDT_PERP_BINANCE) self.exec_client = BacktestExecClient( exchange=self.exchange, diff --git a/tests/unit_tests/backtest/test_exchange_glbx.py b/tests/unit_tests/backtest/test_exchange_glbx.py index b9c0a4551cef..d4e85e4d521d 100644 --- a/tests/unit_tests/backtest/test_exchange_glbx.py +++ b/tests/unit_tests/backtest/test_exchange_glbx.py @@ -95,7 +95,6 @@ def setup(self) -> None: starting_balances=[Money(1_000_000, USD)], default_leverage=Decimal(10), leverages={}, - instruments=[_ESH4_GLBX], modules=[], fill_model=FillModel(), fee_model=MakerTakerFeeModel(), @@ -105,6 +104,7 @@ def setup(self) -> None: clock=self.clock, latency_model=LatencyModel(0), ) + self.exchange.add_instrument(_ESH4_GLBX) self.exec_client = BacktestExecClient( exchange=self.exchange, diff --git a/tests/unit_tests/backtest/test_exchange_l2_mbp.py b/tests/unit_tests/backtest/test_exchange_l2_mbp.py index cc663db39c36..5ec7612e6596 100644 --- a/tests/unit_tests/backtest/test_exchange_l2_mbp.py +++ b/tests/unit_tests/backtest/test_exchange_l2_mbp.py @@ -100,7 +100,6 @@ def setup(self): starting_balances=[Money(1_000_000, USD)], default_leverage=Decimal(50), leverages={}, - instruments=[_USDJPY_SIM], modules=[], fill_model=FillModel(), fee_model=MakerTakerFeeModel(), @@ -111,6 +110,7 @@ def setup(self): book_type=BookType.L2_MBP, # <-- L2 MBP book latency_model=LatencyModel(0), ) + self.exchange.add_instrument(_USDJPY_SIM) self.exec_client = BacktestExecClient( exchange=self.exchange, diff --git a/tests/unit_tests/backtest/test_exchange_margin.py b/tests/unit_tests/backtest/test_exchange_margin.py index 304753e8adcc..493b02531f7a 100644 --- a/tests/unit_tests/backtest/test_exchange_margin.py +++ b/tests/unit_tests/backtest/test_exchange_margin.py @@ -126,7 +126,6 @@ def setup(self) -> None: starting_balances=[Money(1_000_000, USD)], default_leverage=Decimal(50), leverages={_AUDUSD_SIM.id: Decimal(10)}, - instruments=[_USDJPY_SIM], modules=[], fill_model=FillModel(), fee_model=MakerTakerFeeModel(), @@ -136,6 +135,7 @@ def setup(self) -> None: clock=self.clock, latency_model=LatencyModel(0), ) + self.exchange.add_instrument(_USDJPY_SIM) self.exec_client = BacktestExecClient( exchange=self.exchange, @@ -2728,7 +2728,6 @@ def test_adjust_account_when_account_frozen_does_not_change_balance(self) -> Non starting_balances=[Money(1_000_000, USD)], default_leverage=Decimal(50), leverages={}, - instruments=[_USDJPY_SIM], modules=[], fill_model=FillModel(), fee_model=MakerTakerFeeModel(), @@ -2738,6 +2737,7 @@ def test_adjust_account_when_account_frozen_does_not_change_balance(self) -> Non clock=self.clock, frozen_account=True, # <-- Freezing account ) + exchange.add_instrument(_USDJPY_SIM) exchange.register_client(self.exec_client) exchange.reset() @@ -3027,7 +3027,6 @@ def reset(self): starting_balances=[Money(1_000_000, USD)], default_leverage=Decimal(50), leverages={_AUDUSD_SIM.id: Decimal(10)}, - instruments=[_USDJPY_SIM], modules=[self.module], fill_model=FillModel(), fee_model=MakerTakerFeeModel(), @@ -3038,6 +3037,7 @@ def reset(self): latency_model=LatencyModel(0), book_type=BookType.L1_MBP, ) + self.exchange.add_instrument(_USDJPY_SIM) self.exec_client = BacktestExecClient( exchange=self.exchange, diff --git a/tests/unit_tests/backtest/test_exchange_stop_limits.py b/tests/unit_tests/backtest/test_exchange_stop_limits.py index 843b65ba6754..be063b8187bf 100644 --- a/tests/unit_tests/backtest/test_exchange_stop_limits.py +++ b/tests/unit_tests/backtest/test_exchange_stop_limits.py @@ -98,7 +98,6 @@ def setup(self): starting_balances=[Money(1_000_000, USD)], default_leverage=Decimal(50), leverages={AUDUSD_SIM.id: Decimal(10)}, - instruments=[USDJPY_SIM], modules=[], fill_model=FillModel(), fee_model=MakerTakerFeeModel(), @@ -109,6 +108,7 @@ def setup(self): latency_model=LatencyModel(0), reject_stop_orders=False, ) + self.exchange.add_instrument(USDJPY_SIM) self.exec_client = BacktestExecClient( exchange=self.exchange, diff --git a/tests/unit_tests/backtest/test_exchange_trailing_stops.py b/tests/unit_tests/backtest/test_exchange_trailing_stops.py index b3d4b904562a..73af954a7132 100644 --- a/tests/unit_tests/backtest/test_exchange_trailing_stops.py +++ b/tests/unit_tests/backtest/test_exchange_trailing_stops.py @@ -107,7 +107,6 @@ def setup(self) -> None: ], default_leverage=Decimal(50), leverages={AUDUSD_SIM.id: Decimal(10)}, - instruments=[USDJPY_SIM], modules=[], fill_model=FillModel(), fee_model=MakerTakerFeeModel(), @@ -117,6 +116,7 @@ def setup(self) -> None: clock=self.clock, latency_model=LatencyModel(0), ) + self.exchange.add_instrument(USDJPY_SIM) self.exec_client = BacktestExecClient( exchange=self.exchange, diff --git a/tests/unit_tests/execution/test_algorithm.py b/tests/unit_tests/execution/test_algorithm.py index 1101c440d240..c04d137cfa97 100644 --- a/tests/unit_tests/execution/test_algorithm.py +++ b/tests/unit_tests/execution/test_algorithm.py @@ -130,7 +130,6 @@ def setup(self) -> None: starting_balances=[Money(200, ETH), Money(1_000_000, USDT)], default_leverage=Decimal(10), leverages={}, - instruments=[ETHUSDT_PERP_BINANCE], modules=[], fill_model=FillModel(), fee_model=MakerTakerFeeModel(), @@ -139,6 +138,7 @@ def setup(self) -> None: cache=self.cache, clock=self.clock, ) + self.exchange.add_instrument(ETHUSDT_PERP_BINANCE) self.exec_client = BacktestExecClient( exchange=self.exchange, diff --git a/tests/unit_tests/execution/test_emulator_list.py b/tests/unit_tests/execution/test_emulator_list.py index 660ab4677f18..8c60b214eb04 100644 --- a/tests/unit_tests/execution/test_emulator_list.py +++ b/tests/unit_tests/execution/test_emulator_list.py @@ -129,7 +129,6 @@ def setup(self) -> None: starting_balances=[Money(200, ETH), Money(1_000_000, USDT)], default_leverage=Decimal(10), leverages={}, - instruments=[ETHUSDT_PERP_BINANCE], modules=[], fill_model=FillModel(), fee_model=MakerTakerFeeModel(), @@ -139,6 +138,7 @@ def setup(self) -> None: clock=self.clock, support_contingent_orders=False, ) + self.exchange.add_instrument(ETHUSDT_PERP_BINANCE) self.exec_client = BacktestExecClient( exchange=self.exchange, diff --git a/tests/unit_tests/trading/test_strategy.py b/tests/unit_tests/trading/test_strategy.py index b3a07d1c7958..a6c844b646cd 100644 --- a/tests/unit_tests/trading/test_strategy.py +++ b/tests/unit_tests/trading/test_strategy.py @@ -124,7 +124,6 @@ def setup(self) -> None: portfolio=self.portfolio, msgbus=self.msgbus, cache=self.cache, - instruments=[_USDJPY_SIM], modules=[], fill_model=FillModel(), fee_model=MakerTakerFeeModel(), @@ -133,6 +132,7 @@ def setup(self) -> None: support_contingent_orders=False, use_reduce_only=False, ) + self.exchange.add_instrument(_USDJPY_SIM) self.data_client = BacktestMarketDataClient( client_id=ClientId("SIM"), diff --git a/tests/unit_tests/trading/test_trader.py b/tests/unit_tests/trading/test_trader.py index 919a1948d4ee..2fb90b387a5b 100644 --- a/tests/unit_tests/trading/test_trader.py +++ b/tests/unit_tests/trading/test_trader.py @@ -101,12 +101,12 @@ def setup(self) -> None: portfolio=self.portfolio, msgbus=self.msgbus, cache=self.cache, - instruments=[USDJPY_SIM], modules=[], fill_model=FillModel(), fee_model=MakerTakerFeeModel(), clock=self.clock, ) + self.exchange.add_instrument(USDJPY_SIM) self.data_client = BacktestMarketDataClient( client_id=ClientId("SIM"), From d60733de87c3e82b78cbe13c452fc53194e28900 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Wed, 29 May 2024 17:33:06 +1000 Subject: [PATCH 48/59] Improve SandboxExecutionClient instrument handling --- RELEASES.md | 3 +- examples/sandbox/betfair_sandbox.py | 9 +++--- .../binance_futures_testnet_sandbox.py | 29 ------------------- examples/sandbox/bybit_sandbox.py | 24 +-------------- .../sandbox/interactive_brokers_sandbox.py | 10 +++---- nautilus_trader/adapters/sandbox/execution.py | 13 ++++----- nautilus_trader/backtest/exchange.pyx | 6 ++++ .../adapters/sandbox/conftest.py | 3 +- 8 files changed, 25 insertions(+), 72 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 080e461d136b..0448b40374db 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -9,7 +9,7 @@ Released on TBD (UTC). - Improved Bybit handling for top-of-book quotes and order book deltas (#1672), thanks @davidsblom - Improved Interactive Brokers integration test mocks (#1669), thanks @rsmb7z - Improved error message when no tick scheme initialized for an instrument, thanks for reporting @VeraLyu -- Improved `SandboxExecutionClient` instruments filtering to match the venue, thanks @fredmonroe +- Improved `SandboxExecutionClient` instrument handling (instruments just need to be added to cache) - Ported `VolumeWeightedAveragePrice` indicator to Rust (#1665), thanks @Pushkarm029 - Ported `VerticalHorizontalFilter` indicator to Rust (#1666), thanks @Pushkarm029 @@ -17,6 +17,7 @@ Released on TBD (UTC). None ### Fixes +- Fixed `SimulatedExchange` processing of commands in real-time for sandbox mode - Fixed Bybit order book deltas parsing (was appending bid side twice) (#1668), thanks @davidsblom - Fixed `BinanceBar` streaming feather writing (was not setting up writer) diff --git a/examples/sandbox/betfair_sandbox.py b/examples/sandbox/betfair_sandbox.py index 2360c42f8477..99db32f10ab4 100644 --- a/examples/sandbox/betfair_sandbox.py +++ b/examples/sandbox/betfair_sandbox.py @@ -24,7 +24,6 @@ from nautilus_trader.adapters.betfair.factories import get_cached_betfair_instrument_provider from nautilus_trader.adapters.betfair.providers import BetfairInstrumentProviderConfig 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.config import LoggingConfig from nautilus_trader.config import TradingNodeConfig @@ -55,9 +54,6 @@ async def main(instrument_config: BetfairInstrumentProviderConfig) -> TradingNod instruments = provider.list_all() print(f"Found instruments:\n{[ins.id for ins in instruments]}") - # Need to manually set instruments for sandbox exec client - SandboxExecutionClient.INSTRUMENTS = instruments - # Load account currency account_currency = await provider.get_account_currency() @@ -92,6 +88,11 @@ async def main(instrument_config: BetfairInstrumentProviderConfig) -> TradingNod # Setup TradingNode node = TradingNode(config=config) + + # Can manually set instruments for sandbox exec client + for instrument in instruments: + node.cache.add_instrument(instrument) + node.trader.add_strategies(strategies) # Register your client factories with the node (can take user defined factories) diff --git a/examples/sandbox/binance_futures_testnet_sandbox.py b/examples/sandbox/binance_futures_testnet_sandbox.py index db985a5df535..13e3bfdd1afc 100644 --- a/examples/sandbox/binance_futures_testnet_sandbox.py +++ b/examples/sandbox/binance_futures_testnet_sandbox.py @@ -17,16 +17,11 @@ import asyncio from decimal import Decimal -# fmt: off from nautilus_trader.adapters.binance.common.enums import BinanceAccountType from nautilus_trader.adapters.binance.config import BinanceDataClientConfig from nautilus_trader.adapters.binance.factories import BinanceLiveDataClientFactory -from nautilus_trader.adapters.binance.factories import get_cached_binance_futures_instrument_provider -from nautilus_trader.adapters.binance.factories import get_cached_binance_http_client 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.component import LiveClock from nautilus_trader.config import CacheConfig from nautilus_trader.config import InstrumentProviderConfig from nautilus_trader.config import LiveExecEngineConfig @@ -40,9 +35,6 @@ from nautilus_trader.model.identifiers import TraderId -# fmt: on - - # *** THIS IS A TEST STRATEGY WITH NO ALPHA ADVANTAGE WHATSOEVER. *** # *** IT IS NOT INTENDED TO BE USED TO TRADE LIVE WITH REAL MONEY. *** @@ -51,27 +43,6 @@ async def main(): """ Show how to run a strategy in a sandbox for the Binance venue. """ - # Connect to Binance client early to load all instruments - clock = LiveClock() - account_type = BinanceAccountType.USDT_FUTURE - client = get_cached_binance_http_client( - clock=clock, - account_type=account_type, - is_testnet=True, - ) - - provider = get_cached_binance_futures_instrument_provider( - client=client, - clock=clock, - account_type=account_type, - config=InstrumentProviderConfig(), - ) - await provider.load_all_async() - instruments = provider.list_all() - - # Need to manually set instruments for sandbox exec client - SandboxExecutionClient.INSTRUMENTS = instruments - # Configure the trading node config_node = TradingNodeConfig( trader_id=TraderId("TESTER-001"), diff --git a/examples/sandbox/bybit_sandbox.py b/examples/sandbox/bybit_sandbox.py index aa3a40f9ebc0..e3f89338df09 100644 --- a/examples/sandbox/bybit_sandbox.py +++ b/examples/sandbox/bybit_sandbox.py @@ -17,16 +17,11 @@ import asyncio from decimal import Decimal -from nautilus_trader.adapters.bybit.common.constants import BYBIT_ALL_PRODUCTS from nautilus_trader.adapters.bybit.common.enums import BybitProductType from nautilus_trader.adapters.bybit.config import BybitDataClientConfig from nautilus_trader.adapters.bybit.factories import BybitLiveDataClientFactory -from nautilus_trader.adapters.bybit.factories import get_bybit_http_client -from nautilus_trader.adapters.bybit.factories import get_bybit_instrument_provider 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.component import LiveClock from nautilus_trader.config import InstrumentProviderConfig from nautilus_trader.config import LoggingConfig from nautilus_trader.config import TradingNodeConfig @@ -46,27 +41,10 @@ async def main(): """ Show how to run a strategy in a sandbox for the Bybit venue. """ - # Connect to Bybit client early to load all instruments - clock = LiveClock() - client = get_bybit_http_client(clock=clock) - - product_types = BYBIT_ALL_PRODUCTS instrument_provider_config = InstrumentProviderConfig(load_all=True) - provider = get_bybit_instrument_provider( - client=client, - clock=clock, - product_types=product_types, - config=instrument_provider_config, - ) - await provider.load_all_async() - - instruments = provider.list_all() - - # Need to manually set instruments for sandbox exec client - SandboxExecutionClient.INSTRUMENTS = instruments # Set up the execution clients (required per venue) - venues = {str(instrument.venue) for instrument in instruments} + venues = ["BYBIT"] exec_clients = {} for venue in venues: diff --git a/examples/sandbox/interactive_brokers_sandbox.py b/examples/sandbox/interactive_brokers_sandbox.py index 11d3591087ca..2895783c1e05 100644 --- a/examples/sandbox/interactive_brokers_sandbox.py +++ b/examples/sandbox/interactive_brokers_sandbox.py @@ -22,7 +22,6 @@ from nautilus_trader.adapters.interactive_brokers.config import InteractiveBrokersInstrumentProviderConfig from nautilus_trader.adapters.interactive_brokers.factories import InteractiveBrokersLiveDataClientFactory 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.config import LiveDataEngineConfig from nautilus_trader.config import LoggingConfig @@ -41,11 +40,6 @@ catalog = ParquetDataCatalog(CATALOG_PATH) SANDBOX_INSTRUMENTS = catalog.instruments(instrument_ids=["EUR/USD.IDEALPRO"]) -# Need to manually set instruments for sandbox exec client -SandboxExecutionClient.INSTRUMENTS = ( - SANDBOX_INSTRUMENTS # <- ALL INSTRUMENTS MUST HAVE THE SAME VENUE -) - # Set up the Interactive Brokers gateway configuration, this is applicable only when using Docker. gateway = InteractiveBrokersGatewayConfig( start=False, @@ -104,6 +98,10 @@ # Instantiate the node with a configuration node = TradingNode(config=config_node) +# Can manually set instruments for sandbox exec client +for instrument in SANDBOX_INSTRUMENTS: + node.cache.add_instrument(instrument) + # Instantiate strategies strategies = {} for instrument in SANDBOX_INSTRUMENTS: diff --git a/nautilus_trader/adapters/sandbox/execution.py b/nautilus_trader/adapters/sandbox/execution.py index 1986a872ffe8..d8772c5df1d8 100644 --- a/nautilus_trader/adapters/sandbox/execution.py +++ b/nautilus_trader/adapters/sandbox/execution.py @@ -14,7 +14,6 @@ # ------------------------------------------------------------------------------------------------- import asyncio -from typing import ClassVar import pandas as pd @@ -47,7 +46,6 @@ from nautilus_trader.model.identifiers import InstrumentId from nautilus_trader.model.identifiers import Venue from nautilus_trader.model.identifiers import VenueOrderId -from nautilus_trader.model.instruments import Instrument from nautilus_trader.model.objects import Currency from nautilus_trader.model.objects import Money from nautilus_trader.portfolio.base import PortfolioFacade @@ -72,8 +70,6 @@ class SandboxExecutionClient(LiveExecutionClient): """ - INSTRUMENTS: ClassVar[list[Instrument]] = [] - def __init__( self, loop: asyncio.AbstractEventLoop, @@ -130,10 +126,6 @@ def __init__( use_reduce_only=config.use_reduce_only, use_message_queue=False, # Do not use internal message queue for real-time ) - for instrument in self.INSTRUMENTS: - if instrument.venue != sandbox_venue: - continue # Not for this venue - self.exchange.add_instrument(instrument) self._client = BacktestExecClient( exchange=self.exchange, @@ -150,6 +142,11 @@ def connect(self) -> None: """ self._log.info("Connecting...") self._msgbus.subscribe("data.*", handler=self.on_data) + + # Load all instruments for venue + for instrument in self.exchange.cache.instruments(venue=self.venue): + self.exchange.add_instrument(instrument) + self._client._set_connected(True) self._set_connected(True) self._log.info("Connected") diff --git a/nautilus_trader/backtest/exchange.pyx b/nautilus_trader/backtest/exchange.pyx index 2937841e6c45..8e21fd4eebea 100644 --- a/nautilus_trader/backtest/exchange.pyx +++ b/nautilus_trader/backtest/exchange.pyx @@ -669,6 +669,7 @@ cdef class SimulatedExchange: if instrument is None: raise RuntimeError(f"No matching engine found for {delta.instrument_id}") self.add_instrument(instrument) + matching_engine = self._matching_engines[delta.instrument_id] matching_engine.process_order_book_delta(delta) @@ -694,6 +695,7 @@ cdef class SimulatedExchange: if instrument is None: raise RuntimeError(f"No matching engine found for {deltas.instrument_id}") self.add_instrument(instrument) + matching_engine = self._matching_engines[deltas.instrument_id] matching_engine.process_order_book_deltas(deltas) @@ -721,6 +723,7 @@ cdef class SimulatedExchange: if instrument is None: raise RuntimeError(f"No matching engine found for {tick.instrument_id}") self.add_instrument(instrument) + matching_engine = self._matching_engines[tick.instrument_id] matching_engine.process_quote_tick(tick) @@ -748,6 +751,7 @@ cdef class SimulatedExchange: if instrument is None: raise RuntimeError(f"No matching engine found for {tick.instrument_id}") self.add_instrument(instrument) + matching_engine = self._matching_engines[tick.instrument_id] matching_engine.process_trade_tick(tick) @@ -775,6 +779,7 @@ cdef class SimulatedExchange: if instrument is None: raise RuntimeError(f"No matching engine found for {bar.bar_type.instrument_id}") self.add_instrument(instrument) + matching_engine = self._matching_engines[bar.bar_type.instrument_id] matching_engine.process_bar(bar) @@ -820,6 +825,7 @@ cdef class SimulatedExchange: if instrument is None: raise RuntimeError(f"No matching engine found for {data.instrument_id}") self.add_instrument(instrument) + matching_engine = self._matching_engines[data.instrument_id] matching_engine.process_status(data.status) diff --git a/tests/integration_tests/adapters/sandbox/conftest.py b/tests/integration_tests/adapters/sandbox/conftest.py index 8f2c1fc0eb28..b5103772218d 100644 --- a/tests/integration_tests/adapters/sandbox/conftest.py +++ b/tests/integration_tests/adapters/sandbox/conftest.py @@ -40,7 +40,8 @@ def exec_client( clock, venue, ): - SandboxExecutionClient.INSTRUMENTS = [instrument] + cache.add_instrument(instrument) # <-- This might be redundant now + config = SandboxExecutionClientConfig( venue=venue.value, starting_balances=["100_000 USD"], From b319d47274a8c95358a50f67af2ea9914a763da0 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Wed, 29 May 2024 18:21:12 +1000 Subject: [PATCH 49/59] Refactor CacheDatabaseAdapter in Rust --- nautilus_core/common/src/cache/core.rs | 4 +- nautilus_core/common/src/cache/database.rs | 254 ++++-------------- .../infrastructure/src/python/redis/cache.rs | 1 - .../infrastructure/src/redis/cache.rs | 73 ++++- 4 files changed, 108 insertions(+), 224 deletions(-) diff --git a/nautilus_core/common/src/cache/core.rs b/nautilus_core/common/src/cache/core.rs index 570ddbd4d4ff..311c334e96a3 100644 --- a/nautilus_core/common/src/cache/core.rs +++ b/nautilus_core/common/src/cache/core.rs @@ -176,7 +176,7 @@ impl CacheIndex { pub struct Cache { config: CacheConfig, index: CacheIndex, - database: Option, + database: Option>, general: HashMap>, quotes: HashMap>, trades: HashMap>, @@ -202,7 +202,7 @@ impl Default for Cache { impl Cache { /// Creates a new [`Cache`] instance. #[must_use] - pub fn new(config: CacheConfig, database: Option) -> Self { + pub fn new(config: CacheConfig, database: Option>) -> Self { let index = CacheIndex { venue_account: HashMap::new(), venue_orders: HashMap::new(), diff --git a/nautilus_core/common/src/cache/database.rs b/nautilus_core/common/src/cache/database.rs index d53671b526cf..3997dbe8ae8a 100644 --- a/nautilus_core/common/src/cache/database.rs +++ b/nautilus_core/common/src/cache/database.rs @@ -19,14 +19,14 @@ #![allow(dead_code)] #![allow(unused_variables)] -use std::{collections::HashMap, sync::mpsc::Receiver}; +use std::collections::HashMap; -use nautilus_core::{nanos::UnixNanos, uuid::UUID4}; +use nautilus_core::nanos::UnixNanos; use nautilus_model::{ identifiers::{ account_id::AccountId, client_id::ClientId, client_order_id::ClientOrderId, component_id::ComponentId, instrument_id::InstrumentId, position_id::PositionId, - strategy_id::StrategyId, trader_id::TraderId, venue_order_id::VenueOrderId, + strategy_id::StrategyId, venue_order_id::VenueOrderId, }, instruments::{any::InstrumentAny, synthetic::SyntheticInstrument}, orders::{any::OrderAny, base::Order}, @@ -35,254 +35,90 @@ use nautilus_model::{ }; use ustr::Ustr; -use crate::{enums::SerializationEncoding, interface::account::Account}; +use crate::interface::account::Account; -/// A type of database operation. -#[derive(Clone, Debug)] -pub enum DatabaseOperation { - Insert, - Update, - Delete, - Close, -} - -/// Represents a database command to be performed which may be executed in another thread. -#[derive(Clone, Debug)] -pub struct DatabaseCommand { - /// The database operation type. - pub op_type: DatabaseOperation, - /// The primary key for the operation. - pub key: Option, - /// The data payload for the operation. - pub payload: Option>>, -} - -impl DatabaseCommand { - /// Creates a new [`DatabaseCommand`] instance. - #[must_use] - pub fn new(op_type: DatabaseOperation, key: String, payload: Option>>) -> Self { - Self { - op_type, - key: Some(key), - payload, - } - } - - /// Initialize a `Close` database command, this is meant to close the database cache channel. - #[must_use] - pub fn close() -> Self { - Self { - op_type: DatabaseOperation::Close, - key: None, - payload: None, - } - } -} - -/// A generic cache database facade. -/// -/// The main operations take a consistent `key` and `payload` which should provide enough -/// information to implement the cache database in many different technologies. -/// -/// Delete operations may need a `payload` to target specific values. -pub trait CacheDatabase { - type DatabaseType; - - fn new( - trader_id: TraderId, - instance_id: UUID4, - config: HashMap, - ) -> anyhow::Result; - fn close(&mut self) -> anyhow::Result<()>; - fn flushdb(&mut self) -> anyhow::Result<()>; - fn keys(&mut self, pattern: &str) -> anyhow::Result>; - fn read(&mut self, key: &str) -> anyhow::Result>>; - fn insert(&mut self, key: String, payload: Option>>) -> anyhow::Result<()>; - fn update(&mut self, key: String, payload: Option>>) -> anyhow::Result<()>; - fn delete(&mut self, key: String, payload: Option>>) -> anyhow::Result<()>; - fn handle_messages( - rx: Receiver, - trader_key: String, - config: HashMap, - ); -} +pub trait CacheDatabaseAdapter { + fn close(&self) -> anyhow::Result<()>; -pub struct CacheDatabaseAdapter { - pub encoding: SerializationEncoding, - // database: Box, // TBD -} - -impl CacheDatabaseAdapter { - pub fn close(&self) -> anyhow::Result<()> { - Ok(()) // TODO - } + fn flush(&self) -> anyhow::Result<()>; - pub fn flush(&self) -> anyhow::Result<()> { - Ok(()) // TODO - } + fn load(&self) -> anyhow::Result>>; - pub fn keys(&self) -> anyhow::Result> { - Ok(vec![]) - } + fn load_currencies(&self) -> anyhow::Result>; - pub fn load(&self) -> anyhow::Result>> { - Ok(HashMap::new()) // TODO - } + fn load_instruments(&self) -> anyhow::Result>; - pub fn load_currencies(&self) -> anyhow::Result> { - Ok(HashMap::new()) // TODO - } + fn load_synthetics(&self) -> anyhow::Result>; - pub fn load_instruments(&self) -> anyhow::Result> { - Ok(HashMap::new()) // TODO - } + fn load_accounts(&self) -> anyhow::Result>>; - pub fn load_synthetics(&self) -> anyhow::Result> { - Ok(HashMap::new()) // TODO - } + fn load_orders(&self) -> anyhow::Result>; - pub fn load_accounts(&self) -> anyhow::Result>> { - Ok(HashMap::new()) // TODO - } + fn load_positions(&self) -> anyhow::Result>; - pub fn load_orders(&self) -> anyhow::Result> { - Ok(HashMap::new()) // TODO - } + fn load_index_order_position(&self) -> anyhow::Result>; - pub fn load_positions(&self) -> anyhow::Result> { - Ok(HashMap::new()) // TODO - } + fn load_index_order_client(&self) -> anyhow::Result>; - pub fn load_index_order_position(&self) -> anyhow::Result> { - Ok(HashMap::new()) // TODO - } + fn load_currency(&self, code: &Ustr) -> anyhow::Result; - pub fn load_index_order_client(&self) -> anyhow::Result> { - Ok(HashMap::new()) // TODO - } + fn load_instrument(&self, instrument_id: &InstrumentId) -> anyhow::Result; - pub fn load_currency(&self, code: &Ustr) -> anyhow::Result { - todo!() // TODO - } + fn load_synthetic(&self, instrument_id: &InstrumentId) -> anyhow::Result; - pub fn load_instrument(&self, instrument_id: &InstrumentId) -> anyhow::Result { - todo!() // TODO - } + fn load_account(&self, account_id: &AccountId) -> anyhow::Result<()>; - pub fn load_synthetic( - &self, - instrument_id: &InstrumentId, - ) -> anyhow::Result { - todo!() // TODO - } + fn load_order(&self, client_order_id: &ClientOrderId) -> anyhow::Result>; - pub fn load_account(&self, account_id: &AccountId) -> anyhow::Result<()> { - todo!() // TODO - } + fn load_position(&self, position_id: &PositionId) -> anyhow::Result; - pub fn load_order(&self, client_order_id: &ClientOrderId) -> anyhow::Result> { - todo!() // TODO - } + fn load_actor(&self, component_id: &ComponentId) -> anyhow::Result>>; - pub fn load_position(&self, position_id: &PositionId) -> anyhow::Result { - todo!() // TODO - } + fn delete_actor(&self, component_id: &ComponentId) -> anyhow::Result<()>; - pub fn load_actor( - &self, - component_id: &ComponentId, - ) -> anyhow::Result>> { - todo!() // TODO - } - - pub fn delete_actor(&self, component_id: &ComponentId) -> anyhow::Result<()> { - todo!() // TODO - } - - pub fn load_strategy( - &self, - strategy_id: &StrategyId, - ) -> anyhow::Result>> { - todo!() // TODO - } + fn load_strategy(&self, strategy_id: &StrategyId) -> anyhow::Result>>; - pub fn delete_strategy(&self, component_id: &StrategyId) -> anyhow::Result<()> { - todo!() // TODO - } + fn delete_strategy(&self, component_id: &StrategyId) -> anyhow::Result<()>; - pub fn add(&self, key: String, value: Vec) -> anyhow::Result<()> { - todo!() // TODO - } + fn add(&self, key: String, value: Vec) -> anyhow::Result<()>; - pub fn add_currency(&self, currency: &Currency) -> anyhow::Result<()> { - todo!() // TODO - } + fn add_currency(&self, currency: &Currency) -> anyhow::Result<()>; - pub fn add_instrument(&self, instrument: &InstrumentAny) -> anyhow::Result<()> { - todo!() // TODO - } + fn add_instrument(&self, instrument: &InstrumentAny) -> anyhow::Result<()>; - pub fn add_synthetic(&self, synthetic: &SyntheticInstrument) -> anyhow::Result<()> { - todo!() // TODO - } + fn add_synthetic(&self, synthetic: &SyntheticInstrument) -> anyhow::Result<()>; - pub fn add_account(&self, account: &dyn Account) -> anyhow::Result> { - todo!() // TODO - } + fn add_account(&self, account: &dyn Account) -> anyhow::Result>; - pub fn add_order(&self, order: &OrderAny) -> anyhow::Result<()> { - todo!() // TODO - } + fn add_order(&self, order: &OrderAny) -> anyhow::Result<()>; - pub fn add_position(&self, position: &Position) -> anyhow::Result<()> { - todo!() // TODO - } + fn add_position(&self, position: &Position) -> anyhow::Result<()>; - pub fn index_venue_order_id( + fn index_venue_order_id( &self, client_order_id: ClientOrderId, venue_order_id: VenueOrderId, - ) -> anyhow::Result<()> { - todo!() // TODO - } + ) -> anyhow::Result<()>; - pub fn index_order_position( + fn index_order_position( &self, client_order_id: ClientOrderId, position_id: PositionId, - ) -> anyhow::Result<()> { - todo!() // TODO - } + ) -> anyhow::Result<()>; - pub fn update_actor(&self) -> anyhow::Result<()> { - todo!() // TODO - } + fn update_actor(&self) -> anyhow::Result<()>; - pub fn update_strategy(&self) -> anyhow::Result<()> { - todo!() // TODO - } + fn update_strategy(&self) -> anyhow::Result<()>; - pub fn update_account(&self, account: &dyn Account) -> anyhow::Result<()> { - todo!() // TODO - } + fn update_account(&self, account: &dyn Account) -> anyhow::Result<()>; - pub fn update_order(&self, order: &OrderAny) -> anyhow::Result<()> { - todo!() // TODO - } + fn update_order(&self, order: &OrderAny) -> anyhow::Result<()>; - pub fn update_position(&self, position: &Position) -> anyhow::Result<()> { - todo!() // TODO - } + fn update_position(&self, position: &Position) -> anyhow::Result<()>; - pub fn snapshot_order_state(&self, order: &OrderAny) -> anyhow::Result<()> { - todo!() // TODO - } + fn snapshot_order_state(&self, order: &OrderAny) -> anyhow::Result<()>; - pub fn snapshot_position_state(&self, position: &Position) -> anyhow::Result<()> { - todo!() // TODO - } + fn snapshot_position_state(&self, position: &Position) -> anyhow::Result<()>; - pub fn heartbeat(&self, timestamp: UnixNanos) -> anyhow::Result<()> { - todo!() // TODO - } + fn heartbeat(&self, timestamp: UnixNanos) -> anyhow::Result<()>; } diff --git a/nautilus_core/infrastructure/src/python/redis/cache.rs b/nautilus_core/infrastructure/src/python/redis/cache.rs index a3a21208fe8c..2f1ef677898b 100644 --- a/nautilus_core/infrastructure/src/python/redis/cache.rs +++ b/nautilus_core/infrastructure/src/python/redis/cache.rs @@ -15,7 +15,6 @@ use std::collections::HashMap; -use nautilus_common::cache::database::CacheDatabase; use nautilus_core::{ python::{to_pyruntime_err, to_pyvalue_err}, uuid::UUID4, diff --git a/nautilus_core/infrastructure/src/redis/cache.rs b/nautilus_core/infrastructure/src/redis/cache.rs index 23a5dc373834..9903ca0ecb95 100644 --- a/nautilus_core/infrastructure/src/redis/cache.rs +++ b/nautilus_core/infrastructure/src/redis/cache.rs @@ -20,7 +20,7 @@ use std::{ time::{Duration, Instant}, }; -use nautilus_common::cache::database::{CacheDatabase, DatabaseCommand, DatabaseOperation}; +use nautilus_common::enums::SerializationEncoding; use nautilus_core::{correctness::check_slice_not_empty, uuid::UUID4}; use nautilus_model::identifiers::trader_id::TraderId; use redis::{Commands, Connection, Pipeline}; @@ -63,6 +63,48 @@ const INDEX_POSITIONS: &str = "index:positions"; const INDEX_POSITIONS_OPEN: &str = "index:positions_open"; const INDEX_POSITIONS_CLOSED: &str = "index:positions_closed"; +/// A type of database operation. +#[derive(Clone, Debug)] +pub enum DatabaseOperation { + Insert, + Update, + Delete, + Close, +} + +/// Represents a database command to be performed which may be executed in another thread. +#[derive(Clone, Debug)] +pub struct DatabaseCommand { + /// The database operation type. + pub op_type: DatabaseOperation, + /// The primary key for the operation. + pub key: Option, + /// The data payload for the operation. + pub payload: Option>>, +} + +impl DatabaseCommand { + /// Creates a new [`DatabaseCommand`] instance. + #[must_use] + pub fn new(op_type: DatabaseOperation, key: String, payload: Option>>) -> Self { + Self { + op_type, + key: Some(key), + payload, + } + } + + /// Initialize a `Close` database command, this is meant to close the database cache channel. + #[must_use] + pub fn close() -> Self { + Self { + op_type: DatabaseOperation::Close, + key: None, + payload: None, + } + } +} + #[cfg_attr( feature = "python", pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.infrastructure") @@ -75,11 +117,9 @@ pub struct RedisCacheDatabase { handle: Option>, } -impl CacheDatabase for RedisCacheDatabase { - type DatabaseType = RedisCacheDatabase; - +impl RedisCacheDatabase { /// Creates a new [`RedisCacheDatabase`] instance. - fn new( + pub fn new( trader_id: TraderId, instance_id: UUID4, config: HashMap, @@ -110,7 +150,7 @@ impl CacheDatabase for RedisCacheDatabase { }) } - fn close(&mut self) -> anyhow::Result<()> { + pub fn close(&mut self) -> anyhow::Result<()> { debug!("Closing cache database adapter"); self.tx .send(DatabaseCommand::close()) @@ -124,14 +164,14 @@ impl CacheDatabase for RedisCacheDatabase { } } - fn flushdb(&mut self) -> anyhow::Result<()> { + pub fn flushdb(&mut self) -> anyhow::Result<()> { match redis::cmd(FLUSHDB).query::<()>(&mut self.conn) { Ok(_) => Ok(()), Err(e) => Err(e.into()), } } - fn keys(&mut self, pattern: &str) -> anyhow::Result> { + pub fn keys(&mut self, pattern: &str) -> anyhow::Result> { let pattern = format!("{}{DELIMITER}{}", self.trader_key, pattern); debug!("Querying keys: {pattern}"); match self.conn.keys(pattern) { @@ -140,7 +180,7 @@ impl CacheDatabase for RedisCacheDatabase { } } - fn read(&mut self, key: &str) -> anyhow::Result>> { + pub fn read(&mut self, key: &str) -> anyhow::Result>> { let collection = get_collection_key(key)?; let key = format!("{}{DELIMITER}{}", self.trader_key, key); @@ -159,7 +199,7 @@ impl CacheDatabase for RedisCacheDatabase { } } - fn insert(&mut self, key: String, payload: Option>>) -> anyhow::Result<()> { + pub fn insert(&mut self, key: String, payload: Option>>) -> anyhow::Result<()> { let op = DatabaseCommand::new(DatabaseOperation::Insert, key, payload); match self.tx.send(op) { Ok(_) => Ok(()), @@ -167,7 +207,7 @@ impl CacheDatabase for RedisCacheDatabase { } } - fn update(&mut self, key: String, payload: Option>>) -> anyhow::Result<()> { + pub fn update(&mut self, key: String, payload: Option>>) -> anyhow::Result<()> { let op = DatabaseCommand::new(DatabaseOperation::Update, key, payload); match self.tx.send(op) { Ok(_) => Ok(()), @@ -175,7 +215,7 @@ impl CacheDatabase for RedisCacheDatabase { } } - fn delete(&mut self, key: String, payload: Option>>) -> anyhow::Result<()> { + pub fn delete(&mut self, key: String, payload: Option>>) -> anyhow::Result<()> { let op = DatabaseCommand::new(DatabaseOperation::Delete, key, payload); match self.tx.send(op) { Ok(_) => Ok(()), @@ -621,6 +661,15 @@ fn deserialize_payload( } } +#[allow(dead_code)] // Under development +pub struct RedisCacheDatabaseAdapter { + pub encoding: SerializationEncoding, + database: RedisCacheDatabase, +} + +//////////////////////////////////////////////////////////////////////////////// +// Tests +//////////////////////////////////////////////////////////////////////////////// #[cfg(test)] mod tests { use std::collections::HashMap; From 4e9f7c059e71c4a22fb6c838ea23d21368e4b5b7 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Wed, 29 May 2024 19:10:48 +1000 Subject: [PATCH 50/59] Continue RedisCacheDatabaseAdapter in Rust --- nautilus_core/common/src/cache/core.rs | 40 ++-- nautilus_core/common/src/cache/database.rs | 87 ++++---- .../infrastructure/src/redis/cache.rs | 203 +++++++++++++++++- 3 files changed, 268 insertions(+), 62 deletions(-) diff --git a/nautilus_core/common/src/cache/core.rs b/nautilus_core/common/src/cache/core.rs index 311c334e96a3..015962e38d8a 100644 --- a/nautilus_core/common/src/cache/core.rs +++ b/nautilus_core/common/src/cache/core.rs @@ -258,7 +258,7 @@ impl Cache { /// Clears the current general cache and loads the general objects from the cache database. pub fn cache_general(&mut self) -> anyhow::Result<()> { - self.general = match &self.database { + self.general = match &mut self.database { Some(db) => db.load()?, None => HashMap::new(), }; @@ -272,7 +272,7 @@ impl Cache { /// Clears the current currencies cache and loads currencies from the cache database. pub fn cache_currencies(&mut self) -> anyhow::Result<()> { - self.currencies = match &self.database { + self.currencies = match &mut self.database { Some(db) => db.load_currencies()?, None => HashMap::new(), }; @@ -283,7 +283,7 @@ impl Cache { /// Clears the current instruments cache and loads instruments from the cache database. pub fn cache_instruments(&mut self) -> anyhow::Result<()> { - self.instruments = match &self.database { + self.instruments = match &mut self.database { Some(db) => db.load_instruments()?, None => HashMap::new(), }; @@ -295,7 +295,7 @@ impl Cache { /// Clears the current synthetic instruments cache and loads synthetic instruments from the cache /// database. pub fn cache_synthetics(&mut self) -> anyhow::Result<()> { - self.synthetics = match &self.database { + self.synthetics = match &mut self.database { Some(db) => db.load_synthetics()?, None => HashMap::new(), }; @@ -309,7 +309,7 @@ impl Cache { /// Clears the current accounts cache and loads accounts from the cache database. pub fn cache_accounts(&mut self) -> anyhow::Result<()> { - self.accounts = match &self.database { + self.accounts = match &mut self.database { Some(db) => db.load_accounts()?, None => HashMap::new(), }; @@ -323,7 +323,7 @@ impl Cache { /// Clears the current orders cache and loads orders from the cache database. pub fn cache_orders(&mut self) -> anyhow::Result<()> { - self.orders = match &self.database { + self.orders = match &mut self.database { Some(db) => db.load_orders()?, None => HashMap::new(), }; @@ -334,7 +334,7 @@ impl Cache { /// Clears the current positions cache and loads positions from the cache database. pub fn cache_positions(&mut self) -> anyhow::Result<()> { - self.positions = match &self.database { + self.positions = match &mut self.database { Some(db) => db.load_positions()?, None => HashMap::new(), }; @@ -938,8 +938,8 @@ impl Cache { } /// Dispose of the cache which will close any underlying database adapter. - pub fn dispose(&self) -> anyhow::Result<()> { - if let Some(database) = &self.database { + pub fn dispose(&mut self) -> anyhow::Result<()> { + if let Some(database) = &mut self.database { // TODO: Log operations in database adapter database.close()?; } @@ -947,8 +947,8 @@ impl Cache { } /// Flushes the caches database which permanently removes all persisted data. - pub fn flush_db(&self) -> anyhow::Result<()> { - if let Some(database) = &self.database { + pub fn flush_db(&mut self) -> anyhow::Result<()> { + if let Some(database) = &mut self.database { // TODO: Log operations in database adapter database.flush()?; } @@ -966,7 +966,7 @@ impl Cache { debug!("Adding general {key}"); self.general.insert(key.to_string(), value.clone()); - if let Some(database) = &self.database { + if let Some(database) = &mut self.database { database.add(key.to_string(), value)?; } Ok(()) @@ -1067,7 +1067,7 @@ impl Cache { pub fn add_currency(&mut self, currency: Currency) -> anyhow::Result<()> { debug!("Adding `Currency` {}", currency.code); - if let Some(database) = &self.database { + if let Some(database) = &mut self.database { database.add_currency(¤cy)?; } @@ -1079,7 +1079,7 @@ impl Cache { pub fn add_instrument(&mut self, instrument: InstrumentAny) -> anyhow::Result<()> { debug!("Adding `Instrument` {}", instrument.id()); - if let Some(database) = &self.database { + if let Some(database) = &mut self.database { database.add_instrument(&instrument)?; } @@ -1091,7 +1091,7 @@ impl Cache { pub fn add_synthetic(&mut self, synthetic: SyntheticInstrument) -> anyhow::Result<()> { debug!("Adding `SyntheticInstrument` {}", synthetic.id); - if let Some(database) = &self.database { + if let Some(database) = &mut self.database { database.add_synthetic(&synthetic)?; } @@ -1103,7 +1103,7 @@ impl Cache { pub fn add_account(&mut self, account: Box) -> anyhow::Result<()> { debug!("Adding `Account` {}", account.id()); - if let Some(database) = &self.database { + if let Some(database) = &mut self.database { database.add_account(account.as_ref())?; } @@ -2559,21 +2559,21 @@ mod tests { } #[rstest] - fn test_dispose_when_empty(cache: Cache) { + fn test_dispose_when_empty(mut cache: Cache) { let result = cache.dispose(); assert!(result.is_ok()); } #[rstest] - fn test_flush_db_when_empty(cache: Cache) { + fn test_flush_db_when_empty(mut cache: Cache) { let result = cache.flush_db(); assert!(result.is_ok()); } #[rstest] fn test_check_residuals_when_empty(cache: Cache) { - let result = cache.flush_db(); - assert!(result.is_ok()); + let result = cache.check_residuals(); + assert!(!result); } #[rstest] diff --git a/nautilus_core/common/src/cache/database.rs b/nautilus_core/common/src/cache/database.rs index 3997dbe8ae8a..62477d7ca19b 100644 --- a/nautilus_core/common/src/cache/database.rs +++ b/nautilus_core/common/src/cache/database.rs @@ -29,7 +29,7 @@ use nautilus_model::{ strategy_id::StrategyId, venue_order_id::VenueOrderId, }, instruments::{any::InstrumentAny, synthetic::SyntheticInstrument}, - orders::{any::OrderAny, base::Order}, + orders::any::OrderAny, position::Position, types::currency::Currency, }; @@ -38,87 +38,96 @@ use ustr::Ustr; use crate::interface::account::Account; pub trait CacheDatabaseAdapter { - fn close(&self) -> anyhow::Result<()>; + fn close(&mut self) -> anyhow::Result<()>; - fn flush(&self) -> anyhow::Result<()>; + fn flush(&mut self) -> anyhow::Result<()>; - fn load(&self) -> anyhow::Result>>; + fn load(&mut self) -> anyhow::Result>>; - fn load_currencies(&self) -> anyhow::Result>; + fn load_currencies(&mut self) -> anyhow::Result>; - fn load_instruments(&self) -> anyhow::Result>; + fn load_instruments(&mut self) -> anyhow::Result>; - fn load_synthetics(&self) -> anyhow::Result>; + fn load_synthetics(&mut self) -> anyhow::Result>; - fn load_accounts(&self) -> anyhow::Result>>; + fn load_accounts(&mut self) -> anyhow::Result>>; - fn load_orders(&self) -> anyhow::Result>; + fn load_orders(&mut self) -> anyhow::Result>; - fn load_positions(&self) -> anyhow::Result>; + fn load_positions(&mut self) -> anyhow::Result>; - fn load_index_order_position(&self) -> anyhow::Result>; + fn load_index_order_position(&mut self) -> anyhow::Result>; - fn load_index_order_client(&self) -> anyhow::Result>; + fn load_index_order_client(&mut self) -> anyhow::Result>; - fn load_currency(&self, code: &Ustr) -> anyhow::Result; + fn load_currency(&mut self, code: &Ustr) -> anyhow::Result; - fn load_instrument(&self, instrument_id: &InstrumentId) -> anyhow::Result; + fn load_instrument(&mut self, instrument_id: &InstrumentId) -> anyhow::Result; - fn load_synthetic(&self, instrument_id: &InstrumentId) -> anyhow::Result; + fn load_synthetic( + &mut self, + instrument_id: &InstrumentId, + ) -> anyhow::Result; - fn load_account(&self, account_id: &AccountId) -> anyhow::Result<()>; + fn load_account(&mut self, account_id: &AccountId) -> anyhow::Result<()>; - fn load_order(&self, client_order_id: &ClientOrderId) -> anyhow::Result>; + fn load_order(&mut self, client_order_id: &ClientOrderId) -> anyhow::Result; - fn load_position(&self, position_id: &PositionId) -> anyhow::Result; + fn load_position(&mut self, position_id: &PositionId) -> anyhow::Result; - fn load_actor(&self, component_id: &ComponentId) -> anyhow::Result>>; + fn load_actor( + &mut self, + component_id: &ComponentId, + ) -> anyhow::Result>>; - fn delete_actor(&self, component_id: &ComponentId) -> anyhow::Result<()>; + fn delete_actor(&mut self, component_id: &ComponentId) -> anyhow::Result<()>; - fn load_strategy(&self, strategy_id: &StrategyId) -> anyhow::Result>>; + fn load_strategy( + &mut self, + strategy_id: &StrategyId, + ) -> anyhow::Result>>; - fn delete_strategy(&self, component_id: &StrategyId) -> anyhow::Result<()>; + fn delete_strategy(&mut self, component_id: &StrategyId) -> anyhow::Result<()>; - fn add(&self, key: String, value: Vec) -> anyhow::Result<()>; + fn add(&mut self, key: String, value: Vec) -> anyhow::Result<()>; - fn add_currency(&self, currency: &Currency) -> anyhow::Result<()>; + fn add_currency(&mut self, currency: &Currency) -> anyhow::Result<()>; - fn add_instrument(&self, instrument: &InstrumentAny) -> anyhow::Result<()>; + fn add_instrument(&mut self, instrument: &InstrumentAny) -> anyhow::Result<()>; - fn add_synthetic(&self, synthetic: &SyntheticInstrument) -> anyhow::Result<()>; + fn add_synthetic(&mut self, synthetic: &SyntheticInstrument) -> anyhow::Result<()>; - fn add_account(&self, account: &dyn Account) -> anyhow::Result>; + fn add_account(&mut self, account: &dyn Account) -> anyhow::Result>; - fn add_order(&self, order: &OrderAny) -> anyhow::Result<()>; + fn add_order(&mut self, order: &OrderAny) -> anyhow::Result<()>; - fn add_position(&self, position: &Position) -> anyhow::Result<()>; + fn add_position(&mut self, position: &Position) -> anyhow::Result<()>; fn index_venue_order_id( - &self, + &mut self, client_order_id: ClientOrderId, venue_order_id: VenueOrderId, ) -> anyhow::Result<()>; fn index_order_position( - &self, + &mut self, client_order_id: ClientOrderId, position_id: PositionId, ) -> anyhow::Result<()>; - fn update_actor(&self) -> anyhow::Result<()>; + fn update_actor(&mut self) -> anyhow::Result<()>; - fn update_strategy(&self) -> anyhow::Result<()>; + fn update_strategy(&mut self) -> anyhow::Result<()>; - fn update_account(&self, account: &dyn Account) -> anyhow::Result<()>; + fn update_account(&mut self, account: &dyn Account) -> anyhow::Result<()>; - fn update_order(&self, order: &OrderAny) -> anyhow::Result<()>; + fn update_order(&mut self, order: &OrderAny) -> anyhow::Result<()>; - fn update_position(&self, position: &Position) -> anyhow::Result<()>; + fn update_position(&mut self, position: &Position) -> anyhow::Result<()>; - fn snapshot_order_state(&self, order: &OrderAny) -> anyhow::Result<()>; + fn snapshot_order_state(&mut self, order: &OrderAny) -> anyhow::Result<()>; - fn snapshot_position_state(&self, position: &Position) -> anyhow::Result<()>; + fn snapshot_position_state(&mut self, position: &Position) -> anyhow::Result<()>; - fn heartbeat(&self, timestamp: UnixNanos) -> anyhow::Result<()>; + fn heartbeat(&mut self, timestamp: UnixNanos) -> anyhow::Result<()>; } diff --git a/nautilus_core/infrastructure/src/redis/cache.rs b/nautilus_core/infrastructure/src/redis/cache.rs index 9903ca0ecb95..c16cbd73318d 100644 --- a/nautilus_core/infrastructure/src/redis/cache.rs +++ b/nautilus_core/infrastructure/src/redis/cache.rs @@ -20,12 +20,26 @@ use std::{ time::{Duration, Instant}, }; -use nautilus_common::enums::SerializationEncoding; -use nautilus_core::{correctness::check_slice_not_empty, uuid::UUID4}; -use nautilus_model::identifiers::trader_id::TraderId; +use nautilus_common::{ + cache::database::CacheDatabaseAdapter, enums::SerializationEncoding, + interface::account::Account, +}; +use nautilus_core::{correctness::check_slice_not_empty, nanos::UnixNanos, uuid::UUID4}; +use nautilus_model::{ + identifiers::{ + account_id::AccountId, client_id::ClientId, client_order_id::ClientOrderId, + component_id::ComponentId, instrument_id::InstrumentId, position_id::PositionId, + strategy_id::StrategyId, trader_id::TraderId, venue_order_id::VenueOrderId, + }, + instruments::{any::InstrumentAny, synthetic::SyntheticInstrument}, + orders::any::OrderAny, + position::Position, + types::currency::Currency, +}; use redis::{Commands, Connection, Pipeline}; use serde_json::{json, Value}; use tracing::{debug, error}; +use ustr::Ustr; use crate::redis::{create_redis_connection, get_buffer_interval}; @@ -667,6 +681,189 @@ pub struct RedisCacheDatabaseAdapter { database: RedisCacheDatabase, } +#[allow(dead_code)] // Under development +#[allow(unused)] // Under development +impl CacheDatabaseAdapter for RedisCacheDatabaseAdapter { + fn close(&mut self) -> anyhow::Result<()> { + self.database.close() + } + + fn flush(&mut self) -> anyhow::Result<()> { + self.database.flushdb() + } + + fn load(&mut self) -> anyhow::Result>> { + // self.database.load() + Ok(HashMap::new()) // TODO + } + + fn load_currencies(&mut self) -> anyhow::Result> { + let mut currencies = HashMap::new(); + + for key in self.database.keys(&format!("{CURRENCIES}*"))? { + let parts: Vec<&str> = key.as_str().rsplitn(2, ':').collect(); + let currency_code = Ustr::from(parts.first().unwrap()); + let currency = self.load_currency(¤cy_code)?; + currencies.insert(currency_code, currency); + } + + Ok(currencies) + } + + fn load_instruments(&mut self) -> anyhow::Result> { + todo!() + } + + fn load_synthetics(&mut self) -> anyhow::Result> { + todo!() + } + + fn load_accounts(&mut self) -> anyhow::Result>> { + todo!() + } + + fn load_orders(&mut self) -> anyhow::Result> { + todo!() + } + + fn load_positions(&mut self) -> anyhow::Result> { + todo!() + } + + fn load_index_order_position(&mut self) -> anyhow::Result> { + todo!() + } + + fn load_index_order_client(&mut self) -> anyhow::Result> { + todo!() + } + + fn load_currency(&mut self, code: &Ustr) -> anyhow::Result { + todo!() + } + + fn load_instrument(&mut self, instrument_id: &InstrumentId) -> anyhow::Result { + todo!() + } + + fn load_synthetic( + &mut self, + instrument_id: &InstrumentId, + ) -> anyhow::Result { + todo!() + } + + fn load_account(&mut self, account_id: &AccountId) -> anyhow::Result<()> { + todo!() + } + + fn load_order(&mut self, client_order_id: &ClientOrderId) -> anyhow::Result { + todo!() + } + + fn load_position(&mut self, position_id: &PositionId) -> anyhow::Result { + todo!() + } + + fn load_actor( + &mut self, + component_id: &ComponentId, + ) -> anyhow::Result>> { + todo!() + } + + fn delete_actor(&mut self, component_id: &ComponentId) -> anyhow::Result<()> { + todo!() + } + + fn load_strategy( + &mut self, + strategy_id: &StrategyId, + ) -> anyhow::Result>> { + todo!() + } + + fn delete_strategy(&mut self, component_id: &StrategyId) -> anyhow::Result<()> { + todo!() + } + + fn add(&mut self, key: String, value: Vec) -> anyhow::Result<()> { + todo!() + } + + fn add_currency(&mut self, currency: &Currency) -> anyhow::Result<()> { + todo!() + } + + fn add_instrument(&mut self, instrument: &InstrumentAny) -> anyhow::Result<()> { + todo!() + } + + fn add_synthetic(&mut self, synthetic: &SyntheticInstrument) -> anyhow::Result<()> { + todo!() + } + + fn add_account(&mut self, account: &dyn Account) -> anyhow::Result> { + todo!() + } + + fn add_order(&mut self, order: &OrderAny) -> anyhow::Result<()> { + todo!() + } + + fn add_position(&mut self, position: &Position) -> anyhow::Result<()> { + todo!() + } + + fn index_venue_order_id( + &mut self, + client_order_id: ClientOrderId, + venue_order_id: VenueOrderId, + ) -> anyhow::Result<()> { + todo!() + } + + fn index_order_position( + &mut self, + client_order_id: ClientOrderId, + position_id: PositionId, + ) -> anyhow::Result<()> { + todo!() + } + + fn update_actor(&mut self) -> anyhow::Result<()> { + todo!() + } + + fn update_strategy(&mut self) -> anyhow::Result<()> { + todo!() + } + + fn update_account(&mut self, account: &dyn Account) -> anyhow::Result<()> { + todo!() + } + + fn update_order(&mut self, order: &OrderAny) -> anyhow::Result<()> { + todo!() + } + + fn update_position(&mut self, position: &Position) -> anyhow::Result<()> { + todo!() + } + + fn snapshot_order_state(&mut self, order: &OrderAny) -> anyhow::Result<()> { + todo!() + } + + fn snapshot_position_state(&mut self, position: &Position) -> anyhow::Result<()> { + todo!() + } + + fn heartbeat(&mut self, timestamp: UnixNanos) -> anyhow::Result<()> { + todo!() + } +} + //////////////////////////////////////////////////////////////////////////////// // Tests //////////////////////////////////////////////////////////////////////////////// From 3d8543c9a033677939d73f68d2766d172ee5f3d8 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Wed, 29 May 2024 19:12:31 +1000 Subject: [PATCH 51/59] Fix clippy lints --- nautilus_core/indicators/src/momentum/vhf.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nautilus_core/indicators/src/momentum/vhf.rs b/nautilus_core/indicators/src/momentum/vhf.rs index 63ac85dec78f..f344dc74a38d 100644 --- a/nautilus_core/indicators/src/momentum/vhf.rs +++ b/nautilus_core/indicators/src/momentum/vhf.rs @@ -100,14 +100,14 @@ impl VerticalHorizontalFilter { let max_price = self .prices .iter() - .cloned() + .copied() .fold(f64::NEG_INFINITY, f64::max); - let min_price = self.prices.iter().cloned().fold(f64::INFINITY, f64::min); + let min_price = self.prices.iter().copied().fold(f64::INFINITY, f64::min); self.ma.update_raw(f64::abs(close - self.previous_close)); if self.initialized { - self.value = f64::abs(max_price - min_price) / self.period as f64 / self.ma.value() + self.value = f64::abs(max_price - min_price) / self.period as f64 / self.ma.value(); } self.previous_close = close; From 80d52c3a422bc98ed8c54a5fa79ed4e923ac6874 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Thu, 30 May 2024 17:09:10 +1000 Subject: [PATCH 52/59] Update dependencies --- nautilus_core/Cargo.lock | 4 ++-- poetry.lock | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/nautilus_core/Cargo.lock b/nautilus_core/Cargo.lock index 543dc3597384..f7b9955c15ef 100644 --- a/nautilus_core/Cargo.lock +++ b/nautilus_core/Cargo.lock @@ -384,9 +384,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c90a406b4495d129f00461241616194cb8a032c8d1c53c657f0961d5f8e0498" +checksum = "cd066d0b4ef8ecb03a55319dc13aa6910616d0f44008a045bb1835af830abff5" dependencies = [ "bzip2", "flate2", diff --git a/poetry.lock b/poetry.lock index c4fa670036f5..ac2304fb65b2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1980,13 +1980,13 @@ files = [ [[package]] name = "requests" -version = "2.32.2" +version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" files = [ - {file = "requests-2.32.2-py3-none-any.whl", hash = "sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c"}, - {file = "requests-2.32.2.tar.gz", hash = "sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] From f5ab847f0373e187355e8d49de99131f0ca5b09e Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Thu, 30 May 2024 17:55:02 +1000 Subject: [PATCH 53/59] Refine floating point range checks with epsilon --- nautilus_core/core/src/correctness.rs | 29 ++++++++++++++++++++++- nautilus_trader/core/correctness.pyx | 6 ++++- tests/unit_tests/core/test_correctness.py | 5 +++- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/nautilus_core/core/src/correctness.rs b/nautilus_core/core/src/correctness.rs index cbd822f6ddd9..b20d8df4d1a1 100644 --- a/nautilus_core/core/src/correctness.rs +++ b/nautilus_core/core/src/correctness.rs @@ -163,10 +163,12 @@ pub fn check_in_range_inclusive_i64(value: i64, l: i64, r: i64, param: &str) -> /// Checks the `f64` value is in range [`l`, `r`] (inclusive). pub fn check_in_range_inclusive_f64(value: f64, l: f64, r: f64, param: &str) -> anyhow::Result<()> { + const EPSILON: f64 = 1e-15; // Epsilon to account for floating-point precision issues + if value.is_nan() || value.is_infinite() { anyhow::bail!("{FAILED} invalid f64 for '{param}', was {value}") } - if value < l || value > r { + if value < l - EPSILON || value > r + EPSILON { anyhow::bail!("{FAILED} invalid f64 for '{param}' not in range [{l}, {r}], was {value}") } Ok(()) @@ -478,6 +480,31 @@ mod tests { assert!(check_in_range_inclusive_i64(value, l, r, param).is_ok()); } + #[rstest] + #[case(0.0, 0.0, 0.0, "value")] + #[case(0.0, 0.0, 1.0, "value")] + #[case(1.0, 0.0, 1.0, "value")] + fn test_check_in_range_inclusive_f64_when_in_range( + #[case] value: f64, + #[case] l: f64, + #[case] r: f64, + #[case] param: &str, + ) { + assert!(check_in_range_inclusive_f64(value, l, r, param).is_ok()); + } + + #[rstest] + #[case(-1e16, 0.0, 0.0, "value")] + #[case(1.0 + 1e16, 0.0, 1.0, "value")] + fn test_check_in_range_inclusive_f64_when_out_of_range( + #[case] value: f64, + #[case] l: f64, + #[case] r: f64, + #[case] param: &str, + ) { + assert!(check_in_range_inclusive_f64(value, l, r, param).is_err()); + } + #[rstest] #[case(0, 1, 2, "value")] #[case(3, 1, 2, "value")] diff --git a/nautilus_trader/core/correctness.pyx b/nautilus_trader/core/correctness.pyx index 3b60cc33e508..f61100a9f026 100644 --- a/nautilus_trader/core/correctness.pyx +++ b/nautilus_trader/core/correctness.pyx @@ -698,6 +698,9 @@ cdef class Condition: """ Check the real number value is within the specified range (inclusive). + This function accounts for potential floating-point precision issues by using a small + epsilon value of 1e-15. + Parameters ---------- value : scalar @@ -717,7 +720,8 @@ cdef class Condition: If `value` is not within the range (inclusive of the end points). """ - if start <= value <= end: + cdef double epsilon = 1e-15 # Epsilon to account for floating-point precision issues + if start - epsilon <= value <= end + epsilon: return # Check passed raise make_exception( diff --git a/tests/unit_tests/core/test_correctness.py b/tests/unit_tests/core/test_correctness.py index 911654eff049..1242ef27cdd8 100644 --- a/tests/unit_tests/core/test_correctness.py +++ b/tests/unit_tests/core/test_correctness.py @@ -274,7 +274,10 @@ def test_positive_int_when_args_positive_does_nothing(self): @pytest.mark.parametrize( ("value", "start", "end"), - [[-1e-22, 0.0, 1.0], [1.0000001, 0.0, 1.0]], + [ + [-1e16, 0.0, 1.0], + [1 + 1e16, 0.0, 1.0], + ], ) def test_in_range_when_arg_out_of_range_raises_value_error(self, value, start, end): # Arrange, Act, Assert From 197e16b0e9e4b0a5b567db22658f8bf7f1e897b1 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Thu, 30 May 2024 18:00:21 +1000 Subject: [PATCH 54/59] Fix Binance instruments price and size precision parsing --- RELEASES.md | 1 + nautilus_trader/adapters/binance/futures/providers.py | 4 ++-- nautilus_trader/adapters/binance/spot/providers.py | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 0448b40374db..fdd80b46385a 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -19,6 +19,7 @@ None ### Fixes - Fixed `SimulatedExchange` processing of commands in real-time for sandbox mode - Fixed Bybit order book deltas parsing (was appending bid side twice) (#1668), thanks @davidsblom +- Fixed Binance instruments price and size precision parsing (was incorrectly stripping trailing zeros) - Fixed `BinanceBar` streaming feather writing (was not setting up writer) --- diff --git a/nautilus_trader/adapters/binance/futures/providers.py b/nautilus_trader/adapters/binance/futures/providers.py index 6ac222131697..5e5f1d6db9ff 100644 --- a/nautilus_trader/adapters/binance/futures/providers.py +++ b/nautilus_trader/adapters/binance/futures/providers.py @@ -251,8 +251,8 @@ def _parse_instrument( BinanceSymbolFilterType.MIN_NOTIONAL, ) - tick_size = price_filter.tickSize.rstrip("0") - step_size = lot_size_filter.stepSize.rstrip("0") + tick_size = price_filter.tickSize + step_size = lot_size_filter.stepSize PyCondition.in_range(float(tick_size), PRICE_MIN, PRICE_MAX, "tick_size") PyCondition.in_range(float(step_size), QUANTITY_MIN, QUANTITY_MAX, "step_size") diff --git a/nautilus_trader/adapters/binance/spot/providers.py b/nautilus_trader/adapters/binance/spot/providers.py index 76631cdd3f9e..1595476d5932 100644 --- a/nautilus_trader/adapters/binance/spot/providers.py +++ b/nautilus_trader/adapters/binance/spot/providers.py @@ -237,8 +237,8 @@ def _parse_instrument( ) # market_lot_size_filter = symbol_filters.get("MARKET_LOT_SIZE") - tick_size = price_filter.tickSize.rstrip("0") - step_size = lot_size_filter.stepSize.rstrip("0") + tick_size = price_filter.tickSize + step_size = lot_size_filter.stepSize PyCondition.in_range(float(tick_size), PRICE_MIN, PRICE_MAX, "tick_size") PyCondition.in_range(float(step_size), QUANTITY_MIN, QUANTITY_MAX, "step_size") From 6f21dc7521339663be7e64b16fae0e3a194b404e Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Thu, 30 May 2024 18:22:01 +1000 Subject: [PATCH 55/59] Fix backtest high-level tutorial docs errors --- RELEASES.md | 1 + docs/concepts/data.md | 2 +- docs/tutorials/backtest_high_level.md | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index fdd80b46385a..fc2ff484c1aa 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -21,6 +21,7 @@ None - Fixed Bybit order book deltas parsing (was appending bid side twice) (#1668), thanks @davidsblom - Fixed Binance instruments price and size precision parsing (was incorrectly stripping trailing zeros) - Fixed `BinanceBar` streaming feather writing (was not setting up writer) +- Fixed backtest high-level tutorial documentation errors, thanks for reporting @Leonz5288 --- diff --git a/docs/concepts/data.md b/docs/concepts/data.md index 9964a606c860..c53d751cde3c 100644 --- a/docs/concepts/data.md +++ b/docs/concepts/data.md @@ -199,7 +199,7 @@ from nautilus_trader.model.data import OrderBookDelta data_config = BacktestDataConfig( catalog_path=str(catalog.path), data_cls=OrderBookDelta, - instrument_id=instrument.id.value, + instrument_id=instrument.id, start_time=start, end_time=end, ) diff --git a/docs/tutorials/backtest_high_level.md b/docs/tutorials/backtest_high_level.md index 092cc2fa4c27..bd3db45b4029 100644 --- a/docs/tutorials/backtest_high_level.md +++ b/docs/tutorials/backtest_high_level.md @@ -63,7 +63,7 @@ Then we can create Nautilus `QuoteTick` objects by processing the DataFrame with ```python # Here we just take the first data file found and load into a pandas DataFrame -df = CSVTickDataLoader.load(raw_files[0], index_col=0, format="%Y%m%d %H%M%S%f") +df = CSVTickDataLoader.load(raw_files[0], index_col=0, datetime_format="%Y%m%d %H%M%S%f") df.columns = ["bid_price", "ask_price"] # Process quote ticks using a wrangler @@ -141,7 +141,7 @@ data_configs = [ BacktestDataConfig( catalog_path=str(ParquetDataCatalog.from_env().path), data_cls=QuoteTick, - instrument_id=instrument.id.value, + instrument_id=instrument.id, start_time=start, end_time=end, ), @@ -152,7 +152,7 @@ strategies = [ strategy_path="nautilus_trader.examples.strategies.ema_cross:EMACross", config_path="nautilus_trader.examples.strategies.ema_cross:EMACrossConfig", config=dict( - instrument_id=instrument.id.value, + instrument_id=instrument.id, bar_type="EUR/USD.SIM-15-MINUTE-BID-INTERNAL", fast_ema_period=10, slow_ema_period=20, From 46935384a23ae3ad307411d8dbec4565029e9bdc Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Fri, 31 May 2024 17:47:13 +1000 Subject: [PATCH 56/59] Upgrade tokio --- nautilus_core/Cargo.lock | 8 ++++---- nautilus_core/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/nautilus_core/Cargo.lock b/nautilus_core/Cargo.lock index f7b9955c15ef..61ef3ca14afa 100644 --- a/nautilus_core/Cargo.lock +++ b/nautilus_core/Cargo.lock @@ -4826,9 +4826,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", @@ -4845,9 +4845,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", diff --git a/nautilus_core/Cargo.toml b/nautilus_core/Cargo.toml index d4d25459a273..a43a67496d70 100644 --- a/nautilus_core/Cargo.toml +++ b/nautilus_core/Cargo.toml @@ -48,7 +48,7 @@ strum = { version = "0.26.2", features = ["derive"] } thiserror = "1.0.61" thousands = "0.2.0" tracing = "0.1.40" -tokio = { version = "1.37.0", features = ["full"] } +tokio = { version = "1.38.0", features = ["full"] } ustr = { version = "1.0.0", features = ["serde"] } uuid = { version = "1.8.0", features = ["v4"] } From 092ac2c78c654d0f287eda6d9b4013e3c88a0997 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Fri, 31 May 2024 17:58:55 +1000 Subject: [PATCH 57/59] Improve cannot unsubscribe log message --- nautilus_trader/adapters/binance/websocket/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nautilus_trader/adapters/binance/websocket/client.py b/nautilus_trader/adapters/binance/websocket/client.py index 24e9d6c391d2..152e5635506a 100644 --- a/nautilus_trader/adapters/binance/websocket/client.py +++ b/nautilus_trader/adapters/binance/websocket/client.py @@ -496,7 +496,7 @@ async def _subscribe_all(self) -> None: async def _unsubscribe(self, stream: str) -> None: if stream not in self._streams: - self._log.warning(f"Cannot unsubscribe from {stream}: never subscribed") + self._log.warning(f"Cannot unsubscribe from {stream}: not subscribed") return # Not subscribed self._streams.remove(stream) From cbb6dd5dc69bcba2b29fe4e26e3c58c7389bf98a Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Fri, 31 May 2024 18:10:15 +1000 Subject: [PATCH 58/59] Fix DataEngine unsubscribe handling --- RELEASES.md | 1 + nautilus_trader/data/engine.pyx | 21 ++++++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index fc2ff484c1aa..eb19249f694d 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -18,6 +18,7 @@ None ### Fixes - Fixed `SimulatedExchange` processing of commands in real-time for sandbox mode +- Fixed `DataEngine` unsubscribe handling (edge case would attempt to unsubscribe from the client multiple times) - Fixed Bybit order book deltas parsing (was appending bid side twice) (#1668), thanks @davidsblom - Fixed Binance instruments price and size precision parsing (was incorrectly stripping trailing zeros) - Fixed `BinanceBar` streaming feather writing (was not setting up writer) diff --git a/nautilus_trader/data/engine.pyx b/nautilus_trader/data/engine.pyx index 6d04e9fd2b0e..d3f7aa0d6273 100644 --- a/nautilus_trader/data/engine.pyx +++ b/nautilus_trader/data/engine.pyx @@ -1059,7 +1059,8 @@ cdef class DataEngine(Component): if instrument_id is None: if not self._msgbus.has_subscribers(f"data.instrument.{client.id.value}.*"): - client.unsubscribe_instruments() + if client.subscribed_instruments(): + client.unsubscribe_instruments() return else: if instrument_id.is_synthetic(): @@ -1071,7 +1072,8 @@ cdef class DataEngine(Component): f".{instrument_id.venue}" f".{instrument_id.symbol}", ): - client.unsubscribe_instrument(instrument_id) + if instrument_id in client.subscribed_instruments(): + client.unsubscribe_instrument(instrument_id) cpdef void _handle_unsubscribe_order_book_deltas( self, @@ -1092,7 +1094,8 @@ cdef class DataEngine(Component): f".{instrument_id.venue}" f".{instrument_id.symbol}", ): - client.unsubscribe_order_book_deltas(instrument_id) + if instrument_id in client.subscribed_order_book_deltas(): + client.unsubscribe_order_book_deltas(instrument_id) cpdef void _handle_unsubscribe_order_book_snapshots( self, @@ -1113,7 +1116,8 @@ cdef class DataEngine(Component): f".{instrument_id.venue}" f".{instrument_id.symbol}", ): - client.unsubscribe_order_book_snapshots(instrument_id) + if instrument_id in client.subscribed_order_book_snapshots(): + client.unsubscribe_order_book_snapshots(instrument_id) cpdef void _handle_unsubscribe_quote_ticks( self, @@ -1128,7 +1132,8 @@ cdef class DataEngine(Component): f".{instrument_id.venue}" f".{instrument_id.symbol}", ): - client.unsubscribe_quote_ticks(instrument_id) + if instrument_id in client.subscribed_quote_ticks(): + client.unsubscribe_quote_ticks(instrument_id) cpdef void _handle_unsubscribe_trade_ticks( self, @@ -1143,7 +1148,8 @@ cdef class DataEngine(Component): f".{instrument_id.venue}" f".{instrument_id.symbol}", ): - client.unsubscribe_trade_ticks(instrument_id) + if instrument_id in client.subscribed_trade_ticks(): + client.unsubscribe_trade_ticks(instrument_id) cpdef void _handle_unsubscribe_bars( self, @@ -1175,7 +1181,8 @@ cdef class DataEngine(Component): try: if not self._msgbus.has_subscribers(f"data.{data_type}"): - client.unsubscribe(data_type) + if data_type in client.subscribed_custom_data(): + client.unsubscribe(data_type) except NotImplementedError: self._log.error( f"Cannot unsubscribe: {client.id.value} " From f10b5da5f400375fa323a306b8dccd47812f241b Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Fri, 31 May 2024 18:14:32 +1000 Subject: [PATCH 59/59] Update release notes --- RELEASES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index eb19249f694d..c298ed67395e 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,6 +1,6 @@ # NautilusTrader 1.194.0 Beta -Released on TBD (UTC). +Released on 31st May 2024 (UTC). ### Enhancements - Added `DataEngine` order book deltas buffering to `F_LAST` flag (#1673), thanks @davidsblom