From 926296fc5768ee7f0f7bc7e3f130564478ec2128 Mon Sep 17 00:00:00 2001 From: aaron <> Date: Tue, 24 Feb 2026 15:31:32 +0800 Subject: [PATCH] update --- backend/app/config.py | 3 +- backend/app/crypto_agent/crypto_agent.py | 23 +++++---- backend/app/main.py | 61 +++++++++++++++--------- backend/app/services/feishu_service.py | 57 +++++++++++++++++----- backend/app/stock_agent/stock_agent.py | 4 +- 5 files changed, 102 insertions(+), 46 deletions(-) diff --git a/backend/app/config.py b/backend/app/config.py index 31de243..52eb698 100644 --- a/backend/app/config.py +++ b/backend/app/config.py @@ -94,7 +94,8 @@ class Settings(BaseSettings): binance_api_secret: str = "" # 飞书机器人配置 - feishu_webhook_url: str = "https://open.feishu.cn/open-apis/bot/v2/hook/8a1dcf69-6753-41e2-a393-edc4f7822db0" + feishu_crypto_webhook_url: str = "https://open.feishu.cn/open-apis/bot/v2/hook/8a1dcf69-6753-41e2-a393-edc4f7822db0" # 加密货币通知 + feishu_stock_webhook_url: str = "https://open.feishu.cn/open-apis/bot/v2/hook/408ab727-0dcd-4c7a-bde7-4aad38cbf807" # 股票通知 feishu_enabled: bool = True # 是否启用飞书通知 # Telegram 机器人配置 diff --git a/backend/app/crypto_agent/crypto_agent.py b/backend/app/crypto_agent/crypto_agent.py index fbf5080..b81e84a 100644 --- a/backend/app/crypto_agent/crypto_agent.py +++ b/backend/app/crypto_agent/crypto_agent.py @@ -155,20 +155,27 @@ class CryptoAgent: async def _notify_breakeven_triggered(self, result: Dict[str, Any]): """发送移动止损触发通知""" side_text = "做多" if result.get('side') == 'long' else "做空" + side_icon = '🟢' if result.get('side') == 'long' else '🔴' + pnl_percent = result.get('current_pnl_percent', 0) - message = f"""📈 移动止损已启动 + title = f"📈 移动止损已启动 - {result.get('symbol')}" -交易对: {result.get('symbol')} -方向: {side_text} -开仓价: ${result.get('filled_price', 0):,.2f} -当前盈利: {result.get('current_pnl_percent', 0):.2f}% -新止损价: ${result.get('new_stop_loss', 0):,.2f} + content_parts = [ + f"{side_icon} **方向**: {side_text}", + f"", + f"💰 **开仓价**: ${result.get('filled_price', 0):,.2f}", + f"📈 **当前盈利**: {pnl_percent:+.2f}%", + f"🛑 **新止损价**: ${result.get('new_stop_loss', 0):,.2f}", + f"", + f"💰 锁定利润,让利润奔跑" + ] -💰 锁定利润,让利润奔跑""" + content = "\n".join(content_parts) if self.settings.feishu_enabled: - await self.feishu.send_text(message) + await self.feishu.send_card(title, content, "green") if self.settings.telegram_enabled: + message = f"{title}\n\n{content}" await self.telegram.send_message(message) logger.info(f"已发送移动止损通知: {result.get('order_id')}") diff --git a/backend/app/main.py b/backend/app/main.py index d27e238..cc99846 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -136,36 +136,53 @@ async def price_monitor_loop(): if event_type == 'stop_loss_moved': move_type = result.get('move_type', '') side_text = "做多" if result.get('side') == 'long' else "做空" + side_icon = '🟢' if result.get('side') == 'long' else '🔴' pnl = result.get('current_pnl_percent', 0) + symbol_display = result.get('symbol', '') if move_type == 'trailing_first': - message = f"""📈 移动止损已激活 - -交易对: {result.get('symbol')} -方向: {side_text} -当前盈利: {pnl:+.2f}% -新止损价: ${result.get('new_stop_loss', 0):,.2f} -💰 锁定利润,让利润奔跑""" + title = f"📈 移动止损已激活 - {symbol_display}" + content_parts = [ + f"{side_icon} **方向**: {side_text}", + f"", + f"📈 **当前盈利**: {pnl:+.2f}%", + f"🛑 **新止损价**: ${result.get('new_stop_loss', 0):,.2f}", + f"", + f"💰 锁定利润,让利润奔跑" + ] + color = "green" elif move_type == 'trailing_update': - message = f"""📊 止损已上移 - -交易对: {result.get('symbol')} -方向: {side_text} -当前盈利: {pnl:+.2f}% -新止损价: ${result.get('new_stop_loss', 0):,.2f} -🎯 继续锁定更多利润""" + title = f"📊 止损已上移 - {symbol_display}" + content_parts = [ + f"{side_icon} **方向**: {side_text}", + f"", + f"📈 **当前盈利**: {pnl:+.2f}%", + f"🛑 **新止损价**: ${result.get('new_stop_loss', 0):,.2f}", + f"", + f"🎯 继续锁定更多利润" + ] + color = "blue" elif move_type == 'breakeven': - message = f"""📈 移动止损已启动 + title = f"📈 移动止损已启动 - {symbol_display}" + content_parts = [ + f"{side_icon} **方向**: {side_text}", + f"", + f"📈 **当前盈利**: {pnl:+.2f}%", + f"🛑 **新止损价**: ${result.get('new_stop_loss', 0):,.2f}", + f"", + f"💰 锁定利润,让利润奔跑" + ] + color = "green" + else: + continue -交易对: {result.get('symbol')} -方向: {side_text} -当前盈利: {pnl:+.2f}% -止损移至: ${result.get('new_stop_loss', 0):,.2f} -💰 锁定利润,让利润奔跑""" + content = "\n".join(content_parts) # 发送通知 - await feishu.send_text(message) - await telegram.send_message(message) + await feishu.send_card(title, content, color) + if telegram: + message = f"{title}\n\n{content}" + await telegram.send_message(message) logger.info(f"后台监控触发止损移动: {result.get('order_id')} | {symbol}") continue diff --git a/backend/app/services/feishu_service.py b/backend/app/services/feishu_service.py index ba35ca9..ec8ef0f 100644 --- a/backend/app/services/feishu_service.py +++ b/backend/app/services/feishu_service.py @@ -1,5 +1,6 @@ """ 飞书通知服务 - 通过 Webhook 发送交易信号通知 +支持加密货币和股票两个独立的 webhook """ import json import httpx @@ -11,25 +12,40 @@ from app.config import get_settings class FeishuService: """飞书机器人通知服务""" - def __init__(self, webhook_url: str = ""): + def __init__(self, webhook_url: str = "", service_type: str = "crypto"): """ 初始化飞书服务 Args: - webhook_url: 飞书机器人 Webhook URL + webhook_url: 飞书机器人 Webhook URL(如果为空,则根据 service_type 从配置读取) + service_type: 服务类型 ("crypto" 或 "stock") """ settings = get_settings() - self.webhook_url = webhook_url or getattr(settings, 'feishu_webhook_url', '') + + # 如果传入了 webhook_url,直接使用 + if webhook_url: + self.webhook_url = webhook_url + else: + # 否则根据服务类型从配置读取 + if service_type == "crypto": + self.webhook_url = getattr(settings, 'feishu_crypto_webhook_url', '') + elif service_type == "stock": + self.webhook_url = getattr(settings, 'feishu_stock_webhook_url', '') + else: + # 兼容旧配置 + self.webhook_url = getattr(settings, 'feishu_webhook_url', '') + # 检查配置开关和 webhook_url 是否都有效 config_enabled = getattr(settings, 'feishu_enabled', True) self.enabled = config_enabled and bool(self.webhook_url) + self.service_type = service_type if not config_enabled: - logger.info("飞书通知已通过配置禁用") + logger.info(f"飞书通知已通过配置禁用") elif self.enabled: - logger.info("飞书通知服务初始化完成") + logger.info(f"飞书通知服务初始化完成 ({service_type})") else: - logger.warning("飞书 Webhook URL 未配置,通知功能已禁用") + logger.warning(f"飞书 Webhook URL 未配置 ({service_type}),通知功能已禁用") async def send_text(self, message: str) -> bool: """ @@ -266,13 +282,28 @@ class FeishuService: return False -# 全局实例(延迟初始化) -_feishu_service: Optional[FeishuService] = None + +# 全局实例(延迟初始化)- 分别用于加密货币和股票 +_feishu_crypto_service: Optional[FeishuService] = None +_feishu_stock_service: Optional[FeishuService] = None def get_feishu_service() -> FeishuService: - """获取飞书服务实例""" - global _feishu_service - if _feishu_service is None: - _feishu_service = FeishuService() - return _feishu_service + """获取飞书服务实例(默认 crypto,保持向后兼容)""" + return get_feishu_crypto_service() + + +def get_feishu_crypto_service() -> FeishuService: + """获取加密货币飞书服务实例""" + global _feishu_crypto_service + if _feishu_crypto_service is None: + _feishu_crypto_service = FeishuService(service_type="crypto") + return _feishu_crypto_service + + +def get_feishu_stock_service() -> FeishuService: + """获取股票飞书服务实例""" + global _feishu_stock_service + if _feishu_stock_service is None: + _feishu_stock_service = FeishuService(service_type="stock") + return _feishu_stock_service diff --git a/backend/app/stock_agent/stock_agent.py b/backend/app/stock_agent/stock_agent.py index 6c3f668..e2e422f 100644 --- a/backend/app/stock_agent/stock_agent.py +++ b/backend/app/stock_agent/stock_agent.py @@ -10,7 +10,7 @@ import pandas as pd from app.utils.logger import logger from app.config import get_settings from app.services.yfinance_service import get_yfinance_service -from app.services.feishu_service import get_feishu_service +from app.services.feishu_service import get_feishu_stock_service from app.services.telegram_service import get_telegram_service from app.services.signal_database_service import get_signal_db_service from app.services.fundamental_service import get_fundamental_service @@ -86,7 +86,7 @@ class StockAgent: """初始化智能体""" self.settings = get_settings() self.yfinance = get_yfinance_service() - self.feishu = get_feishu_service() + self.feishu = get_feishu_stock_service() self.telegram = get_telegram_service() self.market_analyzer = StockMarketSignalAnalyzer() # 使用新的市场信号分析器 self.signal_db = get_signal_db_service() # 信号数据库服务