update
This commit is contained in:
parent
0a637053fc
commit
1a9bf9f74b
@ -97,6 +97,7 @@ class Settings(BaseSettings):
|
|||||||
feishu_crypto_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_stock_webhook_url: str = "https://open.feishu.cn/open-apis/bot/v2/hook/408ab727-0dcd-4c7a-bde7-4aad38cbf807" # 股票通知
|
||||||
feishu_news_webhook_url: str = "https://open.feishu.cn/open-apis/bot/v2/hook/c7fd0db7-d295-451c-b943-130278a6cd9d" # 新闻智能体通知
|
feishu_news_webhook_url: str = "https://open.feishu.cn/open-apis/bot/v2/hook/c7fd0db7-d295-451c-b943-130278a6cd9d" # 新闻智能体通知
|
||||||
|
feishu_paper_trading_webhook_url: str = "https://open.feishu.cn/open-apis/bot/v2/hook/3f5642e7-420b-45f7-8f88-fff92bb98c69" # 模拟交易通知(交易信号+决策+执行)
|
||||||
feishu_error_webhook_url: str = "https://open.feishu.cn/open-apis/bot/v2/hook/ba6952c9-3b0c-4bc1-8a43-ceaacb27b043" # 系统异常通知
|
feishu_error_webhook_url: str = "https://open.feishu.cn/open-apis/bot/v2/hook/ba6952c9-3b0c-4bc1-8a43-ceaacb27b043" # 系统异常通知
|
||||||
feishu_enabled: bool = True # 是否启用飞书通知
|
feishu_enabled: bool = True # 是否启用飞书通知
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import pandas as pd
|
|||||||
from app.utils.logger import logger
|
from app.utils.logger import logger
|
||||||
from app.config import get_settings
|
from app.config import get_settings
|
||||||
from app.services.bitget_service import bitget_service
|
from app.services.bitget_service import bitget_service
|
||||||
from app.services.feishu_service import get_feishu_service
|
from app.services.feishu_service import get_feishu_service, get_feishu_paper_trading_service
|
||||||
from app.services.telegram_service import get_telegram_service
|
from app.services.telegram_service import get_telegram_service
|
||||||
from app.services.dingtalk_service import get_dingtalk_service
|
from app.services.dingtalk_service import get_dingtalk_service
|
||||||
from app.services.paper_trading_service import get_paper_trading_service
|
from app.services.paper_trading_service import get_paper_trading_service
|
||||||
@ -40,7 +40,8 @@ class CryptoAgent:
|
|||||||
CryptoAgent._initialized = True
|
CryptoAgent._initialized = True
|
||||||
self.settings = get_settings()
|
self.settings = get_settings()
|
||||||
self.exchange = bitget_service # 交易所服务
|
self.exchange = bitget_service # 交易所服务
|
||||||
self.feishu = get_feishu_service()
|
self.feishu = get_feishu_service() # 通用飞书服务(crypto等)
|
||||||
|
self.feishu_paper = get_feishu_paper_trading_service() # 模拟交易专用飞书服务
|
||||||
self.telegram = get_telegram_service()
|
self.telegram = get_telegram_service()
|
||||||
self.dingtalk = get_dingtalk_service() # 添加钉钉服务
|
self.dingtalk = get_dingtalk_service() # 添加钉钉服务
|
||||||
|
|
||||||
@ -138,7 +139,7 @@ class CryptoAgent:
|
|||||||
content = "\n".join(content_parts)
|
content = "\n".join(content_parts)
|
||||||
|
|
||||||
if self.settings.feishu_enabled:
|
if self.settings.feishu_enabled:
|
||||||
await self.feishu.send_card(title, content, "green")
|
await self.feishu_paper.send_card(title, content, "green")
|
||||||
if self.settings.telegram_enabled:
|
if self.settings.telegram_enabled:
|
||||||
message = f"{title}\n\n{content}"
|
message = f"{title}\n\n{content}"
|
||||||
await self.telegram.send_message(message)
|
await self.telegram.send_message(message)
|
||||||
@ -164,7 +165,7 @@ class CryptoAgent:
|
|||||||
content = "\n".join(content_parts)
|
content = "\n".join(content_parts)
|
||||||
|
|
||||||
if self.settings.feishu_enabled:
|
if self.settings.feishu_enabled:
|
||||||
await self.feishu.send_card(title, content, "orange")
|
await self.feishu_paper.send_card(title, content, "orange")
|
||||||
if self.settings.telegram_enabled:
|
if self.settings.telegram_enabled:
|
||||||
message = f"{title}\n\n{content}"
|
message = f"{title}\n\n{content}"
|
||||||
await self.telegram.send_message(message)
|
await self.telegram.send_message(message)
|
||||||
@ -193,7 +194,7 @@ class CryptoAgent:
|
|||||||
content = "\n".join(content_parts)
|
content = "\n".join(content_parts)
|
||||||
|
|
||||||
if self.settings.feishu_enabled:
|
if self.settings.feishu_enabled:
|
||||||
await self.feishu.send_card(title, content, "green")
|
await self.feishu_paper.send_card(title, content, "green")
|
||||||
if self.settings.telegram_enabled:
|
if self.settings.telegram_enabled:
|
||||||
message = f"{title}\n\n{content}"
|
message = f"{title}\n\n{content}"
|
||||||
await self.telegram.send_message(message)
|
await self.telegram.send_message(message)
|
||||||
@ -246,7 +247,7 @@ class CryptoAgent:
|
|||||||
content = "\n".join(content_parts)
|
content = "\n".join(content_parts)
|
||||||
|
|
||||||
if self.settings.feishu_enabled:
|
if self.settings.feishu_enabled:
|
||||||
await self.feishu.send_card(title, content, color)
|
await self.feishu_paper.send_card(title, content, color)
|
||||||
if self.settings.telegram_enabled:
|
if self.settings.telegram_enabled:
|
||||||
message = f"{title}\n\n{content}"
|
message = f"{title}\n\n{content}"
|
||||||
await self.telegram.send_message(message)
|
await self.telegram.send_message(message)
|
||||||
@ -787,8 +788,7 @@ class CryptoAgent:
|
|||||||
if decision_type == 'HOLD':
|
if decision_type == 'HOLD':
|
||||||
reasoning = paper_decision.get('reasoning', '观望')
|
reasoning = paper_decision.get('reasoning', '观望')
|
||||||
logger.info(f"\n📊 交易决策: {reasoning}")
|
logger.info(f"\n📊 交易决策: {reasoning}")
|
||||||
# 有信号但决策为 HOLD,发送未执行通知
|
# HOLD决策的理由已在交易决策通知中说明,无需单独通知
|
||||||
await self._notify_signal_not_executed(market_signal, paper_decision, current_price, is_paper=True)
|
|
||||||
else:
|
else:
|
||||||
logger.info(f"\n📊 【执行交易】")
|
logger.info(f"\n📊 【执行交易】")
|
||||||
|
|
||||||
@ -810,14 +810,11 @@ class CryptoAgent:
|
|||||||
paper_executed = True
|
paper_executed = True
|
||||||
else:
|
else:
|
||||||
logger.error(f" ❌ 订单对象无效: 缺少order_id属性")
|
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:
|
else:
|
||||||
# 有信号但订单创建失败,发送未执行通知
|
# 订单创建失败,理由已在日志中记录,无需单独通知
|
||||||
reason = result.get('message', '订单创建失败') if result else '订单创建失败'
|
reason = result.get('message', '订单创建失败') if result else '订单创建失败'
|
||||||
logger.warning(f" ⚠️ 交易未执行: {reason}")
|
logger.warning(f" ⚠️ 交易未执行: {reason}")
|
||||||
await self._notify_signal_not_executed(market_signal, paper_decision, current_price, is_paper=True, reason=reason)
|
|
||||||
logger.warning(f" ⚠️ 交易未执行: {reason}")
|
|
||||||
elif decision_type == 'CLOSE':
|
elif decision_type == 'CLOSE':
|
||||||
await self._execute_close(paper_decision, paper_trading=True)
|
await self._execute_close(paper_decision, paper_trading=True)
|
||||||
# CLOSE 操作也发送执行通知
|
# CLOSE 操作也发送执行通知
|
||||||
@ -845,8 +842,7 @@ class CryptoAgent:
|
|||||||
if decision_type == 'HOLD':
|
if decision_type == 'HOLD':
|
||||||
reasoning = real_decision.get('reasoning', '观望')
|
reasoning = real_decision.get('reasoning', '观望')
|
||||||
logger.info(f"\n💰 实盘交易: {reasoning}")
|
logger.info(f"\n💰 实盘交易: {reasoning}")
|
||||||
# 有信号但决策为 HOLD,发送未执行通知
|
# HOLD决策的理由已在交易决策通知中说明,无需单独通知
|
||||||
await self._notify_signal_not_executed(market_signal, real_decision, current_price, is_paper=False)
|
|
||||||
else:
|
else:
|
||||||
logger.info(f"\n💰 【执行实盘交易】")
|
logger.info(f"\n💰 【执行实盘交易】")
|
||||||
|
|
||||||
@ -859,10 +855,9 @@ class CryptoAgent:
|
|||||||
await self._send_signal_notification(market_signal, real_decision, current_price, is_paper=False)
|
await self._send_signal_notification(market_signal, real_decision, current_price, is_paper=False)
|
||||||
real_executed = True
|
real_executed = True
|
||||||
else:
|
else:
|
||||||
# 有信号但订单创建失败,发送未执行通知
|
# 订单创建失败,理由已在日志中记录,无需单独通知
|
||||||
reason = result.get('message', '订单创建失败') if result else '订单创建失败'
|
reason = result.get('message', '订单创建失败') if result else '订单创建失败'
|
||||||
await self._notify_signal_not_executed(market_signal, real_decision, current_price, is_paper=False, reason=reason)
|
logger.warning(f" ⚠️ 实盘交易未执行: {reason}")
|
||||||
logger.warning(f" ⚠️ 实盘交易未执行,已发送通知")
|
|
||||||
elif decision_type == 'CLOSE':
|
elif decision_type == 'CLOSE':
|
||||||
await self._execute_close(real_decision, paper_trading=False)
|
await self._execute_close(real_decision, paper_trading=False)
|
||||||
# CLOSE 操作也发送执行通知
|
# CLOSE 操作也发送执行通知
|
||||||
@ -1008,12 +1003,12 @@ class CryptoAgent:
|
|||||||
sl_display = "N/A"
|
sl_display = "N/A"
|
||||||
tp_display = "N/A"
|
tp_display = "N/A"
|
||||||
|
|
||||||
# 构建卡片标题和颜色
|
# 构建卡片标题和颜色 - 添加 [信号] 前缀区分
|
||||||
if sig_action == 'buy':
|
if sig_action == 'buy':
|
||||||
title = f"🟢 {symbol} {timeframe_text}做多信号"
|
title = f"[信号] 🟢 {symbol} {timeframe_text}做多信号"
|
||||||
color = "green"
|
color = "green"
|
||||||
else:
|
else:
|
||||||
title = f"🔴 {symbol} {timeframe_text}做空信号"
|
title = f"[信号] 🔴 {symbol} {timeframe_text}做空信号"
|
||||||
color = "red"
|
color = "red"
|
||||||
|
|
||||||
# 构建卡片内容
|
# 构建卡片内容
|
||||||
@ -1046,7 +1041,7 @@ class CryptoAgent:
|
|||||||
|
|
||||||
content = "\n".join(content_parts)
|
content = "\n".join(content_parts)
|
||||||
|
|
||||||
# 根据配置发送通知
|
# 根据配置发送通知 - [信号] 发送到 crypto webhook
|
||||||
if self.settings.feishu_enabled:
|
if self.settings.feishu_enabled:
|
||||||
await self.feishu.send_card(title, content, color)
|
await self.feishu.send_card(title, content, color)
|
||||||
if self.settings.telegram_enabled:
|
if self.settings.telegram_enabled:
|
||||||
@ -1098,8 +1093,8 @@ class CryptoAgent:
|
|||||||
}
|
}
|
||||||
color = color_map.get(decision_type, 'blue')
|
color = color_map.get(decision_type, 'blue')
|
||||||
|
|
||||||
# 构建标题
|
# 构建标题 - 添加 [决策] 前缀区分
|
||||||
title = f"{account_type} {symbol} 交易决策: {decision_text}"
|
title = f"[决策] {account_type} {symbol} 交易决策: {decision_text}"
|
||||||
|
|
||||||
# 获取最佳信号用于显示
|
# 获取最佳信号用于显示
|
||||||
best_signal = self._get_best_signal_from_market(market_signal)
|
best_signal = self._get_best_signal_from_market(market_signal)
|
||||||
@ -1181,7 +1176,7 @@ class CryptoAgent:
|
|||||||
|
|
||||||
content = "\n".join(content_parts)
|
content = "\n".join(content_parts)
|
||||||
|
|
||||||
# 发送通知
|
# 发送通知 - [决策] 发送到 crypto webhook
|
||||||
if self.settings.feishu_enabled:
|
if self.settings.feishu_enabled:
|
||||||
await self.feishu.send_card(title, content, color)
|
await self.feishu.send_card(title, content, color)
|
||||||
if self.settings.telegram_enabled:
|
if self.settings.telegram_enabled:
|
||||||
@ -1253,26 +1248,26 @@ class CryptoAgent:
|
|||||||
position_map = {'heavy': '🔥 重仓', 'medium': '📊 中仓', 'light': '🌱 轻仓'}
|
position_map = {'heavy': '🔥 重仓', 'medium': '📊 中仓', 'light': '🌱 轻仓'}
|
||||||
position_display = position_map.get(position_size, '🌱 轻仓')
|
position_display = position_map.get(position_size, '🌱 轻仓')
|
||||||
|
|
||||||
# 构建卡片标题和颜色 - 考虑入场方式
|
# 构建卡片标题和颜色 - 考虑入场方式,添加 [执行] 前缀区分
|
||||||
# 挂单时标题显示"挂单",现价单时显示"开仓"/"平仓"等
|
# 挂单时标题显示"挂单",现价单时显示"开仓"/"平仓"等
|
||||||
if decision_type == 'OPEN':
|
if decision_type == 'OPEN':
|
||||||
decision_title = '挂单' if entry_type == 'limit' else '开仓'
|
decision_title = '挂单' if entry_type == 'limit' else '开仓'
|
||||||
title = f"{account_type} {symbol} {decision_title}"
|
title = f"[执行] {account_type} {symbol} {decision_title}"
|
||||||
color = "green"
|
color = "green"
|
||||||
elif decision_type == 'CLOSE':
|
elif decision_type == 'CLOSE':
|
||||||
decision_title = '挂单' if entry_type == 'limit' else '平仓'
|
decision_title = '挂单' if entry_type == 'limit' else '平仓'
|
||||||
title = f"{account_type} {symbol} {decision_title}"
|
title = f"[执行] {account_type} {symbol} {decision_title}"
|
||||||
color = "orange"
|
color = "orange"
|
||||||
elif decision_type == 'ADD':
|
elif decision_type == 'ADD':
|
||||||
decision_title = '挂单' if entry_type == 'limit' else '加仓'
|
decision_title = '挂单' if entry_type == 'limit' else '加仓'
|
||||||
title = f"{account_type} {symbol} {decision_title}"
|
title = f"[执行] {account_type} {symbol} {decision_title}"
|
||||||
color = "green"
|
color = "green"
|
||||||
elif decision_type == 'REDUCE':
|
elif decision_type == 'REDUCE':
|
||||||
decision_title = '挂单' if entry_type == 'limit' else '减仓'
|
decision_title = '挂单' if entry_type == 'limit' else '减仓'
|
||||||
title = f"{account_type} {symbol} {decision_title}"
|
title = f"[执行] {account_type} {symbol} {decision_title}"
|
||||||
color = "orange"
|
color = "orange"
|
||||||
else:
|
else:
|
||||||
title = f"{account_type} {symbol} 交易执行"
|
title = f"[执行] {account_type} {symbol} 交易执行"
|
||||||
color = "blue"
|
color = "blue"
|
||||||
|
|
||||||
# 构建卡片内容
|
# 构建卡片内容
|
||||||
@ -1315,9 +1310,9 @@ class CryptoAgent:
|
|||||||
|
|
||||||
content = "\n".join(content_parts)
|
content = "\n".join(content_parts)
|
||||||
|
|
||||||
# 根据配置发送通知
|
# 根据配置发送通知 - 所有订单执行都发送到 paper trading webhook
|
||||||
if self.settings.feishu_enabled:
|
if self.settings.feishu_enabled:
|
||||||
await self.feishu.send_card(title, content, color)
|
await self.feishu_paper.send_card(title, content, color)
|
||||||
if self.settings.telegram_enabled:
|
if self.settings.telegram_enabled:
|
||||||
# Telegram 使用文本格式
|
# Telegram 使用文本格式
|
||||||
message = f"{title}\n\n{content}"
|
message = f"{title}\n\n{content}"
|
||||||
@ -1806,7 +1801,7 @@ class CryptoAgent:
|
|||||||
content = "\n".join(content_parts)
|
content = "\n".join(content_parts)
|
||||||
|
|
||||||
if self.settings.feishu_enabled:
|
if self.settings.feishu_enabled:
|
||||||
await self.feishu.send_card(title, content, "red")
|
await self.feishu_paper.send_card(title, content, "red")
|
||||||
if self.settings.telegram_enabled:
|
if self.settings.telegram_enabled:
|
||||||
message = f"{title}\n\n{content}"
|
message = f"{title}\n\n{content}"
|
||||||
await self.telegram.send_message(message)
|
await self.telegram.send_message(message)
|
||||||
@ -1859,7 +1854,7 @@ class CryptoAgent:
|
|||||||
content = "\n".join(content_parts)
|
content = "\n".join(content_parts)
|
||||||
|
|
||||||
if self.settings.feishu_enabled:
|
if self.settings.feishu_enabled:
|
||||||
await self.feishu.send_card(title, content, "blue")
|
await self.feishu_paper.send_card(title, content, "blue")
|
||||||
if self.settings.telegram_enabled:
|
if self.settings.telegram_enabled:
|
||||||
message = f"{title}\n\n{content}"
|
message = f"{title}\n\n{content}"
|
||||||
await self.telegram.send_message(message)
|
await self.telegram.send_message(message)
|
||||||
|
|||||||
@ -18,7 +18,7 @@ class FeishuService:
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
webhook_url: 飞书机器人 Webhook URL(如果为空,则根据 service_type 从配置读取)
|
webhook_url: 飞书机器人 Webhook URL(如果为空,则根据 service_type 从配置读取)
|
||||||
service_type: 服务类型 ("crypto" 或 "stock")
|
service_type: 服务类型 ("crypto", "stock", "news", "paper_trading", "error")
|
||||||
"""
|
"""
|
||||||
settings = get_settings()
|
settings = get_settings()
|
||||||
|
|
||||||
@ -33,6 +33,10 @@ class FeishuService:
|
|||||||
self.webhook_url = getattr(settings, 'feishu_stock_webhook_url', '')
|
self.webhook_url = getattr(settings, 'feishu_stock_webhook_url', '')
|
||||||
elif service_type == "news":
|
elif service_type == "news":
|
||||||
self.webhook_url = getattr(settings, 'feishu_news_webhook_url', '')
|
self.webhook_url = getattr(settings, 'feishu_news_webhook_url', '')
|
||||||
|
elif service_type == "paper_trading":
|
||||||
|
self.webhook_url = getattr(settings, 'feishu_paper_trading_webhook_url', '')
|
||||||
|
elif service_type == "error":
|
||||||
|
self.webhook_url = getattr(settings, 'feishu_error_webhook_url', '')
|
||||||
else:
|
else:
|
||||||
# 兼容旧配置
|
# 兼容旧配置
|
||||||
self.webhook_url = getattr(settings, 'feishu_webhook_url', '')
|
self.webhook_url = getattr(settings, 'feishu_webhook_url', '')
|
||||||
@ -285,10 +289,11 @@ class FeishuService:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 全局实例(延迟初始化)- 分别用于加密货币、股票和新闻
|
# 全局实例(延迟初始化)- 分别用于加密货币、股票、新闻和模拟交易
|
||||||
_feishu_crypto_service: Optional[FeishuService] = None
|
_feishu_crypto_service: Optional[FeishuService] = None
|
||||||
_feishu_stock_service: Optional[FeishuService] = None
|
_feishu_stock_service: Optional[FeishuService] = None
|
||||||
_feishu_news_service: Optional[FeishuService] = None
|
_feishu_news_service: Optional[FeishuService] = None
|
||||||
|
_feishu_paper_trading_service: Optional[FeishuService] = None
|
||||||
|
|
||||||
|
|
||||||
def get_feishu_service() -> FeishuService:
|
def get_feishu_service() -> FeishuService:
|
||||||
@ -318,3 +323,11 @@ def get_feishu_news_service() -> FeishuService:
|
|||||||
if _feishu_news_service is None:
|
if _feishu_news_service is None:
|
||||||
_feishu_news_service = FeishuService(service_type="news")
|
_feishu_news_service = FeishuService(service_type="news")
|
||||||
return _feishu_news_service
|
return _feishu_news_service
|
||||||
|
|
||||||
|
|
||||||
|
def get_feishu_paper_trading_service() -> FeishuService:
|
||||||
|
"""获取模拟交易飞书服务实例"""
|
||||||
|
global _feishu_paper_trading_service
|
||||||
|
if _feishu_paper_trading_service is None:
|
||||||
|
_feishu_paper_trading_service = FeishuService(service_type="paper_trading")
|
||||||
|
return _feishu_paper_trading_service
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user