Files
aiagents-stock/news_flow_alert.py
T
2026-01-25 16:53:55 +08:00

515 lines
19 KiB
Python

"""
新闻流量预警系统模块
实现6种预警类型和通知推送
"""
import logging
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Tuple
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class NewsFlowAlertSystem:
"""新闻流量预警系统"""
# 预警类型定义
ALERT_TYPES = {
'heat_surge': {
'name': '热度飙升',
'level': 'warning',
'description': '流量得分超过阈值,市场热度异常升高',
},
'rank_change': {
'name': '排名变化',
'level': 'info',
'description': '热点排名快速变化',
},
'sentiment_extreme': {
'name': '情绪极值',
'level': 'warning',
'description': '情绪指数处于极端状态(过高或过低)',
},
'flow_peak': {
'name': '流量高潮',
'level': 'danger',
'description': '进入"一致"阶段,可能是逃命时刻',
},
'flow_decline': {
'name': '流量退潮',
'level': 'warning',
'description': '进入"退潮"阶段,注意止盈止损',
},
'viral_spread': {
'name': '病毒传播',
'level': 'warning',
'description': 'K值超过阈值,流量呈指数型增长',
},
}
# 预警级别定义
ALERT_LEVELS = {
'info': {'name': '提示', 'color': 'blue', 'priority': 1},
'warning': {'name': '警告', 'color': 'orange', 'priority': 2},
'danger': {'name': '危险', 'color': 'red', 'priority': 3},
}
def __init__(self):
"""初始化预警系统"""
self.db = None
self.notification_service = None
self._init_dependencies()
# 默认阈值配置
self.default_thresholds = {
'heat_threshold': 800,
'rank_change_threshold': 10,
'sentiment_high_threshold': 90,
'sentiment_low_threshold': 20,
'viral_k_threshold': 1.5,
}
def _init_dependencies(self):
"""初始化依赖"""
try:
from news_flow_db import news_flow_db
self.db = news_flow_db
except Exception as e:
logger.warning(f"数据库初始化失败: {e}")
try:
from notification_service import notification_service
self.notification_service = notification_service
except Exception as e:
logger.warning(f"通知服务初始化失败: {e}")
def get_threshold(self, key: str) -> float:
"""获取阈值配置"""
if self.db:
value = self.db.get_alert_config(key)
if value:
try:
return float(value)
except ValueError:
pass
return self.default_thresholds.get(key, 0)
def set_threshold(self, key: str, value: float):
"""设置阈值配置"""
if self.db:
self.db.set_alert_config(key, str(value))
def check_alerts(self, current_data: Dict,
history_data: Dict = None,
sentiment_data: Dict = None,
snapshot_id: int = None) -> List[Dict]:
"""
检查所有预警条件
Args:
current_data: 当前数据,包含flow_data, hot_topics等
history_data: 历史数据,用于比较
sentiment_data: 情绪数据
snapshot_id: 快照ID
Returns:
List[Dict]: 触发的预警列表
"""
alerts = []
flow_data = current_data.get('flow_data', {})
hot_topics = current_data.get('hot_topics', [])
viral_k = current_data.get('viral_k', {})
flow_stage = current_data.get('flow_stage', {})
# 1. 检查热度飙升
heat_alert = self._check_heat_surge(flow_data)
if heat_alert:
heat_alert['snapshot_id'] = snapshot_id
alerts.append(heat_alert)
# 2. 检查排名变化
if history_data:
rank_alert = self._check_rank_change(hot_topics,
history_data.get('hot_topics', []))
if rank_alert:
rank_alert['snapshot_id'] = snapshot_id
alerts.append(rank_alert)
# 3. 检查情绪极值
if sentiment_data:
sentiment_alert = self._check_sentiment_extreme(sentiment_data)
if sentiment_alert:
sentiment_alert['snapshot_id'] = snapshot_id
alerts.append(sentiment_alert)
# 4. 检查流量高潮(一致阶段)
peak_alert = self._check_flow_peak(flow_stage, sentiment_data)
if peak_alert:
peak_alert['snapshot_id'] = snapshot_id
alerts.append(peak_alert)
# 5. 检查流量退潮
decline_alert = self._check_flow_decline(flow_stage)
if decline_alert:
decline_alert['snapshot_id'] = snapshot_id
alerts.append(decline_alert)
# 6. 检查病毒传播
viral_alert = self._check_viral_spread(viral_k)
if viral_alert:
viral_alert['snapshot_id'] = snapshot_id
alerts.append(viral_alert)
# 按优先级排序
alerts.sort(key=lambda x: self.ALERT_LEVELS.get(
x.get('alert_level', 'info'), {}
).get('priority', 0), reverse=True)
# 保存预警到数据库
if self.db and alerts:
for alert in alerts:
self.db.save_alert(alert)
return alerts
def _check_heat_surge(self, flow_data: Dict) -> Optional[Dict]:
"""检查热度飙升"""
threshold = self.get_threshold('heat_threshold')
current_score = flow_data.get('total_score', 0)
if current_score >= threshold:
return {
'alert_type': 'heat_surge',
'alert_level': 'warning',
'title': f'热度飙升预警:流量得分{current_score}',
'content': f"当前流量得分{current_score},超过阈值{threshold}。"
f"市场热度异常升高,可能存在短期机会,但也要注意追高风险。",
'related_topics': [],
'trigger_value': current_score,
'threshold_value': threshold,
}
return None
def _check_rank_change(self, current_topics: List[Dict],
previous_topics: List[Dict]) -> Optional[Dict]:
"""检查排名变化"""
threshold = int(self.get_threshold('rank_change_threshold'))
if not previous_topics:
return None
# 建立之前的排名映射
prev_ranks = {t.get('topic', ''): i for i, t in enumerate(previous_topics)}
# 检查快速上升的话题
rapid_rise_topics = []
for i, topic in enumerate(current_topics[:20]):
topic_name = topic.get('topic', '')
if topic_name in prev_ranks:
rank_change = prev_ranks[topic_name] - i
if rank_change >= threshold:
rapid_rise_topics.append({
'topic': topic_name,
'current_rank': i + 1,
'previous_rank': prev_ranks[topic_name] + 1,
'change': rank_change,
})
if rapid_rise_topics:
topics_text = ', '.join([t['topic'] for t in rapid_rise_topics[:3]])
return {
'alert_type': 'rank_change',
'alert_level': 'info',
'title': f'排名变化提示:{topics_text}',
'content': f"{len(rapid_rise_topics)}个话题排名快速上升(上升{threshold}名以上),"
f"可能是新热点正在发酵。",
'related_topics': [t['topic'] for t in rapid_rise_topics],
'trigger_value': len(rapid_rise_topics),
'threshold_value': threshold,
}
return None
def _check_sentiment_extreme(self, sentiment_data: Dict) -> Optional[Dict]:
"""检查情绪极值"""
high_threshold = self.get_threshold('sentiment_high_threshold')
low_threshold = self.get_threshold('sentiment_low_threshold')
sentiment = sentiment_data.get('sentiment', {})
sentiment_index = sentiment.get('sentiment_index', 50)
sentiment_class = sentiment.get('sentiment_class', '中性')
if sentiment_index >= high_threshold:
return {
'alert_type': 'sentiment_extreme',
'alert_level': 'warning',
'title': f'情绪极值警告:{sentiment_class}({sentiment_index}分)',
'content': f"情绪指数{sentiment_index}分,处于极度乐观状态!"
f"根据'流量高潮=价格高潮'理论,市场可能接近顶部,注意及时止盈。",
'related_topics': [],
'trigger_value': sentiment_index,
'threshold_value': high_threshold,
}
elif sentiment_index <= low_threshold:
return {
'alert_type': 'sentiment_extreme',
'alert_level': 'warning',
'title': f'情绪极值警告:{sentiment_class}({sentiment_index}分)',
'content': f"情绪指数{sentiment_index}分,处于极度悲观状态!"
f"市场恐慌情绪蔓延,可能存在超跌反弹机会,但需谨慎左侧布局。",
'related_topics': [],
'trigger_value': sentiment_index,
'threshold_value': low_threshold,
}
return None
def _check_flow_peak(self, flow_stage: Dict,
sentiment_data: Dict = None) -> Optional[Dict]:
"""
检查流量高潮(逃命预警)
当以下条件同时满足时触发:
1. 流量阶段 = "一致"
2. 情绪指数 > 85
3. K值 > 1.5(可选)
"""
stage = flow_stage.get('stage', '')
stage_name = flow_stage.get('stage_name', '')
# 主要触发条件:一致阶段
if stage not in ['consensus', '一致']:
return None
# 增强条件检查
sentiment_index = 50
if sentiment_data:
sentiment = sentiment_data.get('sentiment', {})
sentiment_index = sentiment.get('sentiment_index', 50)
# 一致阶段就触发危险预警
return {
'alert_type': 'flow_peak',
'alert_level': 'danger',
'title': '⚠️ 流量高潮预警:准备跑路!',
'content': f"流量阶段进入【{stage_name}】!这是最危险的信号!\n\n"
f"根据'流量为王'理论:流量高潮 = 价格高潮 = 逃命时刻\n\n"
f"当热搜、媒体报道、KOL转发同时达到高潮时,就是出货时机。\n\n"
f"建议:立即减仓或清仓,锁定利润!",
'related_topics': [],
'trigger_value': stage_name,
'threshold_value': '一致阶段',
}
def _check_flow_decline(self, flow_stage: Dict) -> Optional[Dict]:
"""检查流量退潮"""
stage = flow_stage.get('stage', '')
stage_name = flow_stage.get('stage_name', '')
avg_growth = flow_stage.get('avg_growth', 0)
if stage not in ['decline', '退潮']:
return None
return {
'alert_type': 'flow_decline',
'alert_level': 'warning',
'title': f'流量退潮警告:及时止盈止损',
'content': f"流量阶段进入【{stage_name}】,增速{avg_growth}%。\n\n"
f"题材热度正在消退,资金开始撤离。\n\n"
f"建议:持仓者及时止盈止损,不要恋战。空仓者不要抄底接飞刀。",
'related_topics': [],
'trigger_value': avg_growth,
'threshold_value': '退潮阶段',
}
def _check_viral_spread(self, viral_k: Dict) -> Optional[Dict]:
"""检查病毒传播"""
threshold = self.get_threshold('viral_k_threshold')
k_value = viral_k.get('k_value', 1.0)
trend = viral_k.get('trend', '')
if k_value >= threshold:
return {
'alert_type': 'viral_spread',
'alert_level': 'warning',
'title': f'病毒传播预警:K值={k_value}',
'content': f"K值={k_value},趋势:{trend}\n\n"
f"流量正在指数型增长,这是病毒式传播的特征。\n\n"
f"题材可能进入加速期,但也要注意:\n"
f"- K值过高意味着接近顶部的风险增加\n"
f"- 指数型增长往往伴随着指数型下跌\n"
f"- 密切关注后续K值变化,一旦开始下降就是离场信号",
'related_topics': [],
'trigger_value': k_value,
'threshold_value': threshold,
}
return None
def send_notification(self, alerts: List[Dict]) -> bool:
"""
发送通知
Args:
alerts: 预警列表
Returns:
bool: 是否发送成功
"""
if not alerts:
return True
if not self.notification_service:
logger.warning("通知服务不可用")
return False
try:
# 按级别分组
danger_alerts = [a for a in alerts if a.get('alert_level') == 'danger']
warning_alerts = [a for a in alerts if a.get('alert_level') == 'warning']
info_alerts = [a for a in alerts if a.get('alert_level') == 'info']
# 构建通知内容
lines = []
lines.append("📊 新闻流量预警通知")
lines.append(f"时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
lines.append("")
if danger_alerts:
lines.append("🔴 【危险预警】")
for alert in danger_alerts:
lines.append(f" • {alert['title']}")
lines.append("")
if warning_alerts:
lines.append("🟠 【警告】")
for alert in warning_alerts:
lines.append(f" • {alert['title']}")
lines.append("")
if info_alerts:
lines.append("🔵 【提示】")
for alert in info_alerts:
lines.append(f" • {alert['title']}")
message = '\n'.join(lines)
# 发送通知
# 使用危险级别发送最高优先级预警
if danger_alerts:
subject = "⚠️ 新闻流量危险预警"
else:
subject = "📊 新闻流量预警通知"
# 调用通知服务
success = self.notification_service.send_analysis_result(
subject=subject,
content=message
)
# 标记为已通知
if success and self.db:
for alert in alerts:
if 'id' in alert:
self.db.mark_alert_notified(alert['id'])
return success
except Exception as e:
logger.error(f"发送通知失败: {e}")
return False
def get_alert_history(self, days: int = 7,
alert_type: str = None) -> List[Dict]:
"""获取预警历史"""
if self.db:
return self.db.get_alerts(days, alert_type)
return []
def get_unnotified_alerts(self) -> List[Dict]:
"""获取未通知的预警"""
if self.db:
return self.db.get_unnotified_alerts()
return []
def get_alert_summary(self, days: int = 7) -> Dict:
"""获取预警统计摘要"""
alerts = self.get_alert_history(days)
# 按类型统计
type_counts = {}
for alert in alerts:
alert_type = alert.get('alert_type', 'unknown')
type_counts[alert_type] = type_counts.get(alert_type, 0) + 1
# 按级别统计
level_counts = {}
for alert in alerts:
level = alert.get('alert_level', 'info')
level_counts[level] = level_counts.get(level, 0) + 1
return {
'total_count': len(alerts),
'type_counts': type_counts,
'level_counts': level_counts,
'danger_count': level_counts.get('danger', 0),
'warning_count': level_counts.get('warning', 0),
'info_count': level_counts.get('info', 0),
}
def get_threshold_config(self) -> Dict:
"""获取所有阈值配置"""
return {
'heat_threshold': self.get_threshold('heat_threshold'),
'rank_change_threshold': self.get_threshold('rank_change_threshold'),
'sentiment_high_threshold': self.get_threshold('sentiment_high_threshold'),
'sentiment_low_threshold': self.get_threshold('sentiment_low_threshold'),
'viral_k_threshold': self.get_threshold('viral_k_threshold'),
}
# 全局实例
news_flow_alert_system = NewsFlowAlertSystem()
# 测试代码
if __name__ == "__main__":
print("=== 测试预警系统 ===")
# 模拟数据
current_data = {
'flow_data': {'total_score': 850, 'level': '极高'},
'hot_topics': [
{'topic': 'AI芯片', 'heat': 95},
{'topic': '新能源', 'heat': 80},
],
'viral_k': {'k_value': 1.8, 'trend': '指数型爆发'},
'flow_stage': {'stage': 'consensus', 'stage_name': '一致', 'avg_growth': 35},
}
sentiment_data = {
'sentiment': {'sentiment_index': 92, 'sentiment_class': '极度乐观'},
}
history_data = {
'hot_topics': [
{'topic': '新能源', 'heat': 70},
{'topic': 'AI芯片', 'heat': 60},
],
}
# 检查预警
alerts = news_flow_alert_system.check_alerts(
current_data, history_data, sentiment_data
)
print(f"\n触发 {len(alerts)} 个预警:")
for alert in alerts:
level_info = NewsFlowAlertSystem.ALERT_LEVELS.get(alert['alert_level'], {})
print(f"\n[{level_info.get('name', alert['alert_level'])}] {alert['title']}")
print(f" {alert['content'][:100]}...")
# 获取阈值配置
print("\n当前阈值配置:")
config = news_flow_alert_system.get_threshold_config()
for key, value in config.items():
print(f" {key}: {value}")