Selaa lähdekoodia

Tighten Bitstamp balance caching

Lukas Goldschmidt 1 kuukausi sitten
vanhempi
commit
9354576fda
3 muutettua tiedostoa jossa 46 lisäystä ja 4 poistoa
  1. 6 0
      src/exec_mcp/repo.py
  2. 30 3
      src/exec_mcp/services_bitstamp.py
  3. 10 1
      src/exec_mcp/services_orders.py

+ 6 - 0
src/exec_mcp/repo.py

@@ -175,6 +175,12 @@ def cache_put(cache_key: str, payload: dict, ttl_seconds: int) -> None:
         conn.commit()
 
 
+def cache_delete(cache_key: str) -> None:
+    with get_connection() as conn:
+        conn.execute("DELETE FROM api_cache WHERE cache_key = ?", (cache_key,))
+        conn.commit()
+
+
 def get_latest_price(market: str) -> float | None:
     with get_connection() as conn:
         row = conn.execute("SELECT price FROM bitstamp_live_prices WHERE market = ?", (market.lower(),)).fetchone()

+ 30 - 3
src/exec_mcp/services_bitstamp.py

@@ -12,10 +12,11 @@ from .bitstamp import BitstampClient
 from .bitstamp_fx import load_eur_usd
 
 BITSTAMP_BASE_URL = "https://www.bitstamp.net"
-BALANCE_CACHE_TTL_SECONDS = 20
-ACCOUNT_INFO_CACHE_TTL_SECONDS = 30
+# Live trading reads should refresh quickly, stale fallback is only a short safety net.
+BALANCE_CACHE_TTL_SECONDS = 5
+ACCOUNT_INFO_CACHE_TTL_SECONDS = 5
 FEES_CACHE_TTL_SECONDS = 10 * 60
-STALE_CACHE_TTL_SECONDS = 10 * 60
+STALE_CACHE_TTL_SECONDS = 30
 _CACHE_LOCKS: dict[str, threading.Lock] = {}
 _CACHE_LOCKS_GUARD = threading.Lock()
 _BITSTAMP_CLIENTS: dict[str, BitstampClient] = {}
@@ -51,6 +52,32 @@ def _stale_key(cache_key: str) -> str:
     return f"{cache_key}:stale"
 
 
+def invalidate_account_cache(account_id: str) -> None:
+    for key in (
+        f"bitstamp:account_balance:{account_id}",
+        f"bitstamp:account_balance:{account_id}:stale",
+        f"bitstamp:account_info:{account_id}",
+        f"bitstamp:account_info:{account_id}:stale",
+    ):
+        repo.cache_delete(key)
+
+
+def invalidate_balance_cache(account_id: str) -> None:
+    for key in (
+        f"bitstamp:account_balance:{account_id}",
+        f"bitstamp:account_balance:{account_id}:stale",
+    ):
+        repo.cache_delete(key)
+
+
+def invalidate_account_info_cache(account_id: str) -> None:
+    for key in (
+        f"bitstamp:account_info:{account_id}",
+        f"bitstamp:account_info:{account_id}:stale",
+    ):
+        repo.cache_delete(key)
+
+
 def _fee_cache_key(market_symbol: str | None = None) -> str:
     return "bitstamp:fees:all" if market_symbol is None else f"bitstamp:fees:{market_symbol.lower()}"
 

+ 10 - 1
src/exec_mcp/services_orders.py

@@ -11,7 +11,7 @@ from uuid import uuid4
 from fastapi import HTTPException
 
 from .bitstamp import BitstampClient, BitstampError
-from .services_bitstamp import get_bitstamp_client, clear_bitstamp_trading_client
+from .services_bitstamp import get_bitstamp_client, clear_bitstamp_trading_client, invalidate_account_cache
 from .bitstamp_metadata import load_market_by_symbol
 from .storage import get_connection
 
@@ -174,6 +174,8 @@ def place_order(*, account_id: str, market: str, side: str, order_type: str, amo
         )
         conn.commit()
 
+    invalidate_account_cache(account_id)
+
     return {"ok": True, "bitstamp_order_id": bitstamp_order_id, "record_id": record_id, "status": str(result.get("status", "open")), "raw": result}
 
 
@@ -218,6 +220,7 @@ def get_open_orders(*, account_id: str, client_id: str | None = None) -> dict:
             status = _normalize_status(result.get("status", "unknown"))
             if status not in OPEN_ORDER_STATUSES:
                 _set_local_order_status(bitstamp_order_id=str(bitstamp_order_id), status=status)
+                invalidate_account_cache(account_id)
                 continue
             order["status"] = status
             order["raw_json"] = json.dumps(result)
@@ -226,6 +229,7 @@ def get_open_orders(*, account_id: str, client_id: str | None = None) -> dict:
             msg = str(exc)
             if "not found" in msg.lower():
                 _set_local_order_status(bitstamp_order_id=str(bitstamp_order_id), status="missing")
+                invalidate_account_cache(account_id)
                 continue
             # Best effort: keep the local record when the exchange cannot be queried.
             orders.append(order)
@@ -258,6 +262,7 @@ def cancel_all_orders(*, account_id: str, client_id: str | None = None) -> dict:
                 break
             if "not found" in detail.lower():
                 _set_local_order_status(bitstamp_order_id=bitstamp_order_id, status="missing")
+                invalidate_account_cache(account_id)
                 results.append({"ok": False, "order_id": bitstamp_order_id, "error": detail, "status": "missing"})
                 continue
             raise
@@ -284,6 +289,8 @@ def query_order(*, account_id: str, order_id, client_order_id: str | None = None
         )
         conn.commit()
 
+    invalidate_account_cache(account_id)
+
     return {"ok": True, "order_id": order_id, "raw": result}
 
 
@@ -309,4 +316,6 @@ def cancel_order(*, account_id: str, order_id) -> dict:
         )
         conn.commit()
 
+    invalidate_account_cache(account_id)
+
     return {"ok": bool(result), "order_id": order_id, "raw": result}