diff --git a/backend/api/routes/account.py b/backend/api/routes/account.py index ce1f763..40e4529 100644 --- a/backend/api/routes/account.py +++ b/backend/api/routes/account.py @@ -641,13 +641,51 @@ async def get_realtime_positions(account_id: int = Depends(get_account_id)): logger.info(f"获取到 {len(positions)} 个持仓") - # 并发获取所有持仓的挂单信息 + # 并发获取所有持仓的挂单信息(包括普通挂单和Algo挂单) open_orders_map = {} try: position_symbols = [p.get('symbol') for p in positions if float(p.get('positionAmt', 0)) != 0] if position_symbols: - logger.info(f"正在获取挂单信息: {position_symbols}") - tasks = [client.get_open_orders(sym) for sym in position_symbols] + # 定义获取函数:同时获取普通挂单和Algo挂单 + async def fetch_both_orders(symbol): + try: + # 并发调用两个接口 + t1 = client.get_open_orders(symbol) + t2 = client.futures_get_open_algo_orders(symbol, algo_type="CONDITIONAL") + res = await asyncio.gather(t1, t2, return_exceptions=True) + + orders = [] + # 1. 普通订单 + if isinstance(res[0], list): + orders.extend(res[0]) + else: + logger.warning(f"获取 {symbol} 普通挂单失败: {res[0]}") + + # 2. Algo订单 (需要标准化为普通订单格式) + if isinstance(res[1], list): + for algo in res[1]: + # 标准化 Algo Order 结构 + orders.append({ + 'orderId': algo.get('algoId'), + 'type': algo.get('orderType'), # Algo订单使用 orderType + 'side': algo.get('side'), + 'stopPrice': algo.get('triggerPrice'), # Algo订单使用 triggerPrice + 'price': 0, # 通常为0 + 'origType': algo.get('algoType'), + 'reduceOnly': algo.get('reduceOnly'), + 'status': 'NEW', # 列表中都是生效中的 + '_is_algo': True + }) + else: + logger.warning(f"获取 {symbol} Algo挂单失败: {res[1]}") + + return orders + except Exception as e: + logger.error(f"获取 {symbol} 订单组合失败: {e}") + return [] + + logger.info(f"正在获取挂单信息(包含Algo): {position_symbols}") + tasks = [fetch_both_orders(sym) for sym in position_symbols] results = await asyncio.gather(*tasks, return_exceptions=True) for sym, orders in zip(position_symbols, results): diff --git a/backend/restart.sh b/backend/restart.sh index b27f164..2966db9 100755 --- a/backend/restart.sh +++ b/backend/restart.sh @@ -3,8 +3,13 @@ cd "$(dirname "$0")" -# 查找运行中的uvicorn进程 -PID=$(ps aux | grep "uvicorn api.main:app" | grep -v grep | awk '{print $2}') +# 查找运行中的uvicorn进程 (优先使用 lsof 查找端口占用) +PID=$(lsof -t -i:8001) + +if [ -z "$PID" ]; then + # 回退到 ps 查找 (如果 lsof 没找到或不可用) + PID=$(ps aux | grep "uvicorn api.main:app" | grep -v grep | awk '{print $2}') +fi if [ -z "$PID" ]; then echo "未找到运行中的后端服务" @@ -16,8 +21,8 @@ else kill $PID sleep 2 - # 检查是否成功停止 - if ps -p $PID > /dev/null 2>&1; then + # 检查是否成功停止 (使用 kill -0 检查进程是否存在,替代 ps -p) + if kill -0 $PID > /dev/null 2>&1; then echo "强制停止服务..." kill -9 $PID sleep 1 diff --git a/持仓记录_2026-02-08T12-22-25.json b/持仓记录_2026-02-08T12-22-25.json new file mode 100644 index 0000000..02fac90 --- /dev/null +++ b/持仓记录_2026-02-08T12-22-25.json @@ -0,0 +1,191 @@ +[ + { + "symbol": "FHEUSDT", + "side": "SELL", + "quantity": 328, + "entry_price": 0.08613, + "entry_value_usdt": 28.25064, + "notional_usdt": 28.47696, + "margin_usdt": 3.55962, + "original_notional_usdt": null, + "original_margin_usdt": null, + "mark_price": 0.08682, + "pnl": -0.22632, + "pnl_percent": -6.357982031789911, + "leverage": 8, + "entry_time": null, + "stop_loss_price": null, + "take_profit_price": null, + "take_profit_1": null, + "take_profit_2": null, + "atr": null, + "entry_order_id": null, + "entry_order_type": null, + "open_orders": [], + "active_sl_orders": "", + "active_tp_orders": "", + "binance_open_orders_raw": [] + }, + { + "symbol": "MUSDT", + "side": "BUY", + "quantity": 13, + "entry_price": 1.6629, + "entry_value_usdt": 21.6177, + "notional_usdt": 19.5280163, + "margin_usdt": 2.4410020375, + "original_notional_usdt": null, + "original_margin_usdt": null, + "mark_price": 1.5021551, + "pnl": -2.0896837, + "pnl_percent": -85.607618014944, + "leverage": 8, + "entry_time": null, + "stop_loss_price": null, + "take_profit_price": null, + "take_profit_1": null, + "take_profit_2": null, + "atr": null, + "entry_order_id": null, + "entry_order_type": null, + "open_orders": [], + "active_sl_orders": "", + "active_tp_orders": "", + "binance_open_orders_raw": [] + }, + { + "symbol": "ENSOUSDT", + "side": "BUY", + "quantity": 17.2, + "entry_price": 1.3622, + "entry_value_usdt": 23.42984, + "notional_usdt": 23.1856, + "margin_usdt": 2.8982, + "original_notional_usdt": null, + "original_margin_usdt": null, + "mark_price": 1.348, + "pnl": -0.24424, + "pnl_percent": -8.427299703264095, + "leverage": 8, + "entry_time": null, + "stop_loss_price": null, + "take_profit_price": null, + "take_profit_1": null, + "take_profit_2": null, + "atr": null, + "entry_order_id": null, + "entry_order_type": null, + "open_orders": [], + "active_sl_orders": "", + "active_tp_orders": "", + "binance_open_orders_raw": [] + }, + { + "symbol": "GPSUSDT", + "side": "BUY", + "quantity": 2129, + "entry_price": 0.009901, + "entry_value_usdt": 21.079229, + "notional_usdt": 20.53067086, + "margin_usdt": 2.5663338575, + "original_notional_usdt": null, + "original_margin_usdt": null, + "mark_price": 0.00964334, + "pnl": -0.54855814, + "pnl_percent": -21.375166695356587, + "leverage": 8, + "entry_time": null, + "stop_loss_price": null, + "take_profit_price": null, + "take_profit_1": null, + "take_profit_2": null, + "atr": null, + "entry_order_id": null, + "entry_order_type": null, + "open_orders": [], + "active_sl_orders": "", + "active_tp_orders": "", + "binance_open_orders_raw": [] + }, + { + "symbol": "WLFIUSDT", + "side": "SELL", + "quantity": 487, + "entry_price": 0.0982, + "entry_value_usdt": 47.8234, + "notional_usdt": 49.8688, + "margin_usdt": 6.2336, + "original_notional_usdt": null, + "original_margin_usdt": null, + "mark_price": 0.1024, + "pnl": -2.0454, + "pnl_percent": -32.8125, + "leverage": 8, + "entry_time": null, + "stop_loss_price": null, + "take_profit_price": null, + "take_profit_1": null, + "take_profit_2": null, + "atr": null, + "entry_order_id": null, + "entry_order_type": null, + "open_orders": [], + "active_sl_orders": "", + "active_tp_orders": "", + "binance_open_orders_raw": [] + }, + { + "symbol": "ZKUSDT", + "side": "SELL", + "quantity": 2163, + "entry_price": 0.02166, + "entry_value_usdt": 46.850579999999994, + "notional_usdt": 46.74243, + "margin_usdt": 5.84280375, + "original_notional_usdt": null, + "original_margin_usdt": null, + "mark_price": 0.02161, + "pnl": 0.10815, + "pnl_percent": 1.8509949097639982, + "leverage": 8, + "entry_time": null, + "stop_loss_price": null, + "take_profit_price": null, + "take_profit_1": null, + "take_profit_2": null, + "atr": null, + "entry_order_id": null, + "entry_order_type": null, + "open_orders": [], + "active_sl_orders": "", + "active_tp_orders": "", + "binance_open_orders_raw": [] + }, + { + "symbol": "ROSEUSDT", + "side": "SELL", + "quantity": 3556, + "entry_price": 0.01252, + "entry_value_usdt": 44.521119999999996, + "notional_usdt": 44.34332, + "margin_usdt": 5.542915, + "original_notional_usdt": null, + "original_margin_usdt": null, + "mark_price": 0.01247, + "pnl": 0.1778, + "pnl_percent": 3.2076984763432246, + "leverage": 8, + "entry_time": null, + "stop_loss_price": null, + "take_profit_price": null, + "take_profit_1": null, + "take_profit_2": null, + "atr": null, + "entry_order_id": null, + "entry_order_type": null, + "open_orders": [], + "active_sl_orders": "", + "active_tp_orders": "", + "binance_open_orders_raw": [] + } +] \ No newline at end of file