""" 账户实时数据API - 从币安API获取实时账户和订单数据 """ from fastapi import APIRouter, HTTPException import sys from pathlib import Path 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')) sys.path.insert(0, str(project_root / 'trading_system')) from database.models import TradingConfig logger = logging.getLogger(__name__) router = APIRouter() async def get_realtime_account_data(): """从币安API实时获取账户数据""" try: # 从数据库读取API密钥 api_key = TradingConfig.get_value('BINANCE_API_KEY') api_secret = TradingConfig.get_value('BINANCE_API_SECRET') use_testnet = TradingConfig.get_value('USE_TESTNET', False) if not api_key or not api_secret: raise HTTPException( status_code=400, detail="API密钥未配置,请在配置界面设置BINANCE_API_KEY和BINANCE_API_SECRET" ) # 导入交易系统的BinanceClient try: from binance_client import BinanceClient except ImportError: # 如果直接导入失败,尝试从trading_system导入 trading_system_path = project_root / 'trading_system' sys.path.insert(0, str(trading_system_path)) from binance_client import BinanceClient # 创建客户端 client = BinanceClient( api_key=api_key, api_secret=api_secret, testnet=use_testnet ) await client.connect() # 获取账户余额 balance = await client.get_account_balance() # 获取持仓 positions = await client.get_open_positions() # 计算总仓位价值和总盈亏 total_position_value = 0 total_pnl = 0 open_positions_count = 0 for pos in positions: position_amt = float(pos.get('positionAmt', 0)) if position_amt == 0: continue entry_price = float(pos.get('entryPrice', 0)) mark_price = float(pos.get('markPrice', 0)) unrealized_pnl = float(pos.get('unRealizedProfit', 0)) if mark_price == 0: # 如果没有标记价格,使用入场价 mark_price = entry_price position_value = abs(position_amt * mark_price) total_position_value += position_value total_pnl += unrealized_pnl open_positions_count += 1 await client.disconnect() return { "total_balance": balance.get('total', 0), "available_balance": balance.get('available', 0), "total_position_value": total_position_value, "total_pnl": total_pnl, "open_positions": open_positions_count } except HTTPException: raise except Exception as e: logger.error(f"获取账户数据失败: {e}", exc_info=True) raise HTTPException(status_code=500, detail=f"获取账户数据失败: {str(e)}") @router.get("/realtime") async def get_realtime_account(): """获取实时账户数据""" return await get_realtime_account_data() @router.get("/positions") async def get_realtime_positions(): """获取实时持仓数据""" try: # 从数据库读取API密钥 api_key = TradingConfig.get_value('BINANCE_API_KEY') api_secret = TradingConfig.get_value('BINANCE_API_SECRET') use_testnet = TradingConfig.get_value('USE_TESTNET', False) logger.info(f"尝试获取实时持仓数据 (testnet={use_testnet})") if not api_key or not api_secret: error_msg = "API密钥未配置" logger.warning(error_msg) raise HTTPException( status_code=400, detail=error_msg ) # 导入BinanceClient try: from binance_client import BinanceClient except ImportError: trading_system_path = project_root / 'trading_system' sys.path.insert(0, str(trading_system_path)) from binance_client import BinanceClient client = BinanceClient( api_key=api_key, api_secret=api_secret, testnet=use_testnet ) logger.info("连接币安API获取持仓...") await client.connect() positions = await client.get_open_positions() await client.disconnect() logger.info(f"获取到 {len(positions)} 个持仓") # 格式化持仓数据 formatted_positions = [] for pos in positions: position_amt = float(pos.get('positionAmt', 0)) if position_amt == 0: continue entry_price = float(pos.get('entryPrice', 0)) mark_price = float(pos.get('markPrice', 0)) unrealized_pnl = float(pos.get('unRealizedProfit', 0)) if mark_price == 0: mark_price = entry_price position_value = abs(position_amt * mark_price) pnl_percent = 0 if entry_price > 0 and position_value > 0: pnl_percent = (unrealized_pnl / position_value) * 100 formatted_positions.append({ "symbol": pos.get('symbol'), "side": "BUY" if position_amt > 0 else "SELL", "quantity": abs(position_amt), "entry_price": entry_price, "mark_price": mark_price, "pnl": unrealized_pnl, "pnl_percent": pnl_percent, "leverage": int(pos.get('leverage', 1)) }) logger.info(f"格式化后 {len(formatted_positions)} 个有效持仓") return formatted_positions except HTTPException: raise except Exception as e: error_msg = f"获取持仓数据失败: {str(e)}" logger.error(error_msg, exc_info=True) raise HTTPException(status_code=500, detail=error_msg)