diff --git a/src/strategy/kline_pattern_strategy.py b/src/strategy/kline_pattern_strategy.py index 4c839e6..466b69e 100644 --- a/src/strategy/kline_pattern_strategy.py +++ b/src/strategy/kline_pattern_strategy.py @@ -452,8 +452,11 @@ class KLinePatternStrategy: } try: - self.notification_manager.send_strategy_summary(results, scan_stats) - logger.info("📱 汇总通知已发送") + success = self.notification_manager.send_strategy_summary(results, scan_stats) + if success: + logger.info("📱 策略信号汇总通知发送完成") + else: + logger.warning("📱 策略信号汇总通知发送失败") except Exception as e: logger.error(f"发送汇总通知失败: {e}") @@ -486,7 +489,8 @@ K线形态策略 - 两阳线+阴线+阳线突破 - 回退到全市场股票列表 通知方式: -- 钉钉webhook汇总推送(单次发送所有信号) +- 钉钉webhook汇总推送(10个信号一组分批发送) +- 包含关键信息:代码、股票名称、K线时间、价格、周期等 - 系统日志详细记录 """ diff --git a/src/utils/notification.py b/src/utils/notification.py index 2558130..fdf5f96 100644 --- a/src/utils/notification.py +++ b/src/utils/notification.py @@ -299,7 +299,7 @@ class NotificationManager: def send_strategy_summary(self, all_signals: Dict[str, Any], scan_stats: Dict[str, Any] = None) -> bool: """ - 发送策略信号汇总通知 + 发送策略信号汇总通知(支持分组发送) Args: all_signals: 所有信号的汇总数据 {stock_code: {timeframe: [signals]}} @@ -313,83 +313,102 @@ class NotificationManager: try: from datetime import datetime + import math current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - # 统计信号数量 + # 收集所有信号详情 + all_signal_details = [] total_signals = 0 total_stocks = len(all_signals) - signal_summary = [] for stock_code, stock_results in all_signals.items(): - stock_signal_count = sum(len(signals) for signals in stock_results.values()) - total_signals += stock_signal_count - - if stock_signal_count > 0: - # 获取股票名称和最新信号 - stock_name = "未知" - latest_signal = None - for timeframe, signals in stock_results.items(): - if signals: - latest_signal = signals[-1] # 取最新信号 - stock_name = latest_signal.get('stock_name', stock_name) - break - - if latest_signal: - signal_summary.append({ + for timeframe, signals in stock_results.items(): + for signal in signals: + total_signals += 1 + all_signal_details.append({ 'stock_code': stock_code, - 'stock_name': stock_name, - 'signal_count': stock_signal_count, - 'price': latest_signal['breakout_price'], - 'date': latest_signal['date'], - 'turnover': latest_signal.get('turnover_ratio', 0) + 'stock_name': signal.get('stock_name', '未知'), + 'timeframe': timeframe, + 'signal_date': signal['date'], + 'price': signal['breakout_price'], + 'turnover': signal.get('turnover_ratio', 0), + 'breakout_pct': signal.get('breakout_pct', 0), + 'ema20_status': '✅上方' if signal.get('above_ema20', False) else '❌下方' }) - # 构建汇总消息 - title = f"📈 K线形态策略信号汇总" + # 如果没有信号,直接返回 + if total_signals == 0: + return True - markdown_text = f""" -# 📈 K线形态策略信号汇总 + # 按10个信号为一组分批发送 + signals_per_group = 10 + total_groups = math.ceil(total_signals / signals_per_group) + + success_count = 0 + + for group_idx in range(total_groups): + start_idx = group_idx * signals_per_group + end_idx = min(start_idx + signals_per_group, total_signals) + group_signals = all_signal_details[start_idx:end_idx] + + # 构建当前组的消息 + if total_groups > 1: + title = f"📈 K线形态策略信号汇总 ({group_idx + 1}/{total_groups})" + else: + title = f"📈 K线形态策略信号汇总" + + markdown_text = f""" +# {title} **扫描统计:** -- 扫描时间: {current_time} -- 发现信号: `{total_signals}` 个 +- 扫描时间: `{current_time}` +- 总信号数: `{total_signals}` 个 +- 本组信号: `{len(group_signals)}` 个 ({start_idx + 1}-{end_idx}) - 涉及股票: `{total_stocks}` 只 - -**信号详情:** """ - # 添加每只股票的信号摘要 - for i, signal in enumerate(signal_summary[:10], 1): # 最多显示10只股票 - markdown_text += f""" -{i}. **{signal['stock_code']} - {signal['stock_name']}** - - 价格: `{signal['price']:.2f}元` - - 信号数: `{signal['signal_count']}个` - - 换手率: `{signal['turnover']:.2f}%` - - 时间: `{signal['date']}` -""" - - if len(signal_summary) > 10: - markdown_text += f"\n*还有 {len(signal_summary) - 10} 只股票...*\n" - - # 添加扫描统计(如果提供) - if scan_stats: - markdown_text += f""" + # 添加扫描范围信息 + if scan_stats: + markdown_text += f""" **扫描范围:** - 扫描股票总数: `{scan_stats.get('total_scanned', 'N/A')}` - 数据源: `{scan_stats.get('data_source', '热门股票')}` """ - markdown_text += """ + markdown_text += "\n**信号详情:**\n" + + # 添加当前组的信号详情 + for i, signal in enumerate(group_signals, start_idx + 1): + markdown_text += f""" +{i}. **{signal['stock_code']} - {signal['stock_name']}** + - K线时间: `{signal['signal_date']}` + - 时间周期: `{signal['timeframe']}` + - 当前价格: `{signal['price']:.2f}元` + - 突破幅度: `{signal['breakout_pct']:.2f}%` + - 换手率: `{signal['turnover']:.2f}%` + - EMA20: `{signal['ema20_status']}` +""" + + markdown_text += """ --- **策略说明:** 两阳线+阴线+阳线形态突破 *量化交易系统自动发送* """ - # 发送钉钉通知 - if self.dingtalk_notifier: - return self.dingtalk_notifier.send_markdown_message(title, markdown_text) + # 发送当前组的通知 + if self.dingtalk_notifier: + if self.dingtalk_notifier.send_markdown_message(title, markdown_text): + success_count += 1 + logger.info(f"📱 发送信号汇总第{group_idx + 1}组成功 ({len(group_signals)}个信号)") + else: + logger.error(f"📱 发送信号汇总第{group_idx + 1}组失败") - return False + # 避免发送过快,添加短暂延迟 + if group_idx < total_groups - 1: # 不是最后一组 + import time + time.sleep(1) # 1秒延迟 + + return success_count > 0 except Exception as e: logger.error(f"发送策略汇总通知异常: {e}")