| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465 |
- """
- Binance data provider.
- Used primarily for high-quality OHLCV data (klines endpoint).
- No API key required for public market data.
- """
- import httpx
- from config import BINANCE_BASE_URL, TIMEFRAME_TO_BINANCE
- from errors import SymbolNotFoundError, ProviderError, UnsupportedTimeframeError
- def _binance_symbol(symbol: str) -> str:
- sym = symbol.upper()
- if not sym.endswith("USDT"):
- sym = sym + "USDT"
- return sym
- async def fetch_price(symbol: str) -> dict:
- import time
- binance_sym = _binance_symbol(symbol)
- url = f"{BINANCE_BASE_URL}/ticker/price"
- params = {"symbol": binance_sym}
- async with httpx.AsyncClient(timeout=10.0) as client:
- try:
- resp = await client.get(url, params=params)
- if resp.status_code == 400:
- raise SymbolNotFoundError(f"Symbol not found on Binance: {symbol}")
- resp.raise_for_status()
- except httpx.HTTPStatusError as e:
- raise ProviderError(f"Binance HTTP error: {e.response.status_code}") from e
- except httpx.RequestError as e:
- raise ProviderError(f"Binance request failed: {e}") from e
- data = resp.json()
- return {"symbol": symbol.upper(), "price": float(data["price"]), "timestamp": int(time.time())}
- async def fetch_ohlcv(symbol: str, timeframe: str, limit: int = 100) -> dict:
- interval = TIMEFRAME_TO_BINANCE.get(timeframe)
- if not interval:
- raise UnsupportedTimeframeError(f"Unsupported timeframe: {timeframe}. Supported: {list(TIMEFRAME_TO_BINANCE.keys())}")
- binance_sym = _binance_symbol(symbol)
- url = f"{BINANCE_BASE_URL}/klines"
- params = {"symbol": binance_sym, "interval": interval, "limit": min(limit, 1000)}
- async with httpx.AsyncClient(timeout=15.0) as client:
- try:
- resp = await client.get(url, params=params)
- if resp.status_code == 400:
- raise SymbolNotFoundError(f"Symbol not found on Binance: {symbol}")
- resp.raise_for_status()
- except httpx.HTTPStatusError as e:
- raise ProviderError(f"Binance klines HTTP error: {e.response.status_code}") from e
- except httpx.RequestError as e:
- raise ProviderError(f"Binance klines request failed: {e}") from e
- raw = resp.json()
- if not raw:
- raise ProviderError(f"Binance returned empty klines for {symbol} {timeframe}")
- candles = [[int(row[0] / 1000), float(row[1]), float(row[2]), float(row[3]), float(row[4]), float(row[5])] for row in raw]
- return {"symbol": symbol.upper(), "timeframe": timeframe, "candles": candles}
|