Files

51 lines
1.8 KiB
Python

from __future__ import annotations
import asyncio
import json
import websockets
from typing import AsyncIterator
from datetime import datetime
from ..typing import Kline
import os
class BinanceWSProvider:
def __init__(self, base_url: str | None = None):
self.base_url = base_url or os.getenv("BINANCE_WS_URL", "wss://stream.binance.com:9443/ws")
async def stream_klines(self, symbol: str, timeframe: str) -> AsyncIterator[Kline]:
# Binance expects lowercase, no slash: BTCUSDT -> btcusdt
stream = f"{symbol.lower()}@kline_{timeframe}"
url = self.base_url.rstrip("/").replace("/ws", "/stream") + f"?streams={stream}"
async for msg in self._ws_loop(url):
try:
data = json.loads(msg)
k = data.get("data", {}).get("k", {})
if not k:
continue
yield Kline(
symbol=symbol,
timeframe=timeframe,
open_time=datetime.fromtimestamp(k["t"] / 1000.0),
close_time=datetime.fromtimestamp(k["T"] / 1000.0),
open=float(k["o"]),
high=float(k["h"]),
low=float(k["l"]),
close=float(k["c"]),
volume=float(k.get("v", 0.0)),
is_closed=bool(k.get("x", False)),
source="binance",
)
except Exception:
continue
async def _ws_loop(self, url: str):
while True:
try:
async with websockets.connect(url, ping_interval=20, ping_timeout=20) as ws:
async for message in ws:
yield message
except Exception:
await asyncio.sleep(2)