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

563 lines
21 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
新闻流量情绪分析模块
实现情绪指数、情绪分类、流量阶段判断、情绪动量计算
"""
import logging
from datetime import datetime
from typing import Dict, List, Optional, Tuple
import statistics
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class SentimentAnalyzer:
"""情绪分析器"""
def __init__(self):
# 情绪分类阈值
self.sentiment_thresholds = {
'extremely_pessimistic': 20, # 极度悲观
'pessimistic': 40, # 悲观
'neutral': 60, # 中性
'optimistic': 80, # 乐观
'extremely_optimistic': 100, # 极度乐观
}
# 流量阶段定义
self.flow_stages = {
'startup': '启动', # 刚开始发酵
'acceleration': '加速', # 增速加快
'divergence': '分歧', # 多空分歧
'consensus': '一致', # 流量高潮(危险!)
'decline': '退潮', # 热度下降
}
# 阶段判断阈值
self.stage_thresholds = {
'startup_growth': 0.20, # 启动期增速阈值
'acceleration_growth': 0.20, # 加速期增速阈值
'divergence_volatility': 0.30, # 分歧期波动率阈值
'consensus_k': 1.5, # 一致期K值阈值
'decline_growth': -0.20, # 退潮期增速阈值
}
# 正面/负面关键词(用于情绪分析)
self.positive_keywords = [
'利好', '大涨', '暴涨', '涨停', '新高', '突破', '牛市',
'反弹', '加仓', '买入', '增持', '推荐', '看好', '机遇',
'政策支持', '业绩预增', '超预期', '景气度', '高增长',
]
self.negative_keywords = [
'利空', '大跌', '暴跌', '跌停', '新低', '破位', '熊市',
'回调', '减仓', '卖出', '减持', '风险', '看空', '危机',
'政策收紧', '业绩下滑', '不及预期', '亏损', '退市',
]
def calculate_sentiment_index(self, platforms_data: List[Dict],
stock_news: List[Dict] = None) -> Dict:
"""
计算情绪指数(0-100)
基于以下因素:
1. 流量规模(占40%)
2. 财经平台活跃度(占30%)
3. 正负面关键词比例(占30%)
Returns:
{
'sentiment_index': int,
'flow_factor': int,
'finance_factor': int,
'keyword_factor': int,
'sentiment_class': str,
'analysis': str,
}
"""
# 1. 流量规模因子(40%
total_news = 0
finance_news = 0
for platform_data in platforms_data:
if not platform_data.get('success'):
continue
count = platform_data.get('count', 0)
total_news += count
if platform_data.get('category') == 'finance':
finance_news += count
# 流量分数:基于新闻数量
if total_news >= 500:
flow_factor = 90
elif total_news >= 300:
flow_factor = 70
elif total_news >= 150:
flow_factor = 50
elif total_news >= 50:
flow_factor = 30
else:
flow_factor = 10
# 2. 财经平台活跃度因子(30%
if total_news > 0:
finance_ratio = finance_news / total_news
finance_factor = min(int(finance_ratio * 200), 100)
else:
finance_factor = 50
# 3. 关键词情绪因子(30%
positive_count = 0
negative_count = 0
if stock_news:
for news in stock_news:
title = news.get('title', '')
content = news.get('content', '')
text = f"{title} {content}"
for kw in self.positive_keywords:
if kw in text:
positive_count += 1
break
for kw in self.negative_keywords:
if kw in text:
negative_count += 1
break
total_sentiment_news = positive_count + negative_count
if total_sentiment_news > 0:
positive_ratio = positive_count / total_sentiment_news
keyword_factor = int(positive_ratio * 100)
else:
keyword_factor = 50 # 中性
# 4. 综合计算情绪指数
sentiment_index = int(
flow_factor * 0.4 +
finance_factor * 0.3 +
keyword_factor * 0.3
)
# 限制范围
sentiment_index = max(0, min(100, sentiment_index))
# 5. 情绪分类
sentiment_class = self.classify_sentiment(sentiment_index)
# 6. 生成分析
analysis = self._generate_sentiment_analysis(
sentiment_index, sentiment_class,
flow_factor, finance_factor, keyword_factor
)
return {
'sentiment_index': sentiment_index,
'flow_factor': flow_factor,
'finance_factor': finance_factor,
'keyword_factor': keyword_factor,
'positive_count': positive_count,
'negative_count': negative_count,
'sentiment_class': sentiment_class,
'analysis': analysis,
}
def classify_sentiment(self, index: int) -> str:
"""
情绪分类
极度悲观(<20) / 悲观(20-40) / 中性(40-60) / 乐观(60-80) / 极度乐观(>80)
"""
if index < 20:
return "极度悲观"
elif index < 40:
return "悲观"
elif index < 60:
return "中性"
elif index < 80:
return "乐观"
else:
return "极度乐观"
def _generate_sentiment_analysis(self, index: int, sentiment_class: str,
flow: int, finance: int, keyword: int) -> str:
"""生成情绪分析文本"""
if sentiment_class == "极度乐观":
return f"情绪指数{index},市场极度乐观!流量爆发({flow}),财经活跃({finance})。警告:可能是情绪顶部,注意获利了结。"
elif sentiment_class == "乐观":
return f"情绪指数{index},市场情绪乐观。题材正在发酵,可关注龙头机会,但需注意节奏。"
elif sentiment_class == "中性":
return f"情绪指数{index},市场情绪中性。缺乏明确方向,建议观望为主。"
elif sentiment_class == "悲观":
return f"情绪指数{index},市场情绪偏悲观。负面因素较多,控制仓位,等待转机。"
else: # 极度悲观
return f"情绪指数{index},市场极度悲观!恐慌情绪蔓延。可能是超跌机会,但需谨慎左侧布局。"
def determine_flow_stage(self, history_scores: List[int],
current_score: int,
current_k: float = None) -> Dict:
"""
判断流量阶段
流量阶段:
1. 启动 - 刚开始发酵,关注
2. 加速 - 增速加快,参与
3. 分歧 - 多空分歧,谨慎
4. 一致 - 流量高潮,危险!准备跑路
5. 退潮 - 热度下降,及时止盈止损
Returns:
{
'stage': str,
'stage_name': str,
'confidence': int,
'signal': str, # 关注/参与/谨慎/危险/离场
'analysis': str,
}
"""
if len(history_scores) < 3:
return {
'stage': 'unknown',
'stage_name': '未知',
'confidence': 0,
'signal': '观察',
'analysis': '历史数据不足,继续观察积累数据',
}
# 计算增长率序列
all_scores = history_scores + [current_score]
growth_rates = []
for i in range(1, len(all_scores)):
if all_scores[i-1] > 0:
rate = (all_scores[i] - all_scores[i-1]) / all_scores[i-1]
growth_rates.append(rate)
# 计算关键指标
recent_growth_rates = growth_rates[-3:] if len(growth_rates) >= 3 else growth_rates
avg_growth = sum(recent_growth_rates) / len(recent_growth_rates) if recent_growth_rates else 0
# 波动率(标准差)
if len(recent_growth_rates) >= 2:
volatility = statistics.stdev(recent_growth_rates)
else:
volatility = 0
# 计算K值(如果没有提供)
if current_k is None and len(all_scores) >= 2:
previous_score = all_scores[-2]
current_k = current_score / previous_score if previous_score > 0 else 1.0
elif current_k is None:
current_k = 1.0
# 判断上升/下降趋势
positive_count = sum(1 for r in recent_growth_rates if r > 0)
negative_count = sum(1 for r in recent_growth_rates if r < 0)
# 阶段判断逻辑
stage = 'unknown'
stage_name = '未知'
confidence = 50
signal = '观察'
analysis = ''
# 1. 一致阶段(最危险)- K值>1.5 且 高增速
if current_k >= 1.5 and avg_growth > 0.3:
stage = 'consensus'
stage_name = '一致'
confidence = 90
signal = '危险'
analysis = f"流量高潮!K值={current_k:.2f},增速{avg_growth*100:.1f}%。市场一致看多,这往往是顶部信号。立即减仓或清仓!"
# 2. 退潮阶段 - 连续下降
elif negative_count >= 2 and avg_growth < -0.15:
stage = 'decline'
stage_name = '退潮'
confidence = 85
signal = '离场'
analysis = f"流量退潮!连续下降,增速{avg_growth*100:.1f}%。题材热度消退,及时止盈止损,不要恋战。"
# 3. 分歧阶段 - 高波动,涨跌交替
elif volatility > 0.25 and positive_count > 0 and negative_count > 0:
stage = 'divergence'
stage_name = '分歧'
confidence = 75
signal = '谨慎'
analysis = f"多空分歧!波动率{volatility*100:.1f}%,市场观点不一。高抛低吸,控制仓位,设好止损。"
# 4. 加速阶段 - 持续上涨,增速加快
elif avg_growth > 0.2 and current_k > 1.1 and positive_count >= 2:
stage = 'acceleration'
stage_name = '加速'
confidence = 80
signal = '参与'
analysis = f"流量加速!增速{avg_growth*100:.1f}%K值{current_k:.2f}。题材正在快速发酵,可参与龙头,但注意仓位。"
# 5. 启动阶段 - 刚开始上涨
elif avg_growth > 0.05 and positive_count >= 2:
stage = 'startup'
stage_name = '启动'
confidence = 70
signal = '关注'
analysis = f"流量启动!增速{avg_growth*100:.1f}%,题材刚开始发酵。可以关注,等待确认后介入。"
# 6. 其他情况
else:
stage = 'stable'
stage_name = '平稳'
confidence = 60
signal = '观察'
analysis = f"流量平稳。增速{avg_growth*100:.1f}%,无明显趋势。保持观望,等待方向明确。"
return {
'stage': stage,
'stage_name': stage_name,
'confidence': confidence,
'signal': signal,
'analysis': analysis,
'avg_growth': round(avg_growth * 100, 1),
'volatility': round(volatility * 100, 1),
'current_k': round(current_k, 2),
}
def calculate_momentum(self, history_data: List[Dict]) -> Dict:
"""
计算情绪动量
情绪动量 = 当前变化速率 / 平均变化速率
动量 > 1.5: 情绪加速
动量 ≈ 1: 情绪稳定
动量 < 0.5: 情绪减速
Args:
history_data: 历史数据列表,每项包含 'sentiment_index' 和 'timestamp'
Returns:
{
'momentum': float,
'momentum_level': str,
'trend': str,
'analysis': str,
}
"""
if len(history_data) < 3:
return {
'momentum': 1.0,
'momentum_level': '正常',
'trend': '数据不足',
'analysis': '历史数据不足,无法计算动量',
}
# 提取情绪指数序列
sentiment_values = [d.get('sentiment_index', 50) for d in history_data]
# 计算变化率序列
changes = []
for i in range(1, len(sentiment_values)):
change = sentiment_values[i] - sentiment_values[i-1]
changes.append(abs(change))
# 计算平均变化率
avg_change = sum(changes) / len(changes) if changes else 1
# 计算当前变化率(最近的变化)
current_change = abs(changes[-1]) if changes else 0
# 计算动量
if avg_change > 0:
momentum = round(current_change / avg_change, 2)
else:
momentum = 1.0
# 确定动量级别和趋势
if momentum >= 2.0:
momentum_level = '极高'
trend = '急剧变化'
analysis = f"情绪动量{momentum},情绪正在急剧变化!市场可能出现转折点,密切关注。"
elif momentum >= 1.5:
momentum_level = '高'
trend = '加速变化'
analysis = f"情绪动量{momentum},情绪变化正在加速。趋势可能强化或反转。"
elif momentum >= 0.8:
momentum_level = '正常'
trend = '稳定'
analysis = f"情绪动量{momentum},情绪变化平稳,市场处于正常状态。"
elif momentum >= 0.3:
momentum_level = '低'
trend = '减速'
analysis = f"情绪动量{momentum},情绪变化正在减速,可能进入盘整期。"
else:
momentum_level = '极低'
trend = '停滞'
analysis = f"情绪动量{momentum},情绪几乎没有变化,市场陷入僵局。"
# 判断方向
if len(sentiment_values) >= 2:
direction = sentiment_values[-1] - sentiment_values[-2]
if direction > 0:
trend += "(向上)"
elif direction < 0:
trend += "(向下)"
return {
'momentum': momentum,
'momentum_level': momentum_level,
'trend': trend,
'current_change': current_change,
'avg_change': round(avg_change, 1),
'analysis': analysis,
}
def run_full_sentiment_analysis(self, platforms_data: List[Dict],
stock_news: List[Dict],
history_scores: List[int],
current_score: int,
history_sentiments: List[Dict] = None) -> Dict:
"""
运行完整的情绪分析
Returns:
{
'sentiment': Dict, # 情绪指数
'flow_stage': Dict, # 流量阶段
'momentum': Dict, # 情绪动量
'summary': str, # 总结
'risk_level': str, # 风险等级
'advice': str, # 操作建议
}
"""
# 1. 计算情绪指数
sentiment = self.calculate_sentiment_index(platforms_data, stock_news)
# 2. 判断流量阶段
flow_stage = self.determine_flow_stage(history_scores, current_score)
# 3. 计算情绪动量
if history_sentiments and len(history_sentiments) >= 3:
momentum = self.calculate_momentum(history_sentiments)
else:
momentum = {
'momentum': 1.0,
'momentum_level': '正常',
'trend': '数据不足',
'analysis': '历史情绪数据不足',
}
# 4. 综合风险评估
risk_level, advice = self._assess_risk(sentiment, flow_stage, momentum)
# 5. 生成总结
summary = self._generate_summary(sentiment, flow_stage, momentum, risk_level)
return {
'sentiment': sentiment,
'flow_stage': flow_stage,
'momentum': momentum,
'summary': summary,
'risk_level': risk_level,
'advice': advice,
'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
}
def _assess_risk(self, sentiment: Dict, flow_stage: Dict,
momentum: Dict) -> Tuple[str, str]:
"""综合风险评估"""
risk_score = 0
# 情绪因素
sentiment_index = sentiment['sentiment_index']
if sentiment_index > 85:
risk_score += 3 # 过度乐观
elif sentiment_index < 25:
risk_score += 2 # 过度悲观
# 流量阶段因素
stage = flow_stage['stage']
if stage == 'consensus':
risk_score += 4 # 一致阶段最危险
elif stage == 'decline':
risk_score += 3
elif stage == 'divergence':
risk_score += 2
# 动量因素
momentum_value = momentum['momentum']
if momentum_value > 2.0:
risk_score += 2 # 变化过快
# 确定风险等级和建议
if risk_score >= 7:
risk_level = "极高"
advice = "立即减仓或清仓!市场处于极端状态,控制风险为第一要务。"
elif risk_score >= 5:
risk_level = "高"
advice = "谨慎操作,建议减仓。不追高,设置严格止损。"
elif risk_score >= 3:
risk_level = "中等"
advice = "正常操作,注意仓位控制。逢高减仓,逢低观察。"
elif risk_score >= 1:
risk_level = "低"
advice = "可适度参与,关注龙头机会。"
else:
risk_level = "极低"
advice = "风险较低,可积极参与,但仍需设置止损。"
return risk_level, advice
def _generate_summary(self, sentiment: Dict, flow_stage: Dict,
momentum: Dict, risk_level: str) -> str:
"""生成综合总结"""
lines = [
f"【情绪】{sentiment['sentiment_class']}{sentiment['sentiment_index']}分)",
f"【阶段】{flow_stage['stage_name']}期,信号:{flow_stage['signal']}",
f"【动量】{momentum['momentum_level']},趋势{momentum['trend']}",
f"【风险】{risk_level}",
]
return '\n'.join(lines)
# 全局实例
sentiment_analyzer = SentimentAnalyzer()
# 测试代码
if __name__ == "__main__":
print("=== 测试情绪分析模块 ===")
# 模拟数据
platforms_data = [
{'success': True, 'category': 'finance', 'count': 100},
{'success': True, 'category': 'social', 'count': 200},
{'success': True, 'category': 'news', 'count': 150},
]
stock_news = [
{'title': 'AI板块大涨,龙头股涨停', 'content': '利好政策推动'},
{'title': '新能源概念股反弹', 'content': '业绩预增超预期'},
{'title': '市场观望情绪浓厚', 'content': '回调风险加大'},
]
history_scores = [300, 350, 420, 500, 580]
current_score = 650
history_sentiments = [
{'sentiment_index': 55},
{'sentiment_index': 60},
{'sentiment_index': 68},
{'sentiment_index': 72},
]
# 运行完整分析
result = sentiment_analyzer.run_full_sentiment_analysis(
platforms_data, stock_news, history_scores, current_score, history_sentiments
)
print(f"\n情绪指数: {result['sentiment']['sentiment_index']} ({result['sentiment']['sentiment_class']})")
print(f"流量阶段: {result['flow_stage']['stage_name']} - {result['flow_stage']['signal']}")
print(f"情绪动量: {result['momentum']['momentum']} ({result['momentum']['momentum_level']})")
print(f"风险等级: {result['risk_level']}")
print(f"\n===总结===\n{result['summary']}")
print(f"\n操作建议: {result['advice']}")