This commit is contained in:
aaron 2026-02-24 23:15:42 +08:00
parent 7e41c0f587
commit 568faa1f8d
3 changed files with 160 additions and 81 deletions

View File

@ -115,40 +115,54 @@ class CryptoAgent:
async def _notify_order_filled(self, result: Dict[str, Any]):
"""发送挂单成交通知"""
side_text = "做多" if result.get('side') == 'long' else "做空"
side_icon = "🟢" if result.get('side') == 'long' else "🔴"
grade = result.get('signal_grade', 'N/A')
message = f"""✅ 挂单成交
title = f"✅ 挂单成交 - {result.get('symbol')}"
交易对: {result.get('symbol')}
方向: {side_text}
等级: {grade}
挂单价: ${result.get('entry_price', 0):,.2f}
成交价: ${result.get('filled_price', 0):,.2f}
仓位: ${result.get('quantity', 0):,.0f}
止损: ${result.get('stop_loss', 0):,.2f}
止盈: ${result.get('take_profit', 0):,.2f}"""
content_parts = [
f"{side_icon} **方向**: {side_text}",
f"⭐ **信号等级**: {grade}",
f"💰 **挂单价**: ${result.get('entry_price', 0):,.2f}",
f"🎯 **成交价**: ${result.get('filled_price', 0):,.2f}",
f"💵 **仓位**: ${result.get('quantity', 0):,.0f}",
]
if result.get('stop_loss'):
content_parts.append(f"🛑 **止损**: ${result.get('stop_loss', 0):,.2f}")
if result.get('take_profit'):
content_parts.append(f"🎯 **止盈**: ${result.get('take_profit', 0):,.2f}")
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')}")
async def _notify_pending_cancelled(self, result: Dict[str, Any]):
"""发送挂单撤销通知"""
side_text = "做多" if result.get('side') == 'long' else "做空"
side_icon = "🟢" if result.get('side') == 'long' else "🔴"
new_side_text = "做多" if result.get('new_side') == 'long' else "做空"
message = f"""⚠️ 挂单撤销
title = f"⚠️ 挂单撤销 - {result.get('symbol')}"
交易对: {result.get('symbol')}
原方向: {side_text}
挂单价: ${result.get('entry_price', 0):,.2f}
原因: 收到反向{new_side_text}信号自动撤销"""
content_parts = [
f"{side_icon} **原方向**: {side_text}",
f"💰 **挂单价**: ${result.get('entry_price', 0):,.2f}",
f"",
f"📝 **原因**: 收到反向{new_side_text}信号,自动撤销",
]
content = "\n".join(content_parts)
if self.settings.feishu_enabled:
await self.feishu.send_text(message)
await self.feishu.send_card(title, content, "orange")
if self.settings.telegram_enabled:
message = f"{title}\n\n{content}"
await self.telegram.send_message(message)
logger.info(f"已发送挂单撤销通知: {result.get('order_id')}")
@ -187,31 +201,42 @@ class CryptoAgent:
if status == 'closed_tp':
emoji = "🎯"
status_text = "止盈平仓"
color = "green"
elif status == 'closed_sl':
emoji = "🛑"
status_text = "止损平仓"
color = "red"
elif status == 'closed_be':
emoji = "📈"
status_text = "移动止损"
color = "orange"
else:
emoji = "📤"
status_text = "手动平仓"
color = "blue"
win_text = "盈利" if is_win else "亏损"
win_emoji = "" if is_win else ""
side_text = "做多" if result.get('side') == 'long' else "做空"
side_icon = "🟢" if result.get('side') == 'long' else "🔴"
message = f"""{emoji} 订单{status_text}
title = f"{emoji} 订单{status_text}"
交易对: {result.get('symbol')}
方向: {side_text}
入场: ${result.get('entry_price', 0):,.2f}
出场: ${result.get('exit_price', 0):,.2f}
{win_text}: {result.get('pnl_percent', 0):+.2f}% (${result.get('pnl_amount', 0):+.2f})
持仓时间: {result.get('hold_duration', 'N/A')}"""
content_parts = [
f"{side_icon} **方向**: {side_text}",
f"💰 **交易对**: {result.get('symbol')}",
f"📊 **入场**: ${result.get('entry_price', 0):,.2f}",
f"🎯 **出场**: ${result.get('exit_price', 0):,.2f}",
f"{win_emoji} **{win_text}**: {result.get('pnl_percent', 0):+.2f}% (${result.get('pnl_amount', 0):+.2f})",
f"⏱️ **持仓时间**: {result.get('hold_duration', 'N/A')}",
]
content = "\n".join(content_parts)
if self.settings.feishu_enabled:
await self.feishu.send_text(message)
await self.feishu.send_card(title, content, color)
if self.settings.telegram_enabled:
message = f"{title}\n\n{content}"
await self.telegram.send_message(message)
logger.info(f"已发送订单平仓通知: {result.get('order_id')}")
@ -1371,25 +1396,30 @@ class CryptoAgent:
"""发送实盘订单创建通知"""
side = signal.get('action', 'buy')
side_text = "做多" if side == 'buy' else "做空"
side_icon = "🟢" if side == 'buy' else "🔴"
grade = signal.get('grade', 'N/A')
position_size = result.get('position_size', 'light')
quantity = result.get('quantity', 0) # 这是保证金金额
position_value = quantity * 20 # 持仓价值 = 保证金 × 杠杆
message = f"""💰 实盘订单已创建
title = f"💰 实盘订单已创建 - {symbol}"
交易对: {symbol}
方向: {side_text}
等级: {grade}
仓位: {position_size}
持仓价值: ${position_value:,.2f}
订单 ID: {result.get('order_id', '')[:12]}...
content_parts = [
f"{side_icon} **方向**: {side_text}",
f"⭐ **信号等级**: {grade}",
f"📊 **仓位**: {position_size}",
f"💰 **持仓价值**: ${position_value:,.2f}",
f"🆔 **订单ID**: {result.get('order_id', '')[:12]}...",
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, "red")
if self.settings.telegram_enabled:
message = f"{title}\n\n{content}"
await self.telegram.send_message(message)
logger.info(f"已发送实盘订单创建通知: {result.get('order_id')}")
@ -1411,32 +1441,36 @@ class CryptoAgent:
}
action_text = action_map.get(action, action)
message = f"""{action_text}
title = f"{action_text} - {symbol}"
交易对: {symbol}
订单: {order_id[:8]}
原因: {reason}"""
content_parts = [
f"📊 **订单**: {order_id[:8]}",
f"📝 **原因**: {reason}",
]
if action == 'ADJUST_SL_TP':
changes = result.get('changes', [])
message += f"\n调整内容: {', '.join(changes)}"
content_parts.append(f"🔄 **调整内容**: {', '.join(changes)}")
elif action in ['PARTIAL_CLOSE', 'FULL_CLOSE']:
pnl_info = result.get('pnl', {})
if pnl_info:
pnl = pnl_info.get('pnl', 0)
pnl_percent = pnl_info.get('pnl_percent', 0)
message += f"\n实现盈亏: ${pnl:+.2f} ({pnl_percent:+.1f}%)"
content_parts.append(f"💰 **实现盈亏**: ${pnl:+.2f} ({pnl_percent:+.1f}%)")
if action == 'PARTIAL_CLOSE':
close_percent = decision.get('close_percent', 0)
remaining = result.get('remaining_quantity', 0)
message += f"\n平仓比例: {close_percent:.0f}%"
message += f"\n剩余仓位: ${remaining:,.0f}"
content_parts.append(f"📊 **平仓比例**: {close_percent:.0f}%")
content_parts.append(f"💵 **剩余仓位**: ${remaining:,.0f}")
content = "\n".join(content_parts)
if self.settings.feishu_enabled:
await self.feishu.send_text(message)
await self.feishu.send_card(title, content, "blue")
if self.settings.telegram_enabled:
message = f"{title}\n\n{content}"
await self.telegram.send_message(message)
async def _notify_signal_not_executed(

View File

@ -50,22 +50,27 @@ async def price_monitor_loop():
# 处理挂单成交事件
if event_type == 'order_filled':
side_text = "做多" if result.get('side') == 'long' else "做空"
side_icon = "🟢" if result.get('side') == 'long' else "🔴"
grade = result.get('signal_grade', 'N/A')
message = f"""✅ 挂单成交
title = f"✅ 挂单成交 - {result.get('symbol')}"
content_parts = [
f"{side_icon} **方向**: {side_text}",
f"⭐ **信号等级**: {grade}",
f"💰 **挂单价**: ${result.get('entry_price', 0):,.2f}",
f"🎯 **成交价**: ${result.get('filled_price', 0):,.2f}",
f"💵 **仓位**: ${result.get('quantity', 0):,.0f}",
]
if result.get('stop_loss'):
content_parts.append(f"🛑 **止损**: ${result.get('stop_loss', 0):,.2f}")
if result.get('take_profit'):
content_parts.append(f"🎯 **止盈**: ${result.get('take_profit', 0):,.2f}")
交易对: {result.get('symbol')}
方向: {side_text}
等级: {grade}
挂单价: ${result.get('entry_price', 0):,.2f}
成交价: ${result.get('filled_price', 0):,.2f}
仓位: ${result.get('quantity', 0):,.0f}
止损: ${result.get('stop_loss', 0):,.2f}
止盈: ${result.get('take_profit', 0):,.2f}"""
content = "\n".join(content_parts)
# 发送通知
await feishu.send_text(message)
await telegram.send_message(message)
await feishu.send_card(title, content, "green")
await telegram.send_message(f"{title}\n\n{content}")
logger.info(f"后台监控触发挂单成交: {result.get('order_id')} | {symbol}")
continue
@ -75,31 +80,38 @@ async def price_monitor_loop():
if status == 'closed_tp':
emoji = "🎯"
status_text = "止盈平仓"
color = "green"
elif status == 'closed_sl':
emoji = "🛑"
status_text = "止损平仓"
color = "red"
elif status == 'closed_be':
emoji = "🔒"
status_text = "保本止损"
color = "orange"
else:
emoji = "📤"
status_text = "平仓"
color = "blue"
win_text = "盈利" if is_win else "亏损"
win_emoji = "" if is_win else ""
side_text = "做多" if result.get('side') == 'long' else "做空"
side_icon = "🟢" if result.get('side') == 'long' else "🔴"
message = f"""{emoji} 订单{status_text}
交易对: {result.get('symbol')}
方向: {side_text}
入场: ${result.get('entry_price', 0):,.2f}
出场: ${result.get('exit_price', 0):,.2f}
{win_text}: {result.get('pnl_percent', 0):+.2f}% (${result.get('pnl_amount', 0):+.2f})
持仓时间: {result.get('hold_duration', 'N/A')}"""
title = f"{emoji} 订单{status_text} - {result.get('symbol')}"
content_parts = [
f"{side_icon} **方向**: {side_text}",
f"📊 **入场**: ${result.get('entry_price', 0):,.2f}",
f"🎯 **出场**: ${result.get('exit_price', 0):,.2f}",
f"{win_emoji} **{win_text}**: {result.get('pnl_percent', 0):+.2f}% (${result.get('pnl_amount', 0):+.2f})",
f"⏱️ **持仓时间**: {result.get('hold_duration', 'N/A')}",
]
content = "\n".join(content_parts)
# 发送通知
await feishu.send_text(message)
await telegram.send_message(message)
await feishu.send_card(title, content, color)
await telegram.send_message(f"{title}\n\n{content}")
logger.info(f"后台监控触发平仓: {result.get('order_id')} | {symbol}")
except Exception as e:
@ -223,31 +235,38 @@ async def price_monitor_loop():
if status == 'closed_tp':
emoji = "🎯"
status_text = "止盈平仓"
color = "green"
elif status == 'closed_sl':
emoji = "🛑"
status_text = "止损平仓"
color = "red"
elif status == 'closed_be':
emoji = "📈"
status_text = "移动止损"
color = "orange"
else:
emoji = "📤"
status_text = "平仓"
color = "blue"
win_text = "盈利" if is_win else "亏损"
win_emoji = "" if is_win else ""
side_text = "做多" if result.get('side') == 'long' else "做空"
side_icon = "🟢" if result.get('side') == 'long' else "🔴"
message = f"""{emoji} 订单{status_text}
交易对: {result.get('symbol')}
方向: {side_text}
入场: ${result.get('entry_price', 0):,.2f}
出场: ${result.get('exit_price', 0):,.2f}
{win_text}: {result.get('pnl_percent', 0):+.2f}% (${result.get('pnl_amount', 0):+.2f})
持仓时间: {result.get('hold_duration', 'N/A')}"""
title = f"{emoji} 订单{status_text} - {result.get('symbol')}"
content_parts = [
f"{side_icon} **方向**: {side_text}",
f"📊 **入场**: ${result.get('entry_price', 0):,.2f}",
f"🎯 **出场**: ${result.get('exit_price', 0):,.2f}",
f"{win_emoji} **{win_text}**: {result.get('pnl_percent', 0):+.2f}% (${result.get('pnl_amount', 0):+.2f})",
f"⏱️ **持仓时间**: {result.get('hold_duration', 'N/A')}",
]
content = "\n".join(content_parts)
# 发送通知
await feishu.send_text(message)
await telegram.send_message(message)
await feishu.send_card(title, content, color)
await telegram.send_message(f"{title}\n\n{content}")
logger.info(f"后台监控触发平仓: {result.get('order_id')} | {symbol}")
except Exception as e:

View File

@ -519,6 +519,8 @@ class StockAgent:
logger.info(f"\n⏸️ 信号质量不高,不发送通知")
return result
logger.info(f"\n📢 【最佳信号】{best_signal.get('action')} {best_signal.get('grade')}{best_signal.get('confidence')}%")
# 检查置信度阈值
threshold = self.settings.stock_llm_threshold * 100
if best_signal.get('confidence', 0) < threshold:
@ -530,7 +532,10 @@ class StockAgent:
logger.info(f"\n⏸️ 信号冷却中,不发送通知")
return result
logger.info(f"\n✅ 满足所有条件,准备发送通知...")
# 发送通知
try:
await self._send_signal_notification(symbol, best_signal, current_price)
result['notified'] = True
result['best_signal'] = best_signal
@ -538,6 +543,12 @@ class StockAgent:
# 更新状态
self.last_signals[symbol] = best_signal
self.signal_cooldown[symbol] = datetime.now()
except Exception as notify_error:
logger.error(f"❌ 发送 {symbol} 通知失败: {notify_error}")
import traceback
logger.error(traceback.format_exc())
result['notified'] = False
result['notify_error'] = str(notify_error)
return result
@ -589,6 +600,8 @@ class StockAgent:
):
"""发送信号通知"""
try:
logger.info(f"📤 准备发送 {symbol} 信号通知...")
from app.utils.signal_formatter import get_signal_formatter
formatter = get_signal_formatter()
@ -597,11 +610,20 @@ class StockAgent:
title = card['title']
content = card['content']
logger.info(f" 标题: {title}")
logger.info(f" 内容长度: {len(content)} 字符")
# 根据信号方向选择颜色
color = "green" if signal.get('action') == 'buy' else "red"
logger.info(f" 颜色: {color}")
# 检查飞书服务
logger.info(f" 飞书服务: {type(self.feishu).__name__}")
logger.info(f" Webhook URL: {self.feishu.webhook_url[:50]}...")
# 发送到飞书
await self.feishu.send_card(title, content, color)
logger.info(f" ✅ 飞书通知发送成功")
# 发送到 Telegram
await self.telegram.send_message(formatter.format_signal_message(signal, symbol, agent_type='stock'))
@ -616,7 +638,11 @@ class StockAgent:
self.signal_db.add_signal(signal_to_save)
except Exception as e:
logger.error(f"发送通知失败: {e}")
logger.error(f"❌ 发送通知失败: {e}")
import traceback
logger.error(traceback.format_exc())
# 重新抛出异常,让上层能够捕获
raise
def _validate_data(self, data: Dict[str, pd.DataFrame]) -> bool:
"""验证数据完整性"""