binance.py 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. """
  2. Binance data provider.
  3. Used primarily for high-quality OHLCV data (klines endpoint).
  4. No API key required for public market data.
  5. """
  6. import httpx
  7. from config import BINANCE_BASE_URL, TIMEFRAME_TO_BINANCE
  8. from errors import SymbolNotFoundError, ProviderError, UnsupportedTimeframeError
  9. def _binance_symbol(symbol: str) -> str:
  10. sym = symbol.upper()
  11. if not sym.endswith("USDT"):
  12. sym = sym + "USDT"
  13. return sym
  14. async def fetch_price(symbol: str) -> dict:
  15. import time
  16. binance_sym = _binance_symbol(symbol)
  17. url = f"{BINANCE_BASE_URL}/ticker/price"
  18. params = {"symbol": binance_sym}
  19. async with httpx.AsyncClient(timeout=10.0) as client:
  20. try:
  21. resp = await client.get(url, params=params)
  22. if resp.status_code == 400:
  23. raise SymbolNotFoundError(f"Symbol not found on Binance: {symbol}")
  24. resp.raise_for_status()
  25. except httpx.HTTPStatusError as e:
  26. raise ProviderError(f"Binance HTTP error: {e.response.status_code}") from e
  27. except httpx.RequestError as e:
  28. raise ProviderError(f"Binance request failed: {e}") from e
  29. data = resp.json()
  30. return {"symbol": symbol.upper(), "price": float(data["price"]), "timestamp": int(time.time())}
  31. async def fetch_ohlcv(symbol: str, timeframe: str, limit: int = 100) -> dict:
  32. interval = TIMEFRAME_TO_BINANCE.get(timeframe)
  33. if not interval:
  34. raise UnsupportedTimeframeError(f"Unsupported timeframe: {timeframe}. Supported: {list(TIMEFRAME_TO_BINANCE.keys())}")
  35. binance_sym = _binance_symbol(symbol)
  36. url = f"{BINANCE_BASE_URL}/klines"
  37. params = {"symbol": binance_sym, "interval": interval, "limit": min(limit, 1000)}
  38. async with httpx.AsyncClient(timeout=15.0) as client:
  39. try:
  40. resp = await client.get(url, params=params)
  41. if resp.status_code == 400:
  42. raise SymbolNotFoundError(f"Symbol not found on Binance: {symbol}")
  43. resp.raise_for_status()
  44. except httpx.HTTPStatusError as e:
  45. raise ProviderError(f"Binance klines HTTP error: {e.response.status_code}") from e
  46. except httpx.RequestError as e:
  47. raise ProviderError(f"Binance klines request failed: {e}") from e
  48. raw = resp.json()
  49. if not raw:
  50. raise ProviderError(f"Binance returned empty klines for {symbol} {timeframe}")
  51. candles = [[int(row[0] / 1000), float(row[1]), float(row[2]), float(row[3]), float(row[4]), float(row[5])] for row in raw]
  52. return {"symbol": symbol.upper(), "timeframe": timeframe, "candles": candles}