Datasourceforcryptocurrency-2 / backend /routers /trading_backtesting_api.py
Really-amin's picture
Upload 1044 files
c17f1e9 verified
#!/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 ==========
@router.get("/price/{symbol}")
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))
@router.get("/ohlcv/{symbol}")
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))
@router.get("/orderbook/{symbol}")
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))
@router.get("/stats/24h/{symbol}")
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 ==========
@router.get("/backtest/historical/{symbol}")
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))
@router.get("/backtest/run/{symbol}")
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))
@router.get("/exchanges/status")
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"]