diff --git a/backend/app/crypto_agent/crypto_agent.py b/backend/app/crypto_agent/crypto_agent.py index cdcf345..173167f 100644 --- a/backend/app/crypto_agent/crypto_agent.py +++ b/backend/app/crypto_agent/crypto_agent.py @@ -519,6 +519,8 @@ class CryptoAgent: market_signal, positions, account, current_price, pending_orders ) self._log_trading_decision(paper_decision) + # 发送交易决策通知 + await self._send_trading_decision_notification(paper_decision, market_signal, current_price, is_paper=True) else: logger.info(f"⏸️ 模拟交易未启用") @@ -532,6 +534,8 @@ class CryptoAgent: market_signal, positions, account, current_price, pending_orders ) self._log_trading_decision(real_decision) + # 发送交易决策通知 + await self._send_trading_decision_notification(real_decision, market_signal, current_price, is_paper=False) else: logger.info(f"⏸️ 实盘自动交易未开启") else: @@ -737,26 +741,44 @@ class CryptoAgent: if decision_type in ['OPEN', 'ADD']: # 先执行交易 + logger.info(f" 准备执行 {'模拟' if paper_trading_enabled else '实盘'} 交易...") result = await self._execute_paper_trade(paper_decision, market_signal, current_price) + # 检查是否成功执行 order = result.get('order') if result else None + logger.info(f" 订单创建检查: order={'存在' if order else '不存在'}, result_key={'order' in (result or {})}") + if order: - # 只有成功创建订单后才发送通知 - await self._send_signal_notification(market_signal, paper_decision, current_price, is_paper=True) - paper_executed = True + # 验证订单对象的有效性 + if hasattr(order, 'order_id') and order.order_id: + logger.info(f" 订单验证通过: {order.order_id}") + # 只有成功创建订单后才发送通知 + await self._send_signal_notification(market_signal, paper_decision, current_price, is_paper=True) + paper_executed = True + else: + logger.error(f" ❌ 订单对象无效: 缺少order_id属性") + reason = "订单对象无效: 缺少order_id" + await self._notify_signal_not_executed(market_signal, paper_decision, current_price, is_paper=True, reason=reason) else: # 有信号但订单创建失败,发送未执行通知 reason = result.get('message', '订单创建失败') if result else '订单创建失败' + logger.warning(f" ⚠️ 模拟交易未执行: {reason}") await self._notify_signal_not_executed(market_signal, paper_decision, current_price, is_paper=True, reason=reason) - logger.warning(f" ⚠️ 模拟交易未执行,已发送通知") + logger.warning(f" ⚠️ 模拟交易未执行: {reason}") elif decision_type == 'CLOSE': await self._execute_close(paper_decision, paper_trading=True) + # CLOSE 操作也发送执行通知 + await self._send_signal_notification(market_signal, paper_decision, current_price, is_paper=True) paper_executed = True elif decision_type == 'CANCEL_PENDING': await self._execute_cancel_pending(paper_decision, paper_trading=True) + # CANCEL_PENDING 操作也发送执行通知 + await self._send_signal_notification(market_signal, paper_decision, current_price, is_paper=True) paper_executed = True elif decision_type == 'REDUCE': await self._execute_reduce(paper_decision, paper_trading=True) + # REDUCE 操作也发送执行通知 + await self._send_signal_notification(market_signal, paper_decision, current_price, is_paper=True) paper_executed = True # ============================================================ @@ -790,12 +812,18 @@ class CryptoAgent: logger.warning(f" ⚠️ 实盘交易未执行,已发送通知") elif decision_type == 'CLOSE': await self._execute_close(real_decision, paper_trading=False) + # CLOSE 操作也发送执行通知 + await self._send_signal_notification(market_signal, real_decision, current_price, is_paper=False) real_executed = True elif decision_type == 'CANCEL_PENDING': await self._execute_cancel_pending(real_decision, paper_trading=False) + # CANCEL_PENDING 操作也发送执行通知 + await self._send_signal_notification(market_signal, real_decision, current_price, is_paper=False) real_executed = True elif decision_type == 'REDUCE': await self._execute_reduce(real_decision, paper_trading=False) + # REDUCE 操作也发送执行通知 + await self._send_signal_notification(market_signal, real_decision, current_price, is_paper=False) real_executed = True # 如果都没有执行,给出提示 @@ -976,6 +1004,134 @@ class CryptoAgent: import traceback logger.debug(traceback.format_exc()) + async def _send_trading_decision_notification(self, decision: Dict[str, Any], + market_signal: Dict[str, Any], + current_price: float, + is_paper: bool = True): + """发送交易决策通知(第二阶段)""" + try: + decision_type = decision.get('decision', 'HOLD') + symbol = market_signal.get('symbol') + + # 账户类型标识 + account_type = "📊 模拟" if is_paper else "💰 实盘" + + # 决策类型映射 + decision_map = { + 'OPEN': '开仓', + 'CLOSE': '平仓', + 'ADD': '加仓', + 'REDUCE': '减仓', + 'CANCEL_PENDING': '取消挂单', + 'HOLD': '观望' + } + + decision_text = decision_map.get(decision_type, decision_type) + + # 根据决策类型设置颜色 + color_map = { + 'OPEN': 'green', + 'ADD': 'green', + 'CLOSE': 'orange', + 'REDUCE': 'orange', + 'CANCEL_PENDING': 'red', + 'HOLD': 'gray' + } + color = color_map.get(decision_type, 'blue') + + # 构建标题 + title = f"{account_type} {symbol} 交易决策: {decision_text}" + + # 获取最佳信号用于显示 + best_signal = self._get_best_signal_from_market(market_signal) + signal_confidence = best_signal.get('confidence', 0) if best_signal else 0 + signal_action = best_signal.get('action', '') if best_signal else '' + + # 方向图标 + if 'buy' in signal_action.lower() or 'long' in signal_action.lower(): + action_icon = '🟢' + action_text = '做多' + elif 'sell' in signal_action.lower() or 'short' in signal_action.lower(): + action_icon = '🔴' + action_text = '做空' + else: + action_icon = '➖' + action_text = '观望' + + # 构建内容 + content_parts = [ + f"{action_icon} **市场信号**: {action_text} | 信心度: {signal_confidence}%", + f"", + f"🎯 **交易决策**: {decision_text}", + f"", + ] + + # 添加决策详情 + if decision_type != 'HOLD': + reasoning = decision.get('reasoning', '') + risk_analysis = decision.get('risk_analysis', '') + position_size = decision.get('position_size', 'N/A') + + # 仓位图标 + position_map = {'heavy': '🔥 重仓', 'medium': '📊 中仓', 'light': '🌱 轻仓', 'micro': '🌱 微仓'} + position_display = position_map.get(position_size, position_size) + + content_parts.extend([ + f"📊 **仓位**: {position_display}", + f"💭 **决策理由**: {reasoning}", + ]) + + if risk_analysis: + content_parts.append(f"⚠️ **风险**: {risk_analysis}") + + # 添加价格信息(如果有) + quantity = decision.get('quantity', 0) + if isinstance(quantity, (int, float)) and quantity > 0: + leverage = 20 # 固定20倍杠杆 + position_value = quantity * leverage + content_parts.append(f"💰 **持仓价值**: ${position_value:,.2f} (保证金 ${quantity:.2f})") + + stop_loss = decision.get('stop_loss') + take_profit = decision.get('take_profit') + if stop_loss: + content_parts.append(f"🛑 **止损**: ${stop_loss}") + if take_profit: + content_parts.append(f"🎯 **止盈**: ${take_profit}") + + # 取消挂单时显示要取消的订单 + if decision_type == 'CANCEL_PENDING': + orders_to_cancel = decision.get('orders_to_cancel', []) + if orders_to_cancel: + content_parts.append(f"🚫 **取消订单**: {len(orders_to_cancel)} 个") + for order_id in orders_to_cancel[:3]: # 最多显示3个 + content_parts.append(f" - {order_id}") + if len(orders_to_cancel) > 3: + content_parts.append(f" - ... 还有 {len(orders_to_cancel) - 3} 个") + else: + # HOLD 决策 + reasoning = decision.get('reasoning', '综合评估后选择观望') + content_parts.append(f"💭 **理由**: {reasoning}") + + content_parts.append("") + content_parts.append(f"⏰ 当前价格: ${current_price:,.2f}") + + content = "\n".join(content_parts) + + # 发送通知 + if self.settings.feishu_enabled: + await self.feishu.send_card(title, content, color) + if self.settings.telegram_enabled: + # Telegram 使用文本格式 + message = f"{title}\n\n{content}" + await self.telegram.send_message(message) + + logger.info(f" 📤 已发送交易决策通知: {decision_text}") + + except Exception as e: + logger.warning(f"发送交易决策通知失败: {e}") + import traceback + logger.debug(traceback.format_exc()) + async def _send_signal_notification(self, market_signal: Dict[str, Any], decision: Dict[str, Any], current_price: float, is_paper: bool = True): @@ -1147,18 +1303,25 @@ class CryptoAgent: logger.debug(f" 订单数据: {order_data}") + logger.info(f" 正在调用 create_order_from_signal...") result = self.paper_trading.create_order_from_signal(order_data, current_price) - logger.debug(f" 创建订单结果: {result}") + logger.info(f" create_order_from_signal 返回结果: {result}") # 记录订单 order = result.get('order') if order: # quantity 是保证金金额,持仓价值 = 保证金 × 20 position_value = quantity * 20 - logger.info(f" 📝 已创建模拟订单: {order.order_id} | 仓位: {position_size} | 持仓价值: ${position_value:.2f}") + logger.info(f" ✅ 已创建模拟订单: {order.order_id} | 仓位: {position_size} | 持仓价值: ${position_value:.2f}") + logger.info(f" 订单状态: {order.status.value} | 入场价: ${order.entry_price:,.2f}") else: - logger.warning(f" ⚠️ 创建模拟订单失败: {result}") + # 订单创建失败,记录详细原因 + reason = result.get('message', '未知原因') + cancelled_info = result.get('cancelled_orders', []) + logger.warning(f" ❌ 创建模拟订单失败: {reason}") + if cancelled_info: + logger.warning(f" 已取消的反向订单: {len(cancelled_info)} 个") # 返回结果 return result diff --git a/backend/app/services/paper_trading_service.py b/backend/app/services/paper_trading_service.py index 798a6eb..4951684 100644 --- a/backend/app/services/paper_trading_service.py +++ b/backend/app/services/paper_trading_service.py @@ -224,6 +224,15 @@ class PaperTradingService: # 获取信号等级 grade = signal.get('signal_grade') or signal.get('grade', 'D') + + # 验证信号等级是否有效 + valid_grades = ['A', 'B', 'C', 'D'] + if grade not in valid_grades: + msg = f"无效的信号等级: {grade},有效值为: {', '.join(valid_grades)}" + logger.warning(f"{msg}: {signal.get('symbol')}") + result['message'] = msg + return result + if grade == 'D': msg = "D级信号,不符合开仓条件" logger.info(f"{msg}: {signal.get('symbol')}") @@ -297,19 +306,32 @@ class PaperTradingService: db.commit() db.refresh(order) + # 验证订单是否成功写入数据库 + if not order.order_id: + raise ValueError("订单提交后没有获得order_id") + # 添加到活跃订单缓存 self.active_orders[order.order_id] = order + # 验证缓存添加成功 + if order.order_id not in self.active_orders: + logger.error(f"订单已写入数据库但未添加到缓存: {order_id}") + raise ValueError("订单未添加到缓存") + entry_type_text = "现价" if entry_type == EntryType.MARKET else "挂单" status_text = "已开仓" if status == OrderStatus.OPEN else "等待触发" - logger.info(f"创建模拟订单: {order_id} | {symbol} {side.value} [{entry_type_text}] @ ${entry_price:,.2f} | {status_text}") - logger.info(f" 保证金: ${margin:,.0f} | 杠杆: {self.leverage}x | 持仓价值: ${position_value:,.0f} | 当前订单数: {len(self.active_orders)}/{self.max_orders}") + logger.info(f"✅ 创建模拟订单成功: {order_id} | {symbol} {side.value} [{entry_type_text}] @ ${entry_price:,.2f} | {status_text}") + logger.info(f" 保证金: ${margin:,.0f} | 杠杆: {self.leverage}x | 持仓价值: ${position_value:,.0f} | 当前订单数: {len(self.active_orders)}/{self.max_orders}") result['order'] = order return result except Exception as e: - logger.error(f"创建模拟订单失败: {e}") + logger.error(f"❌ 创建模拟订单失败: {e}") + logger.error(f" 订单数据: symbol={symbol}, side={side}, entry_price={entry_price}, margin={margin}") + import traceback + logger.error(f" 异常详情: {traceback.format_exc()}") db.rollback() + result['message'] = f"订单创建失败: {str(e)}" return result finally: db.close()