From 06e249fd85b657e15569e32f4a3c0818e030451f Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Mon, 20 May 2024 12:29:28 +1000 Subject: [PATCH 01/28] Update GitHub workflows --- .github/workflows/build-wheels.yml | 2 +- .github/workflows/build.yml | 7 +++---- .github/workflows/coverage.yml | 2 +- .github/workflows/docs.yml | 4 ++-- .github/workflows/release.yml | 8 ++++---- 5 files changed, 11 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml index 5c2111b55af0..f9b7dbbe9ed7 100644 --- a/.github/workflows/build-wheels.yml +++ b/.github/workflows/build-wheels.yml @@ -44,7 +44,7 @@ jobs: - name: Set up Rust tool-chain (Linux, Windows) stable if: (runner.os == 'Linux') || (runner.os == 'Windows') - uses: actions-rust-lang/setup-rust-toolchain@v1.5 + uses: actions-rust-lang/setup-rust-toolchain@v1.8 with: toolchain: ${{ env.RUST_VERSION }} components: rustfmt, clippy diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 549521782cf4..ede213706e08 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -69,7 +69,7 @@ jobs: working-directory: ${{ github.workspace }} - name: Set up Rust tool-chain (Linux, Windows) stable - uses: actions-rust-lang/setup-rust-toolchain@v1.5 + uses: actions-rust-lang/setup-rust-toolchain@v1.8 with: toolchain: ${{ env.RUST_VERSION }} components: rustfmt, clippy @@ -175,7 +175,7 @@ jobs: working-directory: ${{ github.workspace }} - name: Set up Rust tool-chain (Linux, Windows) stable - uses: actions-rust-lang/setup-rust-toolchain@v1.5 + uses: actions-rust-lang/setup-rust-toolchain@v1.8 with: toolchain: ${{ env.RUST_VERSION }} components: rustfmt, clippy @@ -234,8 +234,7 @@ jobs: PARALLEL_BUILD: false build-macos: - if: github.ref == 'refs/heads/master' - # if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/nightly' + if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/nightly' strategy: fail-fast: false matrix: diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 9f1c4cd1133e..6d674aaf8140 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -61,7 +61,7 @@ jobs: working-directory: ${{ github.workspace }} - name: Set up Rust tool-chain (Linux, Windows) stable - uses: actions-rust-lang/setup-rust-toolchain@v1.5 + uses: actions-rust-lang/setup-rust-toolchain@v1.8 with: toolchain: ${{ env.RUST_VERSION }} components: rustfmt, clippy diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 0066f5bdc578..6fae89a600c7 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -37,13 +37,13 @@ jobs: - name: Set up Rust tool-chain (Linux, Windows) stable if: (runner.os == 'Linux') || (runner.os == 'Windows') - uses: actions-rust-lang/setup-rust-toolchain@v1.5 + uses: actions-rust-lang/setup-rust-toolchain@v1.8 with: toolchain: ${{ env.RUST_VERSION }} components: rustfmt, clippy - name: Set up Rust tool-chain (nightly) - uses: actions-rust-lang/setup-rust-toolchain@v1.5 + uses: actions-rust-lang/setup-rust-toolchain@v1.8 with: toolchain: nightly components: rustfmt, clippy diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 83e28c65d379..978f69b3305c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -55,7 +55,7 @@ jobs: - name: Set up Rust tool-chain (Linux, Windows) stable if: (runner.os == 'Linux') || (runner.os == 'Windows') - uses: actions-rust-lang/setup-rust-toolchain@v1.5 + uses: actions-rust-lang/setup-rust-toolchain@v1.8 with: toolchain: ${{ env.RUST_VERSION }} components: rustfmt, clippy @@ -127,7 +127,7 @@ jobs: working-directory: ${{ github.workspace }} - name: Set up Rust tool-chain - uses: actions-rust-lang/setup-rust-toolchain@v1.5 + uses: actions-rust-lang/setup-rust-toolchain@v1.8 with: toolchain: ${{ env.RUST_VERSION }} components: rustfmt, clippy @@ -221,7 +221,7 @@ jobs: working-directory: ${{ github.workspace }} - name: Set up Rust tool-chain - uses: actions-rust-lang/setup-rust-toolchain@v1.5 + uses: actions-rust-lang/setup-rust-toolchain@v1.8 with: toolchain: ${{ env.RUST_VERSION }} components: rustfmt, clippy @@ -329,7 +329,7 @@ jobs: - name: Set up Rust tool-chain (Linux, Windows) stable if: (runner.os == 'Linux') || (runner.os == 'Windows') - uses: actions-rust-lang/setup-rust-toolchain@v1.5 + uses: actions-rust-lang/setup-rust-toolchain@v1.8 with: toolchain: ${{ env.RUST_VERSION }} components: rustfmt, clippy From 67c982750b67037eac66742b596822e580cf670c Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Mon, 20 May 2024 12:35:04 +1000 Subject: [PATCH 02/28] Bump version --- README.md | 1 - RELEASES.md | 15 ++++++ nautilus_core/Cargo.lock | 108 +++++++++++++++++++-------------------- nautilus_core/Cargo.toml | 4 +- poetry.lock | 18 +++---- pyproject.toml | 2 +- version.json | 2 +- 7 files changed, 79 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index 6b847051e5cb..7b4a7a2c1ce8 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,6 @@ | Platform | Rust | Python | | :----------------- | :------ | :----- | | `Linux (x86_64)` | 1.78.0+ | 3.10+ | -| `macOS (x86_64)` | 1.78.0+ | 3.10+ | | `macOS (arm64)` | 1.78.0+ | 3.10+ | | `Windows (x86_64)` | 1.78.0+ | 3.10+ | diff --git a/RELEASES.md b/RELEASES.md index d3e32439bb00..245bb71f4315 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,18 @@ +# NautilusTrader 1.193.0 Beta + +Released on TBD (UTC). + +### Enhancements +None + +### Breaking Changes +None + +### Fixes +None + +--- + # NautilusTrader 1.192.0 Beta Released on 18th May 2024 (UTC). diff --git a/nautilus_core/Cargo.lock b/nautilus_core/Cargo.lock index 2bedd5d2b222..e558f6aefff9 100644 --- a/nautilus_core/Cargo.lock +++ b/nautilus_core/Cargo.lock @@ -144,9 +144,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.84" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18b8795de6d09abb2b178fa5a9e3bb10da935750f33449a132b328b9391b2c6a" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arc-swap" @@ -408,7 +408,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -596,7 +596,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", "syn_derive", ] @@ -715,9 +715,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" dependencies = [ "jobserver", "libc", @@ -845,7 +845,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -1051,9 +1051,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" @@ -1113,7 +1113,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -1124,7 +1124,7 @@ checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -1467,7 +1467,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -1509,7 +1509,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -1519,7 +1519,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -1786,7 +1786,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -1837,9 +1837,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -2406,9 +2406,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libm" @@ -2565,7 +2565,7 @@ dependencies = [ [[package]] name = "nautilus-accounting" -version = "0.22.0" +version = "0.23.0" dependencies = [ "anyhow", "cbindgen", @@ -2581,7 +2581,7 @@ dependencies = [ [[package]] name = "nautilus-adapters" -version = "0.22.0" +version = "0.23.0" dependencies = [ "anyhow", "chrono", @@ -2612,7 +2612,7 @@ dependencies = [ [[package]] name = "nautilus-backtest" -version = "0.22.0" +version = "0.23.0" dependencies = [ "anyhow", "cbindgen", @@ -2629,7 +2629,7 @@ dependencies = [ [[package]] name = "nautilus-cli" -version = "0.22.0" +version = "0.23.0" dependencies = [ "anyhow", "clap 4.5.4", @@ -2646,7 +2646,7 @@ dependencies = [ [[package]] name = "nautilus-common" -version = "0.22.0" +version = "0.23.0" dependencies = [ "anyhow", "cbindgen", @@ -2674,7 +2674,7 @@ dependencies = [ [[package]] name = "nautilus-core" -version = "0.22.0" +version = "0.23.0" dependencies = [ "anyhow", "cbindgen", @@ -2694,7 +2694,7 @@ dependencies = [ [[package]] name = "nautilus-execution" -version = "0.22.0" +version = "0.23.0" dependencies = [ "anyhow", "criterion", @@ -2719,7 +2719,7 @@ dependencies = [ [[package]] name = "nautilus-indicators" -version = "0.22.0" +version = "0.23.0" dependencies = [ "anyhow", "nautilus-core", @@ -2731,7 +2731,7 @@ dependencies = [ [[package]] name = "nautilus-infrastructure" -version = "0.22.0" +version = "0.23.0" dependencies = [ "anyhow", "log", @@ -2754,7 +2754,7 @@ dependencies = [ [[package]] name = "nautilus-model" -version = "0.22.0" +version = "0.23.0" dependencies = [ "anyhow", "cbindgen", @@ -2782,7 +2782,7 @@ dependencies = [ [[package]] name = "nautilus-network" -version = "0.22.0" +version = "0.23.0" dependencies = [ "anyhow", "axum", @@ -2807,7 +2807,7 @@ dependencies = [ [[package]] name = "nautilus-persistence" -version = "0.22.0" +version = "0.23.0" dependencies = [ "anyhow", "binary-heap-plus", @@ -2830,7 +2830,7 @@ dependencies = [ [[package]] name = "nautilus-pyo3" -version = "0.22.0" +version = "0.23.0" dependencies = [ "nautilus-accounting", "nautilus-adapters", @@ -3004,7 +3004,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -3081,7 +3081,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -3297,7 +3297,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -3430,9 +3430,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" dependencies = [ "unicode-ident", ] @@ -3556,7 +3556,7 @@ dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -3569,7 +3569,7 @@ dependencies = [ "proc-macro2", "pyo3-build-config", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -3961,7 +3961,7 @@ dependencies = [ "regex", "relative-path", "rustc_version", - "syn 2.0.64", + "syn 2.0.65", "unicode-ident", ] @@ -4191,7 +4191,7 @@ checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -4249,7 +4249,7 @@ checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -4439,7 +4439,7 @@ checksum = "01b2e185515564f15375f593fb966b5718bc624ba77fe49fa4616ad619690554" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -4690,7 +4690,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -4712,9 +4712,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.64" +version = "2.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f" +checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" dependencies = [ "proc-macro2", "quote", @@ -4730,7 +4730,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -4861,7 +4861,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -4985,7 +4985,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -5135,7 +5135,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -5255,7 +5255,7 @@ checksum = "1f718dfaf347dcb5b983bfc87608144b0bad87970aebcbea5ce44d2a30c08e63" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] @@ -5442,7 +5442,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", "wasm-bindgen-shared", ] @@ -5476,7 +5476,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5788,7 +5788,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.65", ] [[package]] diff --git a/nautilus_core/Cargo.toml b/nautilus_core/Cargo.toml index 253345f20588..234c015038ea 100644 --- a/nautilus_core/Cargo.toml +++ b/nautilus_core/Cargo.toml @@ -19,14 +19,14 @@ members = [ [workspace.package] rust-version = "1.78.0" -version = "0.22.0" +version = "0.23.0" edition = "2021" authors = ["Nautech Systems "] description = "A high-performance algorithmic trading platform and event-driven backtester" documentation = "https://docs.nautilustrader.io" [workspace.dependencies] -anyhow = "1.0.84" +anyhow = "1.0.86" chrono = "0.4.38" derive_builder = "0.20.0" futures = "0.3.30" diff --git a/poetry.lock b/poetry.lock index 13eabb26439c..b4e754164af6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -464,7 +464,7 @@ name = "css-html-js-minify" version = "2.5.5" description = "CSS HTML JS Minifier" optional = false -python-versions = ">=3.6" +python-versions = "*" files = [ {file = "css-html-js-minify-2.5.5.zip", hash = "sha256:4a9f11f7e0496f5284d12111f3ba4ff5ff2023d12f15d195c9c48bd97013746c"}, {file = "css_html_js_minify-2.5.5-py2.py3-none-any.whl", hash = "sha256:3da9d35ac0db8ca648c1b543e0e801d7ca0bab9e6bfd8418fee59d5ae001727a"}, @@ -939,9 +939,13 @@ files = [ {file = "lxml-5.2.2-cp36-cp36m-win_amd64.whl", hash = "sha256:edcfa83e03370032a489430215c1e7783128808fd3e2e0a3225deee278585196"}, {file = "lxml-5.2.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:28bf95177400066596cdbcfc933312493799382879da504633d16cf60bba735b"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a745cc98d504d5bd2c19b10c79c61c7c3df9222629f1b6210c0368177589fb8"}, + {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b590b39ef90c6b22ec0be925b211298e810b4856909c8ca60d27ffbca6c12e6"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b336b0416828022bfd5a2e3083e7f5ba54b96242159f83c7e3eebaec752f1716"}, + {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:c2faf60c583af0d135e853c86ac2735ce178f0e338a3c7f9ae8f622fd2eb788c"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:4bc6cb140a7a0ad1f7bc37e018d0ed690b7b6520ade518285dc3171f7a117905"}, + {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7ff762670cada8e05b32bf1e4dc50b140790909caa8303cfddc4d702b71ea184"}, {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:57f0a0bbc9868e10ebe874e9f129d2917750adf008fe7b9c1598c0fbbfdde6a6"}, + {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:a6d2092797b388342c1bc932077ad232f914351932353e2e8706851c870bca1f"}, {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:60499fe961b21264e17a471ec296dcbf4365fbea611bf9e303ab69db7159ce61"}, {file = "lxml-5.2.2-cp37-cp37m-win32.whl", hash = "sha256:d9b342c76003c6b9336a80efcc766748a333573abf9350f4094ee46b006ec18f"}, {file = "lxml-5.2.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b16db2770517b8799c79aa80f4053cd6f8b716f21f8aca962725a9565ce3ee40"}, @@ -1494,6 +1498,7 @@ files = [ {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, + {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"}, {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, @@ -1932,7 +1937,6 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -1940,16 +1944,8 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -1966,7 +1962,6 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -1974,7 +1969,6 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, diff --git a/pyproject.toml b/pyproject.toml index 7d9c61d321bc..dacbf2609186 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "nautilus_trader" -version = "1.192.0" +version = "1.193.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 5695ec227447..43ccf52343e0 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, "label": "", - "message": "v1.192.0", + "message": "v1.193.0", "color": "orange" } From 5a3fe838d4f8f638f7b894cbb4cc316eb272cf56 Mon Sep 17 00:00:00 2001 From: Filip Macek Date: Mon, 20 May 2024 04:44:44 +0200 Subject: [PATCH 03/28] Fix order event object conversion (#1649) --- nautilus_core/model/src/python/events/order/accepted.rs | 4 ---- .../model/src/python/events/order/cancel_rejected.rs | 4 ---- nautilus_core/model/src/python/events/order/canceled.rs | 4 ---- nautilus_core/model/src/python/events/order/denied.rs | 4 ---- nautilus_core/model/src/python/events/order/emulated.rs | 4 ---- nautilus_core/model/src/python/events/order/expired.rs | 4 ---- nautilus_core/model/src/python/events/order/filled.rs | 4 ---- nautilus_core/model/src/python/events/order/initialized.rs | 4 ---- nautilus_core/model/src/python/events/order/mod.rs | 3 ++- .../model/src/python/events/order/modify_rejected.rs | 4 ---- nautilus_core/model/src/python/events/order/pending_cancel.rs | 4 ---- nautilus_core/model/src/python/events/order/pending_update.rs | 4 ---- nautilus_core/model/src/python/events/order/rejected.rs | 4 ---- nautilus_core/model/src/python/events/order/released.rs | 4 ---- nautilus_core/model/src/python/events/order/submitted.rs | 4 ---- nautilus_core/model/src/python/events/order/triggered.rs | 4 ---- nautilus_core/model/src/python/events/order/updated.rs | 4 ---- 17 files changed, 2 insertions(+), 65 deletions(-) diff --git a/nautilus_core/model/src/python/events/order/accepted.rs b/nautilus_core/model/src/python/events/order/accepted.rs index e750cf98fa6f..5cf88a6710ba 100644 --- a/nautilus_core/model/src/python/events/order/accepted.rs +++ b/nautilus_core/model/src/python/events/order/accepted.rs @@ -74,10 +74,6 @@ impl OrderAccepted { self.to_string() } - fn type_str(&self) -> &str { - stringify!(OrderAccepted) - } - #[staticmethod] #[pyo3(name = "from_dict")] fn py_from_dict(py: Python<'_>, values: Py) -> PyResult { diff --git a/nautilus_core/model/src/python/events/order/cancel_rejected.rs b/nautilus_core/model/src/python/events/order/cancel_rejected.rs index 652cda8635f8..2faf3c0e96e6 100644 --- a/nautilus_core/model/src/python/events/order/cancel_rejected.rs +++ b/nautilus_core/model/src/python/events/order/cancel_rejected.rs @@ -80,10 +80,6 @@ impl OrderCancelRejected { self.to_string() } - fn type_str(&self) -> &str { - stringify!(OrderCancelRejected) - } - #[staticmethod] #[pyo3(name = "from_dict")] fn py_from_dict(py: Python<'_>, values: Py) -> PyResult { diff --git a/nautilus_core/model/src/python/events/order/canceled.rs b/nautilus_core/model/src/python/events/order/canceled.rs index fe80c4aec105..7ecf81c6e455 100644 --- a/nautilus_core/model/src/python/events/order/canceled.rs +++ b/nautilus_core/model/src/python/events/order/canceled.rs @@ -74,10 +74,6 @@ impl OrderCanceled { self.to_string() } - fn type_str(&self) -> &str { - stringify!(OrderCanceled) - } - #[staticmethod] #[pyo3(name = "from_dict")] fn py_from_dict(py: Python<'_>, values: Py) -> PyResult { diff --git a/nautilus_core/model/src/python/events/order/denied.rs b/nautilus_core/model/src/python/events/order/denied.rs index a02be3bb4c73..531ea3d255ab 100644 --- a/nautilus_core/model/src/python/events/order/denied.rs +++ b/nautilus_core/model/src/python/events/order/denied.rs @@ -74,10 +74,6 @@ impl OrderDenied { self.to_string() } - fn type_str(&self) -> &str { - stringify!(OrderDenied) - } - #[staticmethod] #[pyo3(name = "from_dict")] fn py_from_dict(py: Python<'_>, values: Py) -> PyResult { diff --git a/nautilus_core/model/src/python/events/order/emulated.rs b/nautilus_core/model/src/python/events/order/emulated.rs index 3129dfd1c052..3292b2cdda26 100644 --- a/nautilus_core/model/src/python/events/order/emulated.rs +++ b/nautilus_core/model/src/python/events/order/emulated.rs @@ -68,10 +68,6 @@ impl OrderEmulated { self.to_string() } - fn type_str(&self) -> &str { - stringify!(OrderEmulated) - } - #[staticmethod] #[pyo3(name = "from_dict")] fn py_from_dict(py: Python<'_>, values: Py) -> PyResult { diff --git a/nautilus_core/model/src/python/events/order/expired.rs b/nautilus_core/model/src/python/events/order/expired.rs index 225def43f502..5036125f87e6 100644 --- a/nautilus_core/model/src/python/events/order/expired.rs +++ b/nautilus_core/model/src/python/events/order/expired.rs @@ -74,10 +74,6 @@ impl OrderExpired { self.to_string() } - fn type_str(&self) -> &str { - stringify!(OrderExpired) - } - #[staticmethod] #[pyo3(name = "from_dict")] fn py_from_dict(py: Python<'_>, values: Py) -> PyResult { diff --git a/nautilus_core/model/src/python/events/order/filled.rs b/nautilus_core/model/src/python/events/order/filled.rs index c4d5fb288a1f..25fd0cb2fd54 100644 --- a/nautilus_core/model/src/python/events/order/filled.rs +++ b/nautilus_core/model/src/python/events/order/filled.rs @@ -95,10 +95,6 @@ impl OrderFilled { self.to_string() } - fn type_str(&self) -> &str { - stringify!(OrderFilled) - } - #[getter] #[pyo3(name = "is_buy")] fn py_is_buy(&self) -> bool { diff --git a/nautilus_core/model/src/python/events/order/initialized.rs b/nautilus_core/model/src/python/events/order/initialized.rs index 2f5db55042c5..e3a36cc08696 100644 --- a/nautilus_core/model/src/python/events/order/initialized.rs +++ b/nautilus_core/model/src/python/events/order/initialized.rs @@ -144,10 +144,6 @@ impl OrderInitialized { from_dict_pyo3(py, values) } - fn type_str(&self) -> &str { - stringify!(OrderInitiliazed) - } - #[pyo3(name = "to_dict")] fn py_to_dict(&self, py: Python<'_>) -> PyResult { let dict = PyDict::new(py); diff --git a/nautilus_core/model/src/python/events/order/mod.rs b/nautilus_core/model/src/python/events/order/mod.rs index b304d9d7c4ea..c8f9c5bdf33c 100644 --- a/nautilus_core/model/src/python/events/order/mod.rs +++ b/nautilus_core/model/src/python/events/order/mod.rs @@ -65,7 +65,8 @@ pub fn order_event_to_pyobject(py: Python, order_event: OrderEventAny) -> PyResu } pub fn pyobject_to_order_event(py: Python, order_event: PyObject) -> PyResult { - match order_event.getattr(py, "type_str")?.extract::<&str>(py)? { + let class = order_event.getattr(py, "__class__")?; + match class.getattr(py, "__name__")?.extract::<&str>(py)? { stringify!(OrderAccepted) => Ok(OrderEventAny::Accepted( order_event.extract::(py)?, )), diff --git a/nautilus_core/model/src/python/events/order/modify_rejected.rs b/nautilus_core/model/src/python/events/order/modify_rejected.rs index 1089c6665720..29d6f66df65a 100644 --- a/nautilus_core/model/src/python/events/order/modify_rejected.rs +++ b/nautilus_core/model/src/python/events/order/modify_rejected.rs @@ -80,10 +80,6 @@ impl OrderModifyRejected { self.to_string() } - fn type_str(&self) -> &str { - stringify!(OrderModifyRejected) - } - #[staticmethod] #[pyo3(name = "from_dict")] fn py_from_dict(py: Python<'_>, values: Py) -> PyResult { diff --git a/nautilus_core/model/src/python/events/order/pending_cancel.rs b/nautilus_core/model/src/python/events/order/pending_cancel.rs index a125b04c052e..02201516c4c1 100644 --- a/nautilus_core/model/src/python/events/order/pending_cancel.rs +++ b/nautilus_core/model/src/python/events/order/pending_cancel.rs @@ -74,10 +74,6 @@ impl OrderPendingCancel { self.to_string() } - fn type_str(&self) -> &str { - stringify!(OrderPendingCancel) - } - #[staticmethod] #[pyo3(name = "from_dict")] fn py_from_dict(py: Python<'_>, values: Py) -> PyResult { diff --git a/nautilus_core/model/src/python/events/order/pending_update.rs b/nautilus_core/model/src/python/events/order/pending_update.rs index ba8aee7caa12..699f7d9047f4 100644 --- a/nautilus_core/model/src/python/events/order/pending_update.rs +++ b/nautilus_core/model/src/python/events/order/pending_update.rs @@ -74,10 +74,6 @@ impl OrderPendingUpdate { self.to_string() } - fn type_str(&self) -> &str { - stringify!(OrderPendingUpdate) - } - #[staticmethod] #[pyo3(name = "from_dict")] fn py_from_dict(py: Python<'_>, values: Py) -> PyResult { diff --git a/nautilus_core/model/src/python/events/order/rejected.rs b/nautilus_core/model/src/python/events/order/rejected.rs index a35454c57c99..d552cf90d497 100644 --- a/nautilus_core/model/src/python/events/order/rejected.rs +++ b/nautilus_core/model/src/python/events/order/rejected.rs @@ -77,10 +77,6 @@ impl OrderRejected { self.to_string() } - fn type_str(&self) -> &str { - stringify!(OrderRejected) - } - #[staticmethod] #[pyo3(name = "from_dict")] fn py_from_dict(py: Python<'_>, values: Py) -> PyResult { diff --git a/nautilus_core/model/src/python/events/order/released.rs b/nautilus_core/model/src/python/events/order/released.rs index a118b63853f8..ed1eb3561d32 100644 --- a/nautilus_core/model/src/python/events/order/released.rs +++ b/nautilus_core/model/src/python/events/order/released.rs @@ -71,10 +71,6 @@ impl OrderReleased { self.to_string() } - fn type_str(&self) -> &str { - stringify!(OrderReleased) - } - #[staticmethod] #[pyo3(name = "from_dict")] fn py_from_dict(py: Python<'_>, values: Py) -> PyResult { diff --git a/nautilus_core/model/src/python/events/order/submitted.rs b/nautilus_core/model/src/python/events/order/submitted.rs index 8ee722a5d25b..c0c9a7c952fb 100644 --- a/nautilus_core/model/src/python/events/order/submitted.rs +++ b/nautilus_core/model/src/python/events/order/submitted.rs @@ -70,10 +70,6 @@ impl OrderSubmitted { self.to_string() } - fn type_str(&self) -> &str { - stringify!(OrderSubmitted) - } - #[staticmethod] #[pyo3(name = "from_dict")] fn py_from_dict(py: Python<'_>, values: Py) -> PyResult { diff --git a/nautilus_core/model/src/python/events/order/triggered.rs b/nautilus_core/model/src/python/events/order/triggered.rs index 82648358c8f4..0944aa042ccb 100644 --- a/nautilus_core/model/src/python/events/order/triggered.rs +++ b/nautilus_core/model/src/python/events/order/triggered.rs @@ -74,10 +74,6 @@ impl OrderTriggered { self.to_string() } - fn type_str(&self) -> &str { - stringify!(OrderTriggered) - } - #[staticmethod] #[pyo3(name = "from_dict")] fn py_from_dict(py: Python<'_>, values: Py) -> PyResult { diff --git a/nautilus_core/model/src/python/events/order/updated.rs b/nautilus_core/model/src/python/events/order/updated.rs index 365ce043d450..c1952fb69087 100644 --- a/nautilus_core/model/src/python/events/order/updated.rs +++ b/nautilus_core/model/src/python/events/order/updated.rs @@ -81,10 +81,6 @@ impl OrderUpdated { self.to_string() } - fn type_str(&self) -> &str { - stringify!(OrderUpdated) - } - #[staticmethod] #[pyo3(name = "from_dict")] fn py_from_dict(py: Python<'_>, values: Py) -> PyResult { From a3dc265f9fff9b174794bec5019d4a25a7b5afdb Mon Sep 17 00:00:00 2001 From: Ishan Bhanuka Date: Mon, 20 May 2024 22:10:50 -0400 Subject: [PATCH 04/28] Fix streaming order by ts_init when reading row groups (#1656) --- nautilus_core/persistence/src/backend/session.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nautilus_core/persistence/src/backend/session.rs b/nautilus_core/persistence/src/backend/session.rs index d5887b652325..5ec6ba3a2436 100644 --- a/nautilus_core/persistence/src/backend/session.rs +++ b/nautilus_core/persistence/src/backend/session.rs @@ -125,7 +125,7 @@ impl DataBackendSession { parquet_options, ))?; - let default_query = format!("SELECT * FROM {}", &table_name); + let default_query = format!("SELECT * FROM {} ORDER BY ts_init", &table_name); let sql_query = sql_query.unwrap_or(&default_query); let query = self.runtime.block_on(self.session_ctx.sql(sql_query))?; From 1669ecd320825a348ed43bb2688e4797661e2d01 Mon Sep 17 00:00:00 2001 From: Nisayo Date: Tue, 21 May 2024 04:20:35 +0200 Subject: [PATCH 05/28] Fix IB tick level historical data downloading (#1653) --- .../adapters/interactive_brokers/historic/client.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nautilus_trader/adapters/interactive_brokers/historic/client.py b/nautilus_trader/adapters/interactive_brokers/historic/client.py index 98d370e7aeb8..4ab4ab6f95c0 100644 --- a/nautilus_trader/adapters/interactive_brokers/historic/client.py +++ b/nautilus_trader/adapters/interactive_brokers/historic/client.py @@ -429,7 +429,8 @@ def _handle_timestamp_iteration( if min_timestamp.floor("S") == max_timestamp.floor("S"): max_timestamp = max_timestamp.floor("S") + pd.Timedelta(seconds=1) - + if len(ticks) <= 50: + max_timestamp = max_timestamp.floor("S") + pd.Timedelta(minutes=1) if max_timestamp >= end_date_time: return None, False From 43318f1dee76710a5cb57f92baf102e37242d2f8 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Tue, 21 May 2024 12:22:11 +1000 Subject: [PATCH 06/28] Update release notes --- RELEASES.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 245bb71f4315..e2f929fcf019 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -9,7 +9,8 @@ None None ### Fixes -None +- Fixed data order by `ts_init` when streaming (#1656), thanks @twitu +- Fixed Interactive Brokers tick level historical data downloading (#1653), thanks @DracheShiki --- From 8dafdac58d0a82b1a9efc38b3900c6d303fb3d35 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Tue, 21 May 2024 17:40:53 +1000 Subject: [PATCH 07/28] Update dependencies --- nautilus_core/Cargo.lock | 16 ++++++++-------- poetry.lock | 32 +++++++++++++++++++------------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/nautilus_core/Cargo.lock b/nautilus_core/Cargo.lock index e558f6aefff9..dc0cd0598f8e 100644 --- a/nautilus_core/Cargo.lock +++ b/nautilus_core/Cargo.lock @@ -978,9 +978,9 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] @@ -3341,9 +3341,9 @@ checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plotters" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" dependencies = [ "num-traits", "plotters-backend", @@ -3354,15 +3354,15 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" +checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" [[package]] name = "plotters-svg" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" dependencies = [ "plotters-backend", ] diff --git a/poetry.lock b/poetry.lock index b4e754164af6..979d688cea70 100644 --- a/poetry.lock +++ b/poetry.lock @@ -464,7 +464,7 @@ name = "css-html-js-minify" version = "2.5.5" description = "CSS HTML JS Minifier" optional = false -python-versions = "*" +python-versions = ">=3.6" files = [ {file = "css-html-js-minify-2.5.5.zip", hash = "sha256:4a9f11f7e0496f5284d12111f3ba4ff5ff2023d12f15d195c9c48bd97013746c"}, {file = "css_html_js_minify-2.5.5-py2.py3-none-any.whl", hash = "sha256:3da9d35ac0db8ca648c1b543e0e801d7ca0bab9e6bfd8418fee59d5ae001727a"}, @@ -939,13 +939,9 @@ files = [ {file = "lxml-5.2.2-cp36-cp36m-win_amd64.whl", hash = "sha256:edcfa83e03370032a489430215c1e7783128808fd3e2e0a3225deee278585196"}, {file = "lxml-5.2.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:28bf95177400066596cdbcfc933312493799382879da504633d16cf60bba735b"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a745cc98d504d5bd2c19b10c79c61c7c3df9222629f1b6210c0368177589fb8"}, - {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b590b39ef90c6b22ec0be925b211298e810b4856909c8ca60d27ffbca6c12e6"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b336b0416828022bfd5a2e3083e7f5ba54b96242159f83c7e3eebaec752f1716"}, - {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:c2faf60c583af0d135e853c86ac2735ce178f0e338a3c7f9ae8f622fd2eb788c"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:4bc6cb140a7a0ad1f7bc37e018d0ed690b7b6520ade518285dc3171f7a117905"}, - {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7ff762670cada8e05b32bf1e4dc50b140790909caa8303cfddc4d702b71ea184"}, {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:57f0a0bbc9868e10ebe874e9f129d2917750adf008fe7b9c1598c0fbbfdde6a6"}, - {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:a6d2092797b388342c1bc932077ad232f914351932353e2e8706851c870bca1f"}, {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:60499fe961b21264e17a471ec296dcbf4365fbea611bf9e303ab69db7159ce61"}, {file = "lxml-5.2.2-cp37-cp37m-win32.whl", hash = "sha256:d9b342c76003c6b9336a80efcc766748a333573abf9350f4094ee46b006ec18f"}, {file = "lxml-5.2.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b16db2770517b8799c79aa80f4053cd6f8b716f21f8aca962725a9565ce3ee40"}, @@ -1498,7 +1494,6 @@ files = [ {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, - {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"}, {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, @@ -1937,6 +1932,7 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -1944,8 +1940,16 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -1962,6 +1966,7 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -1969,6 +1974,7 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -1976,13 +1982,13 @@ files = [ [[package]] name = "requests" -version = "2.31.0" +version = "2.32.1" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.1-py3-none-any.whl", hash = "sha256:21ac9465cdf8c1650fe1ecde8a71669a93d4e6f147550483a2967d08396a56a5"}, + {file = "requests-2.32.1.tar.gz", hash = "sha256:eb97e87e64c79e64e5b8ac75cee9dd1f97f49e289b083ee6be96268930725685"}, ] [package.dependencies] @@ -2377,13 +2383,13 @@ files = [ [[package]] name = "types-requests" -version = "2.31.0.20240406" +version = "2.32.0.20240521" description = "Typing stubs for requests" optional = false python-versions = ">=3.8" files = [ - {file = "types-requests-2.31.0.20240406.tar.gz", hash = "sha256:4428df33c5503945c74b3f42e82b181e86ec7b724620419a2966e2de604ce1a1"}, - {file = "types_requests-2.31.0.20240406-py3-none-any.whl", hash = "sha256:6216cdac377c6b9a040ac1c0404f7284bd13199c0e1bb235f4324627e8898cf5"}, + {file = "types-requests-2.32.0.20240521.tar.gz", hash = "sha256:c5c4a0ae95aad51f1bf6dae9eed04a78f7f2575d4b171da37b622e08b93eb5d3"}, + {file = "types_requests-2.32.0.20240521-py3-none-any.whl", hash = "sha256:ab728ba43ffb073db31f21202ecb97db8753ded4a9dc49cb480d8a5350c5c421"}, ] [package.dependencies] From cec945ef399b7dbe8447f2cadb238acf27f848d2 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Tue, 21 May 2024 17:44:58 +1000 Subject: [PATCH 08/28] Fix backtest low level tutorial --- docs/tutorials/backtest_low_level.md | 37 ++++++++++++++-------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/docs/tutorials/backtest_low_level.md b/docs/tutorials/backtest_low_level.md index 1ce229d241d1..f0c2c104e1bd 100644 --- a/docs/tutorials/backtest_low_level.md +++ b/docs/tutorials/backtest_low_level.md @@ -27,6 +27,7 @@ from nautilus_trader.examples.strategies.ema_cross_twap import EMACrossTWAP from nautilus_trader.examples.strategies.ema_cross_twap import EMACrossTWAPConfig from nautilus_trader.model.currencies import ETH from nautilus_trader.model.currencies import USDT +from nautilus_trader.model.data import BarType from nautilus_trader.model.enums import AccountType from nautilus_trader.model.enums import OmsType from nautilus_trader.model.identifiers import TraderId @@ -82,24 +83,6 @@ engine = BacktestEngine(config=config) See the [Configuration](../api_reference/config.md) API reference for details of all configuration options available. -## Adding data - -Now we can add data to the backtest engine. First add the `Instrument` object we previously initialized, which matches our data. - -Then we can add the trade ticks we wrangled earlier: -```python -# Add instrument(s) -engine.add_instrument(ETHUSDT_BINANCE) - -# Add data -engine.add_data(ticks) - -``` - -```{note} -The amount of and variety of data types is only limited by machine resources and your imagination (custom types are possible). -``` - ## Adding venues We'll need a venue to trade on, which should match the *market* data being added to the engine. @@ -119,6 +102,24 @@ engine.add_venue( ``` +## Adding data + +Now we can add data to the backtest engine. First add the `Instrument` object we previously initialized, which matches our data. + +Then we can add the trade ticks we wrangled earlier: +```python +# Add instrument(s) +engine.add_instrument(ETHUSDT_BINANCE) + +# Add data +engine.add_data(ticks) + +``` + +```{note} +The amount of and variety of data types is only limited by machine resources and your imagination (custom types are possible). +``` + ```{note} Multiple venues can be used for backtesting, only limited by machine resources. ``` From 46f8a19ea5054dcf5fae806bb33fb15aaa392a67 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Tue, 21 May 2024 17:51:50 +1000 Subject: [PATCH 09/28] Fix OrderBookImbalance Rust strategy --- .../examples/strategies/orderbook_imbalance_rust.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/nautilus_trader/examples/strategies/orderbook_imbalance_rust.py b/nautilus_trader/examples/strategies/orderbook_imbalance_rust.py index 18288addad12..cbfe81a23d5b 100644 --- a/nautilus_trader/examples/strategies/orderbook_imbalance_rust.py +++ b/nautilus_trader/examples/strategies/orderbook_imbalance_rust.py @@ -20,11 +20,9 @@ from nautilus_trader.config import PositiveFloat from nautilus_trader.config import StrategyConfig from nautilus_trader.core import nautilus_pyo3 -from nautilus_trader.core.nautilus_pyo3 import BookImbalanceRatio from nautilus_trader.core.rust.common import LogColor from nautilus_trader.model.book import OrderBook from nautilus_trader.model.data import QuoteTick -from nautilus_trader.model.enums import BookType from nautilus_trader.model.enums import OrderSide from nautilus_trader.model.enums import TimeInForce from nautilus_trader.model.enums import book_type_from_str @@ -103,12 +101,12 @@ def __init__(self, config: OrderBookImbalanceConfig) -> None: self.instrument: Instrument | None = None if self.config.use_quote_ticks: assert self.config.book_type == "L1_MBP" - self.book_type: BookType = book_type_from_str(self.config.book_type) + self.book_type: nautilus_pyo3.BookType = nautilus_pyo3.BookType(self.config.book_type) # We need to initialize the Rust pyo3 objects pyo3_instrument_id = nautilus_pyo3.InstrumentId.from_str(self.instrument_id.value) self.book = nautilus_pyo3.OrderBook(self.book_type, pyo3_instrument_id) - self.imbalance = BookImbalanceRatio() + self.imbalance = nautilus_pyo3.BookImbalanceRatio() def on_start(self) -> None: """ @@ -121,7 +119,7 @@ def on_start(self) -> None: return if self.config.use_quote_ticks: - self.book_type = BookType.L1_MBP + self.book_type = nautilus_pyo3.BookType.L1_MBP self.subscribe_quote_ticks(self.instrument.id) else: self.book_type = book_type_from_str(self.config.book_type) From 826c05305580080d1e88441105a0270f50ff1c0f Mon Sep 17 00:00:00 2001 From: David Blom Date: Tue, 21 May 2024 10:08:39 +0200 Subject: [PATCH 10/28] Add account settings to SandboxExecutionClient (#1658) --- nautilus_trader/adapters/sandbox/config.py | 10 ++++++++++ nautilus_trader/adapters/sandbox/factory.py | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/nautilus_trader/adapters/sandbox/config.py b/nautilus_trader/adapters/sandbox/config.py index 6b1f17fb561b..0ba191c15bae 100644 --- a/nautilus_trader/adapters/sandbox/config.py +++ b/nautilus_trader/adapters/sandbox/config.py @@ -13,6 +13,7 @@ # limitations under the License. # ------------------------------------------------------------------------------------------------- + from nautilus_trader.config import LiveExecClientConfig @@ -30,6 +31,12 @@ class SandboxExecutionClientConfig(LiveExecClientConfig, frozen=True, kw_only=Tr The starting balance for this venue bar_execution: bool If bars should be processed by the matching engine(s) (and move the market). + default_leverage: float + The account default leverage (for margin accounts). + oms_type: str + The order management system type used by the exchange. + account_type : str + The account type for the client. """ @@ -37,3 +44,6 @@ class SandboxExecutionClientConfig(LiveExecClientConfig, frozen=True, kw_only=Tr currency: str balance: int bar_execution: bool = True + default_leverage: float = 10.0 + oms_type: str = "NETTING" + account_type: str = "MARGIN" diff --git a/nautilus_trader/adapters/sandbox/factory.py b/nautilus_trader/adapters/sandbox/factory.py index 213e8c12271c..745f74c9859f 100644 --- a/nautilus_trader/adapters/sandbox/factory.py +++ b/nautilus_trader/adapters/sandbox/factory.py @@ -14,6 +14,7 @@ # ------------------------------------------------------------------------------------------------- import asyncio +from decimal import Decimal from nautilus_trader.adapters.sandbox.config import SandboxExecutionClientConfig from nautilus_trader.adapters.sandbox.execution import SandboxExecutionClient @@ -21,6 +22,8 @@ from nautilus_trader.common.component import LiveClock from nautilus_trader.common.component import MessageBus from nautilus_trader.live.factories import LiveExecClientFactory +from nautilus_trader.model.enums import AccountType +from nautilus_trader.model.enums import OmsType from nautilus_trader.portfolio import PortfolioFacade @@ -74,5 +77,8 @@ def create( # type: ignore balance=config.balance, currency=config.currency, bar_execution=config.bar_execution, + default_leverage=Decimal(config.default_leverage), + oms_type=OmsType[config.oms_type], + account_type=AccountType[config.account_type], ) return exec_client From c246ebd5a0c01514ccc85c7fd9701935655c2be0 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Tue, 21 May 2024 18:12:01 +1000 Subject: [PATCH 11/28] Improve SandboxExecutionClientConfig docstring --- nautilus_trader/adapters/sandbox/config.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/nautilus_trader/adapters/sandbox/config.py b/nautilus_trader/adapters/sandbox/config.py index 0ba191c15bae..de39ee4692b8 100644 --- a/nautilus_trader/adapters/sandbox/config.py +++ b/nautilus_trader/adapters/sandbox/config.py @@ -13,7 +13,6 @@ # limitations under the License. # ------------------------------------------------------------------------------------------------- - from nautilus_trader.config import LiveExecClientConfig @@ -24,18 +23,18 @@ class SandboxExecutionClientConfig(LiveExecClientConfig, frozen=True, kw_only=Tr Parameters ---------- venue : str - The venue to generate a sandbox execution client for - currency: str - The currency for this venue + The venue to generate a sandbox execution client for. + currency : str + The currency for this venue. balance : int - The starting balance for this venue - bar_execution: bool + The starting balance for this venue. + bar_execution : bool, default True If bars should be processed by the matching engine(s) (and move the market). - default_leverage: float + default_leverage : float, default 10.0 The account default leverage (for margin accounts). - oms_type: str + oms_type : str, default 'NETTING' The order management system type used by the exchange. - account_type : str + account_type : str, default 'MARGIN' The account type for the client. """ From bb5c92427415720f2eac9cc6562c8206ca76e145 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Wed, 22 May 2024 18:13:56 +1000 Subject: [PATCH 12/28] Update dependencies --- nautilus_core/Cargo.lock | 4 ++-- poetry.lock | 17 ++++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/nautilus_core/Cargo.lock b/nautilus_core/Cargo.lock index dc0cd0598f8e..c7a011d580f3 100644 --- a/nautilus_core/Cargo.lock +++ b/nautilus_core/Cargo.lock @@ -3092,9 +3092,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "300.2.3+3.2.1" +version = "300.3.0+3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cff92b6f71555b61bb9315f7c64da3ca43d87531622120fea0195fc761b4843" +checksum = "eba8804a1c5765b18c4b3f907e6897ebabeedebc9830e1a0046c4a4cf44663e1" dependencies = [ "cc", ] diff --git a/poetry.lock b/poetry.lock index 979d688cea70..a10eaed782df 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1982,13 +1982,13 @@ files = [ [[package]] name = "requests" -version = "2.32.1" +version = "2.32.2" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" files = [ - {file = "requests-2.32.1-py3-none-any.whl", hash = "sha256:21ac9465cdf8c1650fe1ecde8a71669a93d4e6f147550483a2967d08396a56a5"}, - {file = "requests-2.32.1.tar.gz", hash = "sha256:eb97e87e64c79e64e5b8ac75cee9dd1f97f49e289b083ee6be96268930725685"}, + {file = "requests-2.32.2-py3-none-any.whl", hash = "sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c"}, + {file = "requests-2.32.2.tar.gz", hash = "sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289"}, ] [package.dependencies] @@ -2029,19 +2029,18 @@ files = [ [[package]] name = "setuptools" -version = "69.5.1" +version = "70.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-69.5.1-py3-none-any.whl", hash = "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32"}, - {file = "setuptools-69.5.1.tar.gz", hash = "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987"}, + {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"}, + {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" From 54d05103b79c2859a5b8efe1847627fee1a716fc Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Wed, 22 May 2024 19:05:52 +1000 Subject: [PATCH 13/28] Overhaul SandboxExecutionClient and config --- RELEASES.md | 2 +- examples/live/betfair/betfair_sandbox.py | 4 +- .../interactive_brokers_example.py | 1 - examples/live/interactive_brokers/sandbox.py | 5 +- nautilus_trader/adapters/sandbox/config.py | 19 ++++-- nautilus_trader/adapters/sandbox/execution.py | 59 +++++++++---------- nautilus_trader/adapters/sandbox/factory.py | 15 +---- .../adapters/sandbox/conftest.py | 15 ++--- 8 files changed, 59 insertions(+), 61 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index e2f929fcf019..3cd4c9d48e0d 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -6,7 +6,7 @@ Released on TBD (UTC). None ### Breaking Changes -None +- Overhauled `SandboxExecutionClientConfig` to more closely match `BacktestVenueConfig` (many changes and additions) ### Fixes - Fixed data order by `ts_init` when streaming (#1656), thanks @twitu diff --git a/examples/live/betfair/betfair_sandbox.py b/examples/live/betfair/betfair_sandbox.py index 39c1b56512e8..0e164ebc981c 100644 --- a/examples/live/betfair/betfair_sandbox.py +++ b/examples/live/betfair/betfair_sandbox.py @@ -71,8 +71,8 @@ async def main(instrument_config: BetfairInstrumentProviderConfig) -> TradingNod exec_clients={ "SANDBOX": SandboxExecutionClientConfig( venue="BETFAIR", - currency="AUD", - balance=10_000, + base_currency="AUD", + starting_balances=["10_000 AUD"], ), }, ) diff --git a/examples/live/interactive_brokers/interactive_brokers_example.py b/examples/live/interactive_brokers/interactive_brokers_example.py index 63a202d40757..fb0700ba110b 100644 --- a/examples/live/interactive_brokers/interactive_brokers_example.py +++ b/examples/live/interactive_brokers/interactive_brokers_example.py @@ -14,7 +14,6 @@ # limitations under the License. # ------------------------------------------------------------------------------------------------- - # fmt: off from nautilus_trader.adapters.interactive_brokers.common import IB_VENUE diff --git a/examples/live/interactive_brokers/sandbox.py b/examples/live/interactive_brokers/sandbox.py index cd7c1872da19..11d3591087ca 100644 --- a/examples/live/interactive_brokers/sandbox.py +++ b/examples/live/interactive_brokers/sandbox.py @@ -14,7 +14,6 @@ # limitations under the License. # ------------------------------------------------------------------------------------------------- - from decimal import Decimal # fmt: off @@ -70,8 +69,8 @@ for venue in SANDBOX_VENUES: exec_clients[venue] = SandboxExecutionClientConfig( venue=venue, - currency="USD", - balance=1_000_000, + base_currency="USD", + starting_balances=["1_000_000 USD"], instrument_provider=instrument_provider, ) diff --git a/nautilus_trader/adapters/sandbox/config.py b/nautilus_trader/adapters/sandbox/config.py index de39ee4692b8..51a6d1c80157 100644 --- a/nautilus_trader/adapters/sandbox/config.py +++ b/nautilus_trader/adapters/sandbox/config.py @@ -30,7 +30,7 @@ class SandboxExecutionClientConfig(LiveExecClientConfig, frozen=True, kw_only=Tr The starting balance for this venue. bar_execution : bool, default True If bars should be processed by the matching engine(s) (and move the market). - default_leverage : float, default 10.0 + default_leverage : float, default 1.0 The account default leverage (for margin accounts). oms_type : str, default 'NETTING' The order management system type used by the exchange. @@ -40,9 +40,18 @@ class SandboxExecutionClientConfig(LiveExecClientConfig, frozen=True, kw_only=Tr """ venue: str - currency: str - balance: int - bar_execution: bool = True - default_leverage: float = 10.0 + starting_balances: list[str] + base_currency: str | None = None oms_type: str = "NETTING" account_type: str = "MARGIN" + default_leverage: float = 1.0 + leverages: dict[str, float] | None = None + book_type: str = "L1_MBP" + frozen_account: bool = False + bar_execution: bool = True + reject_stop_orders: bool = True + support_gtd_orders: bool = True + support_contingent_orders: bool = True + use_position_ids: bool = True + use_random_ids: bool = False + use_reduce_only: bool = True diff --git a/nautilus_trader/adapters/sandbox/execution.py b/nautilus_trader/adapters/sandbox/execution.py index e0c2ccdea5fe..2127ada677bd 100644 --- a/nautilus_trader/adapters/sandbox/execution.py +++ b/nautilus_trader/adapters/sandbox/execution.py @@ -14,11 +14,11 @@ # ------------------------------------------------------------------------------------------------- import asyncio -from decimal import Decimal from typing import ClassVar import pandas as pd +from nautilus_trader.adapters.sandbox.config import SandboxExecutionClientConfig from nautilus_trader.backtest.exchange import SimulatedExchange from nautilus_trader.backtest.execution_client import BacktestExecClient from nautilus_trader.backtest.models import FillModel @@ -27,7 +27,6 @@ from nautilus_trader.cache.cache import Cache from nautilus_trader.common.component import LiveClock from nautilus_trader.common.component import MessageBus -from nautilus_trader.common.component import TestClock from nautilus_trader.common.providers import InstrumentProvider from nautilus_trader.core.data import Data from nautilus_trader.execution.reports import FillReport @@ -39,15 +38,15 @@ from nautilus_trader.model.data import OrderBookDeltas from nautilus_trader.model.data import QuoteTick from nautilus_trader.model.data import TradeTick -from nautilus_trader.model.enums import AccountType -from nautilus_trader.model.enums import OmsType +from nautilus_trader.model.enums import account_type_from_str +from nautilus_trader.model.enums import book_type_from_str +from nautilus_trader.model.enums import oms_type_from_str from nautilus_trader.model.identifiers import ClientId from nautilus_trader.model.identifiers import ClientOrderId 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 AccountBalance from nautilus_trader.model.objects import Currency from nautilus_trader.model.objects import Money from nautilus_trader.portfolio.base import PortfolioFacade @@ -81,27 +80,20 @@ def __init__( msgbus: MessageBus, cache: Cache, clock: LiveClock, - venue: str, - currency: str, - balance: int, - oms_type: OmsType = OmsType.NETTING, - account_type: AccountType = AccountType.MARGIN, - default_leverage: Decimal = Decimal(10), - bar_execution: bool = True, + config: SandboxExecutionClientConfig, ) -> None: - self._currency = Currency.from_str(currency) - money = Money(value=balance, currency=self._currency) - self.balance = AccountBalance(total=money, locked=Money(0, money.currency), free=money) - self.test_clock = TestClock() - self._account_type = account_type - sandbox_venue = Venue(venue) + sandbox_venue = Venue(config.venue) + oms_type = oms_type_from_str(config.oms_type) + account_type = account_type_from_str(config.account_type) + base_currency = Currency.from_str(config.base_currency) if config.base_currency else None + super().__init__( loop=loop, - client_id=ClientId(venue), + client_id=ClientId(config.venue), venue=sandbox_venue, oms_type=oms_type, account_type=account_type, - base_currency=self._currency, + base_currency=base_currency, instrument_provider=InstrumentProvider(), msgbus=msgbus, cache=cache, @@ -111,28 +103,35 @@ def __init__( self.exchange = SimulatedExchange( venue=sandbox_venue, oms_type=oms_type, - account_type=self._account_type, - base_currency=self._currency, - starting_balances=[self.balance.free], - default_leverage=default_leverage, - leverages={}, + account_type=account_type, + starting_balances=[Money.from_str(b) for b in config.starting_balances], + base_currency=base_currency, + default_leverage=config.default_leverage, + leverages=config.leverages or {}, instruments=self.INSTRUMENTS, modules=[], portfolio=portfolio, msgbus=self._msgbus, cache=cache, + clock=clock, fill_model=FillModel(), fee_model=MakerTakerFeeModel(), latency_model=LatencyModel(0), - clock=self.test_clock, - bar_execution=bar_execution, - frozen_account=True, # <-- Freezing account + book_type=book_type_from_str(config.book_type), + frozen_account=config.frozen_account, + bar_execution=config.bar_execution, + reject_stop_orders=config.reject_stop_orders, + support_gtd_orders=config.support_gtd_orders, + support_contingent_orders=config.support_contingent_orders, + use_position_ids=config.use_position_ids, + use_random_ids=config.use_random_ids, + use_reduce_only=config.use_reduce_only, ) self._client = BacktestExecClient( exchange=self.exchange, msgbus=msgbus, - cache=self._cache, - clock=self.test_clock, + cache=cache, + clock=clock, ) self.exchange.register_client(self._client) self.exchange.initialize_account() diff --git a/nautilus_trader/adapters/sandbox/factory.py b/nautilus_trader/adapters/sandbox/factory.py index 745f74c9859f..7bfb1eeef9d0 100644 --- a/nautilus_trader/adapters/sandbox/factory.py +++ b/nautilus_trader/adapters/sandbox/factory.py @@ -14,7 +14,6 @@ # ------------------------------------------------------------------------------------------------- import asyncio -from decimal import Decimal from nautilus_trader.adapters.sandbox.config import SandboxExecutionClientConfig from nautilus_trader.adapters.sandbox.execution import SandboxExecutionClient @@ -22,8 +21,6 @@ from nautilus_trader.common.component import LiveClock from nautilus_trader.common.component import MessageBus from nautilus_trader.live.factories import LiveExecClientFactory -from nautilus_trader.model.enums import AccountType -from nautilus_trader.model.enums import OmsType from nautilus_trader.portfolio import PortfolioFacade @@ -51,8 +48,6 @@ def create( # type: ignore The event loop for the client. name : str The custom client ID. - config : dict[str, object] - The configuration for the client. portfolio : PortfolioFacade The read-only portfolio for the client. msgbus : MessageBus @@ -61,6 +56,8 @@ def create( # type: ignore The cache for the client. clock : LiveClock The clock for the client. + config : dict[str, object] + The configuration for the client. Returns ------- @@ -73,12 +70,6 @@ def create( # type: ignore portfolio=portfolio, msgbus=msgbus, cache=cache, - venue=name or config.venue, - balance=config.balance, - currency=config.currency, - bar_execution=config.bar_execution, - default_leverage=Decimal(config.default_leverage), - oms_type=OmsType[config.oms_type], - account_type=AccountType[config.account_type], + config=config, ) return exec_client diff --git a/tests/integration_tests/adapters/sandbox/conftest.py b/tests/integration_tests/adapters/sandbox/conftest.py index 7eacad869e03..8f2c1fc0eb28 100644 --- a/tests/integration_tests/adapters/sandbox/conftest.py +++ b/tests/integration_tests/adapters/sandbox/conftest.py @@ -13,12 +13,11 @@ # limitations under the License. # ------------------------------------------------------------------------------------------------- -from decimal import Decimal import pytest +from nautilus_trader.adapters.sandbox.config import SandboxExecutionClientConfig from nautilus_trader.adapters.sandbox.execution import SandboxExecutionClient -from nautilus_trader.model.enums import AccountType from nautilus_trader.model.events import AccountState from nautilus_trader.model.identifiers import AccountId from nautilus_trader.model.identifiers import Venue @@ -42,17 +41,19 @@ def exec_client( venue, ): SandboxExecutionClient.INSTRUMENTS = [instrument] + config = SandboxExecutionClientConfig( + venue=venue.value, + starting_balances=["100_000 USD"], + base_currency="USD", + account_type="CASH", + ) return SandboxExecutionClient( loop=event_loop, portfolio=portfolio, msgbus=msgbus, cache=cache, clock=clock, - venue=venue.value, - currency="USD", - balance=100_000, - account_type=AccountType.CASH, - default_leverage=Decimal(1), + config=config, ) From a7ce93a606c79376b736aff661aec791ba163a07 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Wed, 22 May 2024 19:08:27 +1000 Subject: [PATCH 14/28] Overhaul SandboxExecutionClient and config --- nautilus_trader/adapters/sandbox/config.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/nautilus_trader/adapters/sandbox/config.py b/nautilus_trader/adapters/sandbox/config.py index 51a6d1c80157..23b5b0ad0c20 100644 --- a/nautilus_trader/adapters/sandbox/config.py +++ b/nautilus_trader/adapters/sandbox/config.py @@ -24,18 +24,18 @@ class SandboxExecutionClientConfig(LiveExecClientConfig, frozen=True, kw_only=Tr ---------- venue : str The venue to generate a sandbox execution client for. - currency : str - The currency for this venue. - balance : int - The starting balance for this venue. - bar_execution : bool, default True - If bars should be processed by the matching engine(s) (and move the market). - default_leverage : float, default 1.0 - The account default leverage (for margin accounts). + starting_balances : list[str] + The starting balances for this sandbox venue. + base_currency : str, optional + The base currency for this venue. oms_type : str, default 'NETTING' The order management system type used by the exchange. account_type : str, default 'MARGIN' The account type for the client. + default_leverage : float, default 1.0 + The account default leverage (for margin accounts). + bar_execution : bool, default True + If bars should be processed by the matching engine(s) (and move the market). """ From a304307b13c11cc9343fa02ebf431e9330294208 Mon Sep 17 00:00:00 2001 From: Filip Macek Date: Wed, 22 May 2024 12:45:19 +0200 Subject: [PATCH 15/28] Add PSQL cache database update orders (#1650) --- nautilus_core/accounting/src/account/cash.rs | 12 +- nautilus_core/accounting/src/stubs.rs | 8 +- .../src/python/sql/cache_database.rs | 7 + .../infrastructure/src/sql/cache_database.rs | 53 +++--- nautilus_core/infrastructure/src/sql/mod.rs | 2 +- .../infrastructure/src/sql/models/orders.rs | 153 +++++++++++++++-- .../infrastructure/src/sql/queries.rs | 38 +++-- .../tests/test_cache_database_postgres.rs | 91 +++++++++- .../model/src/events/order/accepted.rs | 40 ++++- .../model/src/events/order/cancel_rejected.rs | 40 ++++- .../model/src/events/order/canceled.rs | 40 ++++- .../model/src/events/order/denied.rs | 40 ++++- .../model/src/events/order/emulated.rs | 40 ++++- .../model/src/events/order/expired.rs | 40 ++++- .../model/src/events/order/filled.rs | 28 +++ .../model/src/events/order/initialized.rs | 40 ++++- nautilus_core/model/src/events/order/mod.rs | 19 ++- .../model/src/events/order/modify_rejected.rs | 40 ++++- .../model/src/events/order/pending_cancel.rs | 40 ++++- .../model/src/events/order/pending_update.rs | 40 ++++- .../model/src/events/order/rejected.rs | 40 ++++- .../model/src/events/order/released.rs | 40 ++++- .../model/src/events/order/submitted.rs | 40 ++++- .../model/src/events/order/triggered.rs | 40 ++++- .../model/src/events/order/updated.rs | 40 ++++- nautilus_core/model/src/orders/any.rs | 25 ++- nautilus_core/model/src/orders/stubs.rs | 60 ++++++- nautilus_core/model/src/position.rs | 160 +++++++++++++----- .../model/src/python/events/order/filled.rs | 1 + .../src/python/events/order/initialized.rs | 8 +- nautilus_core/model/src/stubs.rs | 8 +- nautilus_trader/cache/postgres/adapter.py | 4 + .../cache/postgres/transformers.py | 114 +++++++++++-- nautilus_trader/core/nautilus_pyo3.pyi | 1 + nautilus_trader/model/orders/stop_market.pyx | 4 +- schema/tables.sql | 7 + .../test_cache_database_postgres.py | 65 +++++++ tests/unit_tests/model/test_position_pyo3.py | 1 + 38 files changed, 1270 insertions(+), 199 deletions(-) diff --git a/nautilus_core/accounting/src/account/cash.rs b/nautilus_core/accounting/src/account/cash.rs index efd9b2265d94..7a7acb51f143 100644 --- a/nautilus_core/accounting/src/account/cash.rs +++ b/nautilus_core/accounting/src/account/cash.rs @@ -461,7 +461,9 @@ mod tests { None, None, None, - ); + Some(AccountId::from("SIM-001")), + ) + .unwrap(); let position = Position::new(audusd_sim, fill).unwrap(); let pnls = cash_account_million_usd .calculate_pnls(audusd_sim.into_any(), fill, Some(position)) @@ -496,7 +498,9 @@ mod tests { None, None, None, - ); + Some(AccountId::from("SIM-001")), + ) + .unwrap(); let position = Position::new(currency_pair_btcusdt, fill1).unwrap(); let result1 = cash_account_multi .calculate_pnls( @@ -526,7 +530,9 @@ mod tests { None, None, None, - ); + Some(AccountId::from("SIM-001")), + ) + .unwrap(); let result2 = cash_account_multi .calculate_pnls(currency_pair_btcusdt.into_any(), fill2, Some(position)) .unwrap(); diff --git a/nautilus_core/accounting/src/stubs.rs b/nautilus_core/accounting/src/stubs.rs index d5a62794b14e..0156eabde7e7 100644 --- a/nautilus_core/accounting/src/stubs.rs +++ b/nautilus_core/accounting/src/stubs.rs @@ -47,7 +47,9 @@ pub fn test_position_long(mut order_factory: OrderFactory, audusd_sim: CurrencyP None, None, None, - ); + None, + ) + .unwrap(); Position::new(audusd_sim, order_filled).unwrap() } @@ -74,6 +76,8 @@ pub fn test_position_short(mut order_factory: OrderFactory, audusd_sim: Currency None, None, None, - ); + None, + ) + .unwrap(); Position::new(audusd_sim, order_filled).unwrap() } diff --git a/nautilus_core/infrastructure/src/python/sql/cache_database.rs b/nautilus_core/infrastructure/src/python/sql/cache_database.rs index f529e25e4c0b..3bba3fcbed55 100644 --- a/nautilus_core/infrastructure/src/python/sql/cache_database.rs +++ b/nautilus_core/infrastructure/src/python/sql/cache_database.rs @@ -132,6 +132,13 @@ impl PostgresCacheDatabase { result.map_err(to_pyruntime_err) } + #[pyo3(name = "update_order")] + fn py_update_order(slf: PyRef<'_, Self>, order: PyObject, py: Python<'_>) -> PyResult<()> { + let order_any = convert_pyobject_to_order_any(py, order)?; + let result = get_runtime().block_on(async { slf.update_order(order_any).await }); + result.map_err(to_pyruntime_err) + } + #[pyo3(name = "load_order")] fn py_load_order( slf: PyRef<'_, Self>, diff --git a/nautilus_core/infrastructure/src/sql/cache_database.rs b/nautilus_core/infrastructure/src/sql/cache_database.rs index 1341d983ebf4..60fe583c13cd 100644 --- a/nautilus_core/infrastructure/src/sql/cache_database.rs +++ b/nautilus_core/infrastructure/src/sql/cache_database.rs @@ -52,7 +52,7 @@ pub enum DatabaseQuery { Add(String, Vec), AddCurrency(Currency), AddInstrument(InstrumentAny), - AddOrder(OrderAny), + AddOrder(OrderAny, bool), } fn get_buffer_interval() -> Duration { @@ -110,52 +110,58 @@ async fn drain_buffer(pool: &PgPool, buffer: &mut VecDeque) { .unwrap() } }, - DatabaseQuery::AddOrder(order_any) => match order_any { + DatabaseQuery::AddOrder(order_any, updated) => match order_any { OrderAny::Limit(order) => { - DatabaseQueries::add_order(pool, "LIMIT", false, Box::new(order)) + DatabaseQueries::add_order(pool, "LIMIT", updated, Box::new(order)) .await .unwrap() } OrderAny::LimitIfTouched(order) => { - DatabaseQueries::add_order(pool, "LIMIT_IF_TOUCHED", false, Box::new(order)) + DatabaseQueries::add_order(pool, "LIMIT_IF_TOUCHED", updated, Box::new(order)) .await .unwrap() } OrderAny::Market(order) => { - DatabaseQueries::add_order(pool, "MARKET", false, Box::new(order)) + DatabaseQueries::add_order(pool, "MARKET", updated, Box::new(order)) .await .unwrap() } OrderAny::MarketIfTouched(order) => { - DatabaseQueries::add_order(pool, "MARKET_IF_TOUCHED", false, Box::new(order)) + DatabaseQueries::add_order(pool, "MARKET_IF_TOUCHED", updated, Box::new(order)) .await .unwrap() } OrderAny::MarketToLimit(order) => { - DatabaseQueries::add_order(pool, "MARKET_TO_LIMIT", false, Box::new(order)) + DatabaseQueries::add_order(pool, "MARKET_TO_LIMIT", updated, Box::new(order)) .await .unwrap() } OrderAny::StopLimit(order) => { - DatabaseQueries::add_order(pool, "STOP_LIMIT", false, Box::new(order)) + DatabaseQueries::add_order(pool, "STOP_LIMIT", updated, Box::new(order)) .await .unwrap() } OrderAny::StopMarket(order) => { - DatabaseQueries::add_order(pool, "STOP_MARKET", false, Box::new(order)) - .await - .unwrap() - } - OrderAny::TrailingStopLimit(order) => { - DatabaseQueries::add_order(pool, "TRAILING_STOP_LIMIT", false, Box::new(order)) - .await - .unwrap() - } - OrderAny::TrailingStopMarket(order) => { - DatabaseQueries::add_order(pool, "TRAILING_STOP_MARKET", false, Box::new(order)) + DatabaseQueries::add_order(pool, "STOP_MARKET", updated, Box::new(order)) .await .unwrap() } + OrderAny::TrailingStopLimit(order) => DatabaseQueries::add_order( + pool, + "TRAILING_STOP_LIMIT", + updated, + Box::new(order), + ) + .await + .unwrap(), + OrderAny::TrailingStopMarket(order) => DatabaseQueries::add_order( + pool, + "TRAILING_STOP_MARKET", + updated, + Box::new(order), + ) + .await + .unwrap(), }, } } @@ -268,7 +274,14 @@ impl PostgresCacheDatabase { } pub async fn add_order(&self, order: OrderAny) -> anyhow::Result<()> { - let query = DatabaseQuery::AddOrder(order); + let query = DatabaseQuery::AddOrder(order, false); + self.tx.send(query).await.map_err(|err| { + anyhow::anyhow!("Failed to send query add_order to database message handler: {err}") + }) + } + + pub async fn update_order(&self, order: OrderAny) -> anyhow::Result<()> { + let query = DatabaseQuery::AddOrder(order, true); self.tx.send(query).await.map_err(|err| { anyhow::anyhow!("Failed to send query add_order to database message handler: {err}") }) diff --git a/nautilus_core/infrastructure/src/sql/mod.rs b/nautilus_core/infrastructure/src/sql/mod.rs index fe66a06ae09e..98c96833e88c 100644 --- a/nautilus_core/infrastructure/src/sql/mod.rs +++ b/nautilus_core/infrastructure/src/sql/mod.rs @@ -15,7 +15,7 @@ // Be careful about ordering and foreign key constraints when deleting data. pub const NAUTILUS_TABLES: [&str; 5] = - ["general", "instrument", "currency", "order_event", "order"]; + ["general", "instrument", "order_event", "order", "currency"]; pub mod cache_database; pub mod models; diff --git a/nautilus_core/infrastructure/src/sql/models/orders.rs b/nautilus_core/infrastructure/src/sql/models/orders.rs index ac94c24ac94f..c5d46cce8702 100644 --- a/nautilus_core/infrastructure/src/sql/models/orders.rs +++ b/nautilus_core/infrastructure/src/sql/models/orders.rs @@ -17,7 +17,10 @@ use std::{collections::HashMap, str::FromStr}; use nautilus_core::{nanos::UnixNanos, uuid::UUID4}; use nautilus_model::{ - enums::{ContingencyType, OrderSide, OrderType, TimeInForce, TrailingOffsetType, TriggerType}, + enums::{ + ContingencyType, LiquiditySide, OrderSide, OrderType, TimeInForce, TrailingOffsetType, + TriggerType, + }, events::order::{ accepted::OrderAccepted, cancel_rejected::OrderCancelRejected, canceled::OrderCanceled, denied::OrderDenied, emulated::OrderEmulated, event::OrderEventAny, expired::OrderExpired, @@ -27,11 +30,12 @@ use nautilus_model::{ triggered::OrderTriggered, updated::OrderUpdated, }, identifiers::{ - client_order_id::ClientOrderId, exec_algorithm_id::ExecAlgorithmId, - instrument_id::InstrumentId, order_list_id::OrderListId, strategy_id::StrategyId, - trader_id::TraderId, + account_id::AccountId, client_order_id::ClientOrderId, exec_algorithm_id::ExecAlgorithmId, + instrument_id::InstrumentId, order_list_id::OrderListId, position_id::PositionId, + strategy_id::StrategyId, trade_id::TradeId, trader_id::TraderId, + venue_order_id::VenueOrderId, }, - types::{price::Price, quantity::Quantity}, + types::{currency::Currency, money::Money, price::Price, quantity::Quantity}, }; use sqlx::{postgres::PgRow, FromRow, Row}; use ustr::Ustr; @@ -116,7 +120,7 @@ impl<'r> FromRow<'r, PgRow> for OrderEventAnyModel { impl<'r> FromRow<'r, PgRow> for OrderInitializedModel { fn from_row(row: &'r PgRow) -> Result { - let order_id = row.try_get::<&str, _>("id").map(UUID4::from)?; + let event_id = row.try_get::<&str, _>("id").map(UUID4::from)?; let client_order_id = row .try_get::<&str, _>("order_id") .map(ClientOrderId::from)?; @@ -225,7 +229,7 @@ impl<'r> FromRow<'r, PgRow> for OrderInitializedModel { .ok() .and_then(|x| x.map(|x| serde_json::from_value::>(x).unwrap())) .map(|x| x.into_iter().map(|x| Ustr::from(x.as_str())).collect()); - let order = OrderInitialized::new( + let order_event = OrderInitialized::new( trader_id, strategy_id, instrument_id, @@ -238,7 +242,7 @@ impl<'r> FromRow<'r, PgRow> for OrderInitializedModel { reduce_only, quote_quantity, reconciliation, - order_id, + event_id, ts_event, ts_init, price, @@ -261,13 +265,43 @@ impl<'r> FromRow<'r, PgRow> for OrderInitializedModel { tags, ) .unwrap(); - Ok(OrderInitializedModel(order)) + Ok(OrderInitializedModel(order_event)) } } impl<'r> FromRow<'r, PgRow> for OrderAcceptedModel { - fn from_row(_row: &'r PgRow) -> Result { - todo!() + fn from_row(row: &'r PgRow) -> Result { + let event_id = row.try_get::<&str, _>("id").map(UUID4::from)?; + let trader_id = row.try_get::<&str, _>("trader_id").map(TraderId::from)?; + let strategy_id = row + .try_get::<&str, _>("strategy_id") + .map(StrategyId::from)?; + let instrument_id = row + .try_get::<&str, _>("instrument_id") + .map(InstrumentId::from)?; + let order_id = row + .try_get::<&str, _>("order_id") + .map(ClientOrderId::from)?; + let venue_order_id = row + .try_get::<&str, _>("venue_order_id") + .map(VenueOrderId::from)?; + let account_id = row.try_get::<&str, _>("account_id").map(AccountId::from)?; + let ts_event = row.try_get::<&str, _>("ts_event").map(UnixNanos::from)?; + let ts_init = row.try_get::<&str, _>("ts_init").map(UnixNanos::from)?; + let order_event = OrderAccepted::new( + trader_id, + strategy_id, + instrument_id, + order_id, + venue_order_id, + account_id, + event_id, + ts_event, + ts_init, + false, + ) + .unwrap(); + Ok(OrderAcceptedModel(order_event)) } } @@ -302,8 +336,68 @@ impl<'r> FromRow<'r, PgRow> for OrderExpiredModel { } impl<'r> FromRow<'r, PgRow> for OrderFilledModel { - fn from_row(_row: &'r PgRow) -> Result { - todo!() + fn from_row(row: &'r PgRow) -> Result { + let event_id = row.try_get::<&str, _>("id").map(UUID4::from)?; + let trader_id = row.try_get::<&str, _>("trader_id").map(TraderId::from)?; + let strategy_id = row + .try_get::<&str, _>("strategy_id") + .map(StrategyId::from)?; + let instrument_id = row + .try_get::<&str, _>("instrument_id") + .map(InstrumentId::from)?; + let order_id = row + .try_get::<&str, _>("order_id") + .map(ClientOrderId::from)?; + let venue_order_id = row + .try_get::<&str, _>("venue_order_id") + .map(VenueOrderId::from)?; + let account_id = row.try_get::<&str, _>("account_id").map(AccountId::from)?; + let trade_id = row.try_get::<&str, _>("trade_id").map(TradeId::from)?; + let order_side = row + .try_get::<&str, _>("order_side") + .map(|x| OrderSide::from_str(x).unwrap())?; + let order_type = row + .try_get::<&str, _>("order_type") + .map(|x| OrderType::from_str(x).unwrap())?; + let last_px = row.try_get::<&str, _>("last_px").map(Price::from)?; + let last_qty = row.try_get::<&str, _>("last_qty").map(Quantity::from)?; + let currency = row.try_get::<&str, _>("currency").map(Currency::from)?; + let test = row.try_get::<&str, _>("liquidity_side").unwrap(); + println!("test: {:?}", test); + let liquidity_side = row + .try_get::<&str, _>("liquidity_side") + .map(|x| LiquiditySide::from_str(x).unwrap())?; + let ts_event = row.try_get::<&str, _>("ts_event").map(UnixNanos::from)?; + let ts_init = row.try_get::<&str, _>("ts_init").map(UnixNanos::from)?; + let position_id = row + .try_get::, _>("position_id") + .map(|x| x.map(PositionId::from))?; + let commission = row + .try_get::, _>("commission") + .map(|x| x.map(|x| Money::from_str(x).unwrap()))?; + let order_event = OrderFilled::new( + trader_id, + strategy_id, + instrument_id, + order_id, + venue_order_id, + account_id, + trade_id, + order_side, + order_type, + last_qty, + last_px, + currency, + liquidity_side, + event_id, + ts_event, + ts_init, + false, + position_id, + commission, + ) + .unwrap(); + Ok(OrderFilledModel(order_event)) } } @@ -338,8 +432,37 @@ impl<'r> FromRow<'r, PgRow> for OrderReleasedModel { } impl<'r> FromRow<'r, PgRow> for OrderSubmittedModel { - fn from_row(_row: &'r PgRow) -> Result { - todo!() + fn from_row(row: &'r PgRow) -> Result { + let trader_id = row.try_get::<&str, _>("trader_id").map(TraderId::from)?; + let strategy_id = row + .try_get::<&str, _>("strategy_id") + .map(StrategyId::from)?; + let instrument_id = row + .try_get::<&str, _>("instrument_id") + .map(InstrumentId::from)?; + let client_order_id = row + .try_get::<&str, _>("order_id") + .map(ClientOrderId::from)?; + let account_id = row.try_get::<&str, _>("account_id").map(AccountId::from)?; + let event_id = row.try_get::<&str, _>("id").map(UUID4::from)?; + let ts_event = row + .try_get::("ts_event") + .map(|res| UnixNanos::from(res.as_str()))?; + let ts_init = row + .try_get::("ts_init") + .map(|res| UnixNanos::from(res.as_str()))?; + let order_event = OrderSubmitted::new( + trader_id, + strategy_id, + instrument_id, + client_order_id, + account_id, + event_id, + ts_event, + ts_init, + ) + .unwrap(); + Ok(OrderSubmittedModel(order_event)) } } diff --git a/nautilus_core/infrastructure/src/sql/queries.rs b/nautilus_core/infrastructure/src/sql/queries.rs index b39d49843e1b..c9ff6f26bcdd 100644 --- a/nautilus_core/infrastructure/src/sql/queries.rs +++ b/nautilus_core/infrastructure/src/sql/queries.rs @@ -270,39 +270,47 @@ impl DatabaseQueries { ) -> anyhow::Result<()> { sqlx::query(r#" INSERT INTO "order_event" ( - id, kind, order_id, order_type, order_side, trader_id, strategy_id, instrument_id, quantity, time_in_force, - post_only, reduce_only, quote_quantity, reconciliation, price, trigger_price, trigger_type, limit_offset, trailing_offset, + id, kind, order_id, order_type, order_side, trader_id, strategy_id, instrument_id, trade_id, currency, quantity, time_in_force, liquidity_side, + post_only, reduce_only, quote_quantity, reconciliation, price, last_px, last_qty, trigger_price, trigger_type, limit_offset, trailing_offset, trailing_offset_type, expire_time, display_qty, emulation_trigger, trigger_instrument_id, contingency_type, order_list_id, linked_order_ids, parent_order_id, - exec_algorithm_id, exec_spawn_id, venue_order_id, account_id, ts_event, ts_init, created_at, updated_at + exec_algorithm_id, exec_spawn_id, venue_order_id, account_id, position_id, commission, ts_event, ts_init, created_at, updated_at ) VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, - $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33, $34, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP + $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31, $32, $33, $34, $35, $36, $37, $38, $39, $40, $41, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP ) ON CONFLICT (id) DO UPDATE SET - kind = $2, order_id = $3, order_type = $4, order_side=$5, trader_id = $6, strategy_id = $7, instrument_id = $8, quantity = $9, time_in_force= $10, - post_only = $11, reduce_only = $12, quote_quantity = $13, reconciliation = $14, price = $15, trigger_price = $16, trigger_type = $17, limit_offset = $18, trailing_offset = $19, - trailing_offset_type = $20, expire_time = $21, display_qty = $22, emulation_trigger = $23, trigger_instrument_id = $24, contingency_type = $25, - order_list_id = $26, linked_order_ids = $27, - parent_order_id = $28, exec_algorithm_id = $29, exec_spawn_id = $30, venue_order_id = $31, account_id = $32, ts_event = $33, ts_init = $34, updated_at = CURRENT_TIMESTAMP + kind = $2, order_id = $3, order_type = $4, order_side=$5, trader_id = $6, strategy_id = $7, instrument_id = $8, trade_id = $9, currency = $10, + quantity = $11, time_in_force = $12, liquidity_side = $13, + post_only = $14, reduce_only = $15, quote_quantity = $16, reconciliation = $17, price = $18, last_px = $19, + last_qty = $20, trigger_price = $21, trigger_type = $22, limit_offset = $23, trailing_offset = $24, + trailing_offset_type = $25, expire_time = $26, display_qty = $27, emulation_trigger = $28, trigger_instrument_id = $29, + contingency_type = $30, order_list_id = $31, linked_order_ids = $32, + parent_order_id = $33, exec_algorithm_id = $34, exec_spawn_id = $35, venue_order_id = $36, account_id = $37, position_id = $38, commission = $39, + ts_event = $40, ts_init = $41, updated_at = CURRENT_TIMESTAMP "#) .bind(order_event.id().to_string()) .bind(order_event.kind()) .bind(order_event.client_order_id().to_string()) .bind(order_event.order_type().map(|x| x.to_string())) - .bind(order_event.order_side().map(|x| format!("{:?}", x))) + .bind(order_event.order_side().map(|x| x.to_string())) .bind(order_event.trader_id().to_string()) .bind(order_event.strategy_id().to_string()) .bind(order_event.instrument_id().to_string()) + .bind(order_event.trade_id().map(|x| x.to_string())) + .bind(order_event.currency().map(|x| x.code.as_str())) .bind(order_event.quantity().map(|x| x.to_string())) - .bind(order_event.time_in_force().map(|x| format!("{:?}", x))) + .bind(order_event.time_in_force().map(|x| x.to_string())) + .bind(order_event.liquidity_side().map(|x| x.to_string())) .bind(order_event.post_only()) .bind(order_event.reduce_only()) .bind(order_event.quote_quantity()) .bind(order_event.reconciliation()) .bind(order_event.price().map(|x| x.to_string())) + .bind(order_event.last_px().map(|x| x.to_string())) + .bind(order_event.last_qty().map(|x| x.to_string())) .bind(order_event.trigger_price().map(|x| x.to_string())) .bind(order_event.trigger_type().map(|x| x.to_string())) .bind(order_event.limit_offset().map(|x| x.to_string())) @@ -320,6 +328,8 @@ impl DatabaseQueries { .bind(order_event.exec_spawn_id().map(|x| x.to_string())) .bind(order_event.venue_order_id().map(|x| x.to_string())) .bind(order_event.account_id().map(|x| x.to_string())) + .bind(order_event.position_id().map(|x| x.to_string())) + .bind(order_event.commission().map(|x| x.to_string())) .bind(order_event.ts_event().to_string()) .bind(order_event.ts_init().to_string()) .execute(pool) @@ -332,11 +342,7 @@ impl DatabaseQueries { pool: &PgPool, order_id: &ClientOrderId, ) -> anyhow::Result> { - sqlx::query_as::<_, OrderEventAnyModel>( - r#" - SELECT * FROM "order_event" event WHERE event.order_id = $1 ORDER BY event.ts_init ASC - "#, - ) + sqlx::query_as::<_, OrderEventAnyModel>(r#"SELECT * FROM "order_event" event WHERE event.order_id = $1 ORDER BY created_at ASC"#) .bind(order_id.to_string()) .fetch_all(pool) .await diff --git a/nautilus_core/infrastructure/tests/test_cache_database_postgres.rs b/nautilus_core/infrastructure/tests/test_cache_database_postgres.rs index f246dd75fbff..50c7060763d6 100644 --- a/nautilus_core/infrastructure/tests/test_cache_database_postgres.rs +++ b/nautilus_core/infrastructure/tests/test_cache_database_postgres.rs @@ -64,7 +64,11 @@ mod tests { use nautilus_core::equality::entirely_equal; use nautilus_model::{ enums::{CurrencyType, OrderSide}, - identifiers::{client_order_id::ClientOrderId, instrument_id::InstrumentId}, + events::order::event::OrderEventAny, + identifiers::{ + account_id::AccountId, client_order_id::ClientOrderId, instrument_id::InstrumentId, + stubs::account_id, trade_id::TradeId, venue_order_id::VenueOrderId, + }, instruments::{ any::InstrumentAny, stubs::{ @@ -73,7 +77,10 @@ mod tests { }, Instrument, }, - orders::{any::OrderAny, stubs::TestOrderStubs}, + orders::{ + any::OrderAny, + stubs::{TestOrderEventStubs, TestOrderStubs}, + }, types::{currency::Currency, price::Price, quantity::Quantity}, }; use serial_test::serial; @@ -238,13 +245,15 @@ mod tests { #[tokio::test] #[serial] async fn test_add_order() { + let client_order_id_1 = ClientOrderId::new("O-19700101-0000-000-001-1").unwrap(); + let client_order_id_2 = ClientOrderId::new("O-19700101-0000-000-001-2").unwrap(); let instrument = currency_pair_ethusdt(); let pg_cache = get_pg_cache_database().await.unwrap(); let market_order = TestOrderStubs::market_order( instrument.id(), OrderSide::Buy, Quantity::from("1.0"), - Some(ClientOrderId::new("O-19700101-0000-000-001-1").unwrap()), + Some(client_order_id_1), None, ); let limit_order = TestOrderStubs::limit_order( @@ -252,7 +261,7 @@ mod tests { OrderSide::Sell, Price::from("100.0"), Quantity::from("1.0"), - Some(ClientOrderId::new("O-19700101-0000-000-001-2").unwrap()), + Some(client_order_id_2), None, ); pg_cache @@ -275,4 +284,78 @@ mod tests { entirely_equal(market_order_result.unwrap(), OrderAny::Market(market_order)); entirely_equal(limit_order_result.unwrap(), OrderAny::Limit(limit_order)); } + + #[tokio::test] + #[serial] + async fn test_update_order_for_open_order() { + let client_order_id_1 = ClientOrderId::new("O-19700101-0000-000-002-1").unwrap(); + let instrument = currency_pair_ethusdt(); + let account = account_id(); + let pg_cache = get_pg_cache_database().await.unwrap(); + // Add the target currency of order + pg_cache + .add_currency(instrument.quote_currency) + .await + .unwrap(); + // 1. create the order + let mut market_order = TestOrderStubs::market_order( + instrument.id(), + OrderSide::Buy, + Quantity::from("1.0"), + Some(client_order_id_1), + None, + ); + pg_cache + .add_order(OrderAny::Market(market_order.clone())) + .await + .unwrap(); + let submitted_event = TestOrderEventStubs::order_submitted(&market_order, account).unwrap(); + market_order + .apply(OrderEventAny::Submitted(submitted_event)) + .unwrap(); + pg_cache + .update_order(OrderAny::Market(market_order.clone())) + .await + .unwrap(); + let accepted_event = TestOrderEventStubs::order_accepted( + &market_order, + account, + VenueOrderId::new("001").unwrap(), + ) + .unwrap(); + market_order + .apply(OrderEventAny::Accepted(accepted_event)) + .unwrap(); + pg_cache + .update_order(OrderAny::Market(market_order.clone())) + .await + .unwrap(); + let order_filled = TestOrderEventStubs::order_filled( + &market_order, + &instrument, + None, + Some(TradeId::new("T-19700101-0000-000-001-1").unwrap()), + None, + Some(Price::from("100.0")), + Some(Quantity::from("1.0")), + None, + None, + Some(AccountId::new("SIM-001").unwrap()), + ) + .unwrap(); + market_order + .apply(OrderEventAny::Filled(order_filled)) + .unwrap(); + pg_cache + .update_order(OrderAny::Market(market_order.clone())) + .await + .unwrap(); + tokio::time::sleep(Duration::from_secs(2)).await; + // assert + let market_order_result = pg_cache + .load_order(&market_order.client_order_id) + .await + .unwrap(); + entirely_equal(market_order_result.unwrap(), OrderAny::Market(market_order)); + } } diff --git a/nautilus_core/model/src/events/order/accepted.rs b/nautilus_core/model/src/events/order/accepted.rs index 5a7d9488ff13..60403674a778 100644 --- a/nautilus_core/model/src/events/order/accepted.rs +++ b/nautilus_core/model/src/events/order/accepted.rs @@ -21,14 +21,18 @@ use serde::{Deserialize, Serialize}; use ustr::Ustr; use crate::{ - enums::{ContingencyType, OrderSide, OrderType, TimeInForce, TrailingOffsetType, TriggerType}, + enums::{ + ContingencyType, LiquiditySide, OrderSide, OrderType, TimeInForce, TrailingOffsetType, + TriggerType, + }, events::order::OrderEvent, identifiers::{ account_id::AccountId, client_order_id::ClientOrderId, exec_algorithm_id::ExecAlgorithmId, - instrument_id::InstrumentId, order_list_id::OrderListId, strategy_id::StrategyId, - trader_id::TraderId, venue_order_id::VenueOrderId, + instrument_id::InstrumentId, order_list_id::OrderListId, position_id::PositionId, + strategy_id::StrategyId, trade_id::TradeId, trader_id::TraderId, + venue_order_id::VenueOrderId, }, - types::{price::Price, quantity::Quantity}, + types::{currency::Currency, money::Money, price::Price, quantity::Quantity}, }; #[repr(C)] @@ -144,6 +148,14 @@ impl OrderEvent for OrderAccepted { self.instrument_id } + fn trade_id(&self) -> Option { + None + } + + fn currency(&self) -> Option { + None + } + fn client_order_id(&self) -> ClientOrderId { self.client_order_id } @@ -160,6 +172,10 @@ impl OrderEvent for OrderAccepted { None } + fn liquidity_side(&self) -> Option { + None + } + fn post_only(&self) -> Option { None } @@ -180,6 +196,14 @@ impl OrderEvent for OrderAccepted { None } + fn last_px(&self) -> Option { + None + } + + fn last_qty(&self) -> Option { + None + } + fn trigger_price(&self) -> Option { None } @@ -248,6 +272,14 @@ impl OrderEvent for OrderAccepted { Some(self.account_id) } + fn position_id(&self) -> Option { + None + } + + fn commission(&self) -> Option { + None + } + fn ts_event(&self) -> UnixNanos { self.ts_event } diff --git a/nautilus_core/model/src/events/order/cancel_rejected.rs b/nautilus_core/model/src/events/order/cancel_rejected.rs index 6a85b9f83152..6bb0c7e832b9 100644 --- a/nautilus_core/model/src/events/order/cancel_rejected.rs +++ b/nautilus_core/model/src/events/order/cancel_rejected.rs @@ -21,14 +21,18 @@ use serde::{Deserialize, Serialize}; use ustr::Ustr; use crate::{ - enums::{ContingencyType, OrderSide, OrderType, TimeInForce, TrailingOffsetType, TriggerType}, + enums::{ + ContingencyType, LiquiditySide, OrderSide, OrderType, TimeInForce, TrailingOffsetType, + TriggerType, + }, events::order::OrderEvent, identifiers::{ account_id::AccountId, client_order_id::ClientOrderId, exec_algorithm_id::ExecAlgorithmId, - instrument_id::InstrumentId, order_list_id::OrderListId, strategy_id::StrategyId, - trader_id::TraderId, venue_order_id::VenueOrderId, + instrument_id::InstrumentId, order_list_id::OrderListId, position_id::PositionId, + strategy_id::StrategyId, trade_id::TradeId, trader_id::TraderId, + venue_order_id::VenueOrderId, }, - types::{price::Price, quantity::Quantity}, + types::{currency::Currency, money::Money, price::Price, quantity::Quantity}, }; #[repr(C)] @@ -150,6 +154,14 @@ impl OrderEvent for OrderCancelRejected { self.instrument_id } + fn trade_id(&self) -> Option { + None + } + + fn currency(&self) -> Option { + None + } + fn client_order_id(&self) -> ClientOrderId { self.client_order_id } @@ -166,6 +178,10 @@ impl OrderEvent for OrderCancelRejected { None } + fn liquidity_side(&self) -> Option { + None + } + fn post_only(&self) -> Option { None } @@ -186,6 +202,14 @@ impl OrderEvent for OrderCancelRejected { None } + fn last_px(&self) -> Option { + None + } + + fn last_qty(&self) -> Option { + None + } + fn trigger_price(&self) -> Option { None } @@ -254,6 +278,14 @@ impl OrderEvent for OrderCancelRejected { self.account_id } + fn position_id(&self) -> Option { + None + } + + fn commission(&self) -> Option { + todo!() + } + fn ts_event(&self) -> UnixNanos { self.ts_event } diff --git a/nautilus_core/model/src/events/order/canceled.rs b/nautilus_core/model/src/events/order/canceled.rs index f74ef8481550..4aef943ab447 100644 --- a/nautilus_core/model/src/events/order/canceled.rs +++ b/nautilus_core/model/src/events/order/canceled.rs @@ -21,14 +21,18 @@ use serde::{Deserialize, Serialize}; use ustr::Ustr; use crate::{ - enums::{ContingencyType, OrderSide, OrderType, TimeInForce, TrailingOffsetType, TriggerType}, + enums::{ + ContingencyType, LiquiditySide, OrderSide, OrderType, TimeInForce, TrailingOffsetType, + TriggerType, + }, events::order::OrderEvent, identifiers::{ account_id::AccountId, client_order_id::ClientOrderId, exec_algorithm_id::ExecAlgorithmId, - instrument_id::InstrumentId, order_list_id::OrderListId, strategy_id::StrategyId, - trader_id::TraderId, venue_order_id::VenueOrderId, + instrument_id::InstrumentId, order_list_id::OrderListId, position_id::PositionId, + strategy_id::StrategyId, trade_id::TradeId, trader_id::TraderId, + venue_order_id::VenueOrderId, }, - types::{price::Price, quantity::Quantity}, + types::{currency::Currency, money::Money, price::Price, quantity::Quantity}, }; #[repr(C)] @@ -144,6 +148,14 @@ impl OrderEvent for OrderCanceled { self.instrument_id } + fn trade_id(&self) -> Option { + None + } + + fn currency(&self) -> Option { + todo!() + } + fn client_order_id(&self) -> ClientOrderId { self.client_order_id } @@ -160,6 +172,10 @@ impl OrderEvent for OrderCanceled { None } + fn liquidity_side(&self) -> Option { + None + } + fn post_only(&self) -> Option { None } @@ -180,6 +196,14 @@ impl OrderEvent for OrderCanceled { None } + fn last_px(&self) -> Option { + None + } + + fn last_qty(&self) -> Option { + None + } + fn trigger_price(&self) -> Option { None } @@ -248,6 +272,14 @@ impl OrderEvent for OrderCanceled { self.account_id } + fn position_id(&self) -> Option { + None + } + + fn commission(&self) -> Option { + None + } + fn ts_event(&self) -> UnixNanos { self.ts_event } diff --git a/nautilus_core/model/src/events/order/denied.rs b/nautilus_core/model/src/events/order/denied.rs index 238a945dd3d8..7819a1196a3c 100644 --- a/nautilus_core/model/src/events/order/denied.rs +++ b/nautilus_core/model/src/events/order/denied.rs @@ -21,14 +21,18 @@ use serde::{Deserialize, Serialize}; use ustr::Ustr; use crate::{ - enums::{ContingencyType, OrderSide, OrderType, TimeInForce, TrailingOffsetType, TriggerType}, + enums::{ + ContingencyType, LiquiditySide, OrderSide, OrderType, TimeInForce, TrailingOffsetType, + TriggerType, + }, events::order::OrderEvent, identifiers::{ account_id::AccountId, client_order_id::ClientOrderId, exec_algorithm_id::ExecAlgorithmId, - instrument_id::InstrumentId, order_list_id::OrderListId, strategy_id::StrategyId, - trader_id::TraderId, venue_order_id::VenueOrderId, + instrument_id::InstrumentId, order_list_id::OrderListId, position_id::PositionId, + strategy_id::StrategyId, trade_id::TradeId, trader_id::TraderId, + venue_order_id::VenueOrderId, }, - types::{price::Price, quantity::Quantity}, + types::{currency::Currency, money::Money, price::Price, quantity::Quantity}, }; #[repr(C)] @@ -133,6 +137,14 @@ impl OrderEvent for OrderDenied { self.instrument_id } + fn trade_id(&self) -> Option { + None + } + + fn currency(&self) -> Option { + None + } + fn client_order_id(&self) -> ClientOrderId { self.client_order_id } @@ -149,6 +161,10 @@ impl OrderEvent for OrderDenied { None } + fn liquidity_side(&self) -> Option { + None + } + fn post_only(&self) -> Option { None } @@ -169,6 +185,14 @@ impl OrderEvent for OrderDenied { None } + fn last_px(&self) -> Option { + None + } + + fn last_qty(&self) -> Option { + None + } + fn trigger_price(&self) -> Option { None } @@ -237,6 +261,14 @@ impl OrderEvent for OrderDenied { None } + fn position_id(&self) -> Option { + None + } + + fn commission(&self) -> Option { + None + } + fn ts_event(&self) -> UnixNanos { self.ts_event } diff --git a/nautilus_core/model/src/events/order/emulated.rs b/nautilus_core/model/src/events/order/emulated.rs index d9077dd7fd97..e17a5eb185af 100644 --- a/nautilus_core/model/src/events/order/emulated.rs +++ b/nautilus_core/model/src/events/order/emulated.rs @@ -21,14 +21,18 @@ use serde::{Deserialize, Serialize}; use ustr::Ustr; use crate::{ - enums::{ContingencyType, OrderSide, OrderType, TimeInForce, TrailingOffsetType, TriggerType}, + enums::{ + ContingencyType, LiquiditySide, OrderSide, OrderType, TimeInForce, TrailingOffsetType, + TriggerType, + }, events::order::OrderEvent, identifiers::{ account_id::AccountId, client_order_id::ClientOrderId, exec_algorithm_id::ExecAlgorithmId, - instrument_id::InstrumentId, order_list_id::OrderListId, strategy_id::StrategyId, - trader_id::TraderId, venue_order_id::VenueOrderId, + instrument_id::InstrumentId, order_list_id::OrderListId, position_id::PositionId, + strategy_id::StrategyId, trade_id::TradeId, trader_id::TraderId, + venue_order_id::VenueOrderId, }, - types::{price::Price, quantity::Quantity}, + types::{currency::Currency, money::Money, price::Price, quantity::Quantity}, }; #[repr(C)] @@ -129,6 +133,14 @@ impl OrderEvent for OrderEmulated { self.instrument_id } + fn trade_id(&self) -> Option { + None + } + + fn currency(&self) -> Option { + None + } + fn client_order_id(&self) -> ClientOrderId { self.client_order_id } @@ -145,6 +157,10 @@ impl OrderEvent for OrderEmulated { None } + fn liquidity_side(&self) -> Option { + None + } + fn post_only(&self) -> Option { None } @@ -165,6 +181,14 @@ impl OrderEvent for OrderEmulated { None } + fn last_px(&self) -> Option { + None + } + + fn last_qty(&self) -> Option { + None + } + fn trigger_price(&self) -> Option { None } @@ -233,6 +257,14 @@ impl OrderEvent for OrderEmulated { None } + fn position_id(&self) -> Option { + None + } + + fn commission(&self) -> Option { + None + } + fn ts_event(&self) -> UnixNanos { self.ts_event } diff --git a/nautilus_core/model/src/events/order/expired.rs b/nautilus_core/model/src/events/order/expired.rs index 3f086e48c477..9671d87ce4da 100644 --- a/nautilus_core/model/src/events/order/expired.rs +++ b/nautilus_core/model/src/events/order/expired.rs @@ -21,14 +21,18 @@ use serde::{Deserialize, Serialize}; use ustr::Ustr; use crate::{ - enums::{ContingencyType, OrderSide, OrderType, TimeInForce, TrailingOffsetType, TriggerType}, + enums::{ + ContingencyType, LiquiditySide, OrderSide, OrderType, TimeInForce, TrailingOffsetType, + TriggerType, + }, events::order::OrderEvent, identifiers::{ account_id::AccountId, client_order_id::ClientOrderId, exec_algorithm_id::ExecAlgorithmId, - instrument_id::InstrumentId, order_list_id::OrderListId, strategy_id::StrategyId, - trader_id::TraderId, venue_order_id::VenueOrderId, + instrument_id::InstrumentId, order_list_id::OrderListId, position_id::PositionId, + strategy_id::StrategyId, trade_id::TradeId, trader_id::TraderId, + venue_order_id::VenueOrderId, }, - types::{price::Price, quantity::Quantity}, + types::{currency::Currency, money::Money, price::Price, quantity::Quantity}, }; #[repr(C)] @@ -145,6 +149,14 @@ impl OrderEvent for OrderExpired { self.instrument_id } + fn trade_id(&self) -> Option { + None + } + + fn currency(&self) -> Option { + None + } + fn client_order_id(&self) -> ClientOrderId { self.client_order_id } @@ -161,6 +173,10 @@ impl OrderEvent for OrderExpired { None } + fn liquidity_side(&self) -> Option { + None + } + fn post_only(&self) -> Option { None } @@ -181,6 +197,14 @@ impl OrderEvent for OrderExpired { None } + fn last_px(&self) -> Option { + None + } + + fn last_qty(&self) -> Option { + None + } + fn trigger_price(&self) -> Option { None } @@ -249,6 +273,14 @@ impl OrderEvent for OrderExpired { self.account_id } + fn position_id(&self) -> Option { + None + } + + fn commission(&self) -> Option { + None + } + fn ts_event(&self) -> UnixNanos { self.ts_event } diff --git a/nautilus_core/model/src/events/order/filled.rs b/nautilus_core/model/src/events/order/filled.rs index f823ba6f6f90..251a58ceb469 100644 --- a/nautilus_core/model/src/events/order/filled.rs +++ b/nautilus_core/model/src/events/order/filled.rs @@ -267,6 +267,14 @@ impl OrderEvent for OrderFilled { self.instrument_id } + fn trade_id(&self) -> Option { + Some(self.trade_id) + } + + fn currency(&self) -> Option { + Some(self.currency) + } + fn client_order_id(&self) -> ClientOrderId { self.client_order_id } @@ -283,6 +291,10 @@ impl OrderEvent for OrderFilled { None } + fn liquidity_side(&self) -> Option { + Some(self.liquidity_side) + } + fn post_only(&self) -> Option { None } @@ -303,6 +315,14 @@ impl OrderEvent for OrderFilled { None } + fn last_px(&self) -> Option { + Some(self.last_px) + } + + fn last_qty(&self) -> Option { + Some(self.last_qty) + } + fn trigger_price(&self) -> Option { None } @@ -371,6 +391,14 @@ impl OrderEvent for OrderFilled { Some(self.account_id) } + fn position_id(&self) -> Option { + self.position_id + } + + fn commission(&self) -> Option { + self.commission + } + fn ts_event(&self) -> UnixNanos { self.ts_event } diff --git a/nautilus_core/model/src/events/order/initialized.rs b/nautilus_core/model/src/events/order/initialized.rs index 8181143226c3..e52e41afa90b 100644 --- a/nautilus_core/model/src/events/order/initialized.rs +++ b/nautilus_core/model/src/events/order/initialized.rs @@ -24,15 +24,19 @@ use serde::{Deserialize, Serialize}; use ustr::Ustr; use crate::{ - enums::{ContingencyType, OrderSide, OrderType, TimeInForce, TrailingOffsetType, TriggerType}, + enums::{ + ContingencyType, LiquiditySide, OrderSide, OrderType, TimeInForce, TrailingOffsetType, + TriggerType, + }, events::order::OrderEvent, identifiers::{ account_id::AccountId, client_order_id::ClientOrderId, exec_algorithm_id::ExecAlgorithmId, - instrument_id::InstrumentId, order_list_id::OrderListId, strategy_id::StrategyId, - trader_id::TraderId, venue_order_id::VenueOrderId, + instrument_id::InstrumentId, order_list_id::OrderListId, position_id::PositionId, + strategy_id::StrategyId, trade_id::TradeId, trader_id::TraderId, + venue_order_id::VenueOrderId, }, orders::any::OrderAny, - types::{price::Price, quantity::Quantity}, + types::{currency::Currency, money::Money, price::Price, quantity::Quantity}, }; #[repr(C)] @@ -399,6 +403,14 @@ impl OrderEvent for OrderInitialized { self.instrument_id } + fn trade_id(&self) -> Option { + None + } + + fn currency(&self) -> Option { + None + } + fn client_order_id(&self) -> ClientOrderId { self.client_order_id } @@ -415,6 +427,10 @@ impl OrderEvent for OrderInitialized { Some(self.time_in_force) } + fn liquidity_side(&self) -> Option { + None + } + fn post_only(&self) -> Option { Some(self.post_only) } @@ -435,6 +451,14 @@ impl OrderEvent for OrderInitialized { self.price } + fn last_px(&self) -> Option { + None + } + + fn last_qty(&self) -> Option { + None + } + fn trigger_price(&self) -> Option { self.trigger_price } @@ -503,6 +527,14 @@ impl OrderEvent for OrderInitialized { None } + fn position_id(&self) -> Option { + None + } + + fn commission(&self) -> Option { + None + } + fn ts_event(&self) -> UnixNanos { self.ts_event } diff --git a/nautilus_core/model/src/events/order/mod.rs b/nautilus_core/model/src/events/order/mod.rs index 1da1f02af7c9..bf64de0a6616 100644 --- a/nautilus_core/model/src/events/order/mod.rs +++ b/nautilus_core/model/src/events/order/mod.rs @@ -17,13 +17,17 @@ use nautilus_core::{nanos::UnixNanos, uuid::UUID4}; use ustr::Ustr; use crate::{ - enums::{ContingencyType, OrderSide, OrderType, TimeInForce, TrailingOffsetType, TriggerType}, + enums::{ + ContingencyType, LiquiditySide, OrderSide, OrderType, TimeInForce, TrailingOffsetType, + TriggerType, + }, identifiers::{ account_id::AccountId, client_order_id::ClientOrderId, exec_algorithm_id::ExecAlgorithmId, - instrument_id::InstrumentId, order_list_id::OrderListId, strategy_id::StrategyId, - trader_id::TraderId, venue_order_id::VenueOrderId, + instrument_id::InstrumentId, order_list_id::OrderListId, position_id::PositionId, + strategy_id::StrategyId, trade_id::TradeId, trader_id::TraderId, + venue_order_id::VenueOrderId, }, - types::{price::Price, quantity::Quantity}, + types::{currency::Currency, money::Money, price::Price, quantity::Quantity}, }; pub mod accepted; @@ -55,15 +59,20 @@ pub trait OrderEvent: 'static + Send { fn trader_id(&self) -> TraderId; fn strategy_id(&self) -> StrategyId; fn instrument_id(&self) -> InstrumentId; + fn trade_id(&self) -> Option; + fn currency(&self) -> Option; fn client_order_id(&self) -> ClientOrderId; fn reason(&self) -> Option; fn quantity(&self) -> Option; fn time_in_force(&self) -> Option; + fn liquidity_side(&self) -> Option; fn post_only(&self) -> Option; fn reduce_only(&self) -> Option; fn quote_quantity(&self) -> Option; fn reconciliation(&self) -> bool; fn price(&self) -> Option; + fn last_px(&self) -> Option; + fn last_qty(&self) -> Option; fn trigger_price(&self) -> Option; fn trigger_type(&self) -> Option; fn limit_offset(&self) -> Option; @@ -81,6 +90,8 @@ pub trait OrderEvent: 'static + Send { fn exec_spawn_id(&self) -> Option; fn venue_order_id(&self) -> Option; fn account_id(&self) -> Option; + fn position_id(&self) -> Option; + fn commission(&self) -> Option; fn ts_event(&self) -> UnixNanos; fn ts_init(&self) -> UnixNanos; } diff --git a/nautilus_core/model/src/events/order/modify_rejected.rs b/nautilus_core/model/src/events/order/modify_rejected.rs index 7da53ef03e8d..a59a3d741903 100644 --- a/nautilus_core/model/src/events/order/modify_rejected.rs +++ b/nautilus_core/model/src/events/order/modify_rejected.rs @@ -21,14 +21,18 @@ use serde::{Deserialize, Serialize}; use ustr::Ustr; use crate::{ - enums::{ContingencyType, OrderSide, OrderType, TimeInForce, TrailingOffsetType, TriggerType}, + enums::{ + ContingencyType, LiquiditySide, OrderSide, OrderType, TimeInForce, TrailingOffsetType, + TriggerType, + }, events::order::OrderEvent, identifiers::{ account_id::AccountId, client_order_id::ClientOrderId, exec_algorithm_id::ExecAlgorithmId, - instrument_id::InstrumentId, order_list_id::OrderListId, strategy_id::StrategyId, - trader_id::TraderId, venue_order_id::VenueOrderId, + instrument_id::InstrumentId, order_list_id::OrderListId, position_id::PositionId, + strategy_id::StrategyId, trade_id::TradeId, trader_id::TraderId, + venue_order_id::VenueOrderId, }, - types::{price::Price, quantity::Quantity}, + types::{currency::Currency, money::Money, price::Price, quantity::Quantity}, }; #[repr(C)] @@ -149,6 +153,14 @@ impl OrderEvent for OrderModifyRejected { self.instrument_id } + fn trade_id(&self) -> Option { + None + } + + fn currency(&self) -> Option { + None + } + fn client_order_id(&self) -> ClientOrderId { self.client_order_id } @@ -165,6 +177,10 @@ impl OrderEvent for OrderModifyRejected { None } + fn liquidity_side(&self) -> Option { + None + } + fn post_only(&self) -> Option { None } @@ -185,6 +201,14 @@ impl OrderEvent for OrderModifyRejected { None } + fn last_px(&self) -> Option { + None + } + + fn last_qty(&self) -> Option { + None + } + fn trigger_price(&self) -> Option { None } @@ -253,6 +277,14 @@ impl OrderEvent for OrderModifyRejected { self.account_id } + fn position_id(&self) -> Option { + None + } + + fn commission(&self) -> Option { + None + } + fn ts_event(&self) -> UnixNanos { self.ts_event } diff --git a/nautilus_core/model/src/events/order/pending_cancel.rs b/nautilus_core/model/src/events/order/pending_cancel.rs index 6254edbca152..1ce6f4dc0b6f 100644 --- a/nautilus_core/model/src/events/order/pending_cancel.rs +++ b/nautilus_core/model/src/events/order/pending_cancel.rs @@ -21,14 +21,18 @@ use serde::{Deserialize, Serialize}; use ustr::Ustr; use crate::{ - enums::{ContingencyType, OrderSide, OrderType, TimeInForce, TrailingOffsetType, TriggerType}, + enums::{ + ContingencyType, LiquiditySide, OrderSide, OrderType, TimeInForce, TrailingOffsetType, + TriggerType, + }, events::order::OrderEvent, identifiers::{ account_id::AccountId, client_order_id::ClientOrderId, exec_algorithm_id::ExecAlgorithmId, - instrument_id::InstrumentId, order_list_id::OrderListId, strategy_id::StrategyId, - trader_id::TraderId, venue_order_id::VenueOrderId, + instrument_id::InstrumentId, order_list_id::OrderListId, position_id::PositionId, + strategy_id::StrategyId, trade_id::TradeId, trader_id::TraderId, + venue_order_id::VenueOrderId, }, - types::{price::Price, quantity::Quantity}, + types::{currency::Currency, money::Money, price::Price, quantity::Quantity}, }; #[repr(C)] @@ -144,6 +148,14 @@ impl OrderEvent for OrderPendingCancel { self.instrument_id } + fn trade_id(&self) -> Option { + None + } + + fn currency(&self) -> Option { + None + } + fn client_order_id(&self) -> ClientOrderId { self.client_order_id } @@ -160,6 +172,10 @@ impl OrderEvent for OrderPendingCancel { None } + fn liquidity_side(&self) -> Option { + None + } + fn post_only(&self) -> Option { None } @@ -180,6 +196,14 @@ impl OrderEvent for OrderPendingCancel { None } + fn last_px(&self) -> Option { + None + } + + fn last_qty(&self) -> Option { + None + } + fn trigger_price(&self) -> Option { None } @@ -248,6 +272,14 @@ impl OrderEvent for OrderPendingCancel { Some(self.account_id) } + fn position_id(&self) -> Option { + None + } + + fn commission(&self) -> Option { + None + } + fn ts_event(&self) -> UnixNanos { self.ts_event } diff --git a/nautilus_core/model/src/events/order/pending_update.rs b/nautilus_core/model/src/events/order/pending_update.rs index f3e74e2add9a..e1926644161d 100644 --- a/nautilus_core/model/src/events/order/pending_update.rs +++ b/nautilus_core/model/src/events/order/pending_update.rs @@ -21,14 +21,18 @@ use serde::{Deserialize, Serialize}; use ustr::Ustr; use crate::{ - enums::{ContingencyType, OrderSide, OrderType, TimeInForce, TrailingOffsetType, TriggerType}, + enums::{ + ContingencyType, LiquiditySide, OrderSide, OrderType, TimeInForce, TrailingOffsetType, + TriggerType, + }, events::order::OrderEvent, identifiers::{ account_id::AccountId, client_order_id::ClientOrderId, exec_algorithm_id::ExecAlgorithmId, - instrument_id::InstrumentId, order_list_id::OrderListId, strategy_id::StrategyId, - trader_id::TraderId, venue_order_id::VenueOrderId, + instrument_id::InstrumentId, order_list_id::OrderListId, position_id::PositionId, + strategy_id::StrategyId, trade_id::TradeId, trader_id::TraderId, + venue_order_id::VenueOrderId, }, - types::{price::Price, quantity::Quantity}, + types::{currency::Currency, money::Money, price::Price, quantity::Quantity}, }; #[repr(C)] @@ -144,6 +148,14 @@ impl OrderEvent for OrderPendingUpdate { self.instrument_id } + fn trade_id(&self) -> Option { + None + } + + fn currency(&self) -> Option { + None + } + fn client_order_id(&self) -> ClientOrderId { self.client_order_id } @@ -160,6 +172,10 @@ impl OrderEvent for OrderPendingUpdate { None } + fn liquidity_side(&self) -> Option { + None + } + fn post_only(&self) -> Option { None } @@ -180,6 +196,14 @@ impl OrderEvent for OrderPendingUpdate { None } + fn last_px(&self) -> Option { + None + } + + fn last_qty(&self) -> Option { + None + } + fn trigger_price(&self) -> Option { None } @@ -248,6 +272,14 @@ impl OrderEvent for OrderPendingUpdate { Some(self.account_id) } + fn position_id(&self) -> Option { + None + } + + fn commission(&self) -> Option { + None + } + fn ts_event(&self) -> UnixNanos { self.ts_event } diff --git a/nautilus_core/model/src/events/order/rejected.rs b/nautilus_core/model/src/events/order/rejected.rs index b76df5f2eadf..9c276f3babb7 100644 --- a/nautilus_core/model/src/events/order/rejected.rs +++ b/nautilus_core/model/src/events/order/rejected.rs @@ -21,14 +21,18 @@ use serde::{Deserialize, Serialize}; use ustr::Ustr; use crate::{ - enums::{ContingencyType, OrderSide, OrderType, TimeInForce, TrailingOffsetType, TriggerType}, + enums::{ + ContingencyType, LiquiditySide, OrderSide, OrderType, TimeInForce, TrailingOffsetType, + TriggerType, + }, events::order::OrderEvent, identifiers::{ account_id::AccountId, client_order_id::ClientOrderId, exec_algorithm_id::ExecAlgorithmId, - instrument_id::InstrumentId, order_list_id::OrderListId, strategy_id::StrategyId, - trader_id::TraderId, venue_order_id::VenueOrderId, + instrument_id::InstrumentId, order_list_id::OrderListId, position_id::PositionId, + strategy_id::StrategyId, trade_id::TradeId, trader_id::TraderId, + venue_order_id::VenueOrderId, }, - types::{price::Price, quantity::Quantity}, + types::{currency::Currency, money::Money, price::Price, quantity::Quantity}, }; #[repr(C)] @@ -144,6 +148,14 @@ impl OrderEvent for OrderRejected { self.instrument_id } + fn trade_id(&self) -> Option { + None + } + + fn currency(&self) -> Option { + None + } + fn client_order_id(&self) -> ClientOrderId { self.client_order_id } @@ -160,6 +172,10 @@ impl OrderEvent for OrderRejected { None } + fn liquidity_side(&self) -> Option { + None + } + fn post_only(&self) -> Option { None } @@ -180,6 +196,14 @@ impl OrderEvent for OrderRejected { None } + fn last_px(&self) -> Option { + None + } + + fn last_qty(&self) -> Option { + None + } + fn trigger_price(&self) -> Option { None } @@ -248,6 +272,14 @@ impl OrderEvent for OrderRejected { Some(self.account_id) } + fn position_id(&self) -> Option { + None + } + + fn commission(&self) -> Option { + None + } + fn ts_event(&self) -> UnixNanos { self.ts_event } diff --git a/nautilus_core/model/src/events/order/released.rs b/nautilus_core/model/src/events/order/released.rs index e2bb4a434e11..1f23481e44f7 100644 --- a/nautilus_core/model/src/events/order/released.rs +++ b/nautilus_core/model/src/events/order/released.rs @@ -21,14 +21,18 @@ use serde::{Deserialize, Serialize}; use ustr::Ustr; use crate::{ - enums::{ContingencyType, OrderSide, OrderType, TimeInForce, TrailingOffsetType, TriggerType}, + enums::{ + ContingencyType, LiquiditySide, OrderSide, OrderType, TimeInForce, TrailingOffsetType, + TriggerType, + }, events::order::OrderEvent, identifiers::{ account_id::AccountId, client_order_id::ClientOrderId, exec_algorithm_id::ExecAlgorithmId, - instrument_id::InstrumentId, order_list_id::OrderListId, strategy_id::StrategyId, - trader_id::TraderId, venue_order_id::VenueOrderId, + instrument_id::InstrumentId, order_list_id::OrderListId, position_id::PositionId, + strategy_id::StrategyId, trade_id::TradeId, trader_id::TraderId, + venue_order_id::VenueOrderId, }, - types::{price::Price, quantity::Quantity}, + types::{currency::Currency, money::Money, price::Price, quantity::Quantity}, }; #[repr(C)] @@ -133,6 +137,14 @@ impl OrderEvent for OrderReleased { self.instrument_id } + fn trade_id(&self) -> Option { + None + } + + fn currency(&self) -> Option { + None + } + fn client_order_id(&self) -> ClientOrderId { self.client_order_id } @@ -149,6 +161,10 @@ impl OrderEvent for OrderReleased { None } + fn liquidity_side(&self) -> Option { + todo!() + } + fn post_only(&self) -> Option { None } @@ -169,6 +185,14 @@ impl OrderEvent for OrderReleased { None } + fn last_px(&self) -> Option { + None + } + + fn last_qty(&self) -> Option { + None + } + fn trigger_price(&self) -> Option { None } @@ -237,6 +261,14 @@ impl OrderEvent for OrderReleased { None } + fn position_id(&self) -> Option { + None + } + + fn commission(&self) -> Option { + None + } + fn ts_event(&self) -> UnixNanos { self.ts_event } diff --git a/nautilus_core/model/src/events/order/submitted.rs b/nautilus_core/model/src/events/order/submitted.rs index 26aa86644988..2b2566a9238c 100644 --- a/nautilus_core/model/src/events/order/submitted.rs +++ b/nautilus_core/model/src/events/order/submitted.rs @@ -21,14 +21,18 @@ use serde::{Deserialize, Serialize}; use ustr::Ustr; use crate::{ - enums::{ContingencyType, OrderSide, OrderType, TimeInForce, TrailingOffsetType, TriggerType}, + enums::{ + ContingencyType, LiquiditySide, OrderSide, OrderType, TimeInForce, TrailingOffsetType, + TriggerType, + }, events::order::OrderEvent, identifiers::{ account_id::AccountId, client_order_id::ClientOrderId, exec_algorithm_id::ExecAlgorithmId, - instrument_id::InstrumentId, order_list_id::OrderListId, strategy_id::StrategyId, - trader_id::TraderId, venue_order_id::VenueOrderId, + instrument_id::InstrumentId, order_list_id::OrderListId, position_id::PositionId, + strategy_id::StrategyId, trade_id::TradeId, trader_id::TraderId, + venue_order_id::VenueOrderId, }, - types::{price::Price, quantity::Quantity}, + types::{currency::Currency, money::Money, price::Price, quantity::Quantity}, }; #[repr(C)] @@ -135,6 +139,14 @@ impl OrderEvent for OrderSubmitted { self.instrument_id } + fn trade_id(&self) -> Option { + None + } + + fn currency(&self) -> Option { + None + } + fn client_order_id(&self) -> ClientOrderId { self.client_order_id } @@ -151,6 +163,10 @@ impl OrderEvent for OrderSubmitted { None } + fn liquidity_side(&self) -> Option { + None + } + fn post_only(&self) -> Option { None } @@ -171,6 +187,14 @@ impl OrderEvent for OrderSubmitted { None } + fn last_px(&self) -> Option { + None + } + + fn last_qty(&self) -> Option { + None + } + fn trigger_price(&self) -> Option { None } @@ -239,6 +263,14 @@ impl OrderEvent for OrderSubmitted { Some(self.account_id) } + fn position_id(&self) -> Option { + None + } + + fn commission(&self) -> Option { + None + } + fn ts_event(&self) -> UnixNanos { self.ts_event } diff --git a/nautilus_core/model/src/events/order/triggered.rs b/nautilus_core/model/src/events/order/triggered.rs index 7e05086443be..6d98b85a2a2b 100644 --- a/nautilus_core/model/src/events/order/triggered.rs +++ b/nautilus_core/model/src/events/order/triggered.rs @@ -21,14 +21,18 @@ use serde::{Deserialize, Serialize}; use ustr::Ustr; use crate::{ - enums::{ContingencyType, OrderSide, OrderType, TimeInForce, TrailingOffsetType, TriggerType}, + enums::{ + ContingencyType, LiquiditySide, OrderSide, OrderType, TimeInForce, TrailingOffsetType, + TriggerType, + }, events::order::OrderEvent, identifiers::{ account_id::AccountId, client_order_id::ClientOrderId, exec_algorithm_id::ExecAlgorithmId, - instrument_id::InstrumentId, order_list_id::OrderListId, strategy_id::StrategyId, - trader_id::TraderId, venue_order_id::VenueOrderId, + instrument_id::InstrumentId, order_list_id::OrderListId, position_id::PositionId, + strategy_id::StrategyId, trade_id::TradeId, trader_id::TraderId, + venue_order_id::VenueOrderId, }, - types::{price::Price, quantity::Quantity}, + types::{currency::Currency, money::Money, price::Price, quantity::Quantity}, }; #[repr(C)] @@ -148,6 +152,14 @@ impl OrderEvent for OrderTriggered { self.instrument_id } + fn trade_id(&self) -> Option { + None + } + + fn currency(&self) -> Option { + None + } + fn client_order_id(&self) -> ClientOrderId { self.client_order_id } @@ -164,6 +176,10 @@ impl OrderEvent for OrderTriggered { None } + fn liquidity_side(&self) -> Option { + None + } + fn post_only(&self) -> Option { None } @@ -184,6 +200,14 @@ impl OrderEvent for OrderTriggered { None } + fn last_px(&self) -> Option { + None + } + + fn last_qty(&self) -> Option { + None + } + fn trigger_price(&self) -> Option { None } @@ -252,6 +276,14 @@ impl OrderEvent for OrderTriggered { self.account_id } + fn position_id(&self) -> Option { + None + } + + fn commission(&self) -> Option { + None + } + fn ts_event(&self) -> UnixNanos { self.ts_event } diff --git a/nautilus_core/model/src/events/order/updated.rs b/nautilus_core/model/src/events/order/updated.rs index 6a5c1de5c82d..d0eaba1e25a7 100644 --- a/nautilus_core/model/src/events/order/updated.rs +++ b/nautilus_core/model/src/events/order/updated.rs @@ -21,14 +21,18 @@ use serde::{Deserialize, Serialize}; use ustr::Ustr; use crate::{ - enums::{ContingencyType, OrderSide, OrderType, TimeInForce, TrailingOffsetType, TriggerType}, + enums::{ + ContingencyType, LiquiditySide, OrderSide, OrderType, TimeInForce, TrailingOffsetType, + TriggerType, + }, events::order::OrderEvent, identifiers::{ account_id::AccountId, client_order_id::ClientOrderId, exec_algorithm_id::ExecAlgorithmId, - instrument_id::InstrumentId, order_list_id::OrderListId, strategy_id::StrategyId, - trader_id::TraderId, venue_order_id::VenueOrderId, + instrument_id::InstrumentId, order_list_id::OrderListId, position_id::PositionId, + strategy_id::StrategyId, trade_id::TradeId, trader_id::TraderId, + venue_order_id::VenueOrderId, }, - types::{price::Price, quantity::Quantity}, + types::{currency::Currency, money::Money, price::Price, quantity::Quantity}, }; #[repr(C)] @@ -160,6 +164,14 @@ impl OrderEvent for OrderUpdated { self.instrument_id } + fn trade_id(&self) -> Option { + None + } + + fn currency(&self) -> Option { + None + } + fn client_order_id(&self) -> ClientOrderId { self.client_order_id } @@ -176,6 +188,10 @@ impl OrderEvent for OrderUpdated { None } + fn liquidity_side(&self) -> Option { + None + } + fn post_only(&self) -> Option { None } @@ -196,6 +212,14 @@ impl OrderEvent for OrderUpdated { self.price } + fn last_px(&self) -> Option { + None + } + + fn last_qty(&self) -> Option { + None + } + fn trigger_price(&self) -> Option { self.trigger_price } @@ -264,6 +288,14 @@ impl OrderEvent for OrderUpdated { self.account_id } + fn position_id(&self) -> Option { + None + } + + fn commission(&self) -> Option { + None + } + fn ts_event(&self) -> UnixNanos { self.ts_event } diff --git a/nautilus_core/model/src/orders/any.rs b/nautilus_core/model/src/orders/any.rs index d3a753964fec..8caba5116456 100644 --- a/nautilus_core/model/src/orders/any.rs +++ b/nautilus_core/model/src/orders/any.rs @@ -105,18 +105,27 @@ impl OrderAny { } pub fn from_events(events: Vec) -> anyhow::Result { + println!("from events"); + println!("events: {:?}", events); if events.is_empty() { anyhow::bail!("No events provided"); - } else if events.len() == 1 { - let init_event = events.first().unwrap(); - match init_event { - OrderEventAny::Initialized(init) => Ok(init.to_owned().into()), - _ => { - anyhow::bail!("First event must be OrderInitialized"); + } + // pop the first event + let init_event = events.first().unwrap(); + match init_event { + OrderEventAny::Initialized(init) => { + let mut order = Self::from(init.clone()); + // apply the rest of the events + for event in events.into_iter().skip(1) { + // apply event to order + println!("applying event: {:?}", event); + order.apply(event).unwrap(); } + Ok(order) + } + _ => { + anyhow::bail!("First event must be OrderInitialized"); } - } else { - anyhow::bail!("Only one event can be provided"); } } } diff --git a/nautilus_core/model/src/orders/stubs.rs b/nautilus_core/model/src/orders/stubs.rs index 3a37a6e60be6..d11dc92f3333 100644 --- a/nautilus_core/model/src/orders/stubs.rs +++ b/nautilus_core/model/src/orders/stubs.rs @@ -20,14 +20,16 @@ use nautilus_core::{nanos::UnixNanos, uuid::UUID4}; use super::{limit::LimitOrder, stop_market::StopMarketOrder}; use crate::{ enums::{LiquiditySide, OrderSide, TimeInForce, TriggerType}, - events::order::filled::OrderFilled, + events::order::{accepted::OrderAccepted, filled::OrderFilled, submitted::OrderSubmitted}, identifiers::{ + account_id::AccountId, client_order_id::ClientOrderId, instrument_id::InstrumentId, position_id::PositionId, strategy_id::StrategyId, stubs::{strategy_id_ema_cross, trader_id}, trade_id::TradeId, + venue_order_id::VenueOrderId, }, instruments::Instrument, orders::{base::Order, market::MarketOrder}, @@ -49,12 +51,15 @@ impl TestOrderEventStubs { last_qty: Option, commission: Option, ts_filled_ns: Option, - ) -> OrderFilled { + account_id: Option, + ) -> anyhow::Result { let trader_id = trader_id(); let strategy_id = strategy_id.unwrap_or(order.strategy_id()); let instrument_id = order.instrument_id(); let venue_order_id = order.venue_order_id().unwrap_or_default(); - let account_id = order.account_id().unwrap_or_default(); + let account_id = account_id + .or(order.account_id()) + .unwrap_or(AccountId::from("SIM-001")); let trade_id = trade_id.unwrap_or( TradeId::new(order.client_order_id().as_str().replace('O', "E").as_str()).unwrap(), ); @@ -66,7 +71,7 @@ impl TestOrderEventStubs { let commission = commission.unwrap_or(Money::from_str("2 USD").unwrap()); let last_px = last_px.unwrap_or(Price::from_str("1.0").unwrap()); let last_qty = last_qty.unwrap_or(order.quantity()); - OrderFilled::new( + Ok(OrderFilled::new( trader_id, strategy_id, instrument_id, @@ -87,7 +92,52 @@ impl TestOrderEventStubs { Some(position_id), Some(commission), ) - .unwrap() + .unwrap()) + } + + pub fn order_submitted( + order: &T, + account_id: AccountId, + ) -> anyhow::Result { + let trader_id = trader_id(); + let strategy_id = order.strategy_id(); + let instrument_id = order.instrument_id(); + let client_order_id = order.client_order_id(); + Ok(OrderSubmitted::new( + trader_id, + strategy_id, + instrument_id, + client_order_id, + account_id, + UUID4::new(), + UnixNanos::default(), + UnixNanos::default(), + ) + .unwrap()) + } + + pub fn order_accepted( + order: &T, + account_id: AccountId, + venue_order_id: VenueOrderId, + ) -> anyhow::Result { + let trader_id = trader_id(); + let strategy_id = order.strategy_id(); + let instrument_id = order.instrument_id(); + let client_order_id = order.client_order_id(); + Ok(OrderAccepted::new( + trader_id, + strategy_id, + instrument_id, + client_order_id, + venue_order_id, + account_id, + UUID4::new(), + UnixNanos::default(), + UnixNanos::default(), + false, + ) + .unwrap()) } } diff --git a/nautilus_core/model/src/position.rs b/nautilus_core/model/src/position.rs index 71be2472e420..efc51bec4015 100644 --- a/nautilus_core/model/src/position.rs +++ b/nautilus_core/model/src/position.rs @@ -582,7 +582,9 @@ mod tests { None, None, None, - ); + None, + ) + .unwrap(); let fill2 = TestOrderEventStubs::order_filled::( &order2, &audusd_sim, @@ -593,7 +595,9 @@ mod tests { None, None, None, - ); + None, + ) + .unwrap(); let mut position = Position::new(audusd_sim, fill1).unwrap(); position.apply(&fill2); } @@ -617,7 +621,9 @@ mod tests { None, None, None, - ); + None, + ) + .unwrap(); let last_price = Price::from_str("1.0005").unwrap(); let position = Position::new(audusd_sim, fill).unwrap(); assert_eq!(position.symbol(), audusd_sim.id.symbol); @@ -683,7 +689,9 @@ mod tests { None, None, None, - ); + None, + ) + .unwrap(); let last_price = Price::from_str("1.00050").unwrap(); let position = Position::new(audusd_sim, fill).unwrap(); assert_eq!(position.symbol(), audusd_sim.id.symbol); @@ -747,7 +755,9 @@ mod tests { Some(Quantity::from(50_000)), None, None, - ); + None, + ) + .unwrap(); let last_price = Price::from_str("1.00048").unwrap(); let position = Position::new(audusd_sim, fill).unwrap(); assert_eq!(position.quantity, Quantity::from(50_000)); @@ -803,7 +813,9 @@ mod tests { Some(Quantity::from(50_000)), None, None, - ); + None, + ) + .unwrap(); let fill2 = TestOrderEventStubs::order_filled::( &order, &audusd_sim, @@ -814,7 +826,9 @@ mod tests { Some(Quantity::from(50_000)), None, None, - ); + None, + ) + .unwrap(); let last_price = Price::from_str("1.0005").unwrap(); let mut position = Position::new(audusd_sim, fill1).unwrap(); position.apply(&fill2); @@ -868,7 +882,9 @@ mod tests { None, None, Some(UnixNanos::from(1_000_000_000)), - ); + None, + ) + .unwrap(); let mut position = Position::new(audusd_sim, fill).unwrap(); let fill2 = OrderFilled::new( @@ -958,7 +974,9 @@ mod tests { None, None, None, - ); + None, + ) + .unwrap(); let mut position = Position::new(audusd_sim, fill1).unwrap(); // create closing from order from different venue but same strategy let fill2 = TestOrderEventStubs::order_filled( @@ -971,7 +989,9 @@ mod tests { Some(Quantity::from(50_000)), None, None, - ); + None, + ) + .unwrap(); let fill3 = TestOrderEventStubs::order_filled( &order2, &audusd_sim, @@ -982,7 +1002,9 @@ mod tests { Some(Quantity::from(50_000)), None, None, - ); + None, + ) + .unwrap(); let last = Price::from("1.0005"); position.apply(&fill2); position.apply(&fill3); @@ -1049,7 +1071,9 @@ mod tests { None, None, None, - ); + None, + ) + .unwrap(); let mut position = Position::new(audusd_sim, fill1).unwrap(); let fill2 = TestOrderEventStubs::order_filled( &order2, @@ -1061,7 +1085,9 @@ mod tests { None, None, None, - ); + None, + ) + .unwrap(); let last = Price::from("1.0005"); position.apply(&fill2); @@ -1135,7 +1161,9 @@ mod tests { None, None, None, - ); + None, + ) + .unwrap(); let fill2 = TestOrderEventStubs::order_filled( &order2, &audusd_sim, @@ -1146,7 +1174,9 @@ mod tests { None, None, None, - ); + None, + ) + .unwrap(); let fill3 = TestOrderEventStubs::order_filled( &order3, &audusd_sim, @@ -1157,7 +1187,9 @@ mod tests { None, None, None, - ); + None, + ) + .unwrap(); let mut position = Position::new(audusd_sim, fill1).unwrap(); let last = Price::from("1.0005"); position.apply(&fill2); @@ -1223,7 +1255,9 @@ mod tests { None, Some(commission1), None, - ); + None, + ) + .unwrap(); let mut position = Position::new(currency_pair_ethusdt, fill1).unwrap(); let quantity2 = Quantity::from(17); let order2 = TestOrderStubs::market_order( @@ -1246,7 +1280,9 @@ mod tests { None, Some(commission2), None, - ); + None, + ) + .unwrap(); position.apply(&fill2); assert_eq!(position.quantity, Quantity::from(29)); assert_eq!( @@ -1275,7 +1311,9 @@ mod tests { None, Some(commission3), None, - ); + None, + ) + .unwrap(); position.apply(&fill3); assert_eq!(position.quantity, Quantity::from(20)); assert_eq!(position.realized_pnl, Some(Money::from("13.89666207 USDT"))); @@ -1301,7 +1339,9 @@ mod tests { None, Some(commission4), None, - ); + None, + ) + .unwrap(); position.apply(&fill4); assert_eq!(position.quantity, Quantity::from("16")); assert_eq!(position.realized_pnl, Some(Money::from("36.19948966 USDT"))); @@ -1327,7 +1367,9 @@ mod tests { None, Some(commission5), None, - ); + None, + ) + .unwrap(); position.apply(&fill5); assert_eq!(position.quantity, Quantity::from("19")); assert_eq!(position.realized_pnl, Some(Money::from("36.16858966 USDT"))); @@ -1355,7 +1397,9 @@ mod tests { None, Some(commission1), Some(UnixNanos::from(1_000_000_000)), - ); + None, + ) + .unwrap(); let mut position = Position::new(audusd_sim, fill1).unwrap(); let fill2 = OrderFilled::new( @@ -1474,7 +1518,9 @@ mod tests { None, Some(commission1), None, - ); + None, + ) + .unwrap(); let mut position = Position::new(currency_pair_btcusdt, fill1).unwrap(); let order2 = TestOrderStubs::market_order( currency_pair_btcusdt.id, @@ -1500,7 +1546,9 @@ mod tests { None, Some(commission2), None, - ); + None, + ) + .unwrap(); position.apply(&fill2); assert_eq!(position.quantity, Quantity::from(29)); assert_eq!( @@ -1532,7 +1580,9 @@ mod tests { None, Some(commission3), None, - ); + None, + ) + .unwrap(); position.apply(&fill3); assert_eq!(position.quantity, Quantity::from(20)); assert_eq!( @@ -1564,7 +1614,9 @@ mod tests { None, Some(commission4), None, - ); + None, + ) + .unwrap(); position.apply(&fill4); assert_eq!(position.quantity, Quantity::from(23)); assert_eq!( @@ -1596,7 +1648,9 @@ mod tests { None, Some(commission5), None, - ); + None, + ) + .unwrap(); position.apply(&fill5); assert_eq!(position.quantity, Quantity::from(19)); assert_eq!( @@ -1631,7 +1685,9 @@ mod tests { None, None, None, - ); + None, + ) + .unwrap(); let position = Position::new(currency_pair_btcusdt, fill).unwrap(); let result = position.calculate_pnl(10500.0, 10500.0, Quantity::from("100000.0")); assert_eq!(result, Money::from("0 USDT")); @@ -1663,7 +1719,9 @@ mod tests { None, Some(commission), None, - ); + None, + ) + .unwrap(); let position = Position::new(currency_pair_btcusdt, fill).unwrap(); let pnl = position.calculate_pnl(10500.0, 10510.0, Quantity::from("12.0")); assert_eq!(pnl, Money::from("120 USDT")); @@ -1705,7 +1763,9 @@ mod tests { None, Some(commission), None, - ); + None, + ) + .unwrap(); let position = Position::new(currency_pair_btcusdt, fill).unwrap(); let pnl = position.calculate_pnl(10500.0, 10480.5, Quantity::from("10.0")); assert_eq!(pnl, Money::from("-195 USDT")); @@ -1747,7 +1807,9 @@ mod tests { None, Some(commission), None, - ); + None, + ) + .unwrap(); let position = Position::new(currency_pair_btcusdt, fill).unwrap(); let pnl = position.calculate_pnl(10500.0, 10390.0, Quantity::from("10.15")); assert_eq!(pnl, Money::from("1116.5 USDT")); @@ -1789,7 +1851,9 @@ mod tests { None, Some(commission), None, - ); + None, + ) + .unwrap(); let position = Position::new(currency_pair_btcusdt, fill).unwrap(); let pnl = position.calculate_pnl(10500.0, 10670.5, Quantity::from("10.0")); assert_eq!(pnl, Money::from("-1705 USDT")); @@ -1827,7 +1891,9 @@ mod tests { None, Some(commission), None, - ); + None, + ) + .unwrap(); let position = Position::new(xbtusd_bitmex, fill).unwrap(); let pnl = position.calculate_pnl(10000.0, 11000.0, Quantity::from("100000.0")); assert_eq!(pnl, Money::from("-0.90909091 BTC")); @@ -1864,7 +1930,9 @@ mod tests { None, Some(commission), None, - ); + None, + ) + .unwrap(); let position = Position::new(ethusdt_bitmex, fill).unwrap(); assert_eq!( @@ -1910,7 +1978,9 @@ mod tests { None, Some(commission1), None, - ); + None, + ) + .unwrap(); let commission2 = calculate_commission( currency_pair_btcusdt, order2.quantity, @@ -1928,7 +1998,9 @@ mod tests { None, Some(commission2), None, - ); + None, + ) + .unwrap(); let mut position = Position::new(currency_pair_btcusdt, fill1).unwrap(); position.apply(&fill2); let pnl = position.unrealized_pnl(Price::from("11505.60")); @@ -1969,7 +2041,9 @@ mod tests { None, Some(commission), None, - ); + None, + ) + .unwrap(); let position = Position::new(currency_pair_btcusdt, fill).unwrap(); let pnl = position.unrealized_pnl(Price::from("10407.15")); assert_eq!(pnl, Money::from("582.03640000 USDT")); @@ -2005,7 +2079,9 @@ mod tests { None, Some(commission), None, - ); + None, + ) + .unwrap(); let position = Position::new(xbtusd_bitmex, fill).unwrap(); let pnl = position.unrealized_pnl(Price::from("11505.60")); @@ -2036,7 +2112,9 @@ mod tests { None, Some(commission), None, - ); + None, + ) + .unwrap(); let position = Position::new(xbtusd_bitmex, fill).unwrap(); let pnl = position.unrealized_pnl(Price::from("12506.65")); @@ -2074,7 +2152,9 @@ mod tests { None, Some(commission), None, - ); + None, + ) + .unwrap(); let position = Position::new(audusd_sim, fill).unwrap(); assert_eq!(position.signed_qty, expected); } diff --git a/nautilus_core/model/src/python/events/order/filled.rs b/nautilus_core/model/src/python/events/order/filled.rs index 25fd0cb2fd54..0cc02b65f449 100644 --- a/nautilus_core/model/src/python/events/order/filled.rs +++ b/nautilus_core/model/src/python/events/order/filled.rs @@ -248,6 +248,7 @@ impl OrderFilled { dict.set_item("ts_event", self.ts_event.as_u64())?; dict.set_item("ts_init", self.ts_init.as_u64())?; dict.set_item("reconciliation", self.reconciliation)?; + dict.set_item("info", PyDict::new(py))?; match self.position_id { Some(position_id) => dict.set_item("position_id", position_id.to_string())?, None => dict.set_item("position_id", py.None())?, diff --git a/nautilus_core/model/src/python/events/order/initialized.rs b/nautilus_core/model/src/python/events/order/initialized.rs index e3a36cc08696..b3de84fac14a 100644 --- a/nautilus_core/model/src/python/events/order/initialized.rs +++ b/nautilus_core/model/src/python/events/order/initialized.rs @@ -161,7 +161,13 @@ impl OrderInitialized { dict.set_item("quote_quantity", self.quote_quantity)?; dict.set_item("reconciliation", self.reconciliation)?; // TODO remove options as in legacy cython only - dict.set_item("options", PyDict::new(py))?; + let options = PyDict::new(py); + if self.order_type == OrderType::StopMarket { + options.set_item("trigger_type", self.trigger_type.map(|x| x.to_string()))?; + options.set_item("trigger_price", self.trigger_price.map(|x| x.to_string()))?; + options.set_item("expire_time_ns", self.expire_time.map(|x| x.to_string()))?; + } + dict.set_item("options", options)?; dict.set_item("event_id", self.event_id.to_string())?; dict.set_item("ts_event", self.ts_event.as_u64())?; dict.set_item("ts_init", self.ts_init.as_u64())?; diff --git a/nautilus_core/model/src/stubs.rs b/nautilus_core/model/src/stubs.rs index cd9db6785ae9..107498b9b566 100644 --- a/nautilus_core/model/src/stubs.rs +++ b/nautilus_core/model/src/stubs.rs @@ -75,7 +75,9 @@ pub fn test_position_long(audusd_sim: CurrencyPair) -> Position { None, None, None, - ); + None, + ) + .unwrap(); Position::new(audusd_sim, order_filled).unwrap() } @@ -98,7 +100,9 @@ pub fn test_position_short(audusd_sim: CurrencyPair) -> Position { None, None, None, - ); + None, + ) + .unwrap(); Position::new(audusd_sim, order_filled).unwrap() } diff --git a/nautilus_trader/cache/postgres/adapter.py b/nautilus_trader/cache/postgres/adapter.py index 294883ae1089..61ccddaa325c 100644 --- a/nautilus_trader/cache/postgres/adapter.py +++ b/nautilus_trader/cache/postgres/adapter.py @@ -78,6 +78,10 @@ def add_order(self, order: Order): order_pyo3 = transform_order_to_pyo3(order) self._backing.add_order(order_pyo3) + def update_order(self, order: Order): + order_pyo3 = transform_order_to_pyo3(order) + self._backing.update_order(order_pyo3) + def load_order(self, client_order_id: ClientOrderId): order_id_pyo3 = nautilus_pyo3.ClientOrderId.from_str(str(client_order_id)) order_pyo3 = self._backing.load_order(order_id_pyo3) diff --git a/nautilus_trader/cache/postgres/transformers.py b/nautilus_trader/cache/postgres/transformers.py index bbac9201816b..55a9a155d920 100644 --- a/nautilus_trader/cache/postgres/transformers.py +++ b/nautilus_trader/cache/postgres/transformers.py @@ -14,9 +14,22 @@ # ------------------------------------------------------------------------------------------------- from nautilus_trader.core import nautilus_pyo3 -from nautilus_trader.core.rust.model import OrderType from nautilus_trader.model.enums import CurrencyType +from nautilus_trader.model.events import OrderAccepted +from nautilus_trader.model.events import OrderCanceled +from nautilus_trader.model.events import OrderDenied +from nautilus_trader.model.events import OrderEmulated +from nautilus_trader.model.events import OrderExpired +from nautilus_trader.model.events import OrderFilled from nautilus_trader.model.events import OrderInitialized +from nautilus_trader.model.events import OrderModifyRejected +from nautilus_trader.model.events import OrderPendingCancel +from nautilus_trader.model.events import OrderPendingUpdate +from nautilus_trader.model.events import OrderRejected +from nautilus_trader.model.events import OrderReleased +from nautilus_trader.model.events import OrderSubmitted +from nautilus_trader.model.events import OrderTriggered +from nautilus_trader.model.events import OrderUpdated from nautilus_trader.model.instruments import CryptoFuture from nautilus_trader.model.instruments import CryptoPerpetual from nautilus_trader.model.instruments import CurrencyPair @@ -27,8 +40,8 @@ from nautilus_trader.model.instruments import OptionsContract from nautilus_trader.model.instruments import OptionsSpread from nautilus_trader.model.objects import Currency -from nautilus_trader.model.orders import MarketOrder from nautilus_trader.model.orders import Order +from nautilus_trader.model.orders.unpacker import OrderUnpacker ################################################################################ @@ -103,7 +116,45 @@ def transform_instrument_from_pyo3(instrument_pyo3) -> Instrument | None: ################################################################################ # Orders ################################################################################ -def transform_order_event_to_pyo3(order_event): +def transform_order_event_to_pyo3(order_event): # noqa: C901 + if isinstance(order_event, OrderInitialized): + return nautilus_pyo3.OrderInitialized.from_dict(OrderInitialized.to_dict(order_event)) + elif isinstance(order_event, OrderDenied): + return nautilus_pyo3.OrderDenied.from_dict(OrderDenied.to_dict(order_event)) + elif isinstance(order_event, OrderEmulated): + return nautilus_pyo3.OrderEmulated.from_dict(OrderEmulated.to_dict(order_event)) + elif isinstance(order_event, OrderReleased): + return nautilus_pyo3.OrderReleased.from_dict(OrderReleased.to_dict(order_event)) + elif isinstance(order_event, OrderSubmitted): + return nautilus_pyo3.OrderSubmitted.from_dict(OrderSubmitted.to_dict(order_event)) + elif isinstance(order_event, OrderAccepted): + order_event_dict = OrderAccepted.to_dict(order_event) + return nautilus_pyo3.OrderAccepted.from_dict(order_event_dict) + elif isinstance(order_event, OrderRejected): + return nautilus_pyo3.OrderRejected.from_dict(OrderRejected.to_dict(order_event)) + elif isinstance(order_event, OrderCanceled): + return nautilus_pyo3.OrderCanceled.from_dict(OrderCanceled.to_dict(order_event)) + elif isinstance(order_event, OrderExpired): + return nautilus_pyo3.OrderExpired.from_dict(OrderExpired.to_dict(order_event)) + elif isinstance(order_event, OrderTriggered): + return nautilus_pyo3.OrderTriggered.from_dict(OrderTriggered.to_dict(order_event)) + elif isinstance(order_event, OrderPendingUpdate): + return nautilus_pyo3.OrderPendingUpdate.from_dict(OrderPendingUpdate.to_dict(order_event)) + elif isinstance(order_event, OrderModifyRejected): + return nautilus_pyo3.OrderModifyRejected.from_dict(OrderModifyRejected.to_dict(order_event)) + elif isinstance(order_event, OrderPendingCancel): + return nautilus_pyo3.OrderPendingCancel.from_dict(OrderPendingCancel.to_dict(order_event)) + elif isinstance(order_event, OrderUpdated): + return nautilus_pyo3.OrderUpdated.from_dict(OrderUpdated.to_dict(order_event)) + elif isinstance(order_event, OrderFilled): + return nautilus_pyo3.OrderFilled.from_dict(OrderFilled.to_dict(order_event)) + elif isinstance(order_event, OrderPendingCancel): + return nautilus_pyo3.OrderPendingCancel.from_dict(OrderPendingCancel.to_dict(order_event)) + else: + raise ValueError(f"Unknown order event type: {order_event}") + + +def from_order_initialized_cython_to_order_pyo3(order_event): order_event_dict = OrderInitialized.to_dict(order_event) # in options field there are some properties we need to attach to dict for key, value in order_event.options.items(): @@ -121,11 +172,44 @@ def transform_order_event_to_pyo3(order_event): raise ValueError(f"Unknown order type: {order_event_pyo3.event_type}") -def transform_order_event_from_pyo3(order_event_pyo3): - order_event_dict = order_event_pyo3.to_dict() - order_event_cython = OrderInitialized.from_dict(order_event_dict) - if order_event_pyo3.order_type == OrderType.MARKET: - return MarketOrder.create(order_event_cython) +def from_order_initialized_pyo3_to_order_cython(order_event): + order_event_cython = OrderInitialized.from_dict(order_event.to_dict()) + return OrderUnpacker.from_init(order_event_cython) + + +def transform_order_event_from_pyo3(order_event_pyo3): # noqa: C901 + if isinstance(order_event_pyo3, nautilus_pyo3.OrderInitialized): + return OrderInitialized.from_dict(order_event_pyo3.to_dict()) + elif isinstance(order_event_pyo3, nautilus_pyo3.OrderDenied): + return OrderDenied.from_dict(order_event_pyo3.to_dict()) + elif isinstance(order_event_pyo3, nautilus_pyo3.OrderEmulated): + return OrderEmulated.from_dict(order_event_pyo3.to_dict()) + elif isinstance(order_event_pyo3, nautilus_pyo3.OrderReleased): + return OrderReleased.from_dict(order_event_pyo3.to_dict()) + elif isinstance(order_event_pyo3, nautilus_pyo3.OrderSubmitted): + return OrderSubmitted.from_dict(order_event_pyo3.to_dict()) + elif isinstance(order_event_pyo3, nautilus_pyo3.OrderAccepted): + return OrderAccepted.from_dict(order_event_pyo3.to_dict()) + elif isinstance(order_event_pyo3, nautilus_pyo3.OrderRejected): + return OrderRejected.from_dict(order_event_pyo3.to_dict()) + elif isinstance(order_event_pyo3, nautilus_pyo3.OrderCanceled): + return OrderCanceled.from_dict(order_event_pyo3.to_dict()) + elif isinstance(order_event_pyo3, nautilus_pyo3.OrderExpired): + return OrderExpired.from_dict(order_event_pyo3.to_dict()) + elif isinstance(order_event_pyo3, nautilus_pyo3.OrderTriggered): + return OrderTriggered.from_dict(order_event_pyo3.to_dict()) + elif isinstance(order_event_pyo3, nautilus_pyo3.OrderPendingUpdate): + return OrderPendingUpdate.from_dict(order_event_pyo3.to_dict()) + elif isinstance(order_event_pyo3, nautilus_pyo3.OrderModifyRejected): + return OrderModifyRejected.from_dict(order_event_pyo3.to_dict()) + elif isinstance(order_event_pyo3, nautilus_pyo3.OrderPendingCancel): + return OrderPendingCancel.from_dict(order_event_pyo3.to_dict()) + elif isinstance(order_event_pyo3, nautilus_pyo3.OrderUpdated): + return OrderUpdated.from_dict(order_event_pyo3.to_dict()) + elif isinstance(order_event_pyo3, nautilus_pyo3.OrderFilled): + return OrderFilled.from_dict(order_event_pyo3.to_dict()) + else: + raise ValueError(f"Unknown order event type: {order_event_pyo3}") def transform_order_to_pyo3(order: Order): @@ -135,11 +219,10 @@ def transform_order_to_pyo3(order: Order): init_event = events.pop(0) if not isinstance(init_event, OrderInitialized): raise KeyError("init event should be of type OrderInitialized") - order_py3: nautilus_pyo3.OrderInitialized = transform_order_event_to_pyo3(init_event) + order_py3 = from_order_initialized_cython_to_order_pyo3(init_event) for event_cython in events: - raise NotImplementedError("Not implemented") - # event_pyo3 = transform_order_event_to_pyo3(event_cython) - # order_py3.apply(event_pyo3) + event_pyo3 = transform_order_event_to_pyo3(event_cython) + order_py3.apply(event_pyo3) return order_py3 @@ -150,9 +233,8 @@ def transform_order_from_pyo3(order_pyo3): init_event = events_pyo3.pop(0) if not isinstance(init_event, nautilus_pyo3.OrderInitialized): raise KeyError("init event should be of type OrderInitialized") - order_cython = transform_order_event_from_pyo3(init_event) + order_cython = from_order_initialized_pyo3_to_order_cython(init_event) for event_pyo3 in events_pyo3: - raise NotImplementedError("Not implemented") - # event_cython = transform_order_event_from_pyo3(event_pyo3) - # order_cython.apply(event_cython) + event_cython = transform_order_event_from_pyo3(event_pyo3) + order_cython.apply(event_cython) return order_cython diff --git a/nautilus_trader/core/nautilus_pyo3.pyi b/nautilus_trader/core/nautilus_pyo3.pyi index 0b46e5aa31e8..325a97a04b37 100644 --- a/nautilus_trader/core/nautilus_pyo3.pyi +++ b/nautilus_trader/core/nautilus_pyo3.pyi @@ -2294,6 +2294,7 @@ class PostgresCacheDatabase: def add_currency(self,currency: Currency) -> None: ... def add_instrument(self, instrument: object) -> None: ... def add_order(self, instrument: object) -> None: ... + def update_order(self, order: object) -> None: ... def load_currency(self, code: str) -> Currency | None: ... def load_currencies(self) -> list[Currency]: ... def load_instrument(self, instrument_id: InstrumentId) -> Instrument | None: ... diff --git a/nautilus_trader/model/orders/stop_market.pyx b/nautilus_trader/model/orders/stop_market.pyx index 0b17119e708d..680ea936e2fc 100644 --- a/nautilus_trader/model/orders/stop_market.pyx +++ b/nautilus_trader/model/orders/stop_market.pyx @@ -339,10 +339,10 @@ cdef class StopMarketOrder(Order): quantity=init.quantity, trigger_price=Price.from_str_c(init.options["trigger_price"]), trigger_type=trigger_type_from_str(init.options["trigger_type"]), - time_in_force=init.time_in_force, - expire_time_ns=init.options["expire_time_ns"], init_id=init.id, ts_init=init.ts_init, + time_in_force=init.time_in_force, + expire_time_ns=init.options["expire_time_ns"] if init.options["expire_time_ns"] is not None else 0, reduce_only=init.reduce_only, quote_quantity=init.quote_quantity, emulation_trigger=init.emulation_trigger, diff --git a/schema/tables.sql b/schema/tables.sql index 5758a2819567..aef9743448a1 100644 --- a/schema/tables.sql +++ b/schema/tables.sql @@ -100,15 +100,20 @@ CREATE TABLE IF NOT EXISTS "order_event" ( strategy_id TEXT NOT NULL, instrument_id TEXT NOT NULL, order_id TEXT DEFAULT NULL, + trade_id TEXT, + currency TEXT REFERENCES currency(code), order_type TEXT, order_side TEXT, quantity TEXT, time_in_force TEXT, + liquidity_side TEXT, post_only BOOLEAN DEFAULT FALSE, reduce_only BOOLEAN DEFAULT FALSE, quote_quantity BOOLEAN DEFAULT FALSE, reconciliation BOOLEAN DEFAULT FALSE, price TEXT, + last_px TEXT, + last_qty TEXT, trigger_price TEXT, trigger_type TEXT, limit_offset TEXT, @@ -127,6 +132,8 @@ CREATE TABLE IF NOT EXISTS "order_event" ( exec_spawn_id TEXT, venue_order_id TEXT, account_id TEXT, + position_id TEXT, + commission TEXT, tags TEXT[], ts_event TEXT NOT NULL, ts_init TEXT NOT NULL, diff --git a/tests/integration_tests/infrastructure/test_cache_database_postgres.py b/tests/integration_tests/infrastructure/test_cache_database_postgres.py index 5e61edab5b47..fe8c8d0ef02a 100644 --- a/tests/integration_tests/infrastructure/test_cache_database_postgres.py +++ b/tests/integration_tests/infrastructure/test_cache_database_postgres.py @@ -33,6 +33,7 @@ from nautilus_trader.test_kit.providers import TestInstrumentProvider from nautilus_trader.test_kit.stubs.component import TestComponentStubs from nautilus_trader.test_kit.stubs.data import TestDataStubs +from nautilus_trader.test_kit.stubs.events import TestEventStubs from nautilus_trader.test_kit.stubs.identifiers import TestIdStubs from nautilus_trader.trading.strategy import Strategy @@ -351,3 +352,67 @@ async def test_add_order(self): result = self.database.load_order(order.client_order_id) assert result == order assert order.to_dict() == result.to_dict() + + @pytest.mark.asyncio + async def test_update_order_for_closed_order(self): + self.database.add_currency(_AUDUSD_SIM.quote_currency) + # Arrange + order = self.strategy.order_factory.market( + _AUDUSD_SIM.id, + OrderSide.BUY, + Quantity.from_int(100_000), + ) + + self.database.add_order(order) + + # Allow MPSC thread to insert + await eventually(lambda: self.database.load_order(order.client_order_id)) + + order.apply(TestEventStubs.order_submitted(order)) + self.database.update_order(order) + + order.apply(TestEventStubs.order_accepted(order)) + self.database.update_order(order) + + fill = TestEventStubs.order_filled( + order, + instrument=_AUDUSD_SIM, + last_px=Price.from_str("1.00001"), + ) + + order.apply(fill) + self.database.update_order(order) + + await asyncio.sleep(0.5) + + result = self.database.load_order(order.client_order_id) + assert result == order + assert order.to_dict() == result.to_dict() + + @pytest.mark.asyncio + async def test_update_order_for_open_order(self): + self.database.add_currency(_AUDUSD_SIM.quote_currency) + order = self.strategy.order_factory.stop_market( + _AUDUSD_SIM.id, + OrderSide.BUY, + Quantity.from_int(100_000), + Price.from_str("1.00000"), + ) + + self.database.add_order(order) + # Allow MPSC thread to insert + await eventually(lambda: self.database.load_order(order.client_order_id)) + + order.apply(TestEventStubs.order_submitted(order)) + self.database.update_order(order) + + order.apply(TestEventStubs.order_accepted(order)) + + # Act + self.database.update_order(order) + + await asyncio.sleep(0.5) + + result = self.database.load_order(order.client_order_id) + assert result == order + assert order.to_dict() == result.to_dict() diff --git a/tests/unit_tests/model/test_position_pyo3.py b/tests/unit_tests/model/test_position_pyo3.py index b7423834e9cf..8130cce6bc45 100644 --- a/tests/unit_tests/model/test_position_pyo3.py +++ b/tests/unit_tests/model/test_position_pyo3.py @@ -91,6 +91,7 @@ def test_position_to_from_dict(): "commission": "2.00 USD", "currency": "USD", "event_id": "038990c6-19d2-b5c8-37a6-fe91f9b7b9ed", + "info": {}, "instrument_id": "AUD/USD.SIM", "last_px": "1.00001", "last_qty": "100000", From 2b6e59b1fa1a216ac372b0e3906666cb1173e960 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Wed, 22 May 2024 21:37:14 +1000 Subject: [PATCH 16/28] Fix SandboxExecutionClient regressions --- nautilus_trader/adapters/sandbox/config.py | 6 ++++-- nautilus_trader/adapters/sandbox/execution.py | 7 +++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/nautilus_trader/adapters/sandbox/config.py b/nautilus_trader/adapters/sandbox/config.py index 23b5b0ad0c20..8b91e70888ee 100644 --- a/nautilus_trader/adapters/sandbox/config.py +++ b/nautilus_trader/adapters/sandbox/config.py @@ -13,6 +13,8 @@ # limitations under the License. # ------------------------------------------------------------------------------------------------- +from decimal import Decimal + from nautilus_trader.config import LiveExecClientConfig @@ -32,7 +34,7 @@ class SandboxExecutionClientConfig(LiveExecClientConfig, frozen=True, kw_only=Tr The order management system type used by the exchange. account_type : str, default 'MARGIN' The account type for the client. - default_leverage : float, default 1.0 + default_leverage : float, default 10.0 The account default leverage (for margin accounts). bar_execution : bool, default True If bars should be processed by the matching engine(s) (and move the market). @@ -44,7 +46,7 @@ class SandboxExecutionClientConfig(LiveExecClientConfig, frozen=True, kw_only=Tr base_currency: str | None = None oms_type: str = "NETTING" account_type: str = "MARGIN" - default_leverage: float = 1.0 + default_leverage: Decimal = Decimal(10) leverages: dict[str, float] | None = None book_type: str = "L1_MBP" frozen_account: bool = False diff --git a/nautilus_trader/adapters/sandbox/execution.py b/nautilus_trader/adapters/sandbox/execution.py index 2127ada677bd..47145542ad19 100644 --- a/nautilus_trader/adapters/sandbox/execution.py +++ b/nautilus_trader/adapters/sandbox/execution.py @@ -27,6 +27,7 @@ from nautilus_trader.cache.cache import Cache from nautilus_trader.common.component import LiveClock from nautilus_trader.common.component import MessageBus +from nautilus_trader.common.component import TestClock from nautilus_trader.common.providers import InstrumentProvider from nautilus_trader.core.data import Data from nautilus_trader.execution.reports import FillReport @@ -87,6 +88,8 @@ def __init__( account_type = account_type_from_str(config.account_type) base_currency = Currency.from_str(config.base_currency) if config.base_currency else None + self.test_clock = TestClock() + super().__init__( loop=loop, client_id=ClientId(config.venue), @@ -113,7 +116,7 @@ def __init__( portfolio=portfolio, msgbus=self._msgbus, cache=cache, - clock=clock, + clock=self.test_clock, fill_model=FillModel(), fee_model=MakerTakerFeeModel(), latency_model=LatencyModel(0), @@ -131,7 +134,7 @@ def __init__( exchange=self.exchange, msgbus=msgbus, cache=cache, - clock=clock, + clock=self.test_clock, ) self.exchange.register_client(self._client) self.exchange.initialize_account() From 75e43f3ea40a047869c58ed1fdc23ad3f88224b6 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Wed, 22 May 2024 21:44:05 +1000 Subject: [PATCH 17/28] Reorganize sandbox examples --- .../betfair => sandbox}/betfair_sandbox.py | 0 .../binance_futures_testnet_sandbox.py | 157 ++++++++++++++++++ .../interactive_brokers_sandbox.py} | 0 3 files changed, 157 insertions(+) rename examples/{live/betfair => sandbox}/betfair_sandbox.py (100%) create mode 100644 examples/sandbox/binance_futures_testnet_sandbox.py rename examples/{live/interactive_brokers/sandbox.py => sandbox/interactive_brokers_sandbox.py} (100%) diff --git a/examples/live/betfair/betfair_sandbox.py b/examples/sandbox/betfair_sandbox.py similarity index 100% rename from examples/live/betfair/betfair_sandbox.py rename to examples/sandbox/betfair_sandbox.py diff --git a/examples/sandbox/binance_futures_testnet_sandbox.py b/examples/sandbox/binance_futures_testnet_sandbox.py new file mode 100644 index 000000000000..c99daa54d131 --- /dev/null +++ b/examples/sandbox/binance_futures_testnet_sandbox.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 +# ------------------------------------------------------------------------------------------------- +# 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. +# ------------------------------------------------------------------------------------------------- + +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 +from nautilus_trader.config import LoggingConfig +from nautilus_trader.config import TradingNodeConfig +from nautilus_trader.examples.strategies.volatility_market_maker import VolatilityMarketMaker +from nautilus_trader.examples.strategies.volatility_market_maker import VolatilityMarketMakerConfig +from nautilus_trader.live.node import TradingNode +from nautilus_trader.model.data import BarType +from nautilus_trader.model.identifiers import InstrumentId +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. *** + +# 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(), +) +provider.load_all() +instruments = provider.list_all() + + +# Configure the trading node +config_node = TradingNodeConfig( + trader_id=TraderId("TESTER-001"), + logging=LoggingConfig( + log_level="INFO", + # log_level_file="DEBUG", + # log_file_format="json", + log_colors=True, + use_pyo3=True, + ), + exec_engine=LiveExecEngineConfig( + reconciliation=True, + reconciliation_lookback_mins=1440, + filter_position_reports=True, + ), + cache=CacheConfig( + # database=DatabaseConfig(timeout=2), + timestamps_as_iso8601=True, + flush_on_start=False, + ), + # message_bus=MessageBusConfig( + # database=DatabaseConfig(timeout=2), + # encoding="json", + # timestamps_as_iso8601=True, + # streams_prefix="quoters", + # use_instance_id=False, + # # types_filter=[QuoteTick], + # autotrim_mins=30, + # ), + # heartbeat_interval=1.0, + # snapshot_orders=True, + # snapshot_positions=True, + # snapshot_positions_interval=5.0, + data_clients={ + "BINANCE": BinanceDataClientConfig( + api_key=None, # 'BINANCE_API_KEY' env var + api_secret=None, # 'BINANCE_API_SECRET' env var + account_type=BinanceAccountType.USDT_FUTURE, + base_url_http=None, # Override with custom endpoint + base_url_ws=None, # Override with custom endpoint + us=False, # If client is for Binance US + testnet=True, # If client uses the testnet + instrument_provider=InstrumentProviderConfig(load_all=True), + ), + }, + exec_clients={ + "SANDBOX": SandboxExecutionClientConfig( + venue="BINANCE", + starting_balances=["10_000 USDT", "10 ETH"], + ), + }, + timeout_connection=30.0, + timeout_reconciliation=10.0, + timeout_portfolio=10.0, + timeout_disconnection=10.0, + timeout_post_stop=5.0, +) + +# Instantiate the node with a configuration +node = TradingNode(config=config_node) + +# Configure your strategy +strat_config = VolatilityMarketMakerConfig( + instrument_id=InstrumentId.from_str("ETHUSDT-PERP.BINANCE"), + external_order_claims=[InstrumentId.from_str("ETHUSDT-PERP.BINANCE")], + bar_type=BarType.from_str("ETHUSDT-PERP.BINANCE-1-MINUTE-LAST-EXTERNAL"), + atr_period=20, + atr_multiple=6.0, + trade_size=Decimal("0.010"), + # manage_gtd_expiry=True, +) +# Instantiate your strategy +strategy = VolatilityMarketMaker(config=strat_config) + +# Add your strategies and modules +node.trader.add_strategy(strategy) + +SandboxExecutionClient.INSTRUMENTS = instruments + +# 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.build() + + +# Stop and dispose of the node with SIGINT/CTRL+C +if __name__ == "__main__": + try: + node.run() + finally: + node.dispose() diff --git a/examples/live/interactive_brokers/sandbox.py b/examples/sandbox/interactive_brokers_sandbox.py similarity index 100% rename from examples/live/interactive_brokers/sandbox.py rename to examples/sandbox/interactive_brokers_sandbox.py From 48812f9ecc5e3a1396275a2295b3f057a08ed2f8 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Wed, 22 May 2024 21:47:56 +1000 Subject: [PATCH 18/28] Fix SandboxExecutionClient regression --- nautilus_trader/adapters/sandbox/config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nautilus_trader/adapters/sandbox/config.py b/nautilus_trader/adapters/sandbox/config.py index 8b91e70888ee..5ad6018b611f 100644 --- a/nautilus_trader/adapters/sandbox/config.py +++ b/nautilus_trader/adapters/sandbox/config.py @@ -34,7 +34,7 @@ class SandboxExecutionClientConfig(LiveExecClientConfig, frozen=True, kw_only=Tr The order management system type used by the exchange. account_type : str, default 'MARGIN' The account type for the client. - default_leverage : float, default 10.0 + default_leverage : decimal.Decimal, default Decimal(1) The account default leverage (for margin accounts). bar_execution : bool, default True If bars should be processed by the matching engine(s) (and move the market). @@ -46,7 +46,7 @@ class SandboxExecutionClientConfig(LiveExecClientConfig, frozen=True, kw_only=Tr base_currency: str | None = None oms_type: str = "NETTING" account_type: str = "MARGIN" - default_leverage: Decimal = Decimal(10) + default_leverage: Decimal = Decimal(1) leverages: dict[str, float] | None = None book_type: str = "L1_MBP" frozen_account: bool = False From 0b269d1cfa397b7b13ad2a8752cf4dff319ec6fe Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Wed, 22 May 2024 22:12:52 +1000 Subject: [PATCH 19/28] Upgrade databento --- nautilus_core/Cargo.lock | 229 ++++-------------- nautilus_core/adapters/Cargo.toml | 4 +- .../adapters/src/databento/bin/sandbox.rs | 4 +- .../adapters/src/databento/decode.rs | 26 +- nautilus_core/adapters/src/databento/live.rs | 6 +- .../adapters/src/databento/loader.rs | 19 +- poetry.lock | 18 +- 7 files changed, 86 insertions(+), 220 deletions(-) diff --git a/nautilus_core/Cargo.lock b/nautilus_core/Cargo.lock index c7a011d580f3..008f896d6c84 100644 --- a/nautilus_core/Cargo.lock +++ b/nautilus_core/Cargo.lock @@ -453,10 +453,10 @@ dependencies = [ "axum-core", "bytes", "futures-util", - "http 1.1.0", - "http-body 1.0.0", + "http", + "http-body", "http-body-util", - "hyper 1.3.1", + "hyper", "hyper-util", "itoa", "matchit", @@ -486,8 +486,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.1.0", - "http-body 1.0.0", + "http", + "http-body", "http-body-util", "mime", "pin-project-lite", @@ -1148,15 +1148,15 @@ checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "databento" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338c81ee0781aa34b24423ca61c083620284e2a1a1198d20e73fb52e63ba2cd" +checksum = "958e83b1f051563971485b8b8acf6de4fe016ec5d4e9cf3e10aa810416b7d160" dependencies = [ "dbn", "futures", "hex", "log", - "reqwest 0.11.27", + "reqwest", "serde", "serde_json", "sha2", @@ -1440,18 +1440,18 @@ dependencies = [ [[package]] name = "dbn" -version = "0.17.1" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75c616347fc28872f993b5e9b80a5d25128db3557b852fc6642a0739b2f97003" +checksum = "f78191ea0a044d346c1d05b11325414bc6d615c6bfdda4934634b19291295b92" dependencies = [ "async-compression", "csv", "dbn-macros", + "fallible-streaming-iterator", "itoa", "json-writer", "num_enum", "serde", - "streaming-iterator", "thiserror", "time", "tokio", @@ -1460,9 +1460,9 @@ dependencies = [ [[package]] name = "dbn-macros" -version = "0.17.1" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "405f6fb410dad990ea1e56ce02609ea103ffd5c153c489c770c909e3bb7b165c" +checksum = "8a1fea28cc8c78a5626ea0d4965f896ef65466237c497f5373d2264eac803011" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -1625,6 +1625,12 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "fastrand" version = "2.1.0" @@ -1858,25 +1864,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap 2.2.6", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "h2" version = "0.4.5" @@ -1888,7 +1875,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.1.0", + "http", "indexmap 2.2.6", "slab", "tokio", @@ -1998,17 +1985,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" version = "1.1.0" @@ -2020,17 +1996,6 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - [[package]] name = "http-body" version = "1.0.0" @@ -2038,7 +2003,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http 1.1.0", + "http", ] [[package]] @@ -2049,8 +2014,8 @@ checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" dependencies = [ "bytes", "futures-core", - "http 1.1.0", - "http-body 1.0.0", + "http", + "http-body", "pin-project-lite", ] @@ -2072,30 +2037,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" -[[package]] -name = "hyper" -version = "0.14.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - [[package]] name = "hyper" version = "1.3.1" @@ -2105,9 +2046,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.5", - "http 1.1.0", - "http-body 1.0.0", + "h2", + "http", + "http-body", "httparse", "httpdate", "itoa", @@ -2117,19 +2058,6 @@ dependencies = [ "want", ] -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper 0.14.28", - "native-tls", - "tokio", - "tokio-native-tls", -] - [[package]] name = "hyper-tls" version = "0.6.0" @@ -2138,7 +2066,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.3.1", + "hyper", "hyper-util", "native-tls", "tokio", @@ -2155,9 +2083,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "hyper 1.3.1", + "http", + "http-body", + "hyper", "pin-project-lite", "socket2", "tokio", @@ -2587,6 +2515,7 @@ dependencies = [ "chrono", "criterion", "databento", + "fallible-streaming-iterator", "indexmap 2.2.6", "itoa", "log", @@ -2601,7 +2530,6 @@ dependencies = [ "rust_decimal_macros", "serde", "serde_json", - "streaming-iterator", "strum", "thiserror", "time", @@ -2790,13 +2718,13 @@ dependencies = [ "dashmap", "futures", "futures-util", - "http 1.1.0", - "hyper 1.3.1", + "http", + "hyper", "nautilus-core", "nonzero_ext", "pyo3", "pyo3-asyncio", - "reqwest 0.12.4", + "reqwest", "rstest", "serde_json", "tokio", @@ -3676,7 +3604,7 @@ dependencies = [ "pin-project-lite", "rustls", "rustls-native-certs", - "rustls-pemfile 2.1.2", + "rustls-pemfile", "rustls-pki-types", "ryu", "sha1_smol", @@ -3766,48 +3694,6 @@ dependencies = [ "bytecheck", ] -[[package]] -name = "reqwest" -version = "0.11.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" -dependencies = [ - "base64 0.21.7", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.28", - "hyper-tls 0.5.0", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls-pemfile 1.0.4", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration", - "tokio", - "tokio-native-tls", - "tokio-util", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "winreg 0.50.0", -] - [[package]] name = "reqwest" version = "0.12.4" @@ -3819,12 +3705,12 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2 0.4.5", - "http 1.1.0", - "http-body 1.0.0", + "h2", + "http", + "http-body", "http-body-util", - "hyper 1.3.1", - "hyper-tls 0.6.0", + "hyper", + "hyper-tls", "hyper-util", "ipnet", "js-sys", @@ -3834,7 +3720,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile 2.1.2", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", @@ -3842,12 +3728,14 @@ dependencies = [ "system-configuration", "tokio", "tokio-native-tls", + "tokio-util", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-streams", "web-sys", - "winreg 0.52.0", + "winreg", ] [[package]] @@ -4040,21 +3928,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.2", + "rustls-pemfile", "rustls-pki-types", "schannel", "security-framework", ] -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64 0.21.7", -] - [[package]] name = "rustls-pemfile" version = "2.1.2" @@ -4642,12 +4521,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "streaming-iterator" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520" - [[package]] name = "stringprep" version = "0.1.4" @@ -5215,7 +5088,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.1.0", + "http", "httparse", "log", "native-tls", @@ -5727,16 +5600,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "winreg" version = "0.52.0" diff --git a/nautilus_core/adapters/Cargo.toml b/nautilus_core/adapters/Cargo.toml index 087576aa2472..11c6da6dd099 100644 --- a/nautilus_core/adapters/Cargo.toml +++ b/nautilus_core/adapters/Cargo.toml @@ -35,8 +35,8 @@ strum = { workspace = true } tokio = { workspace = true } thiserror = { workspace = true } ustr = { workspace = true } -databento = { version = "0.9.1", optional = true } -streaming-iterator = "0.1.9" +databento = { version = "0.10.0", optional = true } +fallible-streaming-iterator = "0.1.9" time = "0.3.36" [dev-dependencies] diff --git a/nautilus_core/adapters/src/databento/bin/sandbox.rs b/nautilus_core/adapters/src/databento/bin/sandbox.rs index b9637d7a1263..bc677b9566ab 100644 --- a/nautilus_core/adapters/src/databento/bin/sandbox.rs +++ b/nautilus_core/adapters/src/databento/bin/sandbox.rs @@ -40,7 +40,9 @@ async fn main() { if let Some(msg) = record.get::() { println!( "Received delta: {} {} flags={}", - count, msg.hd.ts_event, msg.flags, + count, + msg.hd.ts_event, + msg.flags.raw(), ); count += 1; } diff --git a/nautilus_core/adapters/src/databento/decode.rs b/nautilus_core/adapters/src/databento/decode.rs index ac9b81c0057e..796fc2d17a33 100644 --- a/nautilus_core/adapters/src/databento/decode.rs +++ b/nautilus_core/adapters/src/databento/decode.rs @@ -414,7 +414,7 @@ pub fn decode_mbo_msg( instrument_id, parse_book_action(msg.action)?, order, - msg.flags, + msg.flags.raw(), msg.sequence.into(), msg.ts_recv.into(), ts_init, @@ -550,7 +550,7 @@ pub fn decode_mbp10_msg( asks, bid_counts, ask_counts, - msg.flags, + msg.flags.raw(), msg.sequence.into(), msg.ts_recv.into(), ts_init, @@ -1024,8 +1024,8 @@ mod tests { use std::path::PathBuf; use databento::dbn::decode::{dbn::Decoder, DecodeStream}; + use fallible_streaming_iterator::FallibleStreamingIterator; use rstest::*; - use streaming_iterator::StreamingIterator; use super::*; @@ -1038,7 +1038,7 @@ mod tests { let mut dbn_stream = Decoder::from_zstd_file(path) .unwrap() .decode_stream::(); - let msg = dbn_stream.next().unwrap(); + let msg = dbn_stream.next().unwrap().unwrap(); let instrument_id = InstrumentId::from("ESM4.GLBX"); let (delta, _) = decode_mbo_msg(msg, instrument_id, 2, 0.into(), false).unwrap(); @@ -1063,7 +1063,7 @@ mod tests { let mut dbn_stream = Decoder::from_zstd_file(path) .unwrap() .decode_stream::(); - let msg = dbn_stream.next().unwrap(); + let msg = dbn_stream.next().unwrap().unwrap(); let instrument_id = InstrumentId::from("ESM4.GLBX"); let (quote, _) = decode_mbp1_msg(msg, instrument_id, 2, 0.into(), false).unwrap(); @@ -1084,7 +1084,7 @@ mod tests { let mut dbn_stream = Decoder::from_zstd_file(path) .unwrap() .decode_stream::(); - let msg = dbn_stream.next().unwrap(); + let msg = dbn_stream.next().unwrap().unwrap(); let instrument_id = InstrumentId::from("ESM4.GLBX"); let depth10 = decode_mbp10_msg(msg, instrument_id, 2, 0.into()).unwrap(); @@ -1107,7 +1107,7 @@ mod tests { let mut dbn_stream = Decoder::from_zstd_file(path) .unwrap() .decode_stream::(); - let msg = dbn_stream.next().unwrap(); + let msg = dbn_stream.next().unwrap().unwrap(); let instrument_id = InstrumentId::from("ESM4.GLBX"); let trade = decode_trade_msg(msg, instrument_id, 2, 0.into()).unwrap(); @@ -1128,7 +1128,7 @@ mod tests { let mut dbn_stream = Decoder::from_zstd_file(path) .unwrap() .decode_stream::(); - let msg = dbn_stream.next().unwrap(); + let msg = dbn_stream.next().unwrap().unwrap(); let instrument_id = InstrumentId::from("ESM4.GLBX"); let (quote, trade) = decode_tbbo_msg(msg, instrument_id, 2, 0.into()).unwrap(); @@ -1158,7 +1158,7 @@ mod tests { let mut dbn_stream = Decoder::from_zstd_file(path) .unwrap() .decode_stream::(); - let msg = dbn_stream.next().unwrap(); + let msg = dbn_stream.next().unwrap().unwrap(); let instrument_id = InstrumentId::from("ESM4.GLBX"); let bar = decode_ohlcv_msg(msg, instrument_id, 2, 0.into()).unwrap(); @@ -1181,7 +1181,7 @@ mod tests { let mut dbn_stream = Decoder::from_zstd_file(path) .unwrap() .decode_stream::(); - let msg = dbn_stream.next().unwrap(); + let msg = dbn_stream.next().unwrap().unwrap(); let instrument_id = InstrumentId::from("ESM4.GLBX"); let result = decode_instrument_def_msg(msg, instrument_id, 0.into()); @@ -1195,7 +1195,7 @@ mod tests { let mut dbn_stream = Decoder::from_zstd_file(path) .unwrap() .decode_stream::(); - let msg = dbn_stream.next().unwrap(); + let msg = dbn_stream.next().unwrap().unwrap(); let instrument_id = InstrumentId::from("ESM4.GLBX"); let result = decode_instrument_def_msg_v1(msg, instrument_id, 0.into()); @@ -1209,7 +1209,7 @@ mod tests { let mut dbn_stream = Decoder::from_zstd_file(path) .unwrap() .decode_stream::(); - let msg = dbn_stream.next().unwrap(); + let msg = dbn_stream.next().unwrap().unwrap(); let instrument_id = InstrumentId::from("ESM4.GLBX"); let imbalance = decode_imbalance_msg(msg, instrument_id, 2, 0.into()).unwrap(); @@ -1233,7 +1233,7 @@ mod tests { let mut dbn_stream = Decoder::from_zstd_file(path) .unwrap() .decode_stream::(); - let msg = dbn_stream.next().unwrap(); + let msg = dbn_stream.next().unwrap().unwrap(); let instrument_id = InstrumentId::from("ESM4.GLBX"); let statistics = decode_statistics_msg(msg, instrument_id, 2, 0.into()).unwrap(); diff --git a/nautilus_core/adapters/src/databento/live.rs b/nautilus_core/adapters/src/databento/live.rs index 6a8bf30cde13..b2d6945e4b4d 100644 --- a/nautilus_core/adapters/src/databento/live.rs +++ b/nautilus_core/adapters/src/databento/live.rs @@ -262,16 +262,16 @@ impl DatabentoFeedHandler { deltas_count, delta.ts_event, buffering_start, - msg.flags, + msg.flags.raw(), ); // Check if last message in the packet - if !RecordFlag::F_LAST.matches(msg.flags) { + if !RecordFlag::F_LAST.matches(msg.flags.raw()) { continue; // NOT last message } // Check if snapshot - if RecordFlag::F_SNAPSHOT.matches(msg.flags) { + if RecordFlag::F_SNAPSHOT.matches(msg.flags.raw()) { continue; // Buffer snapshot } diff --git a/nautilus_core/adapters/src/databento/loader.rs b/nautilus_core/adapters/src/databento/loader.rs index b67428c726d5..9cc95fde38bf 100644 --- a/nautilus_core/adapters/src/databento/loader.rs +++ b/nautilus_core/adapters/src/databento/loader.rs @@ -20,6 +20,7 @@ use dbn::{ compat::InstrumentDefMsgV1, decode::{dbn::Decoder, DbnMetadata, DecodeStream}, }; +use fallible_streaming_iterator::FallibleStreamingIterator; use indexmap::IndexMap; use nautilus_model::{ data::Data, @@ -27,7 +28,6 @@ use nautilus_model::{ instruments::any::InstrumentAny, types::currency::Currency, }; -use streaming_iterator::StreamingIterator; use ustr::Ustr; use super::{ @@ -153,8 +153,9 @@ impl DatabentoDataLoader { let mut dbn_stream = decoder.decode_stream::(); Ok(std::iter::from_fn(move || { - dbn_stream.advance(); - + if let Err(e) = dbn_stream.advance() { + return Some(Err(e.into())); + } match dbn_stream.get() { Some(rec) => { let record = dbn::RecordRef::from(rec); @@ -198,7 +199,9 @@ impl DatabentoDataLoader { let price_precision = Currency::USD().precision; // Hard coded for now Ok(std::iter::from_fn(move || { - dbn_stream.advance(); + if let Err(e) = dbn_stream.advance() { + return Some(Err(e.into())); + } match dbn_stream.get() { Some(rec) => { let record = dbn::RecordRef::from(rec); @@ -243,7 +246,9 @@ impl DatabentoDataLoader { let price_precision = Currency::USD().precision; // Hard coded for now Ok(std::iter::from_fn(move || { - dbn_stream.advance(); + if let Err(e) = dbn_stream.advance() { + return Some(Err(e.into())); + } match dbn_stream.get() { Some(rec) => { let record = dbn::RecordRef::from(rec); @@ -290,7 +295,9 @@ impl DatabentoDataLoader { let price_precision = Currency::USD().precision; // Hard coded for now Ok(std::iter::from_fn(move || { - dbn_stream.advance(); + if let Err(e) = dbn_stream.advance() { + return Some(Err(e.into())); + } match dbn_stream.get() { Some(rec) => { let record = dbn::RecordRef::from(rec); diff --git a/poetry.lock b/poetry.lock index a10eaed782df..10601fa3e799 100644 --- a/poetry.lock +++ b/poetry.lock @@ -464,7 +464,7 @@ name = "css-html-js-minify" version = "2.5.5" description = "CSS HTML JS Minifier" optional = false -python-versions = ">=3.6" +python-versions = "*" files = [ {file = "css-html-js-minify-2.5.5.zip", hash = "sha256:4a9f11f7e0496f5284d12111f3ba4ff5ff2023d12f15d195c9c48bd97013746c"}, {file = "css_html_js_minify-2.5.5-py2.py3-none-any.whl", hash = "sha256:3da9d35ac0db8ca648c1b543e0e801d7ca0bab9e6bfd8418fee59d5ae001727a"}, @@ -939,9 +939,13 @@ files = [ {file = "lxml-5.2.2-cp36-cp36m-win_amd64.whl", hash = "sha256:edcfa83e03370032a489430215c1e7783128808fd3e2e0a3225deee278585196"}, {file = "lxml-5.2.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:28bf95177400066596cdbcfc933312493799382879da504633d16cf60bba735b"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a745cc98d504d5bd2c19b10c79c61c7c3df9222629f1b6210c0368177589fb8"}, + {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b590b39ef90c6b22ec0be925b211298e810b4856909c8ca60d27ffbca6c12e6"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b336b0416828022bfd5a2e3083e7f5ba54b96242159f83c7e3eebaec752f1716"}, + {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:c2faf60c583af0d135e853c86ac2735ce178f0e338a3c7f9ae8f622fd2eb788c"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:4bc6cb140a7a0ad1f7bc37e018d0ed690b7b6520ade518285dc3171f7a117905"}, + {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7ff762670cada8e05b32bf1e4dc50b140790909caa8303cfddc4d702b71ea184"}, {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:57f0a0bbc9868e10ebe874e9f129d2917750adf008fe7b9c1598c0fbbfdde6a6"}, + {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:a6d2092797b388342c1bc932077ad232f914351932353e2e8706851c870bca1f"}, {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:60499fe961b21264e17a471ec296dcbf4365fbea611bf9e303ab69db7159ce61"}, {file = "lxml-5.2.2-cp37-cp37m-win32.whl", hash = "sha256:d9b342c76003c6b9336a80efcc766748a333573abf9350f4094ee46b006ec18f"}, {file = "lxml-5.2.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b16db2770517b8799c79aa80f4053cd6f8b716f21f8aca962725a9565ce3ee40"}, @@ -1494,6 +1498,7 @@ files = [ {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, + {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"}, {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, @@ -1932,7 +1937,6 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -1940,16 +1944,8 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -1966,7 +1962,6 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -1974,7 +1969,6 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, From 5c1c3128baf1155fd2ea38cb19a89bec2ef9bf74 Mon Sep 17 00:00:00 2001 From: David Blom Date: Thu, 23 May 2024 02:16:21 +0200 Subject: [PATCH 20/28] Add sandbox example for Bybit (#1659) --- examples/sandbox/bybit_sandbox.py | 149 ++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 examples/sandbox/bybit_sandbox.py diff --git a/examples/sandbox/bybit_sandbox.py b/examples/sandbox/bybit_sandbox.py new file mode 100644 index 000000000000..a1eb4b1e5d26 --- /dev/null +++ b/examples/sandbox/bybit_sandbox.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python3 +# ------------------------------------------------------------------------------------------------- +# 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 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 +from nautilus_trader.examples.strategies.volatility_market_maker import VolatilityMarketMaker +from nautilus_trader.examples.strategies.volatility_market_maker import VolatilityMarketMakerConfig +from nautilus_trader.live.node import TradingNode +from nautilus_trader.model.data import BarType +from nautilus_trader.model.identifiers import InstrumentId +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. *** + + +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} + + exec_clients = {} + for venue in venues: + exec_clients[venue] = SandboxExecutionClientConfig( + venue=venue, + starting_balances=["10_000 USDT", "10 ETH"], + instrument_provider=instrument_provider_config, + account_type="MARGIN", + oms_type="NETTING", + ) + + # Configure the trading node + config_node = TradingNodeConfig( + trader_id=TraderId("TESTER-001"), + logging=LoggingConfig( + log_level="INFO", + # log_level_file="DEBUG", + # log_file_format="json", + log_colors=True, + use_pyo3=True, + ), + data_clients={ + "BYBIT": BybitDataClientConfig( + api_key=None, # 'BYBIT_API_KEY' env var + api_secret=None, # 'BYBIT_API_SECRET' env var + instrument_provider=instrument_provider_config, + product_types=[BybitProductType.LINEAR], + testnet=False, # If client uses the testnet + ), + }, + exec_clients=exec_clients, + timeout_connection=30.0, + timeout_reconciliation=10.0, + timeout_portfolio=10.0, + timeout_disconnection=10.0, + timeout_post_stop=5.0, + ) + + # Instantiate the node with a configuration + node = TradingNode(config=config_node) + + # Configure your strategy + strat_config = VolatilityMarketMakerConfig( + instrument_id=InstrumentId.from_str("ETHUSDT-LINEAR.BYBIT"), + external_order_claims=[InstrumentId.from_str("ETHUSDT-LINEAR.BYBIT")], + bar_type=BarType.from_str("ETHUSDT-LINEAR.BYBIT-1-MINUTE-LAST-EXTERNAL"), + atr_period=20, + atr_multiple=6.0, + trade_size=Decimal("0.010"), + # manage_gtd_expiry=True, + ) + # Instantiate your strategy + strategy = VolatilityMarketMaker(config=strat_config) + + # Add your strategies and modules + node.trader.add_strategy(strategy) + + # Register client factories with the node + for data_client in config_node.data_clients: + node.add_data_client_factory(data_client, BybitLiveDataClientFactory) + + for exec_client in config_node.exec_clients: + node.add_exec_client_factory(exec_client, SandboxLiveExecClientFactory) + + node.build() + + try: + await node.run_async() + finally: + await node.stop_async() + await asyncio.sleep(1) + node.dispose() + + +# Stop and dispose of the node with SIGINT/CTRL+C +if __name__ == "__main__": + asyncio.run(main()) From c1956ebe1db261511e6c56901c0240bb36bedb30 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Thu, 23 May 2024 19:46:28 +1000 Subject: [PATCH 21/28] Fix OmsType.HEDGING in type stubs --- nautilus_trader/core/nautilus_pyo3.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nautilus_trader/core/nautilus_pyo3.pyi b/nautilus_trader/core/nautilus_pyo3.pyi index 325a97a04b37..1fcabc1948fb 100644 --- a/nautilus_trader/core/nautilus_pyo3.pyi +++ b/nautilus_trader/core/nautilus_pyo3.pyi @@ -711,7 +711,7 @@ class HaltReason(Enum): class OmsType(Enum): UNSPECIFIED = "UNSPECIFIED" NETTING = "NETTING" - HEDGING = "HEDGIN" + HEDGING = "HEDGING" class OptionKind(Enum): CALL = "CALL" From 33ee0a6fddae161cd3c73835ee7d19100e1cb465 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Thu, 23 May 2024 19:54:48 +1000 Subject: [PATCH 22/28] Upgrade ruff --- .pre-commit-config.yaml | 2 +- poetry.lock | 62 ++++++++++++++++++++++------------------- pyproject.toml | 2 +- 3 files changed, 36 insertions(+), 30 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7c3de2464af8..40eafdd92e32 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -82,7 +82,7 @@ repos: exclude: "docs/_pygments/monokai.py" - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.4 + rev: v0.4.5 hooks: - id: ruff args: ["--fix"] diff --git a/poetry.lock b/poetry.lock index 10601fa3e799..cd3a9e7b99ca 100644 --- a/poetry.lock +++ b/poetry.lock @@ -464,7 +464,7 @@ name = "css-html-js-minify" version = "2.5.5" description = "CSS HTML JS Minifier" optional = false -python-versions = "*" +python-versions = ">=3.6" files = [ {file = "css-html-js-minify-2.5.5.zip", hash = "sha256:4a9f11f7e0496f5284d12111f3ba4ff5ff2023d12f15d195c9c48bd97013746c"}, {file = "css_html_js_minify-2.5.5-py2.py3-none-any.whl", hash = "sha256:3da9d35ac0db8ca648c1b543e0e801d7ca0bab9e6bfd8418fee59d5ae001727a"}, @@ -939,13 +939,9 @@ files = [ {file = "lxml-5.2.2-cp36-cp36m-win_amd64.whl", hash = "sha256:edcfa83e03370032a489430215c1e7783128808fd3e2e0a3225deee278585196"}, {file = "lxml-5.2.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:28bf95177400066596cdbcfc933312493799382879da504633d16cf60bba735b"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a745cc98d504d5bd2c19b10c79c61c7c3df9222629f1b6210c0368177589fb8"}, - {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b590b39ef90c6b22ec0be925b211298e810b4856909c8ca60d27ffbca6c12e6"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b336b0416828022bfd5a2e3083e7f5ba54b96242159f83c7e3eebaec752f1716"}, - {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:c2faf60c583af0d135e853c86ac2735ce178f0e338a3c7f9ae8f622fd2eb788c"}, {file = "lxml-5.2.2-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:4bc6cb140a7a0ad1f7bc37e018d0ed690b7b6520ade518285dc3171f7a117905"}, - {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7ff762670cada8e05b32bf1e4dc50b140790909caa8303cfddc4d702b71ea184"}, {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:57f0a0bbc9868e10ebe874e9f129d2917750adf008fe7b9c1598c0fbbfdde6a6"}, - {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:a6d2092797b388342c1bc932077ad232f914351932353e2e8706851c870bca1f"}, {file = "lxml-5.2.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:60499fe961b21264e17a471ec296dcbf4365fbea611bf9e303ab69db7159ce61"}, {file = "lxml-5.2.2-cp37-cp37m-win32.whl", hash = "sha256:d9b342c76003c6b9336a80efcc766748a333573abf9350f4094ee46b006ec18f"}, {file = "lxml-5.2.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b16db2770517b8799c79aa80f4053cd6f8b716f21f8aca962725a9565ce3ee40"}, @@ -1498,7 +1494,6 @@ files = [ {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, - {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"}, {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, @@ -1937,6 +1932,7 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -1944,8 +1940,16 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -1962,6 +1966,7 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -1969,6 +1974,7 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -1997,28 +2003,28 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "ruff" -version = "0.4.4" +version = "0.4.5" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.4.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:29d44ef5bb6a08e235c8249294fa8d431adc1426bfda99ed493119e6f9ea1bf6"}, - {file = "ruff-0.4.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c4efe62b5bbb24178c950732ddd40712b878a9b96b1d02b0ff0b08a090cbd891"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c8e2f1e8fc12d07ab521a9005d68a969e167b589cbcaee354cb61e9d9de9c15"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60ed88b636a463214905c002fa3eaab19795679ed55529f91e488db3fe8976ab"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b90fc5e170fc71c712cc4d9ab0e24ea505c6a9e4ebf346787a67e691dfb72e85"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8e7e6ebc10ef16dcdc77fd5557ee60647512b400e4a60bdc4849468f076f6eef"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9ddb2c494fb79fc208cd15ffe08f32b7682519e067413dbaf5f4b01a6087bcd"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c51c928a14f9f0a871082603e25a1588059b7e08a920f2f9fa7157b5bf08cfe9"}, - {file = "ruff-0.4.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5eb0a4bfd6400b7d07c09a7725e1a98c3b838be557fee229ac0f84d9aa49c36"}, - {file = "ruff-0.4.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b1867ee9bf3acc21778dcb293db504692eda5f7a11a6e6cc40890182a9f9e595"}, - {file = "ruff-0.4.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1aecced1269481ef2894cc495647392a34b0bf3e28ff53ed95a385b13aa45768"}, - {file = "ruff-0.4.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9da73eb616b3241a307b837f32756dc20a0b07e2bcb694fec73699c93d04a69e"}, - {file = "ruff-0.4.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:958b4ea5589706a81065e2a776237de2ecc3e763342e5cc8e02a4a4d8a5e6f95"}, - {file = "ruff-0.4.4-py3-none-win32.whl", hash = "sha256:cb53473849f011bca6e754f2cdf47cafc9c4f4ff4570003a0dad0b9b6890e876"}, - {file = "ruff-0.4.4-py3-none-win_amd64.whl", hash = "sha256:424e5b72597482543b684c11def82669cc6b395aa8cc69acc1858b5ef3e5daae"}, - {file = "ruff-0.4.4-py3-none-win_arm64.whl", hash = "sha256:39df0537b47d3b597293edbb95baf54ff5b49589eb7ff41926d8243caa995ea6"}, - {file = "ruff-0.4.4.tar.gz", hash = "sha256:f87ea42d5cdebdc6a69761a9d0bc83ae9b3b30d0ad78952005ba6568d6c022af"}, + {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"}, ] [[package]] @@ -2376,13 +2382,13 @@ files = [ [[package]] name = "types-requests" -version = "2.32.0.20240521" +version = "2.32.0.20240523" description = "Typing stubs for requests" optional = false python-versions = ">=3.8" files = [ - {file = "types-requests-2.32.0.20240521.tar.gz", hash = "sha256:c5c4a0ae95aad51f1bf6dae9eed04a78f7f2575d4b171da37b622e08b93eb5d3"}, - {file = "types_requests-2.32.0.20240521-py3-none-any.whl", hash = "sha256:ab728ba43ffb073db31f21202ecb97db8753ded4a9dc49cb480d8a5350c5c421"}, + {file = "types-requests-2.32.0.20240523.tar.gz", hash = "sha256:26b8a6de32d9f561192b9942b41c0ab2d8010df5677ca8aa146289d11d505f57"}, + {file = "types_requests-2.32.0.20240523-py3-none-any.whl", hash = "sha256:f19ed0e2daa74302069bbbbf9e82902854ffa780bc790742a810a9aaa52f65ec"}, ] [package.dependencies] @@ -2662,4 +2668,4 @@ ib = ["async-timeout", "defusedxml", "nautilus_ibapi"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "5b1edf888e0728a59228819f4a2629dee0925bc7505df18513aea7a1d81ba138" +content-hash = "cfe6fbdd3689ed34d7705cb5557a470a2ff29a8a2607b46eb157243931f68e7f" diff --git a/pyproject.toml b/pyproject.toml index dacbf2609186..e2731877c662 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.4" +ruff = "^0.4.5" types-pytz = "^2023.3" types-requests = "^2.31" types-toml = "^0.10.2" From 2f5e20b2228aa4b303296c2b0c9ee2cfb2a4dbb3 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Thu, 23 May 2024 20:42:46 +1000 Subject: [PATCH 23/28] Standardize sandbox examples --- examples/sandbox/betfair_sandbox.py | 6 +- .../binance_futures_testnet_sandbox.py | 218 +++++++++--------- examples/sandbox/bybit_sandbox.py | 3 - 3 files changed, 117 insertions(+), 110 deletions(-) diff --git a/examples/sandbox/betfair_sandbox.py b/examples/sandbox/betfair_sandbox.py index 0e164ebc981c..3ac80c811698 100644 --- a/examples/sandbox/betfair_sandbox.py +++ b/examples/sandbox/betfair_sandbox.py @@ -55,6 +55,9 @@ 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() @@ -91,9 +94,6 @@ async def main(instrument_config: BetfairInstrumentProviderConfig) -> TradingNod node = TradingNode(config=config) node.trader.add_strategies(strategies) - # Need to manually set instruments for sandbox exec client - SandboxExecutionClient.INSTRUMENTS = instruments - # 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) diff --git a/examples/sandbox/binance_futures_testnet_sandbox.py b/examples/sandbox/binance_futures_testnet_sandbox.py index c99daa54d131..6e00bf3f8967 100644 --- a/examples/sandbox/binance_futures_testnet_sandbox.py +++ b/examples/sandbox/binance_futures_testnet_sandbox.py @@ -14,6 +14,7 @@ # limitations under the License. # ------------------------------------------------------------------------------------------------- +import asyncio from decimal import Decimal # fmt: off @@ -45,113 +46,122 @@ # *** THIS IS A TEST STRATEGY WITH NO ALPHA ADVANTAGE WHATSOEVER. *** # *** IT IS NOT INTENDED TO BE USED TO TRADE LIVE WITH REAL MONEY. *** -# 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(), -) -provider.load_all() -instruments = provider.list_all() - - -# Configure the trading node -config_node = TradingNodeConfig( - trader_id=TraderId("TESTER-001"), - logging=LoggingConfig( - log_level="INFO", - # log_level_file="DEBUG", - # log_file_format="json", - log_colors=True, - use_pyo3=True, - ), - exec_engine=LiveExecEngineConfig( - reconciliation=True, - reconciliation_lookback_mins=1440, - filter_position_reports=True, - ), - cache=CacheConfig( - # database=DatabaseConfig(timeout=2), - timestamps_as_iso8601=True, - flush_on_start=False, - ), - # message_bus=MessageBusConfig( - # database=DatabaseConfig(timeout=2), - # encoding="json", - # timestamps_as_iso8601=True, - # streams_prefix="quoters", - # use_instance_id=False, - # # types_filter=[QuoteTick], - # autotrim_mins=30, - # ), - # heartbeat_interval=1.0, - # snapshot_orders=True, - # snapshot_positions=True, - # snapshot_positions_interval=5.0, - data_clients={ - "BINANCE": BinanceDataClientConfig( - api_key=None, # 'BINANCE_API_KEY' env var - api_secret=None, # 'BINANCE_API_SECRET' env var - account_type=BinanceAccountType.USDT_FUTURE, - base_url_http=None, # Override with custom endpoint - base_url_ws=None, # Override with custom endpoint - us=False, # If client is for Binance US - testnet=True, # If client uses the testnet - instrument_provider=InstrumentProviderConfig(load_all=True), + +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"), + logging=LoggingConfig( + log_level="INFO", + # log_level_file="DEBUG", + # log_file_format="json", + log_colors=True, + use_pyo3=True, ), - }, - exec_clients={ - "SANDBOX": SandboxExecutionClientConfig( - venue="BINANCE", - starting_balances=["10_000 USDT", "10 ETH"], + exec_engine=LiveExecEngineConfig( + reconciliation=True, + reconciliation_lookback_mins=1440, + filter_position_reports=True, ), - }, - timeout_connection=30.0, - timeout_reconciliation=10.0, - timeout_portfolio=10.0, - timeout_disconnection=10.0, - timeout_post_stop=5.0, -) - -# Instantiate the node with a configuration -node = TradingNode(config=config_node) - -# Configure your strategy -strat_config = VolatilityMarketMakerConfig( - instrument_id=InstrumentId.from_str("ETHUSDT-PERP.BINANCE"), - external_order_claims=[InstrumentId.from_str("ETHUSDT-PERP.BINANCE")], - bar_type=BarType.from_str("ETHUSDT-PERP.BINANCE-1-MINUTE-LAST-EXTERNAL"), - atr_period=20, - atr_multiple=6.0, - trade_size=Decimal("0.010"), - # manage_gtd_expiry=True, -) -# Instantiate your strategy -strategy = VolatilityMarketMaker(config=strat_config) - -# Add your strategies and modules -node.trader.add_strategy(strategy) - -SandboxExecutionClient.INSTRUMENTS = instruments - -# 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.build() - + cache=CacheConfig( + # database=DatabaseConfig(timeout=2), + timestamps_as_iso8601=True, + flush_on_start=False, + ), + # message_bus=MessageBusConfig( + # database=DatabaseConfig(timeout=2), + # encoding="json", + # timestamps_as_iso8601=True, + # streams_prefix="quoters", + # use_instance_id=False, + # # types_filter=[QuoteTick], + # autotrim_mins=30, + # ), + # heartbeat_interval=1.0, + # snapshot_orders=True, + # snapshot_positions=True, + # snapshot_positions_interval=5.0, + data_clients={ + "BINANCE": BinanceDataClientConfig( + api_key=None, # 'BINANCE_API_KEY' env var + api_secret=None, # 'BINANCE_API_SECRET' env var + account_type=BinanceAccountType.USDT_FUTURE, + base_url_http=None, # Override with custom endpoint + base_url_ws=None, # Override with custom endpoint + us=False, # If client is for Binance US + testnet=True, # If client uses the testnet + instrument_provider=InstrumentProviderConfig(load_all=True), + ), + }, + exec_clients={ + "SANDBOX": SandboxExecutionClientConfig( + venue="BINANCE", + starting_balances=["10_000 USDT", "10 ETH"], + ), + }, + timeout_connection=30.0, + timeout_reconciliation=10.0, + timeout_portfolio=10.0, + timeout_disconnection=10.0, + timeout_post_stop=5.0, + ) + + # Instantiate the node with a configuration + node = TradingNode(config=config_node) + + # Configure your strategy + strat_config = VolatilityMarketMakerConfig( + instrument_id=InstrumentId.from_str("ETHUSDT-PERP.BINANCE"), + external_order_claims=[InstrumentId.from_str("ETHUSDT-PERP.BINANCE")], + bar_type=BarType.from_str("ETHUSDT-PERP.BINANCE-1-MINUTE-LAST-EXTERNAL"), + atr_period=20, + atr_multiple=6.0, + trade_size=Decimal("0.010"), + # manage_gtd_expiry=True, + ) + # Instantiate your strategy + strategy = VolatilityMarketMaker(config=strat_config) + + # Add your strategies and modules + node.trader.add_strategy(strategy) + + # 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.build() -# Stop and dispose of the node with SIGINT/CTRL+C -if __name__ == "__main__": try: - node.run() + await node.run_async() finally: + await node.stop_async() + await asyncio.sleep(1) node.dispose() + + +# Stop and dispose of the node with SIGINT/CTRL+C +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/sandbox/bybit_sandbox.py b/examples/sandbox/bybit_sandbox.py index a1eb4b1e5d26..aa3a40f9ebc0 100644 --- a/examples/sandbox/bybit_sandbox.py +++ b/examples/sandbox/bybit_sandbox.py @@ -38,9 +38,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. *** From e15f090506d61150a16cc032e80d0d12750414e6 Mon Sep 17 00:00:00 2001 From: rsmb7z <105105941+rsmb7z@users.noreply.github.com> Date: Fri, 24 May 2024 00:06:44 +0300 Subject: [PATCH 24/28] Add support for MOC and LOC order types in IB (#1663) --- .../adapters/interactive_brokers/execution.py | 18 ++++++++++++++---- .../interactive_brokers/parsing/execution.py | 5 ++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/nautilus_trader/adapters/interactive_brokers/execution.py b/nautilus_trader/adapters/interactive_brokers/execution.py index de127cc3ac67..dac578e6a82f 100644 --- a/nautilus_trader/adapters/interactive_brokers/execution.py +++ b/nautilus_trader/adapters/interactive_brokers/execution.py @@ -295,14 +295,20 @@ async def _parse_ib_order_to_order_status_report(self, ib_order: IBOrder) -> Ord timestring_to_timestamp(ib_order.goodTillDate) if ib_order.tif == "GTD" else None ) - # TODO: Testing for advanced Open orders + mapped_order_type_info = ib_to_nautilus_order_type[ib_order.orderType] + if isinstance(mapped_order_type_info, tuple): + order_type, time_in_force = mapped_order_type_info + else: + order_type = mapped_order_type_info + time_in_force = ib_to_nautilus_time_in_force[ib_order.tif] + order_status = OrderStatusReport( account_id=self.account_id, instrument_id=instrument.id, venue_order_id=VenueOrderId(str(ib_order.orderId)), order_side=ib_to_nautilus_order_side[ib_order.action], - order_type=ib_to_nautilus_order_type[ib_order.orderType], - time_in_force=ib_to_nautilus_time_in_force[ib_order.tif], + order_type=order_type, + time_in_force=time_in_force, order_status=order_status, quantity=total_qty, filled_qty=Quantity.from_int(0), @@ -498,9 +504,13 @@ async def generate_position_status_reports( def _transform_order_to_ib_order(self, order: Order) -> IBOrder: ib_order = IBOrder() + time_in_force = order.time_in_force for key, field, fn in MAP_ORDER_FIELDS: if value := getattr(order, key, None): - setattr(ib_order, field, fn(value)) + if key == "order_type" and time_in_force == TimeInForce.AT_THE_CLOSE: + setattr(ib_order, field, fn((value, time_in_force))) + else: + setattr(ib_order, field, fn(value)) if self._cache.instrument(order.instrument_id).is_inverse: ib_order.cashQty = int(ib_order.totalQuantity) diff --git a/nautilus_trader/adapters/interactive_brokers/parsing/execution.py b/nautilus_trader/adapters/interactive_brokers/parsing/execution.py index 317af16a6bdb..1fdebeb8f498 100644 --- a/nautilus_trader/adapters/interactive_brokers/parsing/execution.py +++ b/nautilus_trader/adapters/interactive_brokers/parsing/execution.py @@ -40,6 +40,7 @@ TimeInForce.IOC: "IOC", TimeInForce.GTD: "GTD", TimeInForce.AT_THE_OPEN: "OPG", + TimeInForce.AT_THE_CLOSE: "DAY", TimeInForce.FOK: "FOK", # unsupported: 'DTC', } @@ -54,7 +55,7 @@ "SLD": "SELL", } -MAP_ORDER_TYPE: dict[int, str] = { +MAP_ORDER_TYPE: dict[int | tuple[int, int], str] = { OrderType.LIMIT: "LMT", OrderType.LIMIT_IF_TOUCHED: "LIT", OrderType.MARKET: "MKT", @@ -64,6 +65,8 @@ OrderType.STOP_MARKET: "STP", OrderType.TRAILING_STOP_LIMIT: "TRAIL LIMIT", OrderType.TRAILING_STOP_MARKET: "TRAIL", + (OrderType.MARKET, TimeInForce.AT_THE_CLOSE): "MOC", + (OrderType.LIMIT, TimeInForce.AT_THE_CLOSE): "LOC", } From 7712573ecd867b155dd76b47802545941ed1bc18 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Fri, 24 May 2024 07:11:20 +1000 Subject: [PATCH 25/28] Update release notes --- RELEASES.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index 3cd4c9d48e0d..7cef46588eb8 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -3,7 +3,9 @@ Released on TBD (UTC). ### Enhancements -None +- Added Interactive Brokers support for Market-on-Close (MOC) and Limit-on-Close (LOC) order types (#1663), thanks @rsmb7z +- Added Bybit sandbox example (#1659), thanks @davidsblom +- Added Binance sandbox example ### Breaking Changes - Overhauled `SandboxExecutionClientConfig` to more closely match `BacktestVenueConfig` (many changes and additions) From 5c178a6c1098c9abc25b6e7ff051932c2a39d2e0 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Fri, 24 May 2024 17:31:28 +1000 Subject: [PATCH 26/28] Update dependencies --- nautilus_core/Cargo.lock | 58 ++++++++++++++++++++-------------------- poetry.lock | 17 ++++++------ pyproject.toml | 2 +- 3 files changed, 39 insertions(+), 38 deletions(-) diff --git a/nautilus_core/Cargo.lock b/nautilus_core/Cargo.lock index 008f896d6c84..79c5f5270daf 100644 --- a/nautilus_core/Cargo.lock +++ b/nautilus_core/Cargo.lock @@ -408,7 +408,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -596,7 +596,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", "syn_derive", ] @@ -845,7 +845,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1113,7 +1113,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1124,7 +1124,7 @@ checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1467,7 +1467,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1509,7 +1509,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1519,7 +1519,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "206868b8242f27cecce124c19fd88157fbd0dd334df2587f36417bafbc85097b" dependencies = [ "derive_builder_core", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -1792,7 +1792,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -2932,7 +2932,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -3009,7 +3009,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -3225,7 +3225,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -3484,7 +3484,7 @@ dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -3497,7 +3497,7 @@ dependencies = [ "proc-macro2", "pyo3-build-config", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -3849,7 +3849,7 @@ dependencies = [ "regex", "relative-path", "rustc_version", - "syn 2.0.65", + "syn 2.0.66", "unicode-ident", ] @@ -4070,7 +4070,7 @@ checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -4128,7 +4128,7 @@ checksum = "82fe9db325bcef1fbcde82e078a5cc4efdf787e96b3b9cf45b50b529f2083d67" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -4318,7 +4318,7 @@ checksum = "01b2e185515564f15375f593fb966b5718bc624ba77fe49fa4616ad619690554" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -4563,7 +4563,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -4585,9 +4585,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.65" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -4603,7 +4603,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -4734,7 +4734,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -4858,7 +4858,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -5008,7 +5008,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -5128,7 +5128,7 @@ checksum = "1f718dfaf347dcb5b983bfc87608144b0bad87970aebcbea5ce44d2a30c08e63" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] @@ -5315,7 +5315,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", "wasm-bindgen-shared", ] @@ -5349,7 +5349,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5651,7 +5651,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.66", ] [[package]] diff --git a/poetry.lock b/poetry.lock index cd3a9e7b99ca..759228c05a0a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -579,22 +579,23 @@ tomli = ["tomli (>=2.0.0,<3.0.0)"] [[package]] name = "docker" -version = "7.0.0" +version = "7.1.0" description = "A Python library for the Docker Engine API." optional = true python-versions = ">=3.8" files = [ - {file = "docker-7.0.0-py3-none-any.whl", hash = "sha256:12ba681f2777a0ad28ffbcc846a69c31b4dfd9752b47eb425a274ee269c5e14b"}, - {file = "docker-7.0.0.tar.gz", hash = "sha256:323736fb92cd9418fc5e7133bc953e11a9da04f4483f828b527db553f1e7e5a3"}, + {file = "docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0"}, + {file = "docker-7.1.0.tar.gz", hash = "sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c"}, ] [package.dependencies] -packaging = ">=14.0" pywin32 = {version = ">=304", markers = "sys_platform == \"win32\""} requests = ">=2.26.0" urllib3 = ">=1.26.0" [package.extras] +dev = ["coverage (==7.2.7)", "pytest (==7.4.2)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.1.0)", "ruff (==0.1.8)"] +docs = ["myst-parser (==0.18.0)", "sphinx (==5.1.1)"] ssh = ["paramiko (>=2.4.3)"] websockets = ["websocket-client (>=1.3.0)"] @@ -2407,13 +2408,13 @@ files = [ [[package]] name = "typing-extensions" -version = "4.11.0" +version = "4.12.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, - {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, + {file = "typing_extensions-4.12.0-py3-none-any.whl", hash = "sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594"}, + {file = "typing_extensions-4.12.0.tar.gz", hash = "sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8"}, ] [[package]] @@ -2668,4 +2669,4 @@ ib = ["async-timeout", "defusedxml", "nautilus_ibapi"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "cfe6fbdd3689ed34d7705cb5557a470a2ff29a8a2607b46eb157243931f68e7f" +content-hash = "d1ed9ae69af95a1d8a77a9e36c6ca8189e1d0fa62741680b1c2193f1560b2978" diff --git a/pyproject.toml b/pyproject.toml index e2731877c662..b2d926b2e2f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,7 +66,7 @@ uvloop = {version = "^0.19.0", markers = "sys_platform != 'win32'"} async-timeout = {version = "^4.0.3", optional = true} betfair_parser = {version = "==0.12.0", optional = true} # Pinned for stability defusedxml = {version = "^0.7.1", optional = true} -docker = {version = "^7.0.0", optional = true} +docker = {version = "^7.1.0", optional = true} nautilus_ibapi = {version = "==10.19.2", optional = true} # Pinned for stability [tool.poetry.extras] From c84ac916e24a7cf407a6af743b6a5f7b07aeb0f2 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Fri, 24 May 2024 17:40:34 +1000 Subject: [PATCH 27/28] Update pytz version specifier --- poetry.lock | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index 759228c05a0a..4d184f5a2a90 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2669,4 +2669,4 @@ ib = ["async-timeout", "defusedxml", "nautilus_ibapi"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.13" -content-hash = "d1ed9ae69af95a1d8a77a9e36c6ca8189e1d0fa62741680b1c2193f1560b2978" +content-hash = "dea2c5c5e50ed163007e0a92b7d0f66abbcc0335513d0ce953902de8512cf074" diff --git a/pyproject.toml b/pyproject.toml index b2d926b2e2f0..7de9dc786348 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,7 +59,7 @@ fsspec = "==2023.6.0" # Pinned due breaking changes msgspec = "^0.18.6" pandas = "^2.2.2" pyarrow = ">=16.1.0" -pytz = ">=2023.4.0" +pytz = ">=2024.1.0" tqdm = "^4.66.4" uvloop = {version = "^0.19.0", markers = "sys_platform != 'win32'"} From 52897d9bd8dc4c795936b0892e20b647fd4c978a Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Fri, 24 May 2024 17:49:40 +1000 Subject: [PATCH 28/28] Update release notes --- RELEASES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 7cef46588eb8..98f0e7a6932b 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,6 +1,6 @@ # NautilusTrader 1.193.0 Beta -Released on TBD (UTC). +Released on 24th May 2024 (UTC). ### Enhancements - Added Interactive Brokers support for Market-on-Close (MOC) and Limit-on-Close (LOC) order types (#1663), thanks @rsmb7z @@ -11,7 +11,7 @@ Released on TBD (UTC). - Overhauled `SandboxExecutionClientConfig` to more closely match `BacktestVenueConfig` (many changes and additions) ### Fixes -- Fixed data order by `ts_init` when streaming (#1656), thanks @twitu +- Fixed DataFusion backend data ordering by `ts_init` when streaming (#1656), thanks @twitu - Fixed Interactive Brokers tick level historical data downloading (#1653), thanks @DracheShiki ---