|
|
|
|
|
""" |
|
|
Futures Trading API Router |
|
|
=========================== |
|
|
API endpoints for futures trading operations |
|
|
""" |
|
|
|
|
|
from fastapi import APIRouter, HTTPException, Depends, Body, Path, Query |
|
|
from fastapi.responses import JSONResponse |
|
|
from typing import Optional, List, Dict, Any |
|
|
from pydantic import BaseModel, Field |
|
|
from sqlalchemy.orm import Session |
|
|
import logging |
|
|
|
|
|
from backend.services.futures_trading_service import FuturesTradingService |
|
|
from database.db_manager import db_manager |
|
|
|
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
router = APIRouter( |
|
|
prefix="/api/futures", |
|
|
tags=["Futures Trading"] |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OrderRequest(BaseModel): |
|
|
"""Request model for creating an order.""" |
|
|
symbol: str = Field(..., description="Trading pair (e.g., BTC/USDT)") |
|
|
side: str = Field(..., description="Order side: 'buy' or 'sell'") |
|
|
order_type: str = Field(..., description="Order type: 'market', 'limit', 'stop', 'stop_limit'") |
|
|
quantity: float = Field(..., gt=0, description="Order quantity") |
|
|
price: Optional[float] = Field(None, gt=0, description="Limit price (required for limit orders)") |
|
|
stop_price: Optional[float] = Field(None, gt=0, description="Stop price (required for stop orders)") |
|
|
exchange: str = Field("demo", description="Exchange name (default: 'demo')") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_db() -> Session: |
|
|
"""Get database session.""" |
|
|
db = db_manager.SessionLocal() |
|
|
try: |
|
|
yield db |
|
|
finally: |
|
|
db.close() |
|
|
|
|
|
|
|
|
def get_futures_service(db: Session = Depends(get_db)) -> FuturesTradingService: |
|
|
"""Get futures trading service instance.""" |
|
|
return FuturesTradingService(db) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/order") |
|
|
async def execute_order( |
|
|
order_request: OrderRequest, |
|
|
service: FuturesTradingService = Depends(get_futures_service) |
|
|
) -> JSONResponse: |
|
|
""" |
|
|
Execute a futures trading order. |
|
|
|
|
|
Creates and processes a new futures order. For market orders, execution is immediate. |
|
|
For limit and stop orders, the order is placed in the order book. |
|
|
|
|
|
Args: |
|
|
order_request: Order details |
|
|
service: Futures trading service instance |
|
|
|
|
|
Returns: |
|
|
JSON response with order details |
|
|
""" |
|
|
try: |
|
|
order = service.create_order( |
|
|
symbol=order_request.symbol, |
|
|
side=order_request.side, |
|
|
order_type=order_request.order_type, |
|
|
quantity=order_request.quantity, |
|
|
price=order_request.price, |
|
|
stop_price=order_request.stop_price, |
|
|
exchange=order_request.exchange |
|
|
) |
|
|
|
|
|
return JSONResponse( |
|
|
status_code=201, |
|
|
content={ |
|
|
"success": True, |
|
|
"message": "Order created successfully", |
|
|
"data": order |
|
|
} |
|
|
) |
|
|
|
|
|
except ValueError as e: |
|
|
raise HTTPException(status_code=400, detail=str(e)) |
|
|
except Exception as e: |
|
|
logger.error(f"Error executing order: {e}", exc_info=True) |
|
|
raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}") |
|
|
|
|
|
|
|
|
@router.get("/positions") |
|
|
async def get_positions( |
|
|
symbol: Optional[str] = Query(None, description="Filter by trading pair"), |
|
|
is_open: Optional[bool] = Query(True, description="Filter by open status"), |
|
|
service: FuturesTradingService = Depends(get_futures_service) |
|
|
) -> JSONResponse: |
|
|
""" |
|
|
Retrieve open futures positions. |
|
|
|
|
|
Returns all open positions, optionally filtered by symbol. |
|
|
|
|
|
Args: |
|
|
symbol: Optional trading pair filter |
|
|
is_open: Filter by open status (default: True) |
|
|
service: Futures trading service instance |
|
|
|
|
|
Returns: |
|
|
JSON response with list of positions |
|
|
""" |
|
|
try: |
|
|
positions = service.get_positions(symbol=symbol, is_open=is_open) |
|
|
|
|
|
return JSONResponse( |
|
|
status_code=200, |
|
|
content={ |
|
|
"success": True, |
|
|
"count": len(positions), |
|
|
"data": positions |
|
|
} |
|
|
) |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"Error retrieving positions: {e}", exc_info=True) |
|
|
raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}") |
|
|
|
|
|
|
|
|
@router.get("/orders") |
|
|
async def list_orders( |
|
|
symbol: Optional[str] = Query(None, description="Filter by trading pair"), |
|
|
status: Optional[str] = Query(None, description="Filter by order status"), |
|
|
limit: int = Query(100, ge=1, le=1000, description="Maximum number of orders to return"), |
|
|
service: FuturesTradingService = Depends(get_futures_service) |
|
|
) -> JSONResponse: |
|
|
""" |
|
|
List all trading orders. |
|
|
|
|
|
Returns all orders, optionally filtered by symbol and status. |
|
|
|
|
|
Args: |
|
|
symbol: Optional trading pair filter |
|
|
status: Optional order status filter |
|
|
limit: Maximum number of orders to return |
|
|
service: Futures trading service instance |
|
|
|
|
|
Returns: |
|
|
JSON response with list of orders |
|
|
""" |
|
|
try: |
|
|
orders = service.get_orders(symbol=symbol, status=status, limit=limit) |
|
|
|
|
|
return JSONResponse( |
|
|
status_code=200, |
|
|
content={ |
|
|
"success": True, |
|
|
"count": len(orders), |
|
|
"data": orders |
|
|
} |
|
|
) |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"Error retrieving orders: {e}", exc_info=True) |
|
|
raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}") |
|
|
|
|
|
|
|
|
@router.delete("/order/{order_id}") |
|
|
async def cancel_order( |
|
|
order_id: str = Path(..., description="Order ID to cancel"), |
|
|
service: FuturesTradingService = Depends(get_futures_service) |
|
|
) -> JSONResponse: |
|
|
""" |
|
|
Cancel a specific order. |
|
|
|
|
|
Cancels an open or pending order by ID. |
|
|
|
|
|
Args: |
|
|
order_id: The order ID to cancel |
|
|
service: Futures trading service instance |
|
|
|
|
|
Returns: |
|
|
JSON response with cancelled order details |
|
|
""" |
|
|
try: |
|
|
order = service.cancel_order(order_id) |
|
|
|
|
|
return JSONResponse( |
|
|
status_code=200, |
|
|
content={ |
|
|
"success": True, |
|
|
"message": "Order cancelled successfully", |
|
|
"data": order |
|
|
} |
|
|
) |
|
|
|
|
|
except ValueError as e: |
|
|
raise HTTPException(status_code=404, detail=str(e)) |
|
|
except Exception as e: |
|
|
logger.error(f"Error cancelling order: {e}", exc_info=True) |
|
|
raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}") |
|
|
|
|
|
|