feat: trading bot MVP — ICT Order Block + Liquidity Sweep strategy
Full-stack trading bot with: - FastAPI backend with ICT strategy (Order Block + Liquidity Sweep detection) - Backtester engine with rolling window, spread simulation, and performance metrics - Hybrid market data service (yfinance + TwelveData with rate limiting + SQLite cache) - Simulated exchange for paper trading - React/TypeScript frontend with TradingView lightweight-charts v5 - Live dashboard with candlestick chart, OHLC legend, trade markers - Backtest page with configurable parameters, equity curve, and trade table - WebSocket support for real-time updates - Bot runner with asyncio loop for automated trading Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
57
backend/app/api/websocket.py
Normal file
57
backend/app/api/websocket.py
Normal file
@@ -0,0 +1,57 @@
|
||||
"""
|
||||
WebSocket endpoint pour le streaming de données live.
|
||||
|
||||
Broadcast les événements du bot (ticks, nouveaux trades, etc.)
|
||||
vers tous les clients connectés.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
from typing import Set
|
||||
|
||||
from fastapi import WebSocket, WebSocketDisconnect
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ConnectionManager:
|
||||
def __init__(self) -> None:
|
||||
self._active: Set[WebSocket] = set()
|
||||
|
||||
async def connect(self, ws: WebSocket) -> None:
|
||||
await ws.accept()
|
||||
self._active.add(ws)
|
||||
logger.info("WS client connecté — %d total", len(self._active))
|
||||
|
||||
def disconnect(self, ws: WebSocket) -> None:
|
||||
self._active.discard(ws)
|
||||
logger.info("WS client déconnecté — %d restants", len(self._active))
|
||||
|
||||
async def broadcast(self, data: dict) -> None:
|
||||
if not self._active:
|
||||
return
|
||||
message = json.dumps(data, default=str)
|
||||
dead: Set[WebSocket] = set()
|
||||
for ws in list(self._active):
|
||||
try:
|
||||
await ws.send_text(message)
|
||||
except Exception:
|
||||
dead.add(ws)
|
||||
for ws in dead:
|
||||
self.disconnect(ws)
|
||||
|
||||
|
||||
manager = ConnectionManager()
|
||||
|
||||
|
||||
async def websocket_endpoint(ws: WebSocket) -> None:
|
||||
await manager.connect(ws)
|
||||
try:
|
||||
while True:
|
||||
# Garder la connexion ouverte, le client peut envoyer des pings
|
||||
data = await ws.receive_text()
|
||||
if data == "ping":
|
||||
await ws.send_text(json.dumps({"type": "pong"}))
|
||||
except WebSocketDisconnect:
|
||||
manager.disconnect(ws)
|
||||
Reference in New Issue
Block a user