From b26f7713b4a0a1099eeb2f23d90cc1d8f36395a3 Mon Sep 17 00:00:00 2001 From: aaron <> Date: Fri, 27 Feb 2026 23:35:38 +0800 Subject: [PATCH] update --- backend/app/crypto_agent/crypto_agent.py | 28 ++++++++++++- .../crypto_agent/trading_decision_maker.py | 18 +++++++- backend/app/services/telegram_service.py | 42 ++++++++++++++++--- 3 files changed, 80 insertions(+), 8 deletions(-) diff --git a/backend/app/crypto_agent/crypto_agent.py b/backend/app/crypto_agent/crypto_agent.py index b49e8c7..fd127c1 100644 --- a/backend/app/crypto_agent/crypto_agent.py +++ b/backend/app/crypto_agent/crypto_agent.py @@ -1488,6 +1488,7 @@ class CryptoAgent: """ try: symbol = decision.get('symbol') + decision_action = decision.get('action', '') # buy/sell orders_to_cancel = decision.get('orders_to_cancel', []) if not orders_to_cancel: @@ -1501,10 +1502,11 @@ class CryptoAgent: logger.warning(f" {trading_type}交易服务未启用") return - # 安全检查:验证要取消的订单是否属于当前symbol + # 安全检查:验证要取消的订单是否属于当前symbol且方向相反 active_orders = trading_service.get_active_orders() valid_orders = [] invalid_orders = [] + wrong_direction_orders = [] for order_id in orders_to_cancel: # 查找订单 @@ -1520,10 +1522,32 @@ class CryptoAgent: invalid_orders.append(order_id) continue + # 检查订单方向是否与决策相反 + order_side = order.get('side') # long/short + # 决策是buy时应该取消short(做空),决策是sell时应该取消long(做多) + should_cancel = False + if decision_action == 'buy' and order_side == 'short': + should_cancel = True + elif decision_action == 'sell' and order_side == 'long': + should_cancel = True + elif decision_action in ['open_long', 'close_short']: + should_cancel = (order_side == 'short') + elif decision_action in ['open_short', 'close_long']: + should_cancel = (order_side == 'long') + + if not should_cancel: + logger.error(f" ❌ 安全拦截:订单 {order_id} 方向为 {order_side},与决策 {decision_action} 同向,不应取消!") + wrong_direction_orders.append(order_id) + invalid_orders.append(order_id) + continue + valid_orders.append(order_id) if invalid_orders: - logger.error(f" 🚫 拒绝取消 {len(invalid_orders)} 个不属于 {symbol} 的订单") + logger.error(f" 🚫 拒绝取消 {len(invalid_orders)} 个不符合条件的订单") + + if wrong_direction_orders: + logger.error(f" ⚠️ {len(wrong_direction_orders)} 个同向订单被拦截(不应取消同向订单)") if not valid_orders: logger.warning(f" ⚠️ 没有有效的订单可以取消") diff --git a/backend/app/crypto_agent/trading_decision_maker.py b/backend/app/crypto_agent/trading_decision_maker.py index bb67f3f..c210499 100644 --- a/backend/app/crypto_agent/trading_decision_maker.py +++ b/backend/app/crypto_agent/trading_decision_maker.py @@ -484,10 +484,26 @@ class TradingDecisionMaker: prompt_parts.append(f"\n## 当前挂单(仅 {context['symbol']} 的挂单)") if pending_orders: prompt_parts.append(f"⚠️ 重要:以下挂单都属于当前交易对 {context['symbol']},取消订单时只能选择这些订单ID") + prompt_parts.append(f"⚠️ 取消规则:做空信号时只能取消做多(🟢long)挂单,做多信号时只能取消做空(🔴short)挂单") + + # 分类统计挂单方向 + long_orders = [o for o in pending_orders if o.get('side') == 'long'] + short_orders = [o for o in pending_orders if o.get('side') == 'short'] + + # 如果只有同向挂单,明确提示LLM + signals = context.get('signals', []) + if signals: + main_action = signals[0].get('action', 'hold') if signals else 'hold' + if main_action == 'sell' and not long_orders: + prompt_parts.append(f"📌 注意:当前只有做空挂单,与做空信号同向,无需取消!") + elif main_action == 'buy' and not short_orders: + prompt_parts.append(f"📌 注意:当前只有做多挂单,与做多信号同向,无需取消!") + for order in pending_orders: side_icon = "🟢" if order.get('side') == 'long' else "🔴" entry_type = "现价单" if order.get('entry_type') == 'market' else "挂单" - prompt_parts.append(f"- {side_icon} {order.get('symbol')}: {order.get('side')} | {entry_type}") + side_text = "做多" if order.get('side') == 'long' else "做空" + prompt_parts.append(f"- {side_icon} {order.get('symbol')}: {side_text}({order.get('side')}) | {entry_type}") prompt_parts.append(f" 挂单价: ${order.get('entry_price')} | 数量: {order.get('quantity')} USDT") prompt_parts.append(f" 订单ID: {order.get('order_id')}") else: diff --git a/backend/app/services/telegram_service.py b/backend/app/services/telegram_service.py index e6ed217..9e78efe 100644 --- a/backend/app/services/telegram_service.py +++ b/backend/app/services/telegram_service.py @@ -10,17 +10,31 @@ from app.config import get_settings class TelegramService: """Telegram 机器人通知服务""" - def __init__(self, bot_token: str = "", channel_id: str = ""): + def __init__(self, bot_token: str = "", channel_id: str = "", service_type: str = "default"): """ 初始化 Telegram 服务 Args: bot_token: Telegram Bot Token (从 @BotFather 获取) channel_id: 频道 ID (如 @your_channel 或 -1001234567890) + service_type: 服务类型 (crypto/stock/news/default) """ settings = get_settings() self.bot_token = bot_token or getattr(settings, 'telegram_bot_token', '') - self.channel_id = channel_id or getattr(settings, 'telegram_channel_id', '') + self.service_type = service_type + + # 根据服务类型选择频道ID + if channel_id: + self.channel_id = channel_id + else: + # 根据service_type选择对应的频道 + if service_type == "crypto": + self.channel_id = getattr(settings, 'telegram_crypto_channel_id', '') or getattr(settings, 'telegram_channel_id', '') + elif service_type == "stock": + self.channel_id = getattr(settings, 'telegram_stock_channel_id', '') or getattr(settings, 'telegram_channel_id', '') + else: + self.channel_id = getattr(settings, 'telegram_channel_id', '') + # 检查配置开关和必要参数是否都有效 config_enabled = getattr(settings, 'telegram_enabled', True) self.enabled = config_enabled and bool(self.bot_token and self.channel_id) @@ -30,10 +44,10 @@ class TelegramService: logger.info("Telegram 通知已通过配置禁用") elif self.enabled: self.api_base = f"https://api.telegram.org/bot{self.bot_token}" - logger.info(f"Telegram 通知服务初始化完成,频道: {self.channel_id}") + logger.info(f"Telegram 通知服务初始化完成 [{service_type}],频道: {self.channel_id}") else: self.api_base = "" - logger.warning("Telegram Bot Token 或 Channel ID 未配置,通知功能已禁用") + logger.warning(f"Telegram Bot Token 或 Channel ID 未配置 [{service_type}],通知功能已禁用") async def send_message(self, text: str, parse_mode: str = "HTML") -> bool: """ @@ -245,11 +259,29 @@ class TelegramService: # 全局实例(延迟初始化) _telegram_service: Optional[TelegramService] = None +_telegram_crypto_service: Optional[TelegramService] = None +_telegram_stock_service: Optional[TelegramService] = None def get_telegram_service() -> TelegramService: - """获取 Telegram 服务实例""" + """获取 Telegram 服务实例(默认)""" global _telegram_service if _telegram_service is None: _telegram_service = TelegramService() return _telegram_service + + +def get_telegram_crypto_service() -> TelegramService: + """获取加密货币 Telegram 服务实例""" + global _telegram_crypto_service + if _telegram_crypto_service is None: + _telegram_crypto_service = TelegramService(service_type="crypto") + return _telegram_crypto_service + + +def get_telegram_stock_service() -> TelegramService: + """获取股票 Telegram 服务实例""" + global _telegram_stock_service + if _telegram_stock_service is None: + _telegram_stock_service = TelegramService(service_type="stock") + return _telegram_stock_service