""" 交易记录API """ from fastapi import APIRouter, Query, HTTPException from typing import Optional from datetime import datetime, timedelta import sys from pathlib import Path 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 Trade router = APIRouter() def get_date_range(period: Optional[str] = None): """ 根据时间段参数返回开始和结束日期 Args: period: 时间段 ('1d', '7d', '30d', 'custom') Returns: (start_date, end_date) 元组,格式为 'YYYY-MM-DD HH:MM:SS' """ end_date = datetime.now() if period == '1d': start_date = end_date - timedelta(days=1) elif period == '7d': start_date = end_date - timedelta(days=7) elif period == '30d': start_date = end_date - timedelta(days=30) else: return None, None return start_date.strftime('%Y-%m-%d 00:00:00'), end_date.strftime('%Y-%m-%d %H:%M:%S') @router.get("/") async def get_trades( start_date: Optional[str] = Query(None, description="开始日期 (YYYY-MM-DD 或 YYYY-MM-DD HH:MM:SS)"), end_date: Optional[str] = Query(None, description="结束日期 (YYYY-MM-DD 或 YYYY-MM-DD HH:MM:SS)"), period: Optional[str] = Query(None, description="快速时间段筛选: '1d'(最近1天), '7d'(最近7天), '30d'(最近30天)"), symbol: Optional[str] = Query(None, description="交易对筛选"), status: Optional[str] = Query(None, description="状态筛选: 'open', 'closed', 'cancelled'"), limit: int = Query(100, ge=1, le=1000, description="返回记录数限制") ): """ 获取交易记录 支持两种筛选方式: 1. 快速时间段筛选:使用 period 参数 ('1d', '7d', '30d') 2. 自定义时间段筛选:使用 start_date 和 end_date 参数 如果同时提供了 period 和 start_date/end_date,period 优先级更高 """ try: # 如果提供了 period,使用快速时间段筛选 if period: period_start, period_end = get_date_range(period) if period_start and period_end: start_date = period_start end_date = period_end # 格式化日期(如果只提供了日期,添加时间部分) if start_date and len(start_date) == 10: # YYYY-MM-DD start_date = f"{start_date} 00:00:00" if end_date and len(end_date) == 10: # YYYY-MM-DD end_date = f"{end_date} 23:59:59" trades = Trade.get_all(start_date, end_date, symbol, status) return { "total": len(trades), "trades": trades[:limit], "filters": { "start_date": start_date, "end_date": end_date, "period": period, "symbol": symbol, "status": status } } except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.get("/stats") async def get_trade_stats( start_date: Optional[str] = Query(None, description="开始日期 (YYYY-MM-DD 或 YYYY-MM-DD HH:MM:SS)"), end_date: Optional[str] = Query(None, description="结束日期 (YYYY-MM-DD 或 YYYY-MM-DD HH:MM:SS)"), period: Optional[str] = Query(None, description="快速时间段筛选: '1d', '7d', '30d'"), symbol: Optional[str] = Query(None, description="交易对筛选") ): """获取交易统计""" try: # 如果提供了 period,使用快速时间段筛选 if period: period_start, period_end = get_date_range(period) if period_start and period_end: start_date = period_start end_date = period_end # 格式化日期 if start_date and len(start_date) == 10: start_date = f"{start_date} 00:00:00" if end_date and len(end_date) == 10: end_date = f"{end_date} 23:59:59" trades = Trade.get_all(start_date, end_date, symbol, None) closed_trades = [t for t in trades if t['status'] == 'closed'] win_trades = [t for t in closed_trades if float(t['pnl']) > 0] stats = { "total_trades": len(trades), "closed_trades": len(closed_trades), "open_trades": len(trades) - len(closed_trades), "win_trades": len(win_trades), "loss_trades": len(closed_trades) - len(win_trades), "win_rate": len(win_trades) / len(closed_trades) * 100 if closed_trades else 0, "total_pnl": sum(float(t['pnl']) for t in closed_trades), "avg_pnl": sum(float(t['pnl']) for t in closed_trades) / len(closed_trades) if closed_trades else 0, "filters": { "start_date": start_date, "end_date": end_date, "period": period, "symbol": symbol } } return stats except Exception as e: raise HTTPException(status_code=500, detail=str(e))