""" Backtesting API Endpoints. This module provides API endpoints for running backtesting on historical match data and exporting results. """ import logging from datetime import datetime from typing import List, Optional from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session from app.database import get_db from app.services.backtesting_service import BacktestingService from app.schemas.backtesting import ( BacktestingRequest, BacktestingResponse, BacktestingErrorResponse ) logger = logging.getLogger(__name__) router = APIRouter(prefix="/backtesting", tags=["backtesting"]) @router.post("/run", response_model=BacktestingResponse, responses={400: {"model": BacktestingErrorResponse}}) def run_backtesting( request: BacktestingRequest, db: Session = Depends(get_db) ): """ Run backtesting on historical matches. Analyzes historical match predictions against actual results to calculate accuracy metrics and validate prediction system performance. - **leagues**: Optional list of leagues to filter by (e.g., ['Ligue 1', 'Premier League']) - **start_date**: Optional start date for filtering matches (ISO 8601 format) - **end_date**: Optional end date for filtering matches (ISO 8601 format) - **export_format**: Optional export format ('json', 'csv', 'html') Returns: Comprehensive backtesting report including: - Total matches analyzed - Correct/incorrect predictions - Overall accuracy percentage - Validation status (VALIDATED, REVISION_REQUIRED, BELOW_TARGET) - Detailed results per match - Metrics breakdown by league Example: ```json { "leagues": ["Ligue 1", "Premier League"], "start_date": "2025-01-01T00:00:00Z", "end_date": "2025-12-31T23:59:59Z", "export_format": "html" } ``` Raises: 400: If no matches found with specified filters 500: If internal server error occurs """ try: logger.info("Backtesting request received") # Parse dates if provided start_date = None end_date = None if request.start_date: try: start_date = datetime.fromisoformat(request.start_date.replace('Z', '+00:00')) except ValueError as e: raise HTTPException( status_code=400, detail=f"Invalid start_date format. Use ISO 8601 format: {str(e)}" ) if request.end_date: try: end_date = datetime.fromisoformat(request.end_date.replace('Z', '+00:00')) except ValueError as e: raise HTTPException( status_code=400, detail=f"Invalid end_date format. Use ISO 8601 format: {str(e)}" ) # Initialize backtesting service backtesting_service = BacktestingService(db) # Run backtesting result = backtesting_service.run_backtesting( leagues=request.leagues, start_date=start_date, end_date=end_date ) logger.info( f"Backtesting completed: {result['total_matches']} matches, " f"{result['accuracy']:.2f}% accuracy" ) # Export results if format specified if request.export_format: try: exported = backtesting_service.export_results(result, request.export_format) result['export'] = { 'format': request.export_format, 'data': exported } except ValueError as e: logger.warning(f"Export failed: {str(e)}") # Continue without export, just warn # Wrap response with metadata response = { 'data': result, 'meta': { 'timestamp': result.get('timestamp'), 'version': 'v1' } } return response except ValueError as e: logger.error(f"Backtesting value error: {str(e)}") raise HTTPException(status_code=400, detail=str(e)) except Exception as e: logger.error(f"Backtesting error: {str(e)}", exc_info=True) raise HTTPException( status_code=500, detail=f"Internal server error during backtesting: {str(e)}" ) @router.get("/status") def get_backtesting_status(): """ Get backtesting service status. Returns information about the backtesting system configuration and validation thresholds. Returns: Status information including: - Available leagues - Validation thresholds - Service health status """ from app.ml.backtesting import ( ACCURACY_VALIDATED_THRESHOLD, ACCURACY_ALERT_THRESHOLD ) return { 'data': { 'status': 'operational', 'validation_thresholds': { 'validated_threshold': ACCURACY_VALIDATED_THRESHOLD, 'alert_threshold': ACCURACY_ALERT_THRESHOLD }, 'supported_export_formats': ['json', 'csv', 'html'], 'available_filters': { 'leagues': True, 'date_range': True } }, 'meta': { 'timestamp': datetime.utcnow().isoformat(), 'version': 'v1' } }