[project] name = "nautilus_trader" version = "1.231.0" description = "Production-grade Rust-native trading engine with deterministic event-driven architecture" authors = [{ name = "Nautech Systems", email = "info@nautechsystems.io" }] classifiers = [ "License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)", "Operating System :: OS Independent", "Development Status :: 4 - Beta", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Scientific/Engineering", "Topic :: Office/Business :: Financial", "Topic :: Office/Business :: Financial :: Investment", "Intended Audience :: Developers", "Intended Audience :: Financial and Insurance Industry", "Intended Audience :: Science/Research", ] license = { text = "LGPL-3.0-or-later" } readme = "README.md" requires-python = ">=3.12,<3.15" dependencies = [ "click>=8.4.1,<9.0.0", "fsspec>=2026.2.0,<=2026.2.0", "msgspec>=0.21.1,<1.0.0", "numpy>=1.26.4", "pandas>=2.3.3,<4.0.0", "portion>=2.6.1", "pyarrow>=25.0.0", "pytz>=2026.2", "tzdata>=2026.3", "tqdm>=4.68.4,<5.0.0", "uvloop==0.22.1,<1.0.0; sys_platform != \"win32\"", # Pinned to 0.22.1 for stability ] [project.urls] homepage = "https://nautilustrader.io" repository = "https://github.com/nautechsystems/nautilus_trader" docs = "https://nautilustrader.io/docs" # For now we use the poetry build backend until uv supports custom build scripts [build-system] requires = [ "setuptools>=83", "poetry-core==2.3.1", # Pinned to 2.3.1 for stability "numpy>=1.26.4", "cython==3.2.9", # Pinned to 3.2.9 for stability ] build-backend = "poetry.core.masonry.api" [tool.poetry] include = [ # Rust source must be included in the source distributions { path = "crates/*", format = "sdist" }, { path = "Cargo.lock", format = "sdist" }, { path = "Cargo.toml", format = "sdist" }, { path = ".cargo/*", format = "sdist" }, # Compiled extensions must be included in the wheel distributions { path = "nautilus_trader/**/*.so", format = "wheel" }, { path = "nautilus_trader/**/*.pyd", format = "wheel" }, # Include the py.typed file for type checking support { path = "nautilus_trader/py.typed", format = "sdist" }, { path = "nautilus_trader/py.typed", format = "wheel" }, # Include Python interface files for type checking support { path = "nautilus_trader/**/*.pyi", format = "sdist" }, { path = "nautilus_trader/**/*.pyi", format = "wheel" }, ] [tool.poetry.build] script = "build.py" generate-setup-file = false [project.optional-dependencies] betfair = ["betfair-parser==0.19.1"] # Pinned to 0.19.1 for stability ib = [ "defusedxml>=0.7.1,<1.0.0", "nautilus-ibapi==10.45.1", "protobuf==5.29.6", # Patched for GHSA-8qvm-5x2c-j2w7 and CVE-2026-0994 ] docker = ["docker>=7.2.0,<8.0.0"] polymarket = ["py-clob-client-v2>=1.0.2,<2.0.0"] visualization = [ "plotly>=6.9.0,<7.0.0", "kaleido>=1.3.0,<2.0.0", "simplejson==3.20.2", # Pinned to 3.20.2 because 4.0.0 has no cp313 wheels ] [dependency-groups] dev = [ "cython==3.2.9", # Pinned to 3.2.9 for stability "jupytext>=1.19.3,<2.0.0", "setuptools>=83", "mypy==1.20.2,<2.0.0", # Pinned to 1.20.2 (keep in line with .pre-commit-config.yaml) "pandas-stubs>=2.3.3,<4.0.0", "ruff==0.16.0", # Pinned to 0.16.0 (keep in line with .pre-commit-config.yaml) "types-pytz>=2026.2,<2027.0", "types-toml>=0.10.8,<1.0.0", ] test = [ "aiohttp==3.14.1,<4.0.0", "coverage>=7.15.0,<8.0.0", "pytest>=7.4.4,<8.0.0", # Intentionally held at 7.x "pytest-aiohttp>=1.1.1,<2.0.0", "pytest-asyncio==0.23.8", # Pinned to 0.23.8 for stability "pytest-benchmark==5.0.1", # Pinned to 5.0.1 due pytest 8.x requirement "pytest-codspeed==4.3.0", # Pinned to 4.3.0 for stability "pytest-cov==6.3.0", # Pinned to 6.3.0 due pytest 8.x requirement "pytest-mock>=3.15.1,<4.0.0", "pytest-rerunfailures==16.1,<17.0.0", # Pinned to 16.1.x due pytest 8.x requirement "pytest-xdist[psutil]>=3.8.0,<4.0.0", ] docs = [ "numpydoc>=1.10.0,<2.0.0", "linkify-it-py>=2.1.0,<3.0.0", "myst-parser>=5.1.0,<6.0.0", "sphinx-comments>=0.0.3,<1.0.0", "furo==2025.12.19", # Pinned to 2025.12.19 for stability ] [tool.uv] required-version = "==0.11.33" exclude-newer = "3 days" # Block sdist builds for every third-party package in uv.lock so unexpected loss # of wheel publishing fails `uv lock` instead of silently building from source. # Kept in sync with uv.lock by scripts/check-no-build-packages.sh (also a # pre-commit hook). The local `nautilus_trader` package is intentionally absent # because it must be built by the workspace's own build backend. no-build-package = [ "accessible-pygments", "aiohappyeyeballs", "aiohttp", "aiosignal", "alabaster", "annotated-types", "anyio", "attrs", "babel", "beautifulsoup4", "betfair-parser", "bitarray", "certifi", "cffi", "charset-normalizer", "choreographer", "ckzg", "click", "colorama", "coverage", "cython", "cytoolz", "defusedxml", "docker", "docutils", "eth-abi", "eth-account", "eth-hash", "eth-keyfile", "eth-keys", "eth-rlp", "eth-typing", "eth-utils", "execnet", "fastjsonschema", "frozenlist", "fsspec", "furo", "h11", "h2", "hexbytes", "hpack", "httpcore", "httpx", "hyperframe", "idna", "imagesize", "iniconfig", "jinja2", "jsonschema", "jsonschema-specifications", "jupyter-core", "jupytext", "kaleido", "librt", "linkify-it-py", "logistro", "markdown-it-py", "markupsafe", "mdit-py-plugins", "mdurl", "msgspec", "multidict", "mypy", "mypy-extensions", "myst-parser", "narwhals", "nautilus-ibapi", "nbformat", "numpy", "numpydoc", "orjson", "packaging", "pandas", "pandas-stubs", "parsimonious", "pathspec", "platformdirs", "plotly", "pluggy", "poly-eip712-structs", "portion", "propcache", "protobuf", "psutil", "py-clob-client-v2", "py-cpuinfo", "py-order-utils", "pyarrow", "pycparser", "pycryptodome", "pydantic", "pydantic-core", "pygments", "pytest", "pytest-aiohttp", "pytest-asyncio", "pytest-benchmark", "pytest-codspeed", "pytest-cov", "pytest-mock", "pytest-rerunfailures", "pytest-xdist", "python-dateutil", "pytz", "pywin32", "pyyaml", "referencing", "regex", "requests", "rich", "rlp", "roman-numerals", "rpds-py", "ruff", "setuptools", "simplejson", "six", "snowballstemmer", "sortedcontainers", "soupsieve", "sphinx", "sphinx-basic-ng", "sphinx-comments", "sphinxcontrib-applehelp", "sphinxcontrib-devhelp", "sphinxcontrib-htmlhelp", "sphinxcontrib-jsmath", "sphinxcontrib-qthelp", "sphinxcontrib-serializinghtml", "toolz", "tqdm", "traitlets", "types-pytz", "types-toml", "typing-extensions", "typing-inspection", "tzdata", "uc-micro-py", "urllib3", "uvloop", "yarl", ] [tool.uv.exclude-newer-package] # These releases received an extra audit before bypassing the default cooldown. bitarray = "8 hours" ckzg = "8 hours" docker = "8 hours" plotly = "8 hours" pyarrow = "8 hours" regex = "8 hours" ruff = "8 hours" tzdata = "8 hours" [tool.isort] py_version = "312" skip_glob = ["**/core/rust/*"] line_length = 120 ensure_newline_before_comments = true force_single_line = true include_trailing_comma = true lines_after_imports = 2 use_parentheses = true filter_files = true [tool.docformatter] wrap-summaries = 88 wrap-descriptions = 88 make-summary-multi-line = true pre-summary-new-line = true blank = true recursive = true in-place = true [tool.ruff] target-version = "py312" line-length = 100 exclude = [ ".benchmarks", ".eggs", ".git", ".mypy_cache", ".pytest_cache", ".ruff_cache", ".venv", "build", "dist", "patches", "venv", ] [tool.ruff.lint] select = [ "C4", # flake8-comprehensions "E", # pycodestyle errors "F", # pyflakes "W", # pycodestyle warnings "C90", # mccabe complexity "D", # pydocstyle "DTZ", # flake8-datetimez "UP", # pyupgrade "S", # flake8-bandit (security) "T10", # flake8-debugger "ICN", # flake8-import-conventions "PIE", # flake8-pie "PT", # flake8-pytest-style "PYI", # flake8-pyi (stubs) "Q", # flake8-quotes "I", # isort "RSE", # flake8-raise "TID", # flake8-tidy-imports "SIM", # flake8-simplify "B", # flake8-bugbear "PERF", # Perflint "FURB", # refurb "ISC", # flake8-implicit-str-concat "FLY", # flynt "LOG", # flake8-logging "ASYNC", # flake8-async # "ARG", # flake8-unused-arguments - 843 violations # "ERA", # eradicate (commented code) - 1441 violations "PD", # pandas-vet "PGH", # pygrep-hooks "PLE", # pylint errors "PLW", # pylint warnings "NPY", # NumPy-specific rules "RUF", # Ruff-specific rules ] ignore = [ "D100", # Missing docstring in public module **fix** "D101", "D102", # Missing docstring in public method **fix** "D103", # Missing docstring in public function **fix** "D104", # Missing docstring in public package **fix** "D107", "D105", "D200", # One-line docstring should fit on one line with quotes (optional style) "D203", # 1 blank line required before class docstring (optional style) "D205", # 1 blank line required between summary line and description (optional style) "D212", # Multi-line docstring summary should start at the first line (optional style) "D400", # First line should end with a period (not always a first line) "D413", # Missing blank line after last section ('Parameters') "D415", # First line should end with a period, question mark, or exclamation point (not always a first line) "D416", # Section name should end with a colon ('Warnings:', not 'Warnings') (incorrect?) "E501", # Line too long (will lint separately) "E741", # Ambiguous variable name (single char) "FURB166", # int() on sliced str (explicit base is clearer than auto-detection) "ISC004", # multi-line string concatenation in collections is intentional for wrapped messages "LOG004", # custom Logger.exception accepts an explicit exception outside handlers "PERF401", # manual-list-comprehension (16 violations - fix incrementally) "PERF402", # manual-list-copy (3 violations - fix incrementally) "PT007", # pytest-parametrize-values-wrong-type (1621 violations - deferred) "PT011", # Exception type too broad (needs to be fixed case-by-case) "PT014", # Use list comprehensions (not always readable or appropriate) "PT017", # pytest-assert-in-except (1 violation - fix later) "PT028", # pytest-parameter-with-default-argument (4 violations - fix later) "PYI021", # Docstrings should not be included in stubs (OK as this is the natural place for them) "PGH003", # Blanket type-ignore (TODO: make these specific incrementally) "PLE1205", # logging-too-many-args (false positive for custom Logger class) "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar` "RUF059", # Unpacked variable is never used (can still be clearer than a discard in some cases) "S101", # Use of assert detected (OK in test suite, production violations to fix incrementally) "SIM108", # Use ternary operator instead of if-else-block (opinionated style) "B017", # pytest.raises(Exception) is too broad (we need broad catches for now) "B018", # Useless expression (OK in notebooks for displaying figures) "B008", # Function call in default argument (OK for immutable objects like pd.Timedelta, Money) "B024", # Abstract class without abstract methods (intentional for base class pattern) "B904", # raise without from clause (fix later for better exception chaining) "ASYNC109", # async-function-with-timeout (timeout params are a valid API pattern) "ASYNC110", # async-busy-wait (refactoring to Event is non-trivial, current patterns work) "PLW0602", # global-variable-not-assigned (intentional module-level caching pattern) "PLW0603", # global-statement (intentional module-level caching pattern) "PLW2901", # redefined-loop-name (intentional loop variable transformation pattern) "PLW0108", # unnecessary-lambda (lambdas needed for deferred evaluation in tests) "FURB110", # if-exp-instead-of-or-operator (ternary is often clearer than or) "FURB171", # single-item-membership-test (intentional for clarity) "UP042", # Class inherits from both str and Enum (StrEnum pattern used intentionally) ] # Allow autofix for all enabled rules (when `--fix`) is provided fixable = ["ALL"] # E4 = import-related rules (E401: multiple imports on one line, E402: module level import not at top) # E501 = line too long # UP006 = Use list instead of List for type annotation (field name 'list' shadows built-in in Bybit schemas) # UP035 = typing.List is deprecated (field name 'list' shadows built-in in Bybit schemas) # These are ignored and linted separately unfixable = ["E4", "E501", "UP006", "UP035"] # Allow unused variables when underscore-prefixed. dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" [tool.ruff.lint.isort] force-single-line = true lines-after-imports = 2 split-on-trailing-comma = true known-first-party = ["nautilus_trader"] [tool.ruff.format] # Inherits target-version, line-length, exclude from [tool.ruff] docstring-code-format = true [tool.ruff.lint.mccabe] max-complexity = 10 [tool.ruff.lint.per-file-ignores] "build.py" = ["S603", "S607"] # Build script needs subprocess calls "docs/getting_started/**/*.py" = [ "D", "E402", "I001", "S101", "T20", "B018", ] # Jupytext tutorials: cell-split imports, asserts, print statements, bare expressions "docs/tutorials/**/*.py" = [ "D", "E402", "I001", "S101", "T20", "B018", ] # Jupytext tutorials: cell-split imports, asserts, print statements, bare expressions "docs/how_to/**/*.py" = [ "D", "E402", "I001", "S101", "T20", "B018", ] # Jupytext how-to guides: same exemptions as tutorials "examples/**/*.py" = [ "DTZ", "S101", "S105", "S106", ] # Examples can use naive datetimes, asserts, and mock credentials "nautilus_trader/common/config.py" = [ "S105", ] # redacted_password variable is intentional "nautilus_trader/adapters/betfair/**/*.py" = [ "S101", ] # Betfair adapter uses asserts for validation (for now) "python/nautilus_trader/adapters/hyperliquid/__init__.py" = [ "PLE0604", ] # Dynamic __all__ extension from star import "test_perf_logger.py" = ["S311"] "tests/**/*.py" = [ "S101", "S105", "S106", ] # Tests can use asserts, mock credentials, and pickle [tool.mypy] python_version = "3.12" disallow_incomplete_defs = true explicit_package_bases = true ignore_missing_imports = true namespace_packages = true strict_optional = true warn_no_return = true warn_unused_configs = true warn_unused_ignores = true mypy_path = ["."] exclude = "(^python/examples/|^examples/|^docs/getting_started/|^docs/tutorials/|^docs/how_to/)" [[tool.mypy.overrides]] strict_optional = false module = [ "examples/*", "nautilus_trader/adapters/betfair/*", "nautilus_trader/adapters/binance/*", "nautilus_trader/adapters/interactive_brokers/*", "nautilus_trader/indicators/ta_lib/*", ] [tool.pytest.ini_options] testpaths = ["tests"] addopts = "-ra --new-first --failed-first --doctest-modules --doctest-glob=\"*.pyx\"" asyncio_mode = "strict" asyncio_default_fixture_loop_scope = "session" filterwarnings = ["ignore::UserWarning", "ignore::DeprecationWarning"] [tool.coverage.run] plugins = ["Cython.Coverage"] source = ["nautilus_trader"] omit = [ "nautilus_trader/adapters/*", "nautilus_trader/examples/*", "nautilus_trader/test_kit/*", ] [tool.coverage.report] fail_under = 0 show_missing = true