Your Name
feat: UI improvements and error suppression - Enhanced dashboard and market pages with improved header buttons, logo, and currency symbol display - Stopped animated ticker - Removed pie chart legends - Added error suppressor for external service errors (SSE, Permissions-Policy warnings) - Improved header button prominence and icon appearance - Enhanced logo with glow effects and better design - Fixed currency symbol visibility in market tables
8b7b267
| #!/usr/bin/env python3 | |
| """ | |
| Trading & Backtesting API Router | |
| Smart exchange integration for trading and backtesting | |
| Binance & KuCoin with advanced features | |
| """ | |
| from fastapi import APIRouter, Query, HTTPException | |
| from typing import Optional | |
| import logging | |
| from backend.services.trading_backtesting_service import ( | |
| get_trading_service, | |
| get_backtesting_service | |
| ) | |
| logger = logging.getLogger(__name__) | |
| router = APIRouter(prefix="/api/trading", tags=["Trading & Backtesting"]) | |
| # ========== Trading Endpoints ========== | |
| async def get_trading_price( | |
| symbol: str, | |
| exchange: str = Query("binance", description="Exchange (binance/kucoin)"), | |
| enable_proxy: bool = Query(False, description="Enable proxy for geo-restricted access"), | |
| use_fallback: bool = Query(True, description="Use multi-source fallback if primary fails") | |
| ): | |
| """ | |
| Get current trading price from smart exchange client | |
| **Features:** | |
| - Smart routing with geo-block bypass | |
| - DNS over HTTPS (DoH) | |
| - Multi-layer proxies (optional) | |
| - Auto-fallback to multi-source system | |
| **Exchanges:** | |
| - `binance`: Symbol format: BTCUSDT, ETHUSDT, etc. | |
| - `kucoin`: Symbol format: BTC-USDT, ETH-USDT, etc. | |
| **Example:** | |
| ``` | |
| GET /api/trading/price/BTCUSDT?exchange=binance | |
| GET /api/trading/price/BTC-USDT?exchange=kucoin&enable_proxy=true | |
| ``` | |
| """ | |
| try: | |
| service = get_trading_service(enable_proxy=enable_proxy) | |
| result = await service.get_trading_price( | |
| symbol=symbol, | |
| exchange=exchange, | |
| use_fallback=use_fallback | |
| ) | |
| return result | |
| except Exception as e: | |
| logger.error(f"Failed to get price for {symbol}: {e}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def get_trading_ohlcv( | |
| symbol: str, | |
| timeframe: str = Query("1h", description="Timeframe (1m, 5m, 15m, 1h, 4h, 1d, etc.)"), | |
| limit: int = Query(100, ge=1, le=1000, description="Number of candles"), | |
| exchange: str = Query("binance", description="Exchange (binance/kucoin)"), | |
| start_time: Optional[int] = Query(None, description="Start timestamp (milliseconds)"), | |
| end_time: Optional[int] = Query(None, description="End timestamp (milliseconds)"), | |
| enable_proxy: bool = Query(False, description="Enable proxy") | |
| ): | |
| """ | |
| Get OHLCV candlestick data for trading/backtesting | |
| **Features:** | |
| - Up to 1000 candles per request | |
| - Smart client with geo-block bypass | |
| - Historical data with timestamps | |
| **Timeframes:** | |
| - Binance: 1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M | |
| - KuCoin: 1min, 3min, 5min, 15min, 30min, 1hour, 2hour, 4hour, 6hour, 8hour, 12hour, 1day, 1week | |
| **Response:** | |
| ```json | |
| { | |
| "success": true, | |
| "exchange": "binance", | |
| "symbol": "BTCUSDT", | |
| "timeframe": "1h", | |
| "candles": [ | |
| { | |
| "timestamp": 1733491200000, | |
| "open": 43200.00, | |
| "high": 43300.00, | |
| "low": 43150.00, | |
| "close": 43250.50, | |
| "volume": 1234.56 | |
| } | |
| ], | |
| "count": 100 | |
| } | |
| ``` | |
| """ | |
| try: | |
| service = get_trading_service(enable_proxy=enable_proxy) | |
| result = await service.get_trading_ohlcv( | |
| symbol=symbol, | |
| timeframe=timeframe, | |
| limit=limit, | |
| exchange=exchange, | |
| start_time=start_time, | |
| end_time=end_time | |
| ) | |
| return result | |
| except Exception as e: | |
| logger.error(f"Failed to get OHLCV for {symbol}: {e}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def get_orderbook( | |
| symbol: str, | |
| exchange: str = Query("binance", description="Exchange (binance/kucoin)"), | |
| limit: int = Query(100, ge=1, le=5000, description="Depth limit"), | |
| enable_proxy: bool = Query(False, description="Enable proxy") | |
| ): | |
| """ | |
| Get order book for trading | |
| **Features:** | |
| - Real-time bid/ask prices | |
| - Market depth analysis | |
| - Up to 5000 levels (Binance) | |
| **Response:** | |
| ```json | |
| { | |
| "success": true, | |
| "exchange": "binance", | |
| "symbol": "BTCUSDT", | |
| "bids": [ | |
| [43250.50, 1.234], | |
| [43249.00, 0.567] | |
| ], | |
| "asks": [ | |
| [43251.00, 0.890], | |
| [43252.50, 1.456] | |
| ] | |
| } | |
| ``` | |
| """ | |
| try: | |
| service = get_trading_service(enable_proxy=enable_proxy) | |
| result = await service.get_orderbook( | |
| symbol=symbol, | |
| exchange=exchange, | |
| limit=limit | |
| ) | |
| return result | |
| except Exception as e: | |
| logger.error(f"Failed to get orderbook for {symbol}: {e}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def get_24h_stats( | |
| symbol: str, | |
| exchange: str = Query("binance", description="Exchange (binance/kucoin)"), | |
| enable_proxy: bool = Query(False, description="Enable proxy") | |
| ): | |
| """ | |
| Get 24-hour trading statistics | |
| **Metrics:** | |
| - Current price | |
| - 24h change (amount and percentage) | |
| - 24h high/low | |
| - 24h volume | |
| - Number of trades (Binance only) | |
| **Example:** | |
| ``` | |
| GET /api/trading/stats/24h/BTCUSDT?exchange=binance | |
| ``` | |
| **Response:** | |
| ```json | |
| { | |
| "success": true, | |
| "exchange": "binance", | |
| "symbol": "BTCUSDT", | |
| "price": 43250.50, | |
| "change": 850.25, | |
| "change_percent": 2.01, | |
| "high": 43500.00, | |
| "low": 42800.00, | |
| "volume": 12345.67, | |
| "trades": 987654 | |
| } | |
| ``` | |
| """ | |
| try: | |
| service = get_trading_service(enable_proxy=enable_proxy) | |
| result = await service.get_24h_stats( | |
| symbol=symbol, | |
| exchange=exchange | |
| ) | |
| return result | |
| except Exception as e: | |
| logger.error(f"Failed to get 24h stats for {symbol}: {e}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| # ========== Backtesting Endpoints ========== | |
| async def fetch_historical_data( | |
| symbol: str, | |
| timeframe: str = Query("1h", description="Timeframe"), | |
| days: int = Query(30, ge=1, le=365, description="Days of historical data"), | |
| exchange: str = Query("binance", description="Exchange (binance/kucoin)"), | |
| enable_proxy: bool = Query(False, description="Enable proxy") | |
| ): | |
| """ | |
| Fetch historical data for backtesting | |
| **Features:** | |
| - Automatic chunking for large datasets | |
| - Up to 365 days of historical data | |
| - Returns DataFrame-ready format | |
| **Note:** This may take some time for large datasets due to API rate limits. | |
| **Example:** | |
| ``` | |
| GET /api/trading/backtest/historical/BTCUSDT?timeframe=1h&days=30 | |
| ``` | |
| **Response:** | |
| ```json | |
| { | |
| "success": true, | |
| "symbol": "BTCUSDT", | |
| "exchange": "binance", | |
| "timeframe": "1h", | |
| "days": 30, | |
| "candles": [...], | |
| "count": 720 | |
| } | |
| ``` | |
| """ | |
| try: | |
| service = get_trading_service(enable_proxy=enable_proxy) | |
| backtest_service = get_backtesting_service() | |
| df = await backtest_service.fetch_historical_data( | |
| symbol=symbol, | |
| timeframe=timeframe, | |
| days=days, | |
| exchange=exchange | |
| ) | |
| if df.empty: | |
| return { | |
| "success": False, | |
| "error": "No historical data available", | |
| "symbol": symbol, | |
| "exchange": exchange | |
| } | |
| # Convert DataFrame to dict | |
| df_reset = df.reset_index() | |
| candles = df_reset.to_dict('records') | |
| return { | |
| "success": True, | |
| "symbol": symbol, | |
| "exchange": exchange, | |
| "timeframe": timeframe, | |
| "days": days, | |
| "candles": candles, | |
| "count": len(candles) | |
| } | |
| except Exception as e: | |
| logger.error(f"Failed to fetch historical data for {symbol}: {e}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def run_backtest( | |
| symbol: str, | |
| strategy: str = Query(..., description="Strategy name (sma_crossover, rsi, macd)"), | |
| timeframe: str = Query("1h", description="Timeframe"), | |
| days: int = Query(30, ge=1, le=365, description="Historical data period"), | |
| exchange: str = Query("binance", description="Exchange (binance/kucoin)"), | |
| initial_capital: float = Query(10000.0, ge=100, description="Initial capital"), | |
| enable_proxy: bool = Query(False, description="Enable proxy") | |
| ): | |
| """ | |
| Run backtesting with a trading strategy | |
| **Available Strategies:** | |
| 1. **sma_crossover**: Simple Moving Average Crossover | |
| - Buy when fast SMA (10) crosses above slow SMA (30) | |
| - Sell when fast SMA crosses below slow SMA | |
| 2. **rsi**: Relative Strength Index | |
| - Buy when RSI < 30 (oversold) | |
| - Sell when RSI > 70 (overbought) | |
| 3. **macd**: Moving Average Convergence Divergence | |
| - Buy when MACD crosses above signal line | |
| - Sell when MACD crosses below signal line | |
| **Example:** | |
| ``` | |
| GET /api/trading/backtest/run/BTCUSDT?strategy=sma_crossover&days=30&initial_capital=10000 | |
| ``` | |
| **Response:** | |
| ```json | |
| { | |
| "success": true, | |
| "symbol": "BTCUSDT", | |
| "exchange": "binance", | |
| "strategy": "sma_crossover", | |
| "timeframe": "1h", | |
| "days": 30, | |
| "initial_capital": 10000.0, | |
| "final_capital": 10567.89, | |
| "profit": 567.89, | |
| "total_return": 5.68, | |
| "trades": 12, | |
| "candles_analyzed": 720 | |
| } | |
| ``` | |
| """ | |
| try: | |
| backtest_service = get_backtesting_service() | |
| result = await backtest_service.run_backtest( | |
| symbol=symbol, | |
| strategy=strategy, | |
| timeframe=timeframe, | |
| days=days, | |
| exchange=exchange, | |
| initial_capital=initial_capital | |
| ) | |
| return result | |
| except Exception as e: | |
| logger.error(f"Failed to run backtest for {symbol}: {e}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def get_exchanges_status( | |
| enable_proxy: bool = Query(False, description="Enable proxy") | |
| ): | |
| """ | |
| Get status of smart exchange clients | |
| **Features:** | |
| - Test connection to Binance and KuCoin | |
| - Show proxy status | |
| - Show DoH status | |
| **Response:** | |
| ```json | |
| { | |
| "success": true, | |
| "exchanges": { | |
| "binance": { | |
| "available": true, | |
| "endpoints": 5, | |
| "proxy_enabled": false, | |
| "doh_enabled": true | |
| }, | |
| "kucoin": { | |
| "available": true, | |
| "endpoints": 2, | |
| "proxy_enabled": false, | |
| "doh_enabled": true | |
| } | |
| } | |
| } | |
| ``` | |
| """ | |
| try: | |
| service = get_trading_service(enable_proxy=enable_proxy) | |
| # Test Binance | |
| binance_available = False | |
| try: | |
| await service.binance.ping() | |
| binance_available = True | |
| except: | |
| pass | |
| # Test KuCoin | |
| kucoin_available = False | |
| try: | |
| await service.kucoin.get_ticker_price("BTC-USDT") | |
| kucoin_available = True | |
| except: | |
| pass | |
| return { | |
| "success": True, | |
| "exchanges": { | |
| "binance": { | |
| "available": binance_available, | |
| "endpoints": len(service.binance.endpoints), | |
| "current_endpoint": service.binance.endpoints[service.binance.current_endpoint_index], | |
| "proxy_enabled": service.binance.enable_proxy, | |
| "doh_enabled": service.binance.enable_doh | |
| }, | |
| "kucoin": { | |
| "available": kucoin_available, | |
| "endpoints": len(service.kucoin.endpoints), | |
| "current_endpoint": service.kucoin.endpoints[service.kucoin.current_endpoint_index], | |
| "proxy_enabled": service.kucoin.enable_proxy, | |
| "doh_enabled": service.kucoin.enable_doh | |
| } | |
| }, | |
| "timestamp": "2025-12-06T00:00:00Z" | |
| } | |
| except Exception as e: | |
| logger.error(f"Failed to get exchanges status: {e}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| __all__ = ["router"] | |