178 lines
5.4 KiB
Python
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'
|
|
}
|
|
}
|