From db2bd6a3b21cedd1721adf32785529dc43a496d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=87=E8=96=87=E5=AE=89?= Date: Thu, 15 Jan 2026 16:51:31 +0800 Subject: [PATCH] a --- backend/api/routes/recommendations.py | 97 +++++++++++++++++++++ frontend/src/components/Recommendations.jsx | 21 ++++- trading_system/position_manager.py | 20 +++-- 3 files changed, 127 insertions(+), 11 deletions(-) diff --git a/backend/api/routes/recommendations.py b/backend/api/routes/recommendations.py index f34198c..6b3be9b 100644 --- a/backend/api/routes/recommendations.py +++ b/backend/api/routes/recommendations.py @@ -55,6 +55,54 @@ async def get_recommendations( end_date=end_dt ) + # 如果是获取有效推荐,尝试更新实时价格 + if status == 'active' or (status is None and len(recommendations) > 0): + try: + import sys + from pathlib import Path + current_file = Path(__file__) + backend_path = current_file.parent.parent.parent + project_root = backend_path.parent + trading_system_path = project_root / 'trading_system' + + if trading_system_path.exists(): + sys.path.insert(0, str(trading_system_path)) + from binance_client import BinanceClient + import config + + # 创建客户端实例(不需要连接,只需要访问价格缓存) + client = BinanceClient( + api_key=config.BINANCE_API_KEY, + api_secret=config.BINANCE_API_SECRET, + testnet=config.USE_TESTNET + ) + + # 更新推荐中的实时价格和涨跌幅 + for rec in recommendations: + symbol = rec.get('symbol') + if symbol: + # 尝试从WebSocket缓存获取实时价格 + realtime_price = client.get_realtime_price(symbol) + if realtime_price is not None: + # 更新价格 + old_price = rec.get('current_price', 0) + rec['current_price'] = realtime_price + + # 计算新的涨跌幅 + if old_price > 0: + change_percent = ((realtime_price - old_price) / old_price) * 100 + rec['change_percent'] = round(change_percent, 4) + rec['price_updated'] = True # 标记价格已更新 + else: + rec['price_updated'] = False + else: + rec['price_updated'] = False + else: + rec['price_updated'] = False + except Exception as price_update_error: + # 如果价格更新失败,不影响返回推荐列表 + logger.debug(f"更新推荐实时价格失败(不影响返回): {price_update_error}") + return { "success": True, "count": len(recommendations), @@ -69,9 +117,58 @@ async def get_recommendations( async def get_active_recommendations(): """ 获取当前有效的推荐(未过期、未执行、未取消) + 使用WebSocket实时价格更新推荐中的价格信息 """ try: recommendations = TradeRecommendation.get_active() + + # 尝试从WebSocket缓存获取实时价格更新推荐中的价格 + try: + import sys + from pathlib import Path + current_file = Path(__file__) + backend_path = current_file.parent.parent.parent + project_root = backend_path.parent + trading_system_path = project_root / 'trading_system' + + if trading_system_path.exists(): + sys.path.insert(0, str(trading_system_path)) + from binance_client import BinanceClient + import config + + # 创建客户端实例(不需要连接,只需要访问价格缓存) + client = BinanceClient( + api_key=config.BINANCE_API_KEY, + api_secret=config.BINANCE_API_SECRET, + testnet=config.USE_TESTNET + ) + + # 更新推荐中的实时价格和涨跌幅 + for rec in recommendations: + symbol = rec.get('symbol') + if symbol: + # 尝试从WebSocket缓存获取实时价格 + realtime_price = client.get_realtime_price(symbol) + if realtime_price is not None: + # 更新价格 + old_price = rec.get('current_price', 0) + rec['current_price'] = realtime_price + + # 计算新的涨跌幅 + if old_price > 0: + change_percent = ((realtime_price - old_price) / old_price) * 100 + rec['change_percent'] = round(change_percent, 4) + rec['price_updated'] = True # 标记价格已更新 + else: + rec['price_updated'] = False + else: + rec['price_updated'] = False + else: + rec['price_updated'] = False + except Exception as price_update_error: + # 如果价格更新失败,不影响返回推荐列表 + logger.debug(f"更新推荐实时价格失败(不影响返回): {price_update_error}") + return { "success": True, "count": len(recommendations), diff --git a/frontend/src/components/Recommendations.jsx b/frontend/src/components/Recommendations.jsx index a15192b..c6cbdfc 100644 --- a/frontend/src/components/Recommendations.jsx +++ b/frontend/src/components/Recommendations.jsx @@ -13,6 +13,18 @@ function Recommendations() { useEffect(() => { loadRecommendations() + + // 如果是查看有效推荐,每10秒自动刷新一次(获取实时价格) + let interval = null + if (statusFilter === 'active') { + interval = setInterval(loadRecommendations, 10000) // 每10秒刷新 + } + + return () => { + if (interval) { + clearInterval(interval) + } + } }, [statusFilter, directionFilter]) const loadRecommendations = async () => { @@ -208,9 +220,14 @@ function Recommendations() {
- {parseFloat(rec.current_price || 0).toFixed(4)} USDT + + {parseFloat(rec.current_price || 0).toFixed(4)} USDT + {rec.price_updated && ( + 🟢 + )} +
- {rec.change_percent && ( + {rec.change_percent !== undefined && rec.change_percent !== null && (
= 0 ? 'positive' : 'negative'}> diff --git a/trading_system/position_manager.py b/trading_system/position_manager.py index 9d810aa..28ea069 100644 --- a/trading_system/position_manager.py +++ b/trading_system/position_manager.py @@ -533,8 +533,10 @@ class PositionManager: ) # 检查止损(使用更新后的止损价) - stop_loss = position_info['stopLoss'] - if position_info['side'] == 'BUY' and current_price <= stop_loss: + stop_loss = position_info.get('stopLoss') + if stop_loss is None: + logger.warning(f"{symbol} 止损价未设置,跳过止损检查") + elif position_info['side'] == 'BUY' and current_price <= stop_loss: logger.warning( f"{symbol} 触发止损: {current_price:.4f} <= {stop_loss:.4f} " f"(盈亏: {pnl_percent:.2f}%)" @@ -559,7 +561,7 @@ class PositionManager: closed_positions.append(symbol) continue - if position_info['side'] == 'SELL' and current_price >= stop_loss: + if stop_loss is not None and position_info['side'] == 'SELL' and current_price >= stop_loss: logger.warning( f"{symbol} 触发止损: {current_price:.4f} >= {stop_loss:.4f} " f"(盈亏: {pnl_percent:.2f}%)" @@ -591,7 +593,7 @@ class PositionManager: remaining_quantity = position_info.get('remainingQuantity', quantity) # 第一目标:盈亏比1:1,了结50%仓位 - if not partial_profit_taken: + if not partial_profit_taken and take_profit_1 is not None: if position_info['side'] == 'BUY' and current_price >= take_profit_1: logger.info( f"{symbol} 触发第一目标止盈(盈亏比1:1): {current_price:.4f} >= {take_profit_1:.4f} " @@ -620,7 +622,7 @@ class PositionManager: logger.info(f"{symbol} 剩余仓位止损移至成本价,配合移动止损博取更大利润") except Exception as e: logger.error(f"{symbol} 部分止盈失败: {e}") - elif position_info['side'] == 'SELL' and current_price <= take_profit_1: + elif take_profit_1 is not None and position_info['side'] == 'SELL' and current_price <= take_profit_1: logger.info( f"{symbol} 触发第一目标止盈(盈亏比1:1): {current_price:.4f} <= {take_profit_1:.4f} " f"(盈亏: {pnl_percent:.2f}%)" @@ -649,7 +651,7 @@ class PositionManager: logger.error(f"{symbol} 部分止盈失败: {e}") # 第二目标:原始止盈价,平掉剩余仓位 - if partial_profit_taken: + if partial_profit_taken and take_profit_2 is not None: if position_info['side'] == 'BUY' and current_price >= take_profit_2: logger.info( f"{symbol} 触发第二目标止盈: {current_price:.4f} >= {take_profit_2:.4f} " @@ -673,7 +675,7 @@ class PositionManager: if await self.close_position(symbol, reason=exit_reason): closed_positions.append(symbol) continue - elif position_info['side'] == 'SELL' and current_price <= take_profit_2: + elif take_profit_2 is not None and position_info['side'] == 'SELL' and current_price <= take_profit_2: logger.info( f"{symbol} 触发第二目标止盈: {current_price:.4f} <= {take_profit_2:.4f} " f"(盈亏: {pnl_percent:.2f}%)" @@ -699,7 +701,7 @@ class PositionManager: else: # 如果未部分止盈,但达到第二目标,直接全部平仓 take_profit = position_info.get('takeProfit') - if position_info['side'] == 'BUY' and current_price >= take_profit: + if take_profit is not None and position_info['side'] == 'BUY' and current_price >= take_profit: logger.info( f"{symbol} 触发止盈: {current_price:.4f} >= {take_profit:.4f} " f"(盈亏: {pnl_percent:.2f}%)" @@ -723,7 +725,7 @@ class PositionManager: closed_positions.append(symbol) continue - if position_info['side'] == 'SELL' and current_price <= take_profit: + if take_profit is not None and position_info['side'] == 'SELL' and current_price <= take_profit: logger.info( f"{symbol} 触发止盈: {current_price:.4f} <= {take_profit:.4f} " f"(盈亏: {pnl_percent:.2f}%)"