From 9ef188519c1e2d8d17f1df85bc8a3338079f540d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Gr=C3=A9goire?= Date: Sun, 30 Nov 2025 12:35:56 -0500 Subject: [PATCH 1/8] Fixed a bad reference to current_lob in before_lob_update --- src/meatpy/market_processor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/meatpy/market_processor.py b/src/meatpy/market_processor.py index a923e78..05ad8ba 100644 --- a/src/meatpy/market_processor.py +++ b/src/meatpy/market_processor.py @@ -84,7 +84,7 @@ def before_lob_update(self, new_timestamp: Timestamp) -> None: new_timestamp: The new timestamp for the upcoming update """ for x in self.handlers: - x.before_lob_update(self.current_lob, new_timestamp) + x.before_lob_update(self, new_timestamp) def message_event(self, timestamp: Timestamp, message: MarketMessage) -> None: """Notify handlers of a raw message event. From 18052a5c4530025b6c6c6296a2eacea83fe79f25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Gr=C3=A9goire?= Date: Sun, 30 Nov 2025 12:45:17 -0500 Subject: [PATCH 2/8] Updated tests to reflect expected behavior --- tests/test_market_processor.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/test_market_processor.py b/tests/test_market_processor.py index 26f7d8b..8e2b82e 100644 --- a/tests/test_market_processor.py +++ b/tests/test_market_processor.py @@ -74,13 +74,17 @@ def setup_method(self): def test_before_lob_update(self): """Test before_lob_update method.""" self.processor.before_lob_update(self.timestamp) - self.handler.before_lob_update.assert_called_once_with(self.lob, self.timestamp) + self.handler.before_lob_update.assert_called_once_with( + self.processor, self.timestamp + ) def test_before_lob_update_with_no_lob(self): """Test before_lob_update method with no LOB.""" self.processor.current_lob = None self.processor.before_lob_update(self.timestamp) - self.handler.before_lob_update.assert_called_once_with(None, self.timestamp) + self.handler.before_lob_update.assert_called_once_with( + self.processor, self.timestamp + ) def test_message_event(self): """Test message_event method.""" @@ -224,12 +228,16 @@ def test_pre_lob_event(self): """Test pre_lob_event method.""" self.processor.pre_lob_event(self.timestamp) # This method should call before_lob_update on handlers - self.handler.before_lob_update.assert_called_once_with(self.lob, self.timestamp) + self.handler.before_lob_update.assert_called_once_with( + self.processor, self.timestamp + ) def test_pre_lob_event_with_new_snapshot(self): """Test pre_lob_event method with new snapshot flag.""" self.processor.pre_lob_event(self.timestamp, new_snapshot=True) - self.handler.before_lob_update.assert_called_once_with(self.lob, self.timestamp) + self.handler.before_lob_update.assert_called_once_with( + self.processor, self.timestamp + ) class TestMarketProcessorCleanup: From 7c0c641c029370620193fa344c73e413f0fa2ae1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 30 Nov 2025 17:47:24 +0000 Subject: [PATCH 3/8] chore: bump version to 0.2.8 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 67ae55a..0e9e8a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "meatpy" -version = "0.2.7" +version = "0.2.8" description = "Read and process limit order book data" authors = [ { name = "Vincent Grégoire", email = "vincent.gregoire@hec.ca" }, From 26adee8ecee9f5a35711567540392476dd9b7ae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Gr=C3=A9goire?= Date: Fri, 9 Jan 2026 08:50:24 -0500 Subject: [PATCH 4/8] fix: populate _order_refs when processing AddOrderMessage When processing AddOrderMessage or AddOrderMPIDMessage, add the order_ref to self._order_refs so subsequent message handlers (executions, cancels, deletes, replacements) can properly track and process orders for this instrument. Without this fix, _order_refs remained empty and all subsequent handlers would exit early, causing the limit order book to not be updated correctly. Fixes #68 Co-Authored-By: Claude Opus 4.5 --- src/meatpy/itch50/itch50_market_processor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/meatpy/itch50/itch50_market_processor.py b/src/meatpy/itch50/itch50_market_processor.py index bcb135f..17ff902 100644 --- a/src/meatpy/itch50/itch50_market_processor.py +++ b/src/meatpy/itch50/itch50_market_processor.py @@ -157,6 +157,7 @@ def process_message( ): if message.stock != self.instrument: return + self._order_refs.add(message.order_ref) if self.track_lob: if message.bsindicator == b"B": order_type = OrderType.BID From 5c4101f4a4d75e903ccf29d29f5999ad9abd0d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Gr=C3=A9goire?= Date: Fri, 9 Jan 2026 08:51:32 -0500 Subject: [PATCH 5/8] chore: update uv.lock Co-Authored-By: Claude Opus 4.5 --- uv.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uv.lock b/uv.lock index cf452c1..dfbe331 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.11" resolution-markers = [ "python_full_version >= '3.12'", @@ -1005,7 +1005,7 @@ wheels = [ [[package]] name = "meatpy" -version = "0.2.6" +version = "0.2.8" source = { editable = "." } dependencies = [ { name = "pyarrow" }, From 2f03250fe87a3c1733d03cdcc276e8b1d4a49913 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Gr=C3=A9goire?= Date: Fri, 9 Jan 2026 08:52:07 -0500 Subject: [PATCH 6/8] Fix AttributeError in OrderExecutedPriceMessage: use execution_price instead of price The ITCH 5.0 OrderExecutedPriceMessage class uses `execution_price` as its attribute name, but the code was incorrectly referencing `message.price`. This fixes the AttributeError when processing OrderExecutedPriceMessage. Fixes #69 Co-Authored-By: Claude Opus 4.5 --- src/meatpy/itch50/itch50_exec_trade_recorder.py | 4 ++-- src/meatpy/itch50/itch50_market_processor.py | 2 +- src/meatpy/itch50/itch50_order_event_recorder.py | 2 +- src/meatpy/itch50/itch50_top_of_book_message_recorder.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/meatpy/itch50/itch50_exec_trade_recorder.py b/src/meatpy/itch50/itch50_exec_trade_recorder.py index edd3679..1871874 100644 --- a/src/meatpy/itch50/itch50_exec_trade_recorder.py +++ b/src/meatpy/itch50/itch50_exec_trade_recorder.py @@ -97,7 +97,7 @@ def message_event( "Queue": "Ask", "Volume": message.shares, "OrderID": message.order_ref, - "Price": message.price, + "Price": message.execution_price, } (queue, i, j) = lob.find_order(message.order_ref) record["OrderTimestamp"] = queue[i].queue[j].timestamp @@ -110,7 +110,7 @@ def message_event( "Queue": "Bid", "Volume": message.shares, "OrderID": message.order_ref, - "Price": message.price, + "Price": message.execution_price, } (queue, i, j) = lob.find_order(message.order_ref) record["OrderTimestamp"] = queue[i].queue[j].timestamp diff --git a/src/meatpy/itch50/itch50_market_processor.py b/src/meatpy/itch50/itch50_market_processor.py index bcb135f..79f9de7 100644 --- a/src/meatpy/itch50/itch50_market_processor.py +++ b/src/meatpy/itch50/itch50_market_processor.py @@ -197,7 +197,7 @@ def process_message( volume=message.shares, order_id=message.order_ref, trade_ref=message.match, - price=message.price, + price=message.execution_price, ) elif isinstance(message, OrderCancelMessage): if message.order_ref not in self._order_refs: diff --git a/src/meatpy/itch50/itch50_order_event_recorder.py b/src/meatpy/itch50/itch50_order_event_recorder.py index 9ab05c8..18c7609 100644 --- a/src/meatpy/itch50/itch50_order_event_recorder.py +++ b/src/meatpy/itch50/itch50_order_event_recorder.py @@ -111,7 +111,7 @@ def message_event(self, market_processor, timestamp, message) -> None: "order_ref": message.order_ref, "bsindicator": "", "shares": message.shares, - "price": message.price, + "price": message.execution_price, "neworder_ref": "", "MessageType": "OrderExecutedPrice", } diff --git a/src/meatpy/itch50/itch50_top_of_book_message_recorder.py b/src/meatpy/itch50/itch50_top_of_book_message_recorder.py index 0c0a955..4d1e724 100644 --- a/src/meatpy/itch50/itch50_top_of_book_message_recorder.py +++ b/src/meatpy/itch50/itch50_top_of_book_message_recorder.py @@ -123,7 +123,7 @@ def message_event(self, market_processor, timestamp, message) -> None: "Queue": "Ask", "Volume": message.shares, "OrderID": message.order_ref, - "Price": message.price, + "Price": message.execution_price, } self.records.append((timestamp, record)) elif len(lob.bid_levels) > 0 and lob.bid_levels[0].order_on_book( @@ -134,7 +134,7 @@ def message_event(self, market_processor, timestamp, message) -> None: "Queue": "Bid", "Volume": message.shares, "OrderID": message.order_ref, - "Price": message.price, + "Price": message.execution_price, } self.records.append((timestamp, record)) elif isinstance(message, OrderCancelMessage): From 50a61cf95dedddf57663b6de4572810667b060af Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 9 Jan 2026 13:53:37 +0000 Subject: [PATCH 7/8] chore: bump version to 0.2.9 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0e9e8a5..c823af0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "meatpy" -version = "0.2.8" +version = "0.2.9" description = "Read and process limit order book data" authors = [ { name = "Vincent Grégoire", email = "vincent.gregoire@hec.ca" }, From 016b3f555be2369b6dc0f8ecb4cfc5b7908899c1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 9 Jan 2026 13:54:13 +0000 Subject: [PATCH 8/8] chore: bump version to 0.2.10 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c823af0..adec99e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "meatpy" -version = "0.2.9" +version = "0.2.10" description = "Read and process limit order book data" authors = [ { name = "Vincent Grégoire", email = "vincent.gregoire@hec.ca" },