diff --git a/CHANGELOG.md b/CHANGELOG.md index f9039d3..0585a0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,23 @@ # Changelog +## 0.70.0 - 2026-01-27 + +#### Enhancements +- Added new off-market publisher for Cboe Futures Exchange (`XCBF_PITCH_XOFF`) +- Upgraded `databento-dbn` to 0.48.0: + - Changed the `__repr__` implementation of all records in Python to be more Pythonic + - Added new `StatType` variants to be used by `XCBF.PITCH` dataset: + - `UPPER_PRICE_LIMIT` + - `LOWER_PRICE_LIMIT` + - `BLOCK_VOLUME` + - `VENUE_SPECIFIC_VOLUME_1` +- Added new publisher for Blue Ocean ATS (`OCEA_MEMOIR_OCEA`) +- Reduced the log level of end-of-interval `SystemMsg` records to the debug level + ## 0.69.0 - 2026-01-13 #### Enhancements -- Upgraded `databento-dbn` to 0.46.0 +- Upgraded `databento-dbn` to 0.46.0: - Added `DBNRecord` union type to Python which includes all record types - Removed `Record` class from Python type stubs to match code: the record classes don't share a base class. Use `DBNRecord` instead. @@ -28,7 +42,7 @@ This release adds support for Python 3.14. #### Enhancements - Added support for Python 3.14 - Functions which accept a path as an argument now expand user directories -- Upgraded `databento-dbn` to 0.45.0 +- Upgraded `databento-dbn` to 0.45.0: - Added support for Python 3.14 ## 0.67.0 - 2025-12-02 @@ -50,7 +64,7 @@ This release adds support for Python 3.14. - Added a property `Live.session_id` which returns the streaming session ID when the client is connected - Streams added with `Live.add_stream()` which do not define an exception handler will now emit a warning if an exception is raised while executing the callback - Callback functions added with `Live.add_callback()` which do not define an exception handler will now emit a warning if an exception is raised while executing the callback -- Upgraded `databento-dbn` to 0.44.0 +- Upgraded `databento-dbn` to 0.44.0: - Added logic to set `code` when upgrading version 1 `SystemMsg` to newer versions #### Bug fixes @@ -63,7 +77,7 @@ This release adds support for Python 3.14. #### Enhancements - Added export of `CBBOMsg` and `BBOMsg` from `databento_dbn` to the root `databento` package -- Upgraded `databento-dbn` to 0.43.0 +- Upgraded `databento-dbn` to 0.43.0: - Added export of `F_PUBLISHER_SPECIFIC` constant to Python - Added explicit `Unset` variant for `SystemCode` and `ErrorCode` - Changed Python getters for enum fields to return the underlying type when no known variant can be found. As a result, these getters no longer raise an exception @@ -74,7 +88,7 @@ This release adds support for Python 3.14. ## 0.64.0 - 2025-09-30 #### Enhancements -- Upgraded `databento-dbn` to 0.42.0 +- Upgraded `databento-dbn` to 0.42.0: - Added `ts_index` and `pretty_ts_index` properties for records in Python which provides the timestamp that is most appropriate for indexing - Fixed type stub for `channel_id` to allow None @@ -99,7 +113,7 @@ This release delivers a number of breaking changes to the Python interface for D #### Breaking changes - Removed `bill_id` from the response of `batch.list_jobs()` and `batch.submit_job()` -- Upgraded `databento-dbn` to 0.40.0 +- Upgraded `databento-dbn` to 0.40.0: - Removed `hd` property from records in Python. Header fields are accessible directly from the record - Removed ability to directly instantiate most enums from an `int` in Python and coercion @@ -142,7 +156,7 @@ This release delivers a number of breaking changes to the Python interface for D #### Enhancements - Added `parquet_schema` option to `DBNStore.to_parquet()` for overriding the pyarrow schema. -- Upgraded `databento-dbn` to 0.39.0 +- Upgraded `databento-dbn` to 0.39.0: - Added `side()` and `unpaired_side()` methods to `ImbalanceMsg` that convert the fields of the same name to the `Side` enum - Added `pretty_auction_time` property in Python for `ImbalanceMsg` @@ -163,7 +177,7 @@ Python ## 0.59.0 - 2025-07-15 #### Enhancements -- Upgraded `databento-dbn` to 0.37.1 +- Upgraded `databento-dbn` to 0.37.1: - Fix buffer growth in `DbnFsm::write_all()`, which is used by `DBNDecoder.write()` #### Breaking changes @@ -178,7 +192,7 @@ Python #### Enhancements - Changed the `tz` parameter in `DBNStore.to_df()` to accept `datetime.tzinfo` instead of `pytz.BaseTzInfo` explicitly - Modified the dependency specification for `databento_dbn` to allow for compatible patch versions -- Upgraded `databento-dbn` to 0.36.2 +- Upgraded `databento-dbn` to 0.36.2: - Fixed change in behavior where Python `DBNDecoder.decode()` wouldn't always decode all available data on the first call ## 0.57.1 - 2025-06-17 @@ -187,7 +201,7 @@ Python - Changed the following Venue, Publisher, and Dataset descriptions: - "ICE Futures Europe (Financials)" renamed to "ICE Europe Financials" - "ICE Futures Europe (Commodities)" renamed to "ICE Europe Commodities" -- Upgraded `databento-dbn` to 0.36.1 +- Upgraded `databento-dbn` to 0.36.1: - Fixed setting of `ts_out` property of DbnFsm based on decoded metadata. This was preventing `ts_out` from being correctly decoded in the Python DBNDecoder - Fixed decoding of `ts_out` with first records in DBNDecoder @@ -198,7 +212,7 @@ was preventing `ts_out` from being correctly decoded in the Python DBNDecoder ## 0.57.0 - 2025-06-10 #### Enhancements -- Upgraded `databento-dbn` to 0.36.0 +- Upgraded `databento-dbn` to 0.36.0: - Added missing Python type stubs for several leg properties of `InstrumentDefMsg` #### Bug fixes @@ -273,7 +287,7 @@ was preventing `ts_out` from being correctly decoded in the Python DBNDecoder - Added `exchanges` parameter to `Reference.corporate_actions.get_range(...)` - Added `is_last` field to live subscription requests which will be used to improve the handling of split subscription requests -- Upgraded `databento-dbn` to 0.35.0 +- Upgraded `databento-dbn` to 0.35.0: - This version delivers DBN version 3 (DBNv3), which is the new default - Improved the performance of the Python `DBNDecoder` @@ -290,7 +304,7 @@ was preventing `ts_out` from being correctly decoded in the Python DBNDecoder ## 0.53.0 - 2025-04-29 #### Enhancements -- Upgraded `databento-dbn` to 0.33.1 +- Upgraded `databento-dbn` to 0.33.1: - Added `SystemCode` and `ErrorCode` enums to indicate types of system and error messages - Added `code()` methods to SystemMsg and ErrorMsg to retrieve the enum value if one exists and equivalent properties in Python @@ -301,7 +315,7 @@ was preventing `ts_out` from being correctly decoded in the Python DBNDecoder #### Enhancements - Added new optional `id` field to `SubscriptionRequest` class which will be used for improved error messages -- Upgraded `databento-dbn` to 0.32.0 +- Upgraded `databento-dbn` to 0.32.0: - Fixed `RType` variant names in Python to match `Schema` - Added missing Python type declarations for `RType` variants - Fixed issue with Python `_hidden_fields` definition that caused `KeyError: _reserved1_00` @@ -310,7 +324,7 @@ was preventing `ts_out` from being correctly decoded in the Python DBNDecoder ## 0.51.0 - 2025-04-08 #### Enhancements -- Upgraded `databento-dbn` to 0.31.0 +- Upgraded `databento-dbn` to 0.31.0: - Fixed Python type annotation for `SystemMsg.is_heartbeat()` method that was previously annotated as a property ## 0.50.0 - 2025-03-18 @@ -336,7 +350,7 @@ was preventing `ts_out` from being correctly decoded in the Python DBNDecoder - `UNDEF_STAT_QUANTITY` - `UNDEF_TIMESTAMP` - Added export of `BidAskPair` and `ConsolidatedBidAskPair` from `databento_dbn` to the root `databento` package -- Upgraded `databento-dbn` to 0.29.0 +- Upgraded `databento-dbn` to 0.29.0: - Added `COMMODITY_SPOT` `InstrumentClass` variant - Improved handling of `datetime` and `date` objects in `start` and `end` parameters @@ -358,7 +372,7 @@ was preventing `ts_out` from being correctly decoded in the Python DBNDecoder - Added export of `TradingEvent` enum from `databento_dbn` to the root `databento` package - Added new dataset `EQUS.MINI` and new publishers `EQUS.MINI.EQUS`, `XNYS.TRADES.EQUS` - Removed upper bound for supported `python` versions; the constraint is now `^3.9` -- Upgraded `databento-dbn` to 0.27.0 +- Upgraded `databento-dbn` to 0.27.0: - Fixed export of `InstrumentDefMsgV3` to Python #### Bug fixes @@ -368,7 +382,7 @@ was preventing `ts_out` from being correctly decoded in the Python DBNDecoder ## 0.47.0 - 2024-12-17 #### Enhancements -- Upgraded `databento-dbn` to 0.25.0 +- Upgraded `databento-dbn` to 0.25.0: - Added type aliases for `TBBOMsg`, `BBO1SMsg`, `BBO1MMsg`, `TCBBOMsg`, `CBBO1SMsg`, `CBBO1MMsg` in Python - Removed exports for `CBBOMsg` and `BBOMsg` in the root `databento` package in favor of aliased versions from `databento-dbn` @@ -377,7 +391,7 @@ was preventing `ts_out` from being correctly decoded in the Python DBNDecoder #### Enhancements - Removed deprecated `packaging` parameter from `Historical.batch.submit_job`. Job files can be downloaded individually or as zip files after the job completes -- Upgraded `databento-dbn` to 0.24.0 +- Upgraded `databento-dbn` to 0.24.0: - Added handling for `UNDEF_TIMESTAMP` in `pretty_` timestamp getters for Python. They now return `None` in the case of `UNDEF_TIMESTAMP` ## 0.45.0 - 2024-11-12 @@ -387,7 +401,7 @@ This release adds support for Python 3.13. #### Enhancements - Added support for Python 3.13 - Added new IntelligentCross venues `ASPN`, `ASMT`, and `ASPI` -- Upgraded `databento-dbn` to 0.23.1 +- Upgraded `databento-dbn` to 0.23.1: - Fixed `pretty_activation` getter in `databento_dbn` returning `expiration` instead - Fixed some `pretty_` getters in `databento_dbn` didn't correctly handle `UNDEF_PRICE` diff --git a/README.md b/README.md index a53d7bf..50ea4ec 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ The library is fully compatible with distributions of Anaconda 2023.x and above. The minimum dependencies as found in the `pyproject.toml` are also listed below: - python = "^3.10" - aiohttp = "^3.8.3" -- databento-dbn = "~0.46.0" +- databento-dbn = "~0.48.0" - numpy = ">=1.23.5" - pandas = ">=1.5.3" - pip-system-certs = ">=4.0" (Windows only) diff --git a/databento/common/dbnstore.py b/databento/common/dbnstore.py index f194c71..4504ee3 100644 --- a/databento/common/dbnstore.py +++ b/databento/common/dbnstore.py @@ -1528,8 +1528,7 @@ def _format_px( if price_type == PriceType.DECIMAL: df[px_fields] = ( - df[px_fields].replace(UNDEF_PRICE, np.nan).applymap(decimal.Decimal) - / FIXED_PRICE_SCALE + df[px_fields].replace(UNDEF_PRICE, np.nan).map(decimal.Decimal) / FIXED_PRICE_SCALE ) elif price_type == PriceType.FLOAT: df[px_fields] = df[px_fields].replace(UNDEF_PRICE, np.nan) / FIXED_PRICE_SCALE diff --git a/databento/common/publishers.py b/databento/common/publishers.py index dbe27a8..3a77e47 100644 --- a/databento/common/publishers.py +++ b/databento/common/publishers.py @@ -120,6 +120,8 @@ class Venue(StringyMixin, str, Enum): European Energy Exchange. XCBF Cboe Futures Exchange. + OCEA + Blue Ocean ATS. """ @@ -175,6 +177,7 @@ class Venue(StringyMixin, str, Enum): XEUR = "XEUR" XEEE = "XEEE" XCBF = "XCBF" + OCEA = "OCEA" @classmethod def from_int(cls, value: int) -> Venue: @@ -285,6 +288,8 @@ def from_int(cls, value: int) -> Venue: return Venue.XEEE if value == 52: return Venue.XCBF + if value == 53: + return Venue.OCEA raise ValueError(f"Integer value {value} does not correspond with any Venue variant") def to_int(self) -> int: @@ -395,6 +400,8 @@ def to_int(self) -> int: return 51 if self == Venue.XCBF: return 52 + if self == Venue.OCEA: + return 53 raise ValueError("Invalid Venue") @property @@ -506,6 +513,8 @@ def description(self) -> str: return "European Energy Exchange" if self == Venue.XCBF: return "Cboe Futures Exchange" + if self == Venue.OCEA: + return "Blue Ocean ATS" raise ValueError("Unexpected Venue value") @@ -595,6 +604,8 @@ class Dataset(StringyMixin, str, Enum): European Energy Exchange EOBI. XCBF_PITCH Cboe Futures Exchange PITCH. + OCEA_MEMOIR + Blue Ocean ATS MEMOIR Depth. """ @@ -638,6 +649,7 @@ class Dataset(StringyMixin, str, Enum): XEUR_EOBI = "XEUR.EOBI" XEEE_EOBI = "XEEE.EOBI" XCBF_PITCH = "XCBF.PITCH" + OCEA_MEMOIR = "OCEA.MEMOIR" @classmethod def from_int(cls, value: int) -> Dataset: @@ -724,6 +736,8 @@ def from_int(cls, value: int) -> Dataset: return Dataset.XEEE_EOBI if value == 40: return Dataset.XCBF_PITCH + if value == 41: + return Dataset.OCEA_MEMOIR raise ValueError(f"Integer value {value} does not correspond with any Dataset variant") def to_int(self) -> int: @@ -810,6 +824,8 @@ def to_int(self) -> int: return 39 if self == Dataset.XCBF_PITCH: return 40 + if self == Dataset.OCEA_MEMOIR: + return 41 raise ValueError("Invalid Dataset") @property @@ -897,6 +913,8 @@ def description(self) -> str: return "European Energy Exchange EOBI" if self == Dataset.XCBF_PITCH: return "Cboe Futures Exchange PITCH" + if self == Dataset.OCEA_MEMOIR: + return "Blue Ocean ATS MEMOIR Depth" raise ValueError("Unexpected Dataset value") @@ -1116,6 +1134,10 @@ class Publisher(StringyMixin, str, Enum): European Energy Exchange EOBI - Off-Market Trades. XCBF_PITCH_XCBF Cboe Futures Exchange. + XCBF_PITCH_XOFF + Cboe Futures Exchange - Off-Market Trades. + OCEA_MEMOIR_OCEA + Blue Ocean ATS MEMOIR. """ @@ -1224,6 +1246,8 @@ class Publisher(StringyMixin, str, Enum): XEUR_EOBI_XOFF = "XEUR.EOBI.XOFF" XEEE_EOBI_XOFF = "XEEE.EOBI.XOFF" XCBF_PITCH_XCBF = "XCBF.PITCH.XCBF" + XCBF_PITCH_XOFF = "XCBF.PITCH.XOFF" + OCEA_MEMOIR_OCEA = "OCEA.MEMOIR.OCEA" @classmethod def from_int(cls, value: int) -> Publisher: @@ -1440,6 +1464,10 @@ def from_int(cls, value: int) -> Publisher: return Publisher.XEEE_EOBI_XOFF if value == 105: return Publisher.XCBF_PITCH_XCBF + if value == 106: + return Publisher.XCBF_PITCH_XOFF + if value == 107: + return Publisher.OCEA_MEMOIR_OCEA raise ValueError(f"Integer value {value} does not correspond with any Publisher variant") def to_int(self) -> int: @@ -1656,6 +1684,10 @@ def to_int(self) -> int: return 104 if self == Publisher.XCBF_PITCH_XCBF: return 105 + if self == Publisher.XCBF_PITCH_XOFF: + return 106 + if self == Publisher.OCEA_MEMOIR_OCEA: + return 107 raise ValueError("Invalid Publisher") @property @@ -1873,6 +1905,10 @@ def venue(self) -> Venue: return Venue.XOFF if self == Publisher.XCBF_PITCH_XCBF: return Venue.XCBF + if self == Publisher.XCBF_PITCH_XOFF: + return Venue.XOFF + if self == Publisher.OCEA_MEMOIR_OCEA: + return Venue.OCEA raise ValueError("Unexpected Publisher value") @property @@ -2090,6 +2126,10 @@ def dataset(self) -> Dataset: return Dataset.XEEE_EOBI if self == Publisher.XCBF_PITCH_XCBF: return Dataset.XCBF_PITCH + if self == Publisher.XCBF_PITCH_XOFF: + return Dataset.XCBF_PITCH + if self == Publisher.OCEA_MEMOIR_OCEA: + return Dataset.OCEA_MEMOIR raise ValueError("Unexpected Publisher value") @property @@ -2307,4 +2347,8 @@ def description(self) -> str: return "European Energy Exchange EOBI - Off-Market Trades" if self == Publisher.XCBF_PITCH_XCBF: return "Cboe Futures Exchange" + if self == Publisher.XCBF_PITCH_XOFF: + return "Cboe Futures Exchange - Off-Market Trades" + if self == Publisher.OCEA_MEMOIR_OCEA: + return "Blue Ocean ATS MEMOIR" raise ValueError("Unexpected Publisher value") diff --git a/databento/historical/api/batch.py b/databento/historical/api/batch.py index 5143b88..238bcae 100644 --- a/databento/historical/api/batch.py +++ b/databento/historical/api/batch.py @@ -135,6 +135,8 @@ def submit_job( The input symbology type to resolve from. stype_out : SType or str, default 'instrument_id' The output symbology type to resolve to. + Must be a valid symbology combination with `stype_in`. + See `symbology combinations`. https://www.databento.com/standards-and-conventions/symbology#supported-symbology-combinations limit : int, optional The maximum number of records to return. If `None` then no limit. diff --git a/databento/historical/api/symbology.py b/databento/historical/api/symbology.py index 11001b0..dbbb6df 100644 --- a/databento/historical/api/symbology.py +++ b/databento/historical/api/symbology.py @@ -50,6 +50,8 @@ def resolve( The input symbology type to resolve from. stype_out : SType or str, default 'instrument_id' The output symbology type to resolve to. + Must be a valid symbology combination with `stype_in`. + See `symbology combinations`. https://www.databento.com/standards-and-conventions/symbology#supported-symbology-combinations start_date : date or str The inclusive UTC start date of the request range. end_date : date or str, optional diff --git a/databento/historical/api/timeseries.py b/databento/historical/api/timeseries.py index a4b763d..1bacc59 100644 --- a/databento/historical/api/timeseries.py +++ b/databento/historical/api/timeseries.py @@ -80,6 +80,8 @@ def get_range( The input symbology type to resolve from. stype_out : SType or str, default 'instrument_id' The output symbology type to resolve to. + Must be a valid symbology combination with `stype_in`. + See `symbology combinations`. https://www.databento.com/standards-and-conventions/symbology#supported-symbology-combinations limit : int, optional The maximum number of records to return. If `None` then no limit. path : PathLike[str] or str, optional @@ -178,6 +180,8 @@ async def get_range_async( The input symbology type to resolve from. stype_out : SType or str, default 'instrument_id' The output symbology type to resolve to. + Must be a valid symbology combination with `stype_in`. + See `symbology combinations`. https://www.databento.com/standards-and-conventions/symbology#supported-symbology-combinations limit : int, optional The maximum number of records to return. If `None` then no limit. path : PathLike[str] or str, optional diff --git a/databento/live/protocol.py b/databento/live/protocol.py index 30fec68..da71c07 100644 --- a/databento/live/protocol.py +++ b/databento/live/protocol.py @@ -11,6 +11,7 @@ from databento_dbn import Metadata from databento_dbn import Schema from databento_dbn import SType +from databento_dbn import SystemCode from databento_dbn import VersionUpgradePolicy from databento.common import cram @@ -388,7 +389,12 @@ def _process_dbn(self, data: bytes) -> None: if record.is_heartbeat(): logger.debug("gateway heartbeat") else: - logger.info( + if record.code == SystemCode.END_OF_INTERVAL: + system_msg_level = logging.DEBUG + else: + system_msg_level = logging.INFO + logger.log( + system_msg_level, "system message code=%s msg='%s'", record.code, record.msg, diff --git a/databento/version.py b/databento/version.py index 786600a..6df865b 100644 --- a/databento/version.py +++ b/databento/version.py @@ -1 +1 @@ -__version__ = "0.69.0" +__version__ = "0.70.0" diff --git a/pyproject.toml b/pyproject.toml index d875ba5..de3acbd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "databento" -version = "0.69.0" +version = "0.70.0" description = "Official Python client library for Databento" readme = "README.md" requires-python = ">=3.10" @@ -10,10 +10,10 @@ dynamic = [ "classifiers" ] dependencies = [ "aiohttp>=3.8.3,<4.0.0; python_version < '3.12'", "aiohttp>=3.9.0,<4.0.0; python_version >= '3.12'", - "databento-dbn~=0.46.0", + "databento-dbn~=0.48.0", "numpy>=1.23.5; python_version < '3.12'", "numpy>=1.26.0; python_version >= '3.12'", - "pandas>=1.5.3", + "pandas>=1.5.3,<4.0.0", "pip-system-certs>=4.0; platform_system == 'Windows'", "pyarrow>=13.0.0", "requests>=2.27.0", @@ -49,12 +49,10 @@ black = "^23.9.1" mypy = "1.5.1" pytest = "^7.4.2" pytest-asyncio = "==0.21.1" -ruff = "^0.0.291" -types-requests = "^2.30.0.0" +ruff = "^0.14.0" tomli = "^2.0.1" +types-requests = "^2.30.0.0" teamcity-messages = "^1.32" -types-pytz = "^2024.1.0.20240203" -types-aiofiles = "^23.2.0.20240403" [build-system] requires = ["poetry-core"] diff --git a/tests/test_historical_bento.py b/tests/test_historical_bento.py index 50f67a3..0ec0be9 100644 --- a/tests/test_historical_bento.py +++ b/tests/test_historical_bento.py @@ -8,11 +8,11 @@ from typing import Any from typing import Literal from unittest.mock import MagicMock +from zoneinfo import ZoneInfo import numpy as np import pandas as pd import pytest -import pytz import zstandard from databento_dbn import Compression from databento_dbn import DBNRecord @@ -1579,24 +1579,24 @@ def test_dbnstore_to_df_with_timezone( @pytest.mark.parametrize( "timezone", [ - pytz.timezone("US/Central"), - pytz.timezone("US/Eastern"), - pytz.timezone("Europe/Vienna"), - pytz.timezone("Asia/Dubai"), - pytz.timezone("UTC"), + ZoneInfo("US/Central"), + ZoneInfo("US/Eastern"), + ZoneInfo("Europe/Vienna"), + ZoneInfo("Asia/Dubai"), + ZoneInfo("UTC"), ], ) @pytest.mark.parametrize( "schema", [pytest.param(schema, id=str(schema)) for schema in Schema.variants()], ) -def test_dbnstore_to_df_with_pytz_timezone( +def test_dbnstore_to_df_with_zoneinfo( test_data_path: Callable[[Dataset, Schema], Path], schema: Schema, - timezone: pytz.BaseTzInfo, + timezone: ZoneInfo, ) -> None: """ - Test that setting the `tz` parameter in `DBNStore.to_df` accepts `pytz` + Test that setting the `tz` parameter in `DBNStore.to_df` accepts `ZoneInfo` timezone objects. """ # Arrange