feat: add PDF report generation for backtest executions

- Implemented `generate_backtest_report` to create PDF reports for backtest results.
- Added `report_file` field to `StrategyExecution` for storing report paths.
- Introduced `/executions/<execution_id>/report/` endpoint for downloading reports.
- Enhanced backtesting flow to generate and save reports upon completion.
- Updated dependencies to include `matplotlib` for report generation.
This commit is contained in:
2026-02-08 14:39:23 +09:00
parent 0d7574e3c9
commit 940d9bfe6e
7 changed files with 222 additions and 6 deletions

View File

@@ -1,17 +1,21 @@
import json
import logging
import os
import threading
import time
import requests
from django.conf import settings
from django.http import FileResponse, JsonResponse
from django.shortcuts import render, get_object_or_404
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
from django.utils import timezone
import json
import threading
import time
import requests
import logging
from .models import QuantStrategy, StrategyVersion, StrategyExecution
from .base import StrategyRegistry
from .services.backtest import run_backtest
from .services.report import generate_backtest_report
from . import implementations # 구현체들을 로드하여 레지스트리에 등록
logger = logging.getLogger(__name__)
@@ -218,6 +222,9 @@ def execution_status(request, execution_id):
if execution.status == 'completed' and execution.result:
response_data['result'] = execution.result
if execution.report_file:
response_data['report_url'] = f'/api/executions/{execution.id}/report/'
if execution.status == 'failed' and execution.error_message:
response_data['error_message'] = execution.error_message
@@ -312,6 +319,13 @@ def backtest_strategy(request):
execution.status = 'completed'
execution.result = result
execution.completed_at = timezone.now()
try:
report_path = generate_backtest_report(result, execution.id)
execution.report_file = report_path
except Exception as report_err:
logger.exception(f'PDF report generation failed for execution {execution.id}: {report_err}')
execution.save()
except Exception as e:
@@ -343,3 +357,20 @@ def backtest_strategy(request):
return JsonResponse({
'error': str(e)
}, status=500)
@require_http_methods(["GET"])
def download_report(request, execution_id):
"""백테스트 PDF 리포트 다운로드"""
execution = get_object_or_404(StrategyExecution, id=execution_id)
if not execution.report_file:
return JsonResponse({'error': 'Report not available'}, status=404)
file_path = os.path.join(settings.MEDIA_ROOT, execution.report_file)
if not os.path.exists(file_path):
return JsonResponse({'error': 'Report file not found'}, status=404)
response = FileResponse(open(file_path, 'rb'), content_type='application/pdf')
response['Content-Disposition'] = f'attachment; filename="backtest_{execution_id}.pdf"'
return response