auto_trade_sys/backend/api/routes/stats.py
薇薇安 a3129f57b6 a
2026-01-13 20:58:29 +08:00

152 lines
6.2 KiB
Python

"""
统计分析API
"""
from fastapi import APIRouter, Query
import sys
from pathlib import Path
from datetime import datetime, timedelta
import logging
project_root = Path(__file__).parent.parent.parent.parent
sys.path.insert(0, str(project_root))
sys.path.insert(0, str(project_root / 'backend'))
from database.models import AccountSnapshot, Trade, MarketScan, TradingSignal
from fastapi import HTTPException
logger = logging.getLogger(__name__)
router = APIRouter()
@router.get("/performance")
async def get_performance_stats(days: int = Query(7, ge=1, le=365)):
"""获取性能统计"""
try:
# 账户快照
snapshots = AccountSnapshot.get_recent(days)
# 交易统计
start_date = (datetime.now() - timedelta(days=days)).strftime('%Y-%m-%d')
trades = Trade.get_all(start_date=start_date)
return {
"snapshots": snapshots,
"trades": trades,
"period": f"Last {days} days"
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@router.get("/dashboard")
async def get_dashboard_data():
"""获取仪表板数据"""
try:
account_data = None
account_error = None
# 优先尝试获取实时账户数据
try:
from api.routes.account import get_realtime_account_data
account_data = await get_realtime_account_data()
logger.info("成功获取实时账户数据")
except HTTPException as e:
# HTTPException 需要特殊处理,提取错误信息
account_error = e.detail
logger.warning(f"获取实时账户数据失败 (HTTP {e.status_code}): {account_error}")
# 回退到数据库快照
try:
snapshots = AccountSnapshot.get_recent(1)
if snapshots:
account_data = {
"total_balance": snapshots[0].get('total_balance', 0),
"available_balance": snapshots[0].get('available_balance', 0),
"total_position_value": snapshots[0].get('total_position_value', 0),
"total_pnl": snapshots[0].get('total_pnl', 0),
"open_positions": snapshots[0].get('open_positions', 0)
}
logger.info("使用数据库快照作为账户数据")
else:
logger.warning("数据库中没有账户快照数据")
except Exception as db_error:
logger.error(f"从数据库获取账户快照失败: {db_error}")
except Exception as e:
account_error = str(e)
logger.warning(f"获取实时账户数据失败: {account_error}", exc_info=True)
# 回退到数据库快照
try:
snapshots = AccountSnapshot.get_recent(1)
if snapshots:
account_data = {
"total_balance": snapshots[0].get('total_balance', 0),
"available_balance": snapshots[0].get('available_balance', 0),
"total_position_value": snapshots[0].get('total_position_value', 0),
"total_pnl": snapshots[0].get('total_pnl', 0),
"open_positions": snapshots[0].get('open_positions', 0)
}
logger.info("使用数据库快照作为账户数据")
except Exception as db_error:
logger.error(f"从数据库获取账户快照失败: {db_error}")
# 获取持仓数据(优先实时,回退到数据库)
open_trades = []
positions_error = None
try:
from api.routes.account import get_realtime_positions
positions = await get_realtime_positions()
# 转换为前端需要的格式
open_trades = positions
logger.info(f"成功获取实时持仓数据: {len(open_trades)} 个持仓")
except HTTPException as e:
positions_error = e.detail
logger.warning(f"获取实时持仓失败 (HTTP {e.status_code}): {positions_error}")
# 回退到数据库记录
try:
open_trades = Trade.get_all(status='open')[:10]
logger.info(f"使用数据库记录作为持仓数据: {len(open_trades)} 个持仓")
except Exception as db_error:
logger.error(f"从数据库获取持仓记录失败: {db_error}")
except Exception as e:
positions_error = str(e)
logger.warning(f"获取实时持仓失败: {positions_error}", exc_info=True)
# 回退到数据库记录
try:
open_trades = Trade.get_all(status='open')[:10]
logger.info(f"使用数据库记录作为持仓数据: {len(open_trades)} 个持仓")
except Exception as db_error:
logger.error(f"从数据库获取持仓记录失败: {db_error}")
# 最近的扫描记录
recent_scans = []
try:
recent_scans = MarketScan.get_recent(10)
except Exception as e:
logger.error(f"获取扫描记录失败: {e}")
# 最近的信号
recent_signals = []
try:
recent_signals = TradingSignal.get_recent(20)
except Exception as e:
logger.error(f"获取交易信号失败: {e}")
result = {
"account": account_data,
"open_trades": open_trades,
"recent_scans": recent_scans,
"recent_signals": recent_signals
}
# 如果有错误,在响应中包含错误信息(但不影响返回)
if account_error or positions_error:
result["warnings"] = {}
if account_error:
result["warnings"]["account"] = account_error
if positions_error:
result["warnings"]["positions"] = positions_error
return result
except Exception as e:
logger.error(f"获取仪表板数据失败: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"获取仪表板数据失败: {str(e)}")