|
@@ -52,29 +52,9 @@ For `order_records` and `account_balance_snapshots`, consider a configurable ret
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
-## Fix 6 — Price lookup caching in `crypto_client.py`
|
|
|
|
|
|
|
+## Note — Price lookups must remain uncached
|
|
|
|
|
|
|
|
-**Problem**: `_price_for_asset` calls `get_crypto_price` which calls `get_price_sync`, which creates a `ThreadPoolExecutor` + `asyncio.run()` + MCP SSE connection **per asset, per call**. A balance with 10 assets triggers 10 separate SSE connections. Now that this runs in the background (Fix 2), it won't block the WS loop, but it's still very wasteful.
|
|
|
|
|
-
|
|
|
|
|
-**Fix**: Add a short-TTL in-memory price cache (e.g., 5 seconds) in `crypto_client.py`:
|
|
|
|
|
-```python
|
|
|
|
|
-_PRICE_CACHE: dict[str, tuple[float, float]] = {} # symbol -> (price, expires_at)
|
|
|
|
|
-_PRICE_CACHE_TTL = 5.0
|
|
|
|
|
-
|
|
|
|
|
-def get_price_sync(symbol: str, base_url: str | None = None) -> dict[str, Any]:
|
|
|
|
|
- now = time.monotonic()
|
|
|
|
|
- cached = _PRICE_CACHE.get(symbol)
|
|
|
|
|
- if cached and cached[1] > now:
|
|
|
|
|
- return {"symbol": symbol.upper(), "price": cached[0]}
|
|
|
|
|
- # ... existing fetch logic ...
|
|
|
|
|
- if "price" in result:
|
|
|
|
|
- _PRICE_CACHE[symbol] = (float(result["price"]), now + _PRICE_CACHE_TTL)
|
|
|
|
|
- return result
|
|
|
|
|
-```
|
|
|
|
|
-
|
|
|
|
|
-This caps price lookups to at most 1 per 5 seconds per symbol, regardless of how many balance snapshots fire. Freshness is preserved (5s TTL is short enough for downstream services that poll balance info).
|
|
|
|
|
-
|
|
|
|
|
-**Location**: `crypto_client.py`, `_price_for_asset` in `services_bitstamp.py`.
|
|
|
|
|
|
|
+Price lookups via `crypto_client.py` must always return fresh data. Caching is handled upstream. Do not add a local TTL cache here.
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|