chartbastan/backend/app/api/v1/backtesting.py
2026-02-01 09:31:38 +01:00

178 lines
5.4 KiB
Python

"""
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'
}
}