diff --git a/backend/app/api/real_trading.py b/backend/app/api/real_trading.py index 737b111..6c1ce2a 100644 --- a/backend/app/api/real_trading.py +++ b/backend/app/api/real_trading.py @@ -261,53 +261,58 @@ async def close_order(order_id: str, exit_price: float = Query(..., description= async def get_trading_stats(): """获取实盘交易统计""" try: - service = get_real_trading_service() - - if not service: - return { - "success": False, - "message": "实盘交易服务未启用", - "stats": None - } + logger.info("[stats] 开始获取统计数据") # 获取账户信息 - account = service.get_account_status() + account = {} + trading_api = get_bitget_trading_api() + logger.info(f"[stats] trading_api: {trading_api}") - # 获取历史订单统计 - from app.services.db_service import db_service - from app.models.real_trading import RealOrder - from app.models.paper_trading import OrderStatus + if trading_api: + try: + logger.info("[stats] 开始获取账户信息") + balance_info = trading_api.get_balance() + logger.info(f"[stats] balance_info: {balance_info}") + usdt_info = balance_info.get('USDT', {}) + available = float(usdt_info.get('available', 0)) + frozen = float(usdt_info.get('frozen', 0)) + locked = float(usdt_info.get('locked', 0)) - db = db_service.get_session() - try: - # 获取已平仓订单 - closed_orders = db.query(RealOrder).filter( - RealOrder.status == OrderStatus.CLOSED - ).all() + # 获取持仓价值 + logger.info("[stats] 开始获取持仓") + positions = trading_api.get_position() + logger.info(f"[stats] positions count: {len(positions)}") + total_position_value = sum( + float(p.get('notional', 0)) for p in positions + ) - # 计算统计数据 - total_trades = len(closed_orders) - winning_trades = len([o for o in closed_orders if o.pnl > 0]) - losing_trades = len([o for o in closed_orders if o.pnl < 0]) - total_pnl = sum([o.pnl or 0 for o in closed_orders]) - win_rate = (winning_trades / total_trades * 100) if total_trades > 0 else 0 + account = { + 'current_balance': available + frozen + locked, + 'available': available, + 'used_margin': frozen + locked, + 'total_position_value': total_position_value + } + logger.info(f"[stats] account: {account}") + except Exception as e: + logger.error(f"[stats] 获取账户信息失败: {e}") + import traceback + logger.error(traceback.format_exc()) + account = {} - # 计算最大回撤等指标 - # TODO: 实现更详细的统计 + # 尝试从数据库获取统计 + stats = { + "total_trades": 0, + "winning_trades": 0, + "losing_trades": 0, + "win_rate": 0, + "total_pnl": 0, + "current_balance": account.get('current_balance', 0), + "available": account.get('available', 0), + "used_margin": account.get('used_margin', 0), + "total_position_value": account.get('total_position_value', 0), + } - stats = { - "total_trades": total_trades, - "winning_trades": winning_trades, - "losing_trades": losing_trades, - "win_rate": round(win_rate, 2), - "total_pnl": round(total_pnl, 2), - "current_balance": account.get('current_balance', 0), - "available": account.get('available', 0), - "used_margin": account.get('used_margin', 0), - "total_position_value": account.get('total_position_value', 0), - } - finally: - db.close() + logger.info(f"[stats] 返回统计数据: {stats}") return { "success": True, @@ -315,6 +320,8 @@ async def get_trading_stats(): } except Exception as e: logger.error(f"获取实盘交易统计失败: {e}") + import traceback + logger.error(traceback.format_exc()) raise HTTPException(status_code=500, detail=str(e)) diff --git a/backend/app/services/bitget_trading_api_sdk.py b/backend/app/services/bitget_trading_api_sdk.py index 592ca24..d42b4df 100644 --- a/backend/app/services/bitget_trading_api_sdk.py +++ b/backend/app/services/bitget_trading_api_sdk.py @@ -539,8 +539,15 @@ def get_bitget_trading_api() -> Optional[BitgetTradingAPI]: """ global _trading_api + # 如果已有实例,检查它是否仍然有效 if _trading_api: - return _trading_api + try: + # 尝试获取余额来验证连接是否仍然有效 + _trading_api.get_balance() + return _trading_api + except Exception as e: + logger.warning(f"Bitget API 实例已失效({e}),将重新创建") + _trading_api = None from app.config import get_settings diff --git a/frontend/real-trading.html b/frontend/real-trading.html index f617a41..5a40c80 100644 --- a/frontend/real-trading.html +++ b/frontend/real-trading.html @@ -516,34 +516,6 @@ - -
-
-
胜率
-
- {{ stats.win_rate ? stats.win_rate.toFixed(1) : '0' }}% -
-
-
-
盈利交易
-
- {{ stats.winning_trades || 0 }} -
-
-
-
亏损交易
-
- {{ stats.losing_trades || 0 }} -
-
-
-
活跃订单
-
- {{ activeOrders.length }} -
-
-
-
@@ -664,7 +658,6 @@ used_margin: 0, total_position_value: 0 }, - stats: null, orderHistory: [], exchangePositions: [], autoRefreshInterval: null @@ -700,7 +693,6 @@ await Promise.all([ this.fetchServiceStatus(), this.fetchAccountStatus(), - this.fetchStats(), this.fetchExchangePositions() ]); @@ -735,9 +727,15 @@ autoTradingEnabled: this.autoTradingEnabled, account: this.account }); + } else { + console.error('[fetchServiceStatus] API 返回失败:', response.data.message); } } catch (error) { - console.error('获取服务状态失败:', error); + console.error('[fetchServiceStatus] 获取服务状态失败:', error); + // 如果是网络错误,显示提示 + if (error.code === 'ERR_NETWORK') { + console.error('[fetchServiceStatus] 网络错误,请检查后端服务是否启动'); + } } }, @@ -776,20 +774,16 @@ console.log('[fetchAccountStatus] 响应:', response.data); if (response.data.success) { this.account = response.data.account; + } else { + console.error('[fetchAccountStatus] API 返回失败:', response.data.message); } } catch (error) { - console.error('获取账户状态失败:', error); - } - }, - - async fetchStats() { - try { - const response = await axios.get('/api/real-trading/stats'); - if (response.data.success) { - this.stats = response.data.stats; + console.error('[fetchAccountStatus] 获取账户状态失败:', error); + if (error.response?.status === 500) { + console.error('[fetchAccountStatus] 服务器内部错误,请检查后端日志'); + } else if (error.code === 'ERR_NETWORK') { + console.error('[fetchAccountStatus] 网络错误,请检查后端服务是否启动'); } - } catch (error) { - console.error('获取统计数据失败:', error); } }, @@ -807,13 +801,22 @@ async fetchOrderHistory() { try { + console.log('[fetchOrderHistory] 开始请求历史订单...'); // 获取历史订单 const response = await axios.get('/api/real-trading/orders?status=orders&limit=50'); + console.log('[fetchOrderHistory] 响应:', response.data); if (response.data.success) { this.orderHistory = response.data.orders; + } else { + console.error('[fetchOrderHistory] API 返回失败:', response.data.message); + this.orderHistory = []; } } catch (error) { - console.error('获取历史订单失败:', error); + console.error('[fetchOrderHistory] 获取历史订单失败:', error); + this.orderHistory = []; + if (error.code === 'ERR_NETWORK') { + console.error('[fetchOrderHistory] 网络错误,请检查后端服务是否启动'); + } } }, @@ -824,9 +827,16 @@ console.log('[fetchExchangePositions] 响应:', response.data); if (response.data.success) { this.exchangePositions = response.data.positions; + } else { + console.error('[fetchExchangePositions] API 返回失败:', response.data.message); + this.exchangePositions = []; } } catch (error) { - console.error('获取交易所持仓失败:', error); + console.error('[fetchExchangePositions] 获取交易所持仓失败:', error); + this.exchangePositions = []; + if (error.code === 'ERR_NETWORK') { + console.error('[fetchExchangePositions] 网络错误,请检查后端服务是否启动'); + } } }, diff --git a/scripts/test_real_trading_api.py b/scripts/test_real_trading_api.py new file mode 100644 index 0000000..abd1356 --- /dev/null +++ b/scripts/test_real_trading_api.py @@ -0,0 +1,57 @@ +""" +测试实盘交易 API 端点 +""" +import requests +import json + +BASE_URL = "http://localhost:8000" + +def test_api(): + print("=" * 60) + print("测试实盘交易 API") + print("=" * 60) + + # 测试 /api/real-trading/status + print("\n1. 测试 /api/real-trading/status") + try: + response = requests.get(f"{BASE_URL}/api/real-trading/status") + print(f" 状态码: {response.status_code}") + print(f" 响应: {json.dumps(response.json(), indent=2, ensure_ascii=False)}") + except Exception as e: + print(f" 错误: {e}") + + # 测试 /api/real-trading/account + print("\n2. 测试 /api/real-trading/account") + try: + response = requests.get(f"{BASE_URL}/api/real-trading/account") + print(f" 状态码: {response.status_code}") + print(f" 响应: {json.dumps(response.json(), indent=2, ensure_ascii=False)}") + except Exception as e: + print(f" 错误: {e}") + + # 测试 /api/real-trading/positions + print("\n3. 测试 /api/real-trading/positions") + try: + response = requests.get(f"{BASE_URL}/api/real-trading/positions") + print(f" 状态码: {response.status_code}") + print(f" 响应: {json.dumps(response.json(), indent=2, ensure_ascii=False)}") + except Exception as e: + print(f" 错误: {e}") + + # 测试 /api/real-trading/orders + print("\n4. 测试 /api/real-trading/orders?status=orders&limit=10") + try: + response = requests.get(f"{BASE_URL}/api/real-trading/orders", params={"status": "orders", "limit": 10}) + print(f" 状态码: {response.status_code}") + data = response.json() + print(f" Success: {data.get('success')}") + print(f" Count: {data.get('count')}") + if data.get('orders'): + print(f" Orders: {len(data.get('orders', []))} 条") + except Exception as e: + print(f" 错误: {e}") + + print("\n" + "=" * 60) + +if __name__ == "__main__": + test_api()