|
@@ -11,8 +11,10 @@ from . import repo
|
|
|
from .bitstamp import BitstampClient
|
|
from .bitstamp import BitstampClient
|
|
|
from .bitstamp_fx import load_eur_usd
|
|
from .bitstamp_fx import load_eur_usd
|
|
|
|
|
|
|
|
|
|
+BITSTAMP_BASE_URL = "https://www.bitstamp.net"
|
|
|
BALANCE_CACHE_TTL_SECONDS = 20
|
|
BALANCE_CACHE_TTL_SECONDS = 20
|
|
|
ACCOUNT_INFO_CACHE_TTL_SECONDS = 30
|
|
ACCOUNT_INFO_CACHE_TTL_SECONDS = 30
|
|
|
|
|
+FEES_CACHE_TTL_SECONDS = 10 * 60
|
|
|
STALE_CACHE_TTL_SECONDS = 10 * 60
|
|
STALE_CACHE_TTL_SECONDS = 10 * 60
|
|
|
_CACHE_LOCKS: dict[str, threading.Lock] = {}
|
|
_CACHE_LOCKS: dict[str, threading.Lock] = {}
|
|
|
_CACHE_LOCKS_GUARD = threading.Lock()
|
|
_CACHE_LOCKS_GUARD = threading.Lock()
|
|
@@ -49,6 +51,54 @@ def _stale_key(cache_key: str) -> str:
|
|
|
return f"{cache_key}:stale"
|
|
return f"{cache_key}:stale"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+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()}"
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _normalize_fee_entry(item: dict) -> dict:
|
|
|
|
|
+ fees = item.get("fees") or {}
|
|
|
|
|
+ return {
|
|
|
|
|
+ "currency_pair": item.get("currency_pair"),
|
|
|
|
|
+ "market": item.get("market"),
|
|
|
|
|
+ "fees": {
|
|
|
|
|
+ "maker": fees.get("maker"),
|
|
|
|
|
+ "taker": fees.get("taker"),
|
|
|
|
|
+ },
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def fetch_trading_fees(account_id: str, market_symbol: str | None = None) -> dict:
|
|
|
|
|
+ cache_key = _fee_cache_key(market_symbol)
|
|
|
|
|
+ cached = repo.cache_get(cache_key)
|
|
|
|
|
+ if cached is not None:
|
|
|
|
|
+ return cached
|
|
|
|
|
+
|
|
|
|
|
+ with _cache_lock(cache_key):
|
|
|
|
|
+ cached = repo.cache_get(cache_key)
|
|
|
|
|
+ if cached is not None:
|
|
|
|
|
+ return cached
|
|
|
|
|
+
|
|
|
|
|
+ try:
|
|
|
|
|
+ client = get_bitstamp_client(account_id)
|
|
|
|
|
+ if market_symbol:
|
|
|
|
|
+ payload = client.trading.fees_trading_market(market_symbol)
|
|
|
|
|
+ else:
|
|
|
|
|
+ payload = client.trading.fees_trading()
|
|
|
|
|
+ except Exception as exc:
|
|
|
|
|
+ _cache_error(cache_key, str(exc))
|
|
|
|
|
+ raise
|
|
|
|
|
+
|
|
|
|
|
+ if isinstance(payload, list):
|
|
|
|
|
+ result = {"source": "bitstamp", "market": market_symbol, "fees": [_normalize_fee_entry(item) for item in payload]}
|
|
|
|
|
+ elif isinstance(payload, dict):
|
|
|
|
|
+ result = {"source": "bitstamp", "market": market_symbol, **_normalize_fee_entry(payload)}
|
|
|
|
|
+ else:
|
|
|
|
|
+ result = {"source": "bitstamp", "market": market_symbol, "raw": payload}
|
|
|
|
|
+
|
|
|
|
|
+ repo.cache_put(cache_key, result, _ttl_from_env("BITSTAMP_FEES_CACHE_TTL_SECONDS", FEES_CACHE_TTL_SECONDS))
|
|
|
|
|
+ return result
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
def _require_client() -> None:
|
|
def _require_client() -> None:
|
|
|
if bitstamp is None:
|
|
if bitstamp is None:
|
|
|
raise RuntimeError("bitstamp-python-client dependency is not installed")
|
|
raise RuntimeError("bitstamp-python-client dependency is not installed")
|
|
@@ -210,3 +260,7 @@ def fetch_account_info(account_id: str) -> dict:
|
|
|
repo.cache_put(cache_key, result, _ttl_from_env("BITSTAMP_ACCOUNT_INFO_CACHE_TTL_SECONDS", ACCOUNT_INFO_CACHE_TTL_SECONDS))
|
|
repo.cache_put(cache_key, result, _ttl_from_env("BITSTAMP_ACCOUNT_INFO_CACHE_TTL_SECONDS", ACCOUNT_INFO_CACHE_TTL_SECONDS))
|
|
|
repo.cache_put(_stale_key(cache_key), result, STALE_CACHE_TTL_SECONDS)
|
|
repo.cache_put(_stale_key(cache_key), result, STALE_CACHE_TTL_SECONDS)
|
|
|
return result
|
|
return result
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def fetch_account_fees(account_id: str, market_symbol: str | None = None) -> dict:
|
|
|
|
|
+ return fetch_trading_fees(account_id, market_symbol)
|