Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Dependencies

- Bump `cachetools` from 6.1.0 to 7.2.0 (requires `>=7.1.7`). Runtime
use remains `TTLCache` only; typing updated for cachetools 7 stubs
(#119).

## [4.3.2] - 2026-09-26

### Fixed
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ dependencies = [
"pyyaml>=6.0.2",
"orjson>=3.11.6",
"lz4>=4.4.4",
"cachetools>=6.1.0",
"cachetools>=7.1.7",
"deprecated>=1.2.18",
]

Expand Down
11 changes: 4 additions & 7 deletions src/project_x_py/client/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,14 @@
import gc
import io
import logging
from typing import TYPE_CHECKING, Any, cast
from typing import Any

import lz4.frame
import polars as pl
from cachetools import TTLCache

from project_x_py.models import Instrument

if TYPE_CHECKING:
pass

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -84,8 +81,8 @@ def cache_ttl(self, value: float) -> None:
"""
self._cache_ttl = value
# Recreate caches with new TTL
self._opt_instrument_cache = TTLCache(maxsize=1000, ttl=value)
self._opt_market_data_cache = TTLCache(maxsize=10000, ttl=value)
self._opt_instrument_cache = TTLCache[str, Instrument](maxsize=1000, ttl=value)
self._opt_market_data_cache = TTLCache[str, bytes](maxsize=10000, ttl=value)

def _serialize_dataframe(self, df: pl.DataFrame) -> bytes:
"""
Expand Down Expand Up @@ -174,7 +171,7 @@ def get_cached_instrument(self, symbol: str) -> Instrument | None:
Cached instrument or None if not found or expired.
"""
cache_key = symbol.upper()
instrument = cast(Instrument | None, self._opt_instrument_cache.get(cache_key))
instrument = self._opt_instrument_cache.get(cache_key)
if instrument:
self.cache_hit_count += 1
return instrument
Expand Down
14 changes: 8 additions & 6 deletions src/project_x_py/order_manager/tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -1364,18 +1364,20 @@ async def configure_memory_limits(
old_tracked = dict(self.tracked_orders)
old_status = dict(self.order_status_cache)

self.tracked_orders = TTLCache(
self.tracked_orders = TTLCache[str, dict[str, Any]](
maxsize=max_tracked_orders, ttl=self._order_ttl_seconds
)
self.order_status_cache = TTLCache(
self.order_status_cache = TTLCache[str, int](
maxsize=max_tracked_orders, ttl=self._order_ttl_seconds
)

# Restore data up to new limit
for key, value in list(old_tracked.items())[-max_tracked_orders:]:
self.tracked_orders[key] = value
for key, value in list(old_status.items())[-max_tracked_orders:]:
self.order_status_cache[key] = value
for order_id, order_data in list(old_tracked.items())[
-max_tracked_orders:
]:
self.tracked_orders[order_id] = order_data
for order_id, status in list(old_status.items())[-max_tracked_orders:]:
self.order_status_cache[order_id] = status

if order_ttl_seconds is not None:
self._order_ttl_seconds = order_ttl_seconds
Expand Down
11 changes: 10 additions & 1 deletion tests/order_manager/test_order_tracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -717,8 +717,17 @@ async def test_configure_memory_limits(self, mock_order_manager):
assert om._order_ttl_seconds == 1800
assert om._cleanup_interval == 150

# Should have kept most recent 3 orders
# Should have kept most recent 3 orders with typed cache values intact
assert isinstance(om.tracked_orders, TTLCache)
assert isinstance(om.order_status_cache, TTLCache)
assert om.tracked_orders.maxsize == 3
assert om.order_status_cache.maxsize == 3
assert len(om.tracked_orders) <= 3
assert len(om.order_status_cache) <= 3
assert all(
isinstance(order_data, dict) for order_data in om.tracked_orders.values()
)
assert all(isinstance(status, int) for status in om.order_status_cache.values())

def test_clear_order_tracking(self, mock_order_manager):
"""Test clearing all tracking data."""
Expand Down
8 changes: 4 additions & 4 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading