import asyncio from fastapi import APIRouter, WebSocket, WebSocketDisconnect from app.bars.models import Timeframe router = APIRouter() def snapshot(runtime, tf: Timeframe) -> dict: return { "type": "snapshot", "tf": tf.value, "bars": [bar.to_dict() for bar in runtime.store.get(tf, 1000)], "levels": [], "clusters": [], "price": runtime.store.get(Timeframe.M1, 1)[-1].c if runtime.store.get(Timeframe.M1, 1) else None, } @router.websocket("/ws") async def websocket_endpoint(websocket: WebSocket): await websocket.accept() runtime = websocket.app.state.runtime queue: asyncio.Queue = asyncio.Queue(maxsize=100) runtime.subscribers.add(queue) tf = Timeframe.M1 await websocket.send_json(snapshot(runtime, tf)) async def receive(): nonlocal tf while True: message = await websocket.receive_json() if message.get("type") == "subscribe": tf = Timeframe(message.get("tf", "1m")) await websocket.send_json(snapshot(runtime, tf)) receiver = asyncio.create_task(receive()) try: while True: bar = await queue.get() if bar.tf is tf: await websocket.send_json({"type": "bar", "tf": tf.value, "bar": bar.to_dict()}) except (WebSocketDisconnect, asyncio.CancelledError): pass finally: receiver.cancel() runtime.subscribers.discard(queue)