增加新闻流量监测板块

This commit is contained in:
oficcejo
2026-01-25 16:53:55 +08:00
parent be76e444c7
commit 73491aa22d
18 changed files with 8365 additions and 13 deletions
+4
View File
@@ -2,6 +2,10 @@
- 初心:在股市摸爬滚打多年,自学自编各种指标,花冤柉钱学习了各种战法各种策略,也曾入各种小班,总是赚少赔多,逐渐失去在股市玩的信心。自从去年deepseek上市,一直探索用ai辅助分析,且近日受tradingagents项目启发(感谢原作),多agent结合跟踪主力资金战法(某指每年收费6000rmb),用各种ai辅助编程,拼凑了这么个小程序,根据软件提供的辅助信息,实盘测试盈率还是挺高的,并且逐步形成了自己的交易系统,近一个月来,账户也慢慢在扰亏为盈。开源此软件的目的,就是为了使像我一样的小散,不再迷范。也许这个软件不能让你发大财,但是他能给你足够的信心。最后提醒:股市有风险,入市需谨慎! - 初心:在股市摸爬滚打多年,自学自编各种指标,花冤柉钱学习了各种战法各种策略,也曾入各种小班,总是赚少赔多,逐渐失去在股市玩的信心。自从去年deepseek上市,一直探索用ai辅助分析,且近日受tradingagents项目启发(感谢原作),多agent结合跟踪主力资金战法(某指每年收费6000rmb),用各种ai辅助编程,拼凑了这么个小程序,根据软件提供的辅助信息,实盘测试盈率还是挺高的,并且逐步形成了自己的交易系统,近一个月来,账户也慢慢在扰亏为盈。开源此软件的目的,就是为了使像我一样的小散,不再迷范。也许这个软件不能让你发大财,但是他能给你足够的信心。最后提醒:股市有风险,入市需谨慎!
## ⭐20261.25第一更 - 新闻流量监测 📈
实时监测百度、微博、东财、财联社、抖音、B站等20个平台热点新闻,调用ai分析对A股板块及股票的影响,生成分析报告
## ⭐ 1214更新 - 净利增长策略 📈 ## ⭐ 1214更新 - 净利增长策略 📈
新增“净利增长策略”选股板块,专注稳健成长股票: 新增“净利增长策略”选股板块,专注稳健成长股票:
+25 -12
View File
@@ -22,6 +22,7 @@ from main_force_ui import display_main_force_selector
from sector_strategy_ui import display_sector_strategy from sector_strategy_ui import display_sector_strategy
from longhubang_ui import display_longhubang from longhubang_ui import display_longhubang
from smart_monitor_ui import smart_monitor_ui from smart_monitor_ui import smart_monitor_ui
from news_flow_ui import display_news_flow_monitor
# 页面配置 # 页面配置
st.set_page_config( st.set_page_config(
@@ -294,7 +295,7 @@ def main():
if st.button("🏠 股票分析", width='stretch', key="nav_home", help="返回首页,进行单只股票的深度分析"): if st.button("🏠 股票分析", width='stretch', key="nav_home", help="返回首页,进行单只股票的深度分析"):
# 清除所有功能页面标志 # 清除所有功能页面标志
for key in ['show_history', 'show_monitor', 'show_config', 'show_main_force', for key in ['show_history', 'show_monitor', 'show_config', 'show_main_force',
'show_sector_strategy', 'show_longhubang', 'show_portfolio', 'show_low_price_bull']: 'show_sector_strategy', 'show_longhubang', 'show_portfolio', 'show_low_price_bull', 'show_news_flow']:
if key in st.session_state: if key in st.session_state:
del st.session_state[key] del st.session_state[key]
@@ -307,28 +308,28 @@ def main():
if st.button("💰 主力选股", width='stretch', key="nav_main_force", help="基于主力资金流向的选股策略"): if st.button("💰 主力选股", width='stretch', key="nav_main_force", help="基于主力资金流向的选股策略"):
st.session_state.show_main_force = True st.session_state.show_main_force = True
for key in ['show_history', 'show_monitor', 'show_config', 'show_sector_strategy', for key in ['show_history', 'show_monitor', 'show_config', 'show_sector_strategy',
'show_longhubang', 'show_portfolio', 'show_low_price_bull']: 'show_longhubang', 'show_portfolio', 'show_low_price_bull', 'show_news_flow']:
if key in st.session_state: if key in st.session_state:
del st.session_state[key] del st.session_state[key]
if st.button("🐂 低价擒牛", width='stretch', key="nav_low_price_bull", help="低价高成长股票筛选策略"): if st.button("🐂 低价擒牛", width='stretch', key="nav_low_price_bull", help="低价高成长股票筛选策略"):
st.session_state.show_low_price_bull = True st.session_state.show_low_price_bull = True
for key in ['show_history', 'show_monitor', 'show_config', 'show_sector_strategy', for key in ['show_history', 'show_monitor', 'show_config', 'show_sector_strategy',
'show_longhubang', 'show_portfolio', 'show_main_force', 'show_small_cap', 'show_profit_growth']: 'show_longhubang', 'show_portfolio', 'show_main_force', 'show_small_cap', 'show_profit_growth', 'show_news_flow']:
if key in st.session_state: if key in st.session_state:
del st.session_state[key] del st.session_state[key]
if st.button("📊 小市值策略", width='stretch', key="nav_small_cap", help="小盘高成长股票筛选策略"): if st.button("📊 小市值策略", width='stretch', key="nav_small_cap", help="小盘高成长股票筛选策略"):
st.session_state.show_small_cap = True st.session_state.show_small_cap = True
for key in ['show_history', 'show_monitor', 'show_config', 'show_sector_strategy', for key in ['show_history', 'show_monitor', 'show_config', 'show_sector_strategy',
'show_longhubang', 'show_portfolio', 'show_main_force', 'show_low_price_bull', 'show_profit_growth']: 'show_longhubang', 'show_portfolio', 'show_main_force', 'show_low_price_bull', 'show_profit_growth', 'show_news_flow']:
if key in st.session_state: if key in st.session_state:
del st.session_state[key] del st.session_state[key]
if st.button("📈 净利增长", width='stretch', key="nav_profit_growth", help="净利润增长稳健股票筛选策略"): if st.button("📈 净利增长", width='stretch', key="nav_profit_growth", help="净利润增长稳健股票筛选策略"):
st.session_state.show_profit_growth = True st.session_state.show_profit_growth = True
for key in ['show_history', 'show_monitor', 'show_config', 'show_sector_strategy', for key in ['show_history', 'show_monitor', 'show_config', 'show_sector_strategy',
'show_longhubang', 'show_portfolio', 'show_main_force', 'show_low_price_bull', 'show_small_cap']: 'show_longhubang', 'show_portfolio', 'show_main_force', 'show_low_price_bull', 'show_small_cap', 'show_news_flow']:
if key in st.session_state: if key in st.session_state:
del st.session_state[key] del st.session_state[key]
@@ -339,14 +340,21 @@ def main():
if st.button("🎯 智策板块", width='stretch', key="nav_sector_strategy", help="AI板块策略分析"): if st.button("🎯 智策板块", width='stretch', key="nav_sector_strategy", help="AI板块策略分析"):
st.session_state.show_sector_strategy = True st.session_state.show_sector_strategy = True
for key in ['show_history', 'show_monitor', 'show_config', 'show_main_force', for key in ['show_history', 'show_monitor', 'show_config', 'show_main_force',
'show_longhubang', 'show_portfolio', 'show_smart_monitor', 'show_low_price_bull']: 'show_longhubang', 'show_portfolio', 'show_smart_monitor', 'show_low_price_bull', 'show_news_flow']:
if key in st.session_state: if key in st.session_state:
del st.session_state[key] del st.session_state[key]
if st.button("🐉 智瞰龙虎", width='stretch', key="nav_longhubang", help="龙虎榜深度分析"): if st.button("🐉 智瞰龙虎", width='stretch', key="nav_longhubang", help="龙虎榜深度分析"):
st.session_state.show_longhubang = True st.session_state.show_longhubang = True
for key in ['show_history', 'show_monitor', 'show_config', 'show_main_force', for key in ['show_history', 'show_monitor', 'show_config', 'show_main_force',
'show_sector_strategy', 'show_portfolio', 'show_smart_monitor', 'show_low_price_bull']: 'show_sector_strategy', 'show_portfolio', 'show_smart_monitor', 'show_low_price_bull', 'show_news_flow']:
if key in st.session_state:
del st.session_state[key]
if st.button("📰 新闻流量", width='stretch', key="nav_news_flow", help="新闻流量监测与短线指导"):
st.session_state.show_news_flow = True
for key in ['show_history', 'show_monitor', 'show_config', 'show_main_force',
'show_sector_strategy', 'show_portfolio', 'show_smart_monitor', 'show_low_price_bull', 'show_longhubang']:
if key in st.session_state: if key in st.session_state:
del st.session_state[key] del st.session_state[key]
@@ -357,21 +365,21 @@ def main():
if st.button("📊 持仓分析", width='stretch', key="nav_portfolio", help="投资组合分析与定时跟踪"): if st.button("📊 持仓分析", width='stretch', key="nav_portfolio", help="投资组合分析与定时跟踪"):
st.session_state.show_portfolio = True st.session_state.show_portfolio = True
for key in ['show_history', 'show_monitor', 'show_config', 'show_main_force', for key in ['show_history', 'show_monitor', 'show_config', 'show_main_force',
'show_sector_strategy', 'show_longhubang', 'show_smart_monitor', 'show_low_price_bull']: 'show_sector_strategy', 'show_longhubang', 'show_smart_monitor', 'show_low_price_bull', 'show_news_flow']:
if key in st.session_state: if key in st.session_state:
del st.session_state[key] del st.session_state[key]
if st.button("🤖 AI盯盘", width='stretch', key="nav_smart_monitor", help="DeepSeek AI自动盯盘决策交易(支持A股T+1)"): if st.button("🤖 AI盯盘", width='stretch', key="nav_smart_monitor", help="DeepSeek AI自动盯盘决策交易(支持A股T+1)"):
st.session_state.show_smart_monitor = True st.session_state.show_smart_monitor = True
for key in ['show_history', 'show_monitor', 'show_config', 'show_main_force', for key in ['show_history', 'show_monitor', 'show_config', 'show_main_force',
'show_sector_strategy', 'show_longhubang', 'show_portfolio', 'show_low_price_bull']: 'show_sector_strategy', 'show_longhubang', 'show_portfolio', 'show_low_price_bull', 'show_news_flow']:
if key in st.session_state: if key in st.session_state:
del st.session_state[key] del st.session_state[key]
if st.button("📡 实时监测", width='stretch', key="nav_monitor", help="价格监控与预警提醒"): if st.button("📡 实时监测", width='stretch', key="nav_monitor", help="价格监控与预警提醒"):
st.session_state.show_monitor = True st.session_state.show_monitor = True
for key in ['show_history', 'show_main_force', 'show_longhubang', 'show_portfolio', for key in ['show_history', 'show_main_force', 'show_longhubang', 'show_portfolio',
'show_config', 'show_sector_strategy', 'show_smart_monitor', 'show_low_price_bull']: 'show_config', 'show_sector_strategy', 'show_smart_monitor', 'show_low_price_bull', 'show_news_flow']:
if key in st.session_state: if key in st.session_state:
del st.session_state[key] del st.session_state[key]
@@ -381,7 +389,7 @@ def main():
if st.button("📖 历史记录", width='stretch', key="nav_history", help="查看历史分析记录"): if st.button("📖 历史记录", width='stretch', key="nav_history", help="查看历史分析记录"):
st.session_state.show_history = True st.session_state.show_history = True
for key in ['show_monitor', 'show_longhubang', 'show_portfolio', 'show_config', for key in ['show_monitor', 'show_longhubang', 'show_portfolio', 'show_config',
'show_main_force', 'show_sector_strategy', 'show_low_price_bull']: 'show_main_force', 'show_sector_strategy', 'show_low_price_bull', 'show_news_flow']:
if key in st.session_state: if key in st.session_state:
del st.session_state[key] del st.session_state[key]
@@ -389,7 +397,7 @@ def main():
if st.button("⚙️ 环境配置", width='stretch', key="nav_config", help="系统设置与API配置"): if st.button("⚙️ 环境配置", width='stretch', key="nav_config", help="系统设置与API配置"):
st.session_state.show_config = True st.session_state.show_config = True
for key in ['show_history', 'show_monitor', 'show_main_force', 'show_sector_strategy', for key in ['show_history', 'show_monitor', 'show_main_force', 'show_sector_strategy',
'show_longhubang', 'show_portfolio', 'show_low_price_bull']: 'show_longhubang', 'show_portfolio', 'show_low_price_bull', 'show_news_flow']:
if key in st.session_state: if key in st.session_state:
del st.session_state[key] del st.session_state[key]
@@ -521,6 +529,11 @@ def main():
display_portfolio_manager() display_portfolio_manager()
return return
# 检查是否显示新闻流量监测
if 'show_news_flow' in st.session_state and st.session_state.show_news_flow:
display_news_flow_monitor()
return
# 检查是否显示环境配置 # 检查是否显示环境配置
if 'show_config' in st.session_state and st.session_state.show_config: if 'show_config' in st.session_state and st.session_state.show_config:
display_config_manager() display_config_manager()
+400
View File
@@ -0,0 +1,400 @@
# 新闻流量监测功能说明
## 📰 功能概述
**新闻流量监测**是基于"流量为王"炒股理念为本系统新增的短线交易指导功能模块。通过实时监测20+主流平台的新闻热点和流量数据,帮助短线投资者把握题材炒作的节奏,识别流量高潮(卖出信号)和流量爆发(买入机会)。
---
## 🎯 核心理念
### "流量为王"投机市场规律
```
接盘总量 = 流量 × 转化率 × 客单价
```
**关键理论:**
1. **流量高潮 = 价格高潮 = 逃命时刻**
2. **流量爆发 = 题材启动 = 进场机会**
3. **流量衰减 = 资金离场 = 减仓信号**
### 两种流量类型
**1. 存量流量型**(快速爆发)
- 特征:顶级流量,快速到位,不需要发酵
- 案例:政策事件、突发新闻、知名人物发声
- 时间窗口:短(几小时到几天)
- 策略:手快吃肉,手慢接盘
**2. 增量流量型**(裂变传播)
- 特征:初始流量小,有病毒传播能力,指数增长
- 判断指标:K值(病毒系数)>1.5为指数型爆发
- 时间窗口:长(几天到几周)
- 策略:埋伏早期,分批进出
---
## 🚀 功能特性
### 1. 多平台数据聚合
**支持20+主流平台:**
| 类别 | 平台 | 权重 | 说明 |
|------|------|------|------|
| 社交媒体 | 微博热搜 | 10 | 核心流量指标 |
| 社交媒体 | 抖音热点 | 9 | 短视频流量 |
| 社交媒体 | 知乎热榜 | 7 | 深度内容流量 |
| 财经平台 | 新浪财经 | 9 | 股市相关 |
| 财经平台 | 东方财富 | 9 | 散户关注度 |
| 财经平台 | 雪球 | 8 | 投资者讨论 |
| 财经平台 | 财联社 | 8 | 专业资讯 |
| 新闻媒体 | 百度热搜 | 8 | 全民热点 |
| 新闻媒体 | 今日头条 | 7 | 算法推荐 |
### 2. 智能流量分析
- **流量得分计算**:综合权重×新闻数量,归一化到0-1000分
- **流量等级判断**
- 极高(≥800分):流量爆发,警惕见顶
- 高(500-800分):流量活跃,关注机会
- 中(200-500分):流量正常,保持观望
- 低(<200分):流量低迷,空仓为主
- **历史对比**:计算当前得分的百分位,了解历史水平
### 3. 股票相关新闻提取
- 自动识别40+股票关键词(涨停、跌停、概念股、龙头股等)
- 按平台权重和关键词匹配数排序
- 提取高频提及的个股代码
- 生成潜力个股TOP10
### 4. 热门话题挖掘
- 基于jieba分词的话题提取
- 过滤停用词,保留有效话题
- 统计话题热度(0-100分)
- 识别热门板块(AI、新能源、半导体等)
### 5. 交易信号生成
**基于流量等级的交易信号:**
| 流量等级 | 得分范围 | 信号 | 信心度 | 操作建议 |
|----------|----------|------|--------|----------|
| 极高(顶部) | ≥900 | 卖出 | 9/10 | 立即减仓或清仓,锁定利润 |
| 极高(高位) | 800-900 | 观望 | 7/10 | 持仓观望,逢高减仓 |
| 高 | 500-800 | 买入 | 8/10 | 关注龙头股,轻仓试探 |
| 中 | 200-500 | 观望 | 5/10 | 保持观望,等待信号明确 |
| 低 | <200 | 观望 | 3/10 | 空仓观望,等待市场转暖 |
**输出内容:**
- 整体信号(买入/观望/卖出)
- 信心度(0-10分)
- 热门板块TOP3
- 潜力个股TOP10
- 风险提示
- 操作建议
### 6. 流量趋势分析
- 7-30天流量趋势图
- 平均/最高/最低流量得分
- 趋势判断(上升/下降/平稳)
- 每日统计汇总
### 7. 历史记录管理
- 完整的监测历史保存
- 快照详情查看(股票新闻、热门话题)
- 历史数据对比分析
- 支持按关键词搜索
---
## 💻 技术架构
### 模块组成
```
news_flow_data.py # 数据获取模块
├── NewsFlowDataFetcher # 新闻数据获取器
├── get_platform_news() # 单平台数据
├── get_multi_platform_news() # 多平台聚合
├── extract_stock_related_news() # 股票新闻提取
├── calculate_flow_score() # 流量得分计算
└── get_hot_topics() # 热门话题挖掘
news_flow_db.py # 数据库模块
├── NewsFlowDatabase # 数据库管理类
├── flow_snapshots # 流量快照表
├── platform_news # 平台新闻表
├── stock_related_news # 股票相关新闻表
├── hot_topics # 热门话题表
└── flow_statistics # 每日统计表
news_flow_engine.py # 分析引擎
├── NewsFlowEngine # 流量分析引擎
├── run_full_analysis() # 完整分析流程
├── _generate_trading_signals() # 生成交易信号
├── get_flow_trend() # 流量趋势分析
└── compare_with_history() # 历史对比
news_flow_ui.py # UI界面
└── display_news_flow_monitor() # 主界面
├── display_realtime_monitor() # 实时监测
├── display_flow_trend() # 流量趋势
├── display_history_records() # 历史记录
└── display_settings() # 设置
```
### 数据库表结构
**1. flow_snapshots(流量快照表)**
- 记录每次监测的整体情况
- 字段:total_score, flow_level, social_score, finance_score, analysis等
**2. platform_news(平台新闻表)**
- 存储各平台的新闻数据
- 字段:platform, title, content, url, publish_time等
**3. stock_related_news(股票相关新闻表)**
- 股票相关新闻筛选结果
- 字段:title, matched_keywords, keyword_count等
**4. hot_topics(热门话题表)**
- 热门话题词频统计
- 字段:topic, count, heat
**5. flow_statistics(每日统计表)**
- 按天汇总的流量统计
- 字段:date, avg_score, max_score, snapshot_count等
---
## 📊 使用指南
### 快速开始
1. **进入新闻流量监测**
- 点击侧边栏"📊 策略分析" → "📰 新闻流量"
2. **选择监测模式**
- 全平台监测:监测所有20+平台
- 社交媒体:仅监测微博、抖音、知乎等
- 财经平台:仅监测新浪财经、东财、雪球等
- 新闻媒体:仅监测百度、头条、腾讯网等
- 自定义:手动选择平台
3. **开始监测**
- 点击"🚀 开始监测"按钮
- 等待数据获取和分析(约30-60秒)
4. **查看结果**
- 流量得分和等级
- 交易信号和操作建议
- 热门板块TOP3
- 潜力个股TOP10
- 股票相关新闻
- 热门话题词云
### 监测频率建议
**盘前监测(9:00-9:25**
- 目的:了解当日市场热点
- 重点:关注新增热点题材
- 操作:制定当日交易计划
**盘后监测(15:30-16:00**
- 目的:复盘当日市场情绪
- 重点:分析流量变化趋势
- 操作:调整持仓和策略
**晚间监测(20:00-21:00**
- 目的:准备次日策略
- 重点:深度研究热点板块
- 操作:精选次日目标股
### 信号解读原则
**1. 流量极高(≥900分)**
- **理论**:流量高潮=价格高潮,后面没有更傻的接盘侠
- **特征**:主流媒体报道、全民讨论、短视频霸屏
- **操作**:立即减仓,不要贪恋最后一跌
- **案例**:深度求索概念(2024年)
**2. 流量高(500-800分)**
- **理论**:流量快速上升,题材正在发酵
- **特征**:热搜上榜、财经媒体关注、散户开始涌入
- **操作**:关注龙头股,轻仓试探,设好止损
- **策略**:先涨为王,名字为王
**3. 流量中(200-500分)**
- **理论**:流量正常波动,方向不明确
- **特征**:常规新闻,没有突出热点
- **操作**:保持观望,可关注潜力题材
- **策略**:等待流量信号明确
**4. 流量低(<200分)**
- **理论**:流量低迷,市场情绪冷淡
- **特征**:成交量萎缩、缺乏热点、媒体冷清
- **操作**:空仓观望,等待市场转暖
- **策略**:可布局低估值个股
### 配合使用建议
**与其他功能协同:**
1. **新闻流量 + 智瞰龙虎**
- 流量识别热点 → 龙虎榜确认游资动向
- 双重验证,提高成功率
2. **新闻流量 + 智策板块**
- 流量发现题材 → 智策分析板块轮动
- 把握进场和退出时机
3. **新闻流量 + 主力选股**
- 流量筛选热点 → 主力资金确认标的
- 跟随聪明钱,提高胜率
4. **新闻流量 + AI盯盘**
- 流量判断大盘情绪 → AI盯盘执行个股交易
- 自动化交易,解放双手
---
## ⚠️ 风险提示
### 使用注意事项
1. **流量≠价格**
- 流量高只代表关注度高,不代表股价一定上涨
- 必须结合基本面、技术面、资金面综合判断
2. **反向指标**
- 当你的反向指标(如擦鞋童、小区大妈)都在讨论某个股票时,往往是见顶信号
- 记住:连擦鞋童都在炒股了 → 肯尼迪立即清仓
3. **流量高潮识别**
- 短视频热搜排名停止上升
- 评论增速明显放缓
- 主流媒体不再报道
- 新用户涌入停滞
4. **数据时效性**
- 新闻API数据有一定延迟(约10-30分钟)
- 不适用于超高频交易
- 建议配合实时盘口观察
5. **市场风险**
- 题材炒作具有高风险,可能快速反转
- 严格执行止损,不要死扛
- 仓位控制:单只股票不超过30%
- 本功能仅供参考,不构成投资建议
### 免责声明
本功能基于公开的新闻数据和"流量为王"理论进行分析,所有分析结果仅供参考。股市有风险,投资需谨慎。使用本功能进行投资决策的风险由用户自行承担。
---
## 🔧 技术依赖
### Python包
```bash
pip install requests # HTTP请求
pip install jieba # 中文分词
pip install plotly # 数据可视化
pip install pandas # 数据处理
```
### 外部API
- **新闻API**: https://newsapi.ws4.cn/api/v1/dailynews/
- **免费使用**:无需API Key
- **速率限制**:建议每次请求间隔0.5秒以上
---
## 📈 实战案例
### 案例1:流量爆发 - 深度求索概念(2024年)
**背景**
- 东大自研AI大模型技术突破
- 主流媒体报道,短视频热搜霸榜
- 带有民族情绪加成
**流量特征**
- 存量流量型(自带顶流)
- 流量得分:950+(极高)
- 全平台关注度爆发
**交易信号**
- 第1天:流量500分,信号"买入",龙头涨停
- 第2天:流量800分,信号"观望",持仓待涨
- 第3天:流量950分,信号"卖出",立即减仓 ← **流量高潮!**
- 第4天:流量600分,题材降温,股价回调
**结论**:严格按流量信号操作,第3天卖出,规避后续回调。
### 案例2:流量裂变 - 新能源题材(持续炒作)
**背景**
- 政策支持,产业发展
- K值>1.5,病毒式传播
**流量特征**
- 增量流量型(裂变传播)
- 流量逐步上升:300 → 500 → 700 → 850
- 时间跨度:2-3周
**交易信号**
- 第1周:流量300分,埋伏早期
- 第2周:流量500分,加仓龙头
- 第3周:流量700分,持仓待涨
- 第4周:流量850分,逐步减仓
**结论**:增量流量型时间窗口长,可分批进出,把握多波段行情。
---
## 📚 相关文档
- **理论基础**: `新闻流量转化炒股法.md`
- **API文档**: `新闻监测API调用说明.md`
- **主程序**: `app.py`
- **数据模块**: `news_flow_data.py`
- **数据库**: `news_flow_db.py`
- **分析引擎**: `news_flow_engine.py`
- **UI界面**: `news_flow_ui.py`
---
## 🎯 后续优化方向
1. **AI增强**
- 接入DeepSeek AI分析新闻内容
- 自动识别题材炒作阶段(萌芽/爆发/高潮/退潮)
2. **流量追踪**
- 实时监控流量变化(每分钟)
- 流量拐点自动告警
3. **个股关联**
- 从新闻中自动提取股票代码
- 与龙虎榜、主力资金数据关联
4. **情绪量化**
- NLP情感分析(正面/负面)
- 社交媒体情绪指数
5. **策略回测**
- 历史流量与股价涨跌相关性分析
- 优化流量阈值和交易信号
---
**祝你交易顺利!** 📈💰
记住:"流量为王,见顶就跑!"
+186
View File
@@ -0,0 +1,186 @@
# 新闻流量监测功能 - 快速开始指南
## 🚀 快速开始
### 1. 安装依赖
```bash
# 激活虚拟环境(如果有)
.\venv\Scripts\Activate.ps1
# 安装新增依赖
pip install jieba>=0.42.1
```
### 2. 启动系统
```bash
# 方式1:使用run.py
python run.py
# 方式2:直接启动streamlit
streamlit run app.py
```
### 3. 访问功能
1. 打开浏览器访问:http://localhost:8501
2. 点击侧边栏"📊 策略分析" → "📰 新闻流量"
3. 选择监测模式,点击"🚀 开始监测"
---
## 📊 功能概览
### 核心功能
- ✅ 实时监测20+主流平台新闻热点
- ✅ 智能计算流量得分(0-1000分)
- ✅ 自动生成交易信号(买入/观望/卖出)
- ✅ 识别热门板块TOP3
- ✅ 提取潜力个股TOP10
- ✅ 流量趋势分析(7-30天)
- ✅ 完整的历史记录管理
### 支持平台
**社交媒体**:微博、抖音、知乎、B站
**财经平台**:新浪财经、东方财富、雪球、财联社
**新闻媒体**:百度、今日头条、腾讯网
---
## 💡 使用示例
### 场景1:盘前快速监测(推荐)
**时间**9:00-9:25
**目的**:了解当日市场热点
```
1. 进入"📰 新闻流量"
2. 选择"财经平台"模式
3. 点击"开始监测"
4. 查看:
- 流量得分和等级
- 交易信号
- 热门板块
- 潜力个股
5. 制定当日交易计划
```
### 场景2:盘后复盘分析
**时间**15:30-16:00
**目的**:复盘当日市场情绪
```
1. 进入"📰 新闻流量"
2. 选择"全平台监测"
3. 执行监测,查看结果
4. 切换到"📈 流量趋势"标签
5. 对比今日与历史流量
6. 调整持仓和策略
```
### 场景3:深度研究热点题材
**时间**:晚间20:00-21:00
**目的**:准备次日策略
```
1. 全平台监测获取完整数据
2. 重点关注:
- 股票相关新闻TOP10
- 热门话题词云
- 潜力个股列表
3. 结合"🎯 智瞰龙虎"确认游资动向
4. 结合"💰 主力选股"验证资金流向
5. 精选次日目标股
```
---
## 🎯 信号解读速查表
| 流量得分 | 流量等级 | 交易信号 | 信心度 | 操作 |
|---------|---------|---------|--------|------|
| ≥900 | 极高 | 卖出 | 9/10 | 立即减仓 |
| 800-900 | 极高 | 观望 | 7/10 | 逢高减仓 |
| 500-800 | 高 | 买入 | 8/10 | 轻仓试探 |
| 200-500 | 中 | 观望 | 5/10 | 保持观望 |
| <200 | 低 | 观望 | 3/10 | 空仓观望 |
---
## ⚠️ 重要提示
### 必读原则
1. **流量高潮 = 价格高潮 = 逃命时刻**
当流量得分≥900时,立即减仓,不要贪恋最后一跌
2. **反向指标警惕**
当你的反向指标(小区大妈、出租车司机)都在讨论某个股票时,往往是见顶信号
3. **严格止损**
题材炒作风险高,务必设置止损位(-5%)
4. **仓位控制**
单只股票不超过30%,避免重仓一把梭
5. **配合使用**
结合基本面、技术面、资金面综合判断,不要单纯依赖流量信号
### 数据说明
- 新闻API数据有10-30分钟延迟
- 不适用于超高频交易
- 仅供参考,不构成投资建议
---
## 🔧 故障排查
### 问题1:监测失败
**现象**:点击开始监测后报错
**解决方案**
1. 检查网络连接是否正常
2. 确认新闻API服务可用(访问:https://newsapi.ws4.cn/api/v1/dailynews/?platform=baidu
3. 查看终端错误日志
### 问题2jieba未安装
**现象**:报错"ModuleNotFoundError: No module named 'jieba'"
**解决方案**
```bash
pip install jieba
```
### 问题3:分析时间过长
**现象**:监测超过2分钟仍未完成
**解决方案**
1. 减少监测平台数量(选择"财经平台"而非"全平台"
2. 检查网络速度
3. 等待完成(首次获取需要更长时间)
---
## 📚 深入学习
### 相关文档
- **功能详解**`docs/新闻流量监测功能说明.md`
- **理论基础**`新闻流量转化炒股法.md`
- **API文档**`新闻监测API调用说明.md`
### 配合使用
- **智瞰龙虎**:验证游资动向
- **智策板块**:分析板块轮动
- **主力选股**:跟踪主力资金
- **AI盯盘**:自动化交易执行
---
## 💬 反馈与支持
如有问题或建议,请联系:ws3101001@126.com
**祝你交易顺利!记住:流量为王,见顶就跑!** 📈💰
BIN
View File
Binary file not shown.
+996
View File
@@ -0,0 +1,996 @@
"""
新闻流量智能分析代理模块
使用DeepSeek进行AI驱动的分析
包含:板块影响分析、股票推荐、风险评估、投资建议
"""
import json
import logging
import time
from datetime import datetime
from typing import Dict, List, Optional
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class NewsFlowAgents:
"""新闻流量智能分析代理"""
def __init__(self, model: str = "deepseek-chat"):
"""
初始化代理
Args:
model: 使用的模型,默认 deepseek-chat
"""
self.model = model
self.deepseek_client = None
self._init_client()
def _init_client(self):
"""初始化DeepSeek客户端"""
try:
from deepseek_client import DeepSeekClient
self.deepseek_client = DeepSeekClient(model=self.model)
logger.info(f"✅ DeepSeek客户端初始化成功,模型: {self.model}")
except Exception as e:
logger.error(f"❌ DeepSeek客户端初始化失败: {e}")
self.deepseek_client = None
def is_available(self) -> bool:
"""检查AI是否可用"""
return self.deepseek_client is not None
def sector_impact_agent(self, hot_topics: List[Dict],
stock_news: List[Dict],
flow_data: Dict = None) -> Dict:
"""
板块影响分析代理
分析热点可能影响的板块
Returns:
{
'affected_sectors': List[Dict],
'analysis': str,
'success': bool,
}
"""
if not self.is_available():
return self._fallback_sector_analysis(hot_topics, stock_news)
# 准备数据
topics_text = '\n'.join([
f"- {t['topic']} (热度:{t.get('heat', 0)}, 跨{t.get('cross_platform', 0)}平台)"
for t in hot_topics[:20]
])
news_text = '\n'.join([
f"- [{n.get('platform_name', '')}] {n.get('title', '')}"
for n in stock_news[:15]
])
flow_info = ""
if flow_data:
flow_info = f"""
当前流量状态:
- 流量得分: {flow_data.get('total_score', 'N/A')}/1000
- 流量等级: {flow_data.get('level', 'N/A')}
- 社交媒体热度: {flow_data.get('social_score', 'N/A')}
- 财经平台热度: {flow_data.get('finance_score', 'N/A')}
"""
prompt = f"""你是一名资深的A股短线投资分析师,专注于热点题材挖掘和板块轮动分析。
【重要】请根据以下全网热点数据,进行深度的A股题材分析:
=== 全网热门话题TOP20 ===
{topics_text}
=== 股票相关新闻TOP15 ===
{news_text}
{flow_info}
请完成以下分析任务:
1. **题材挖掘**:从以上热点中挖掘出可能引爆A股的核心题材概念
2. **板块分析**:分析最可能受益的A股板块(要具体到申万行业或同花顺概念板块)
3. **热度评估**:评估每个板块的潜在炒作热度和持续性
4. **龙头预判**:推测可能的龙头股特征
请以JSON格式输出:
{{
"hot_themes": [
{{"theme": "题材名称", "source": "来源热点", "heat_level": "极高/高/中", "sustainability": "持续性评估"}}
],
"benefited_sectors": [
{{
"name": "板块名称(要具体如:AI算力、低空经济、机器人等)",
"impact": "利好",
"confidence": 85,
"reason": "详细分析原因",
"related_concepts": ["相关概念1", "相关概念2"],
"leader_characteristics": "龙头股特征描述"
}}
],
"damaged_sectors": [
{{"name": "板块名称", "impact": "利空", "confidence": 60, "reason": "原因"}}
],
"opportunity_assessment": "今日A股投资机会综合评估(100字以内)",
"trading_suggestion": "短线操作建议",
"key_points": ["核心要点1", "核心要点2", "核心要点3"]
}}
只输出JSON,不要其他文字。"""
try:
messages = [
{"role": "system", "content": "你是专业的A股市场分析师,输出必须是纯JSON格式。"},
{"role": "user", "content": prompt}
]
response = self.deepseek_client.call_api(messages, temperature=0.5, max_tokens=2000)
# 解析JSON
result = self._parse_json_response(response)
if result:
return {
'hot_themes': result.get('hot_themes', []),
'affected_sectors': result.get('benefited_sectors', []) + result.get('damaged_sectors', []),
'benefited_sectors': result.get('benefited_sectors', []),
'damaged_sectors': result.get('damaged_sectors', []),
'opportunity_assessment': result.get('opportunity_assessment', ''),
'trading_suggestion': result.get('trading_suggestion', ''),
'key_points': result.get('key_points', []),
'success': True,
'raw_response': response,
}
else:
return self._fallback_sector_analysis(hot_topics, stock_news)
except Exception as e:
logger.error(f"板块分析失败: {e}")
return self._fallback_sector_analysis(hot_topics, stock_news)
def stock_recommend_agent(self, hot_sectors: List[Dict],
flow_stage: str,
sentiment_class: str) -> Dict:
"""
股票推荐代理
基于热门板块和市场状态推荐股票
Returns:
{
'recommended_stocks': List[Dict],
'strategy': str,
'success': bool,
}
"""
if not self.is_available():
return self._fallback_stock_recommend(hot_sectors)
sectors_text = '\n'.join([
f"- {s.get('name', '')}{s.get('impact', '利好')},置信度{s.get('confidence', 50)}%\n 原因:{s.get('reason', '')}\n 龙头特征:{s.get('leader_characteristics', 'N/A')}"
for s in hot_sectors[:5]
])
related_concepts = []
for s in hot_sectors[:5]:
related_concepts.extend(s.get('related_concepts', []))
concepts_text = ', '.join(list(set(related_concepts))[:10]) if related_concepts else ''
prompt = f"""你是一名资深的A股短线游资操盘手,专注于热点题材龙头股挖掘。
=== 当前市场状态 ===
- 流量阶段: {flow_stage}
- 情绪状态: {sentiment_class}
- 相关概念: {concepts_text}
=== 热门受益板块分析 ===
{sectors_text}
=== 选股要求 ===
请根据"流量为王"理念,推荐5-8只A股短线标的:
选股法则(必须遵循):
1. **先涨为王**:优先选择已经启动、走势强势的股票
2. **名字为王**:股票名称与热点高度相关(如AI概念选""字头)
3. **龙头优先**:选择板块内最强势的龙头或人气股
4. **题材纯正**:主业与热点题材高度相关
5. **流通盘适中**:30-150亿市值为佳,便于资金操作
请以JSON格式输出:
{{
"recommended_stocks": [
{{
"code": "股票代码(6位数字,如000001或600001",
"name": "股票名称",
"sector": "所属板块",
"market": "沪市/深市/创业板/科创板",
"market_cap": "市值(亿)",
"reason": "推荐理由(与热点的关联性)",
"catalyst": "催化剂/驱动因素",
"strategy": "操作策略(进场/加仓/止损建议)",
"target_space": "目标空间(如15-20%",
"risk_level": "低/中/高",
"attention_points": ["注意事项1", "注意事项2"]
}}
],
"overall_strategy": "整体操作策略和仓位建议",
"timing_advice": "最佳介入时机判断",
"risk_warning": "风险提示(必须包含投资风险提醒)"
}}
【重要】只推荐真实存在的A股股票,代码必须正确。只输出JSON。"""
try:
messages = [
{"role": "system", "content": "你是专业的A股投资顾问,只输出纯JSON格式。"},
{"role": "user", "content": prompt}
]
response = self.deepseek_client.call_api(messages, temperature=0.6, max_tokens=2000)
result = self._parse_json_response(response)
if result:
return {
'recommended_stocks': result.get('recommended_stocks', []),
'overall_strategy': result.get('overall_strategy', ''),
'timing_advice': result.get('timing_advice', ''),
'risk_warning': result.get('risk_warning', ''),
'success': True,
'raw_response': response,
}
else:
return self._fallback_stock_recommend(hot_sectors)
except Exception as e:
logger.error(f"股票推荐失败: {e}")
return self._fallback_stock_recommend(hot_sectors)
def risk_assess_agent(self, flow_stage: str,
sentiment_data: Dict,
viral_k: float,
flow_type: str) -> Dict:
"""
风险评估代理
评估当前市场风险
Returns:
{
'risk_level': str,
'risk_factors': List[str],
'risk_score': int,
'analysis': str,
'success': bool,
}
"""
if not self.is_available():
return self._fallback_risk_assess(flow_stage, sentiment_data, viral_k)
prompt = f"""你是一名专业的风险管理分析师。
请根据以下市场数据评估当前投资风险:
市场状态:
- 流量阶段: {flow_stage}
- 情绪指数: {sentiment_data.get('sentiment_index', 50)}
- 情绪分类: {sentiment_data.get('sentiment_class', '中性')}
- K值(病毒系数): {viral_k}
- 流量类型: {flow_type}
核心理念:
- 流量高潮 = 价格高潮 = 逃命时刻
- K值>1.5表示指数型爆发,风险上升
- 情绪极端(>85或<20)都意味着风险
请分析:
1. 当前风险等级(极低/低/中等/高/极高)
2. 主要风险因素
3. 风险分数(0-100
4. 详细分析
以JSON格式输出:
{{
"risk_level": "",
"risk_score": 75,
"risk_factors": ["风险因素1", "风险因素2", ...],
"opportunities": ["机会1", "机会2", ...],
"analysis": "详细分析文字",
"key_warning": "最重要的警告"
}}
只输出JSON。"""
try:
messages = [
{"role": "system", "content": "你是专业的风险管理分析师,只输出纯JSON格式。"},
{"role": "user", "content": prompt}
]
response = self.deepseek_client.call_api(messages, temperature=0.4, max_tokens=1500)
result = self._parse_json_response(response)
if result:
return {
'risk_level': result.get('risk_level', '中等'),
'risk_score': result.get('risk_score', 50),
'risk_factors': result.get('risk_factors', []),
'opportunities': result.get('opportunities', []),
'analysis': result.get('analysis', ''),
'key_warning': result.get('key_warning', ''),
'success': True,
'raw_response': response,
}
else:
return self._fallback_risk_assess(flow_stage, sentiment_data, viral_k)
except Exception as e:
logger.error(f"风险评估失败: {e}")
return self._fallback_risk_assess(flow_stage, sentiment_data, viral_k)
def investment_advisor_agent(self, sector_analysis: Dict,
stock_recommend: Dict,
risk_assess: Dict,
flow_data: Dict,
sentiment_data: Dict) -> Dict:
"""
投资建议代理(综合)
综合所有分析给出最终投资建议
Returns:
{
'advice': str, # 买入/持有/观望/回避
'confidence': int,
'summary': str,
'action_plan': List[str],
'success': bool,
}
"""
if not self.is_available():
return self._fallback_investment_advice(risk_assess, flow_data)
# 构建综合信息
sectors_text = ', '.join([s.get('name', '') for s in sector_analysis.get('benefited_sectors', [])[:3]])
stocks_text = ', '.join([f"{s.get('name', '')}({s.get('code', '')})"
for s in stock_recommend.get('recommended_stocks', [])[:3]])
prompt = f"""你是一名首席投资策略师,需要给出最终的投资建议。
综合分析数据:
【流量分析】
- 流量得分: {flow_data.get('total_score', 'N/A')}
- 流量等级: {flow_data.get('level', 'N/A')}
【情绪分析】
- 情绪指数: {sentiment_data.get('sentiment_index', 50)}
- 情绪分类: {sentiment_data.get('sentiment_class', '中性')}
- 流量阶段: {sentiment_data.get('flow_stage', '未知')}
【板块分析】
- 受益板块: {sectors_text}
- 机会评估: {sector_analysis.get('opportunity_assessment', 'N/A')}
【股票推荐】
- 推荐股票: {stocks_text}
【风险评估】
- 风险等级: {risk_assess.get('risk_level', '中等')}
- 风险分数: {risk_assess.get('risk_score', 50)}
- 主要风险: {', '.join(risk_assess.get('risk_factors', [])[:3])}
核心原则(流量为王):
- 流量高潮 = 价格高潮 = 逃命时刻
- 当热搜、媒体报道、KOL转发同时达到高潮时,就是出货时机
- 短线操作:快进快出,紧跟龙头
请给出最终投资建议:
1. 操作建议(买入/持有/观望/回避)
2. 置信度(0-100
3. 综合总结
4. 具体行动计划
以JSON格式输出:
{{
"advice": "观望",
"confidence": 75,
"summary": "综合总结文字",
"action_plan": [
"行动1",
"行动2",
...
],
"position_suggestion": "仓位建议",
"timing": "时机判断",
"key_message": "最重要的一句话"
}}
只输出JSON。"""
try:
start_time = time.time()
messages = [
{"role": "system", "content": "你是首席投资策略师,必须给出明确的投资建议,只输出纯JSON格式。"},
{"role": "user", "content": prompt}
]
response = self.deepseek_client.call_api(messages, temperature=0.5, max_tokens=2000)
result = self._parse_json_response(response)
analysis_time = time.time() - start_time
if result:
return {
'advice': result.get('advice', '观望'),
'confidence': result.get('confidence', 50),
'summary': result.get('summary', ''),
'action_plan': result.get('action_plan', []),
'position_suggestion': result.get('position_suggestion', ''),
'timing': result.get('timing', ''),
'key_message': result.get('key_message', ''),
'success': True,
'analysis_time': round(analysis_time, 2),
'raw_response': response,
}
else:
return self._fallback_investment_advice(risk_assess, flow_data)
except Exception as e:
logger.error(f"投资建议生成失败: {e}")
return self._fallback_investment_advice(risk_assess, flow_data)
def run_full_analysis(self, hot_topics: List[Dict],
stock_news: List[Dict],
flow_data: Dict,
sentiment_data: Dict,
viral_k: float = 1.0,
flow_type: str = "未知") -> Dict:
"""
运行完整的AI分析
Returns:
{
'sector_analysis': Dict,
'stock_recommend': Dict,
'risk_assess': Dict,
'investment_advice': Dict,
'success': bool,
'analysis_time': float,
}
"""
start_time = time.time()
logger.info("🤖 开始AI分析...")
# 1. 板块影响分析
logger.info(" 📊 分析板块影响...")
sector_analysis = self.sector_impact_agent(hot_topics, stock_news, flow_data)
# 2. 股票推荐
logger.info(" 📈 生成股票推荐...")
flow_stage = sentiment_data.get('flow_stage', {}).get('stage_name', '未知')
sentiment_class = sentiment_data.get('sentiment', {}).get('sentiment_class', '中性')
stock_recommend = self.stock_recommend_agent(
sector_analysis.get('benefited_sectors', []),
flow_stage,
sentiment_class
)
# 3. 风险评估
logger.info(" ⚠️ 评估风险...")
risk_assess = self.risk_assess_agent(
flow_stage,
sentiment_data.get('sentiment', {}),
viral_k,
flow_type
)
# 4. 综合投资建议
logger.info(" 💡 生成投资建议...")
investment_advice = self.investment_advisor_agent(
sector_analysis,
stock_recommend,
risk_assess,
flow_data,
sentiment_data.get('sentiment', {})
)
total_time = time.time() - start_time
logger.info(f"✅ AI分析完成,耗时 {total_time:.2f}")
# 汇总结果
return {
'sector_analysis': sector_analysis,
'stock_recommend': stock_recommend,
'risk_assess': risk_assess,
'investment_advice': investment_advice,
'success': all([
sector_analysis.get('success', False),
stock_recommend.get('success', False),
risk_assess.get('success', False),
investment_advice.get('success', False),
]),
'analysis_time': round(total_time, 2),
'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
}
def analyze_sector_deep(self, sector_name: str, related_news: List[Dict],
hot_topics: List[Dict]) -> Dict:
"""
深度分析单个板块
为每个热门板块单独调用DeepSeek进行深度分析
"""
if not self.is_available():
return {'success': False, 'error': 'AI不可用'}
news_text = '\n'.join([
f"- [{n.get('platform_name', '')}] {n.get('title', '')}"
for n in related_news[:20]
])
topics_text = '\n'.join([
f"- {t['topic']} (热度:{t.get('heat', 0)})"
for t in hot_topics[:10]
])
prompt = f"""你是{sector_name}板块的专业分析师。
请对以下与{sector_name}相关的新闻进行深度分析:
【相关新闻】
{news_text}
【相关热点话题】
{topics_text}
请分析:
1. {sector_name}板块当前的市场热度和关注度
2. 驱动因素分析(政策/技术/资金/事件)
3. 短期(1-3天)走势预判
4. 核心龙头股分析(至少3只)
5. 投资建议和风险提示
以JSON格式输出:
{{
"sector_name": "{sector_name}",
"heat_level": "极高/高/中/低",
"heat_score": 85,
"drivers": [
{{"type": "政策", "content": "具体驱动因素", "impact": "正面/负面"}}
],
"short_term_outlook": "看涨/震荡/看跌",
"outlook_reason": "预判理由",
"leader_stocks": [
{{
"code": "600000",
"name": "股票名称",
"reason": "龙头理由",
"strategy": "操作策略"
}}
],
"investment_advice": "具体投资建议",
"risk_warning": "风险提示",
"key_indicators": {{
"关注度": "",
"资金流向": "净流入",
"情绪指数": 75
}}
}}
只输出JSON。"""
try:
messages = [
{"role": "system", "content": f"你是{sector_name}板块专业分析师,只输出JSON格式。"},
{"role": "user", "content": prompt}
]
response = self.deepseek_client.call_api(messages, temperature=0.5, max_tokens=2000)
result = self._parse_json_response(response)
if result:
result['success'] = True
return result
else:
return {'success': False, 'sector_name': sector_name}
except Exception as e:
logger.error(f"{sector_name}板块分析失败: {e}")
return {'success': False, 'sector_name': sector_name, 'error': str(e)}
def run_multi_sector_analysis(self, hot_topics: List[Dict],
stock_news: List[Dict],
target_sectors: List[str] = None) -> Dict:
"""
多板块并行分析
对多个热门板块分别调用DeepSeek进行深度分析
Args:
hot_topics: 热门话题列表
stock_news: 股票相关新闻
target_sectors: 指定分析的板块列表,为None则自动识别
Returns:
{
'sector_analyses': List[Dict], # 各板块分析结果
'summary': str, # 综合总结
'top_sectors': List[str], # 最热门板块
'success': bool
}
"""
if not self.is_available():
return {'success': False, 'error': 'AI不可用', 'sector_analyses': []}
start_time = time.time()
# 如果没有指定板块,先识别热门板块
if not target_sectors:
target_sectors = self._identify_hot_sectors(hot_topics, stock_news)
logger.info(f"🔍 开始分析 {len(target_sectors)} 个热门板块: {target_sectors}")
# 对每个板块进行深度分析
sector_analyses = []
for sector in target_sectors[:5]: # 最多分析5个板块
logger.info(f" 📊 分析板块: {sector}")
# 筛选与该板块相关的新闻
related_news = self._filter_news_by_sector(stock_news, sector)
related_topics = self._filter_topics_by_sector(hot_topics, sector)
analysis = self.analyze_sector_deep(sector, related_news, related_topics)
if analysis.get('success'):
sector_analyses.append(analysis)
# 生成综合总结
summary = self._generate_multi_sector_summary(sector_analyses)
total_time = time.time() - start_time
logger.info(f"✅ 多板块分析完成,耗时 {total_time:.2f}")
return {
'sector_analyses': sector_analyses,
'summary': summary,
'top_sectors': target_sectors[:5],
'analysis_count': len(sector_analyses),
'analysis_time': round(total_time, 2),
'success': len(sector_analyses) > 0
}
def _identify_hot_sectors(self, hot_topics: List[Dict], stock_news: List[Dict]) -> List[str]:
"""识别热门板块"""
# 板块关键词映射
sector_keywords = {
'AI人工智能': ['AI', '人工智能', '大模型', 'ChatGPT', '算力', '智能', 'DeepSeek', '机器人'],
'新能源': ['新能源', '光伏', '锂电', '储能', '电池', '充电桩', '风电'],
'半导体芯片': ['芯片', '半导体', '光刻', '封装', '晶圆', '国产替代'],
'医药生物': ['医药', '生物', '疫苗', '创新药', '医疗', 'CXO'],
'消费': ['消费', '白酒', '食品', '零售', '餐饮', '旅游'],
'金融': ['银行', '保险', '券商', '证券', '金融'],
'房地产': ['房地产', '地产', '楼市', '房价'],
'军工': ['军工', '国防', '航空', '航天', '武器'],
'汽车': ['汽车', '新能源车', '智能驾驶', '无人驾驶'],
'低空经济': ['低空', '无人机', '飞行汽车', 'eVTOL'],
'机器人': ['机器人', '人形机器人', '工业机器人', '减速器'],
'数据要素': ['数据', '数据要素', '数据交易', '数字经济'],
}
# 统计各板块的热度
sector_scores = {}
# 从话题中统计
for topic in hot_topics:
topic_text = topic.get('topic', '')
for sector, keywords in sector_keywords.items():
for kw in keywords:
if kw in topic_text:
sector_scores[sector] = sector_scores.get(sector, 0) + topic.get('heat', 1)
break
# 从新闻中统计
for news in stock_news:
news_text = (news.get('title') or '') + (news.get('content') or '')
for sector, keywords in sector_keywords.items():
for kw in keywords:
if kw in news_text:
sector_scores[sector] = sector_scores.get(sector, 0) + news.get('weight', 1)
break
# 按热度排序
sorted_sectors = sorted(sector_scores.items(), key=lambda x: x[1], reverse=True)
return [s[0] for s in sorted_sectors[:5]]
def _filter_news_by_sector(self, news_list: List[Dict], sector: str) -> List[Dict]:
"""筛选与板块相关的新闻"""
sector_keywords = {
'AI人工智能': ['AI', '人工智能', '大模型', 'ChatGPT', '算力', '智能', 'DeepSeek', '机器人'],
'新能源': ['新能源', '光伏', '锂电', '储能', '电池', '充电桩', '风电'],
'半导体芯片': ['芯片', '半导体', '光刻', '封装', '晶圆'],
'医药生物': ['医药', '生物', '疫苗', '创新药', '医疗'],
'消费': ['消费', '白酒', '食品', '零售', '餐饮'],
'金融': ['银行', '保险', '券商', '证券', '金融'],
'房地产': ['房地产', '地产', '楼市'],
'军工': ['军工', '国防', '航空', '航天'],
'汽车': ['汽车', '新能源车', '智能驾驶'],
'低空经济': ['低空', '无人机', '飞行汽车'],
'机器人': ['机器人', '人形机器人', '减速器'],
'数据要素': ['数据', '数据要素', '数字经济'],
}
keywords = sector_keywords.get(sector, [sector])
related = []
for news in news_list:
text = (news.get('title') or '') + (news.get('content') or '')
for kw in keywords:
if kw in text:
related.append(news)
break
return related[:20]
def _filter_topics_by_sector(self, topics: List[Dict], sector: str) -> List[Dict]:
"""筛选与板块相关的话题"""
sector_keywords = {
'AI人工智能': ['AI', '人工智能', '大模型', 'ChatGPT', '算力', '智能'],
'新能源': ['新能源', '光伏', '锂电', '储能', '电池'],
'半导体芯片': ['芯片', '半导体', '光刻'],
'医药生物': ['医药', '生物', '疫苗', '医疗'],
'消费': ['消费', '白酒', '食品', '餐饮'],
}
keywords = sector_keywords.get(sector, [sector])
related = []
for topic in topics:
text = topic.get('topic', '')
for kw in keywords:
if kw in text:
related.append(topic)
break
return related[:10]
def _generate_multi_sector_summary(self, sector_analyses: List[Dict]) -> str:
"""生成多板块分析总结"""
if not sector_analyses:
return "暂无板块分析数据"
# 按热度排序
sorted_analyses = sorted(
sector_analyses,
key=lambda x: x.get('heat_score', 0),
reverse=True
)
summary_parts = []
summary_parts.append(f"共分析{len(sector_analyses)}个热门板块:")
for i, analysis in enumerate(sorted_analyses[:3], 1):
sector = analysis.get('sector_name', '未知')
heat = analysis.get('heat_level', '')
outlook = analysis.get('short_term_outlook', '震荡')
summary_parts.append(f"{i}. {sector}(热度{heat}{outlook}")
return ' '.join(summary_parts)
def _parse_json_response(self, response: str) -> Optional[Dict]:
"""解析JSON响应"""
try:
# 清理响应文本
text = response.strip()
# 处理markdown代码块
if '```json' in text:
text = text.split('```json')[1].split('```')[0]
elif '```' in text:
text = text.split('```')[1].split('```')[0]
# 移除可能的推理过程
if '【推理过程】' in text:
parts = text.split('【推理过程】')
text = parts[-1] if len(parts) > 1 else parts[0]
# 查找JSON部分
start = text.find('{')
end = text.rfind('}') + 1
if start >= 0 and end > start:
json_text = text[start:end]
return json.loads(json_text)
return None
except json.JSONDecodeError as e:
logger.error(f"JSON解析失败: {e}")
return None
# ==================== 降级方法 ====================
def _fallback_sector_analysis(self, hot_topics: List[Dict],
stock_news: List[Dict]) -> Dict:
"""板块分析降级方法"""
# 基于关键词的简单分析
sector_keywords = {
'AI人工智能': ['AI', '人工智能', 'ChatGPT', '大模型', '算力', 'GPT'],
'新能源': ['新能源', '锂电', '光伏', '风电', '储能', '充电桩'],
'半导体': ['芯片', '半导体', '光刻机', '集成电路', '封测'],
'医药生物': ['医药', '疫苗', '创新药', '医疗', '生物'],
'消费': ['消费', '白酒', '食品', '零售', '餐饮'],
'金融': ['银行', '保险', '券商', '金融', '信托'],
}
sector_hits = {}
for topic in hot_topics:
topic_text = topic.get('topic', '')
heat = topic.get('heat', 0)
for sector, keywords in sector_keywords.items():
if any(kw in topic_text for kw in keywords):
if sector not in sector_hits:
sector_hits[sector] = 0
sector_hits[sector] += heat
# 排序获取TOP板块
sorted_sectors = sorted(sector_hits.items(), key=lambda x: x[1], reverse=True)
benefited_sectors = [
{
'name': sector,
'impact': '利好',
'confidence': min(60, score // 2),
'reason': f'热点话题关联度较高,热度得分{score}'
}
for sector, score in sorted_sectors[:5]
]
return {
'affected_sectors': benefited_sectors,
'benefited_sectors': benefited_sectors,
'damaged_sectors': [],
'opportunity_assessment': '基于关键词匹配的简单分析,建议参考AI深度分析结果。',
'key_points': ['AI分析不可用,使用降级方法'],
'success': True,
'fallback': True,
}
def _fallback_stock_recommend(self, hot_sectors: List[Dict]) -> Dict:
"""股票推荐降级方法"""
return {
'recommended_stocks': [],
'overall_strategy': 'AI分析不可用,建议自行研究热门板块龙头股。',
'risk_warning': '此为降级结果,请谨慎参考。',
'success': True,
'fallback': True,
}
def _fallback_risk_assess(self, flow_stage: str,
sentiment_data: Dict,
viral_k: float) -> Dict:
"""风险评估降级方法"""
risk_score = 50
risk_factors = []
# 基于规则的简单风险评估
if flow_stage in ['一致', 'consensus']:
risk_score += 30
risk_factors.append('流量处于一致阶段,可能是顶部')
elif flow_stage in ['退潮', 'decline']:
risk_score += 20
risk_factors.append('流量正在退潮')
sentiment_index = sentiment_data.get('sentiment_index', 50)
if sentiment_index > 85:
risk_score += 15
risk_factors.append('情绪过度乐观')
elif sentiment_index < 20:
risk_score += 10
risk_factors.append('情绪过度悲观')
if viral_k > 1.5:
risk_score += 15
risk_factors.append(f'K值={viral_k},流量指数型增长')
risk_score = min(100, risk_score)
if risk_score >= 80:
risk_level = '极高'
elif risk_score >= 60:
risk_level = ''
elif risk_score >= 40:
risk_level = '中等'
elif risk_score >= 20:
risk_level = ''
else:
risk_level = '极低'
return {
'risk_level': risk_level,
'risk_score': risk_score,
'risk_factors': risk_factors,
'opportunities': [],
'analysis': '基于规则的简单风险评估,AI分析不可用。',
'key_warning': '请谨慎参考,建议开启AI分析获取更准确的评估。',
'success': True,
'fallback': True,
}
def _fallback_investment_advice(self, risk_assess: Dict,
flow_data: Dict) -> Dict:
"""投资建议降级方法"""
risk_level = risk_assess.get('risk_level', '中等')
if risk_level in ['极高', '']:
advice = '回避'
confidence = 70
summary = '当前风险较高,建议保持观望或减仓。'
elif risk_level == '中等':
advice = '观望'
confidence = 60
summary = '市场状态中性,建议观望等待更明确的信号。'
else:
advice = '关注'
confidence = 55
summary = '风险较低,可关注热点板块机会。'
return {
'advice': advice,
'confidence': confidence,
'summary': summary,
'action_plan': ['AI分析不可用,请自行判断'],
'position_suggestion': '建议仓位不超过30%',
'timing': '等待确认信号',
'key_message': '此为降级结果,请谨慎参考。',
'success': True,
'fallback': True,
}
# 全局实例
news_flow_agents = NewsFlowAgents()
# 测试代码
if __name__ == "__main__":
print("=== 测试新闻流量智能分析代理 ===")
# 检查AI是否可用
if news_flow_agents.is_available():
print("✅ AI客户端可用")
else:
print("⚠️ AI客户端不可用,将使用降级方法")
# 模拟数据
hot_topics = [
{'topic': 'AI芯片', 'heat': 95, 'cross_platform': 5},
{'topic': '新能源汽车', 'heat': 80, 'cross_platform': 4},
{'topic': '涨停板', 'heat': 75, 'cross_platform': 3},
]
stock_news = [
{'platform_name': '东方财富', 'title': 'AI概念股集体大涨,龙头股涨停'},
{'platform_name': '雪球', 'title': '新能源板块反弹,锂电池领涨'},
]
flow_data = {
'total_score': 650,
'level': '',
}
sentiment_data = {
'sentiment': {'sentiment_index': 72, 'sentiment_class': '乐观'},
'flow_stage': {'stage_name': '加速'},
}
# 运行板块分析
print("\n--- 板块影响分析 ---")
sector_result = news_flow_agents.sector_impact_agent(hot_topics, stock_news, flow_data)
print(f"受益板块: {[s.get('name', '') for s in sector_result.get('benefited_sectors', [])]}")
print(f"是否降级: {sector_result.get('fallback', False)}")
+514
View File
@@ -0,0 +1,514 @@
"""
新闻流量预警系统模块
实现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}")
+702
View File
@@ -0,0 +1,702 @@
"""
新闻流量数据获取模块
用于获取各大平台的热点新闻和流量数据
支持22个平台,包含排名、K值计算等功能
"""
import requests
import logging
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Tuple
import time
from collections import Counter
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class NewsFlowDataFetcher:
"""新闻流量数据获取器"""
def __init__(self):
self.base_url = "https://newsapi.ws4.cn/api/v1/dailynews/"
self.timeout = 10
# 支持的平台配置 - 扩展到22个平台
self.platforms = {
# 社交媒体平台(核心流量指标)- 8个
'weibo': {'name': '微博热搜', 'category': 'social', 'weight': 10, 'influence': 'high'},
'douyin': {'name': '抖音热点', 'category': 'social', 'weight': 9, 'influence': 'high'},
'zhihu': {'name': '知乎热榜', 'category': 'social', 'weight': 7, 'influence': 'medium'},
'bilibili': {'name': '哔哩哔哩', 'category': 'social', 'weight': 6, 'influence': 'medium'},
'xiaohongshu': {'name': '小红书', 'category': 'social', 'weight': 7, 'influence': 'medium'},
'kuaishou': {'name': '快手', 'category': 'social', 'weight': 6, 'influence': 'medium'},
'tieba': {'name': '百度贴吧', 'category': 'social', 'weight': 5, 'influence': 'low'},
'weixin': {'name': '微信热点', 'category': 'social', 'weight': 8, 'influence': 'high'},
# 新闻媒体平台 - 6个
'baidu': {'name': '百度热搜', 'category': 'news', 'weight': 8, 'influence': 'high'},
'jinritoutiao': {'name': '今日头条', 'category': 'news', 'weight': 7, 'influence': 'high'},
'tenxunwang': {'name': '腾讯网', 'category': 'news', 'weight': 6, 'influence': 'medium'},
'netease': {'name': '网易新闻', 'category': 'news', 'weight': 6, 'influence': 'medium'},
'ifeng': {'name': '凤凰网', 'category': 'news', 'weight': 5, 'influence': 'medium'},
'sina': {'name': '新浪新闻', 'category': 'news', 'weight': 6, 'influence': 'medium'},
# 财经平台(股市相关)- 5个
'sina_finance': {'name': '新浪财经', 'category': 'finance', 'weight': 9, 'influence': 'high'},
'eastmoney': {'name': '东方财富', 'category': 'finance', 'weight': 9, 'influence': 'high'},
'xueqiu': {'name': '雪球', 'category': 'finance', 'weight': 8, 'influence': 'high'},
'cls': {'name': '财联社', 'category': 'finance', 'weight': 8, 'influence': 'high'},
'wallstreetcn': {'name': '华尔街见闻', 'category': 'finance', 'weight': 7, 'influence': 'medium'},
# 科技平台 - 3个
'tskr': {'name': '36氪', 'category': 'tech', 'weight': 6, 'influence': 'medium'},
'sspai': {'name': '少数派', 'category': 'tech', 'weight': 5, 'influence': 'low'},
'juejin': {'name': '掘金', 'category': 'tech', 'weight': 5, 'influence': 'low'},
}
# 平台类别权重(用于转化率计算)
self.category_weights = {
'finance': 1.5, # 财经平台转化率高
'social': 1.2, # 社交媒体传播快
'news': 1.0, # 新闻媒体正常
'tech': 0.8, # 科技平台相关性低
}
# 停用词(过滤无意义的词)
self.stop_words = {
'', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '',
'', '', '', '', '', '', '', '', '', '',
}
def get_platform_news(self, platform: str) -> Dict:
"""
获取单个平台的新闻数据
Args:
platform: 平台代码(如 'weibo', 'baidu'等)
Returns:
{
'success': bool,
'platform': str,
'platform_name': str,
'category': str,
'weight': int,
'influence': str,
'data': List[Dict],
'count': int,
'fetch_time': str,
'error': str (如果失败)
}
"""
try:
url = f"{self.base_url}?platform={platform}"
logger.info(f"正在获取 {platform} 平台数据...")
response = requests.get(url, timeout=self.timeout)
response.raise_for_status()
data = response.json()
if data.get('status') == '200':
news_list = data.get('data', [])
platform_info = self.platforms.get(platform, {})
# 为每条新闻添加排名信息
for i, news in enumerate(news_list):
news['rank'] = i + 1
news['platform'] = platform
return {
'success': True,
'platform': platform,
'platform_name': platform_info.get('name', platform),
'category': platform_info.get('category', 'other'),
'weight': platform_info.get('weight', 5),
'influence': platform_info.get('influence', 'medium'),
'data': news_list,
'count': len(news_list),
'fetch_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
}
else:
return {
'success': False,
'platform': platform,
'error': f"API返回错误: {data.get('msg', '未知错误')}"
}
except requests.exceptions.Timeout:
return {
'success': False,
'platform': platform,
'error': f"请求超时({self.timeout}秒)"
}
except requests.exceptions.ConnectionError:
return {
'success': False,
'platform': platform,
'error': "网络连接失败"
}
except Exception as e:
return {
'success': False,
'platform': platform,
'error': f"获取数据失败: {str(e)}"
}
def get_multi_platform_news(self, platforms: List[str] = None,
category: str = None) -> Dict:
"""
获取多个平台的新闻数据
Args:
platforms: 平台列表,None表示获取所有
category: 按类别筛选('social', 'news', 'finance', 'tech'
Returns:
{
'success': bool,
'total_platforms': int,
'success_count': int,
'failed_count': int,
'platforms_data': List[Dict],
'fetch_time': str
}
"""
# 确定要获取的平台列表
if platforms is None:
if category:
target_platforms = [
p for p, info in self.platforms.items()
if info.get('category') == category
]
else:
target_platforms = list(self.platforms.keys())
else:
target_platforms = platforms
results = []
success_count = 0
failed_count = 0
for platform in target_platforms:
result = self.get_platform_news(platform)
results.append(result)
if result['success']:
success_count += 1
else:
failed_count += 1
# 避免请求过快,休息0.3秒
time.sleep(0.3)
return {
'success': success_count > 0,
'total_platforms': len(target_platforms),
'success_count': success_count,
'failed_count': failed_count,
'platforms_data': results,
'fetch_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
}
def extract_stock_related_news(self, platforms_data: List[Dict],
keywords: List[str] = None) -> List[Dict]:
"""
从新闻数据中提取股票相关的新闻
Args:
platforms_data: 平台数据列表
keywords: 股票相关关键词列表
Returns:
List[Dict]: 股票相关新闻列表
"""
if keywords is None:
keywords = [
# 股市基础词汇
'', '股市', '股票', 'A股', '港股', '美股', '创业板', '科创板', '北交所',
'涨停', '跌停', '大涨', '暴涨', '飙升', '暴跌', '涨幅', '跌幅', '翻倍',
'概念股', '龙头股', '妖股', '题材股', '白马股', '蓝筹股', '成长股',
'上市', 'IPO', '重组', '并购', '收购', '增发', '回购', '减持', '增持',
'业绩', '财报', '利好', '利空', '预增', '预减', '盈利', '亏损',
'牛市', '熊市', '反弹', '回调', '震荡', '突破', '新高',
'主力', '游资', '北向资金', '外资', '机构', '资金流入', '资金流出',
'板块', '行业', '赛道', '题材', '轮动', '热点',
# 热门板块关键词
'芯片', '半导体', '光刻机', '封装', '存储',
'新能源', '锂电', '光伏', '储能', '风电', '氢能',
'AI', '人工智能', '大模型', 'ChatGPT', 'DeepSeek', '算力', 'GPU',
'机器人', '人形机器人', '工业机器人', '减速器', '伺服电机',
'低空经济', '无人机', 'eVTOL', '飞行汽车',
'数据要素', '数字经济', '信创', '国产替代',
'医药', '创新药', 'CXO', '医疗器械', '中药',
'消费', '白酒', '食品', '旅游', '免税',
'军工', '国防', '航空', '航天', '船舶',
'汽车', '新能源车', '智能驾驶', '无人驾驶', '充电桩',
'地产', '房地产', '楼市', '房价',
'金融', '银行', '保险', '券商', '证券',
# 政策相关
'政策', '利率', '降息', '降准', '货币政策', '财政政策',
'国常会', '证监会', '央行', '发改委', '工信部',
]
stock_related = []
for platform_data in platforms_data:
if not platform_data.get('success'):
continue
platform = platform_data['platform']
platform_name = platform_data['platform_name']
category = platform_data['category']
weight = platform_data['weight']
influence = platform_data.get('influence', 'medium')
for news in platform_data.get('data', []):
title = news.get('title') or ''
content = news.get('content') or ''
rank = news.get('rank', 99)
# 检查是否包含股票关键词
text = f"{title} {content}"
matched_keywords = [kw for kw in keywords if kw in text]
if matched_keywords:
# 计算综合得分(排名越靠前、权重越高得分越高)
rank_score = max(0, 100 - rank * 2) # 排名1得98分,排名50得0分
weight_score = weight * 10
keyword_score = len(matched_keywords) * 5
total_score = rank_score + weight_score + keyword_score
stock_related.append({
'platform': platform,
'platform_name': platform_name,
'category': category,
'weight': weight,
'influence': influence,
'rank': rank,
'title': title,
'content': content,
'url': news.get('url') or '',
'source': news.get('source') or platform_name,
'publish_time': news.get('publish_time') or '',
'matched_keywords': matched_keywords,
'keyword_count': len(matched_keywords),
'score': total_score,
})
# 按综合得分排序
stock_related.sort(key=lambda x: x['score'], reverse=True)
return stock_related
def calculate_flow_score(self, platforms_data: List[Dict]) -> Dict:
"""
计算流量得分
根据"流量为王"理念,评估当前市场热度
Returns:
{
'total_score': int, # 总流量得分(0-1000
'social_score': int, # 社交媒体流量得分
'news_score': int, # 新闻媒体流量得分
'finance_score': int, # 财经平台流量得分
'tech_score': int, # 科技平台流量得分
'level': str, # 流量等级(低/中/高/极高)
'analysis': str, # 流量分析
'platform_details': List[Dict], # 各平台详情
}
"""
scores = {
'social': 0,
'news': 0,
'finance': 0,
'tech': 0,
}
platform_details = []
for platform_data in platforms_data:
if not platform_data.get('success'):
continue
category = platform_data['category']
weight = platform_data['weight']
count = platform_data['count']
platform_name = platform_data['platform_name']
# 计算得分:权重 × 新闻数量
score = weight * count
scores[category] = scores.get(category, 0) + score
platform_details.append({
'platform': platform_data['platform'],
'platform_name': platform_name,
'category': category,
'count': count,
'score': score,
})
# 计算总分
total_score = sum(scores.values())
# 归一化到0-1000
if total_score > 0:
normalized_score = min(int(total_score / 50), 1000)
else:
normalized_score = 0
# 确定流量等级
if normalized_score >= 800:
level = "极高"
analysis = "流量爆发!市场情绪极度活跃,大量新闻热点,存在热点题材炒作机会。建议:密切关注龙头股,注意追高风险。"
elif normalized_score >= 500:
level = ""
analysis = "流量较高。市场有明确热点,资金活跃度较好。建议:关注热点板块,注意节奏把握。"
elif normalized_score >= 200:
level = ""
analysis = "流量正常。市场处于常态,有一定热点但不突出。建议:观望为主,等待明确信号。"
else:
level = ""
analysis = "流量较低。市场情绪低迷,缺乏热点。建议:控制仓位,等待市场转暖。"
return {
'total_score': normalized_score,
'social_score': scores.get('social', 0),
'news_score': scores.get('news', 0),
'finance_score': scores.get('finance', 0),
'tech_score': scores.get('tech', 0),
'level': level,
'analysis': analysis,
'platform_details': platform_details,
}
def get_hot_topics(self, platforms_data: List[Dict], top_n: int = 20) -> List[Dict]:
"""
获取热门话题(基于标题词频分析)
Returns:
List[Dict]: 热门话题列表
"""
import jieba
# 收集所有标题及其来源信息
all_titles = []
title_sources = {} # 记录每个标题来自哪些平台
for platform_data in platforms_data:
if platform_data.get('success'):
platform_name = platform_data['platform_name']
for news in platform_data.get('data', []):
title = news.get('title') or ''
if title:
all_titles.append(title)
if title not in title_sources:
title_sources[title] = []
title_sources[title].append(platform_name)
# 分词并统计
word_counter = Counter()
word_sources = {} # 记录每个词出现在哪些平台
for title in all_titles:
if title:
words = jieba.cut(title)
for word in words:
if len(word) >= 2 and word not in self.stop_words:
word_counter[word] += 1
if word not in word_sources:
word_sources[word] = set()
for source in title_sources.get(title, []):
word_sources[word].add(source)
# 获取TOP N
hot_topics = []
total_titles = len(all_titles) if all_titles else 1
for word, count in word_counter.most_common(top_n):
sources = list(word_sources.get(word, []))
cross_platform = len(sources) # 跨平台数
heat = min(int(count / total_titles * 1000), 100)
# 跨平台加成
if cross_platform >= 5:
heat = min(heat + 20, 100)
elif cross_platform >= 3:
heat = min(heat + 10, 100)
hot_topics.append({
'topic': word,
'count': count,
'heat': heat,
'cross_platform': cross_platform,
'sources': sources[:5], # 最多显示5个来源
})
return hot_topics
def get_platform_ranking(self, platforms_data: List[Dict]) -> List[Dict]:
"""
获取跨平台热度排名
合并所有平台的新闻,按热度排序
Returns:
List[Dict]: 排名列表
"""
all_news = []
for platform_data in platforms_data:
if not platform_data.get('success'):
continue
platform = platform_data['platform']
platform_name = platform_data['platform_name']
weight = platform_data['weight']
for news in platform_data.get('data', []):
title = news.get('title') or ''
rank = news.get('rank', 99)
if not title:
continue
# 计算综合热度分数
# 排名越靠前分数越高,平台权重越高分数越高
heat_score = (100 - rank) * weight
all_news.append({
'title': title,
'platform': platform,
'platform_name': platform_name,
'original_rank': rank,
'heat_score': heat_score,
'url': news.get('url') or '',
'content': news.get('content') or '',
})
# 按热度分数排序
all_news.sort(key=lambda x: x['heat_score'], reverse=True)
# 添加全局排名
for i, news in enumerate(all_news):
news['global_rank'] = i + 1
return all_news
def calculate_viral_coefficient(self, current_data: Dict,
previous_data: Dict) -> Dict:
"""
计算病毒系数K值
K值 = 当前传播量 / 上期传播量
Args:
current_data: 当前数据
previous_data: 上期数据
Returns:
{
'k_value': float, # K值
'trend': str, # 趋势(指数型/线性/衰减)
'analysis': str, # 分析
}
"""
current_score = current_data.get('total_score', 0)
previous_score = previous_data.get('total_score', 0)
if previous_score == 0:
k_value = 1.0
trend = "无历史数据"
analysis = "首次采集,无法计算K值"
else:
k_value = round(current_score / previous_score, 2)
if k_value > 1.5:
trend = "指数型爆发"
analysis = f"K值={k_value},流量正在指数型增长!这是病毒式传播的特征,题材可能进入加速期。"
elif k_value > 1.0:
trend = "线性增长"
analysis = f"K值={k_value},流量稳步增长,题材正在发酵中。"
elif k_value == 1.0:
trend = "平稳"
analysis = f"K值={k_value},流量保持稳定,市场处于平衡状态。"
else:
trend = "衰减"
analysis = f"K值={k_value},流量正在衰减,题材热度下降,注意风险。"
return {
'k_value': k_value,
'current_score': current_score,
'previous_score': previous_score,
'trend': trend,
'analysis': analysis,
}
def detect_flow_type(self, history_scores: List[int],
current_score: int) -> Dict:
"""
识别流量类型(存量流量型 vs 增量流量型)
存量流量型:出生自带顶流,流量快速到位(政策/大事件)
增量流量型:初始流量小,具备病毒传播能力(话题发酵)
Args:
history_scores: 历史得分列表(从旧到新)
current_score: 当前得分
Returns:
{
'flow_type': str, # 存量流量型/增量流量型/未知
'characteristics': List[str], # 特征
'time_window': str, # 时间窗口建议
'operation': str, # 操作建议
}
"""
if len(history_scores) < 2:
return {
'flow_type': '未知',
'characteristics': ['历史数据不足'],
'time_window': '无法判断',
'operation': '继续观察',
}
# 计算初始得分和增长率
initial_score = history_scores[0] if history_scores else 0
avg_score = sum(history_scores) / len(history_scores)
# 计算增长趋势
growth_rates = []
for i in range(1, len(history_scores)):
if history_scores[i-1] > 0:
rate = (history_scores[i] - history_scores[i-1]) / history_scores[i-1]
growth_rates.append(rate)
avg_growth = sum(growth_rates) / len(growth_rates) if growth_rates else 0
# 判断流量类型
if initial_score >= 500:
# 初始热度高 -> 存量流量型
flow_type = "存量流量型"
characteristics = [
"初始热度高",
"流量快速到位",
"可能与政策/大事件相关",
]
time_window = "时间窗口短(2-3天)"
operation = "快进快出,密切关注热度变化"
elif avg_growth > 0.2 and len([r for r in growth_rates if r > 0]) >= 2:
# 持续增长 -> 增量流量型
flow_type = "增量流量型"
characteristics = [
"初始热度低",
"逐步攀升",
"具备病毒传播特征",
]
time_window = "时间窗口长(5-10天)"
operation = "可以埋伏,等待加速"
else:
flow_type = "常规流量"
characteristics = [
"热度波动正常",
"无明显趋势",
]
time_window = "无特定窗口"
operation = "保持观望"
return {
'flow_type': flow_type,
'characteristics': characteristics,
'time_window': time_window,
'operation': operation,
'initial_score': initial_score,
'current_score': current_score,
'avg_growth': round(avg_growth * 100, 1), # 百分比
}
def get_platform_list(self) -> List[Dict]:
"""
获取所有支持的平台列表
Returns:
List[Dict]: 平台列表
"""
result = []
for code, info in self.platforms.items():
result.append({
'code': code,
'name': info['name'],
'category': info['category'],
'weight': info['weight'],
'influence': info.get('influence', 'medium'),
})
# 按权重排序
result.sort(key=lambda x: x['weight'], reverse=True)
return result
def get_platforms_by_category(self) -> Dict[str, List[str]]:
"""
按类别获取平台列表
Returns:
Dict[str, List[str]]: 类别 -> 平台代码列表
"""
categories = {}
for code, info in self.platforms.items():
category = info['category']
if category not in categories:
categories[category] = []
categories[category].append(code)
return categories
# 测试代码
if __name__ == "__main__":
fetcher = NewsFlowDataFetcher()
# 测试获取平台列表
print("=== 支持的平台列表 ===")
platforms = fetcher.get_platform_list()
print(f"共支持 {len(platforms)} 个平台:")
for p in platforms[:5]:
print(f" - {p['name']} ({p['code']}) - 权重:{p['weight']}")
# 测试获取单个平台
print("\n=== 测试获取微博热搜 ===")
result = fetcher.get_platform_news('weibo')
if result['success']:
print(f"✅ 成功获取 {result['count']} 条新闻")
print(f"前3条标题:")
for news in result['data'][:3]:
print(f" {news['rank']}. {news['title']}")
else:
print(f"❌ 失败: {result['error']}")
print("\n=== 测试获取财经平台 ===")
result = fetcher.get_multi_platform_news(category='finance')
print(f"总共 {result['total_platforms']} 个平台")
print(f"成功 {result['success_count']}")
print(f"失败 {result['failed_count']}")
# 提取股票相关新闻
stock_news = fetcher.extract_stock_related_news(result['platforms_data'])
print(f"\n✅ 提取到 {len(stock_news)} 条股票相关新闻")
# 计算流量得分
flow_score = fetcher.calculate_flow_score(result['platforms_data'])
print(f"\n流量得分: {flow_score['total_score']}")
print(f"流量等级: {flow_score['level']}")
# 获取热门话题
hot_topics = fetcher.get_hot_topics(result['platforms_data'])
print(f"\n热门话题TOP5:")
for i, topic in enumerate(hot_topics[:5], 1):
print(f" {i}. {topic['topic']} (热度:{topic['heat']}, 跨{topic['cross_platform']}平台)")
+1021
View File
File diff suppressed because it is too large Load Diff
+620
View File
@@ -0,0 +1,620 @@
"""
新闻流量分析引擎
基于"流量为王"理念的短线炒股指导系统
整合数据获取、流量模型、情绪分析、AI分析、预警系统
"""
import logging
import time
from datetime import datetime, timedelta
from typing import Dict, List, Optional
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class NewsFlowEngine:
"""新闻流量分析引擎"""
def __init__(self):
"""初始化引擎"""
# 核心模块
self.fetcher = None
self.model = None
self.sentiment = None
self.agents = None
self.alerts = None
self.db = None
self._init_modules()
logger.info("✅ 新闻流量引擎初始化完成")
def _init_modules(self):
"""初始化所有模块"""
try:
from news_flow_data import NewsFlowDataFetcher
self.fetcher = NewsFlowDataFetcher()
except Exception as e:
logger.error(f"数据获取模块初始化失败: {e}")
try:
from news_flow_model import NewsFlowModel
self.model = NewsFlowModel()
except Exception as e:
logger.error(f"流量模型模块初始化失败: {e}")
try:
from news_flow_sentiment import SentimentAnalyzer
self.sentiment = SentimentAnalyzer()
except Exception as e:
logger.error(f"情绪分析模块初始化失败: {e}")
try:
from news_flow_agents import NewsFlowAgents
self.agents = NewsFlowAgents()
except Exception as e:
logger.error(f"AI分析模块初始化失败: {e}")
try:
from news_flow_alert import NewsFlowAlertSystem
self.alerts = NewsFlowAlertSystem()
except Exception as e:
logger.error(f"预警系统模块初始化失败: {e}")
try:
from news_flow_db import news_flow_db
self.db = news_flow_db
except Exception as e:
logger.error(f"数据库模块初始化失败: {e}")
def run_quick_analysis(self, platforms: List[str] = None,
category: str = None) -> Dict:
"""
运行快速分析(不含AI)
用于定时同步和快速查看
Returns:
{
'success': bool,
'snapshot_id': int,
'flow_data': Dict,
'model_data': Dict,
'sentiment_data': Dict,
'stock_news': List,
'hot_topics': List,
'fetch_time': str,
}
"""
try:
logger.info("🚀 开始快速分析...")
start_time = time.time()
# 1. 获取多平台新闻数据
logger.info("📊 获取新闻数据...")
if not self.fetcher:
return {'success': False, 'error': '数据获取模块不可用'}
multi_result = self.fetcher.get_multi_platform_news(
platforms=platforms, category=category
)
if not multi_result['success']:
return {'success': False, 'error': '获取新闻数据失败'}
platforms_data = multi_result['platforms_data']
success_count = multi_result['success_count']
# 2. 提取股票相关新闻
logger.info("🔍 提取股票相关新闻...")
stock_news = self.fetcher.extract_stock_related_news(platforms_data)
# 3. 获取热门话题
logger.info("🔥 分析热门话题...")
hot_topics = self.fetcher.get_hot_topics(platforms_data, top_n=20)
# 4. 计算流量得分(基础)
logger.info("📈 计算流量得分...")
flow_data = self.fetcher.calculate_flow_score(platforms_data)
# 5. 运行流量模型
logger.info("🔬 运行流量模型...")
history_scores = self._get_history_scores(hours=24)
model_data = None
if self.model:
model_data = self.model.run_full_model(
platforms_data, hot_topics, history_scores
)
# 6. 情绪分析
logger.info("💭 分析市场情绪...")
sentiment_data = None
if self.sentiment:
history_sentiments = self._get_history_sentiments(limit=10)
sentiment_data = self.sentiment.run_full_sentiment_analysis(
platforms_data, stock_news, history_scores,
flow_data['total_score'], history_sentiments
)
# 7. 保存到数据库
logger.info("💾 保存分析结果...")
snapshot_id = None
if self.db:
snapshot_id = self.db.save_flow_snapshot(
flow_data, platforms_data, stock_news, hot_topics
)
# 保存情绪记录
if sentiment_data and snapshot_id:
sentiment_record = {
'sentiment_index': sentiment_data.get('sentiment', {}).get('sentiment_index', 50),
'sentiment_class': sentiment_data.get('sentiment', {}).get('sentiment_class', '中性'),
'flow_stage': sentiment_data.get('flow_stage', {}).get('stage_name', '未知'),
'momentum': sentiment_data.get('momentum', {}).get('momentum', 1.0),
'viral_k': model_data.get('viral_k', {}).get('k_value', 1.0) if model_data else 1.0,
'flow_type': model_data.get('flow_type', {}).get('flow_type', '未知') if model_data else '未知',
'stage_analysis': sentiment_data.get('flow_stage', {}).get('analysis', ''),
}
self.db.save_sentiment_record(snapshot_id, sentiment_record)
duration = time.time() - start_time
logger.info(f"✅ 快速分析完成,耗时 {duration:.2f}")
return {
'success': True,
'snapshot_id': snapshot_id,
'success_count': success_count,
'flow_data': flow_data,
'model_data': model_data,
'sentiment_data': sentiment_data,
'stock_news': stock_news,
'hot_topics': hot_topics,
'platforms_data': platforms_data,
'fetch_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'duration': round(duration, 2),
}
except Exception as e:
logger.error(f"❌ 快速分析失败: {e}")
return {'success': False, 'error': str(e)}
def run_full_analysis(self, platforms: List[str] = None,
category: str = None,
include_ai: bool = True) -> Dict:
"""
运行完整分析(含AI
Returns:
{
'success': bool,
'snapshot_id': int,
'flow_data': Dict,
'model_data': Dict,
'sentiment_data': Dict,
'ai_analysis': Dict,
'trading_signals': Dict,
'stock_news': List,
'hot_topics': List,
}
"""
try:
logger.info("🚀 开始完整分析...")
start_time = time.time()
# 1. 先运行快速分析
quick_result = self.run_quick_analysis(platforms, category)
if not quick_result['success']:
return quick_result
# 2. AI智能分析
ai_analysis = None
if include_ai:
if not self.agents:
logger.warning("⚠️ AI代理模块未初始化")
elif not self.agents.is_available():
logger.warning("⚠️ DeepSeek API不可用,请检查API密钥配置")
else:
logger.info("🤖 运行AI分析...")
model_data = quick_result.get('model_data', {})
sentiment_data = quick_result.get('sentiment_data', {})
# 基础AI分析
ai_analysis = self.agents.run_full_analysis(
quick_result['hot_topics'],
quick_result['stock_news'],
quick_result['flow_data'],
sentiment_data,
viral_k=model_data.get('viral_k', {}).get('k_value', 1.0) if model_data else 1.0,
flow_type=model_data.get('flow_type', {}).get('flow_type', '未知') if model_data else '未知',
)
# 多板块深度分析(多次调用DeepSeek)
logger.info("🔍 开始多板块深度分析...")
multi_sector_analysis = self.agents.run_multi_sector_analysis(
quick_result['hot_topics'],
quick_result['stock_news']
)
# 合并多板块分析结果
if ai_analysis and multi_sector_analysis.get('success'):
ai_analysis['multi_sector'] = multi_sector_analysis
# 保存AI分析结果
if ai_analysis and self.db and quick_result.get('snapshot_id'):
ai_record = {
'affected_sectors': ai_analysis.get('sector_analysis', {}).get('benefited_sectors', []),
'recommended_stocks': ai_analysis.get('stock_recommend', {}).get('recommended_stocks', []),
'risk_level': ai_analysis.get('risk_assess', {}).get('risk_level', '未知'),
'risk_factors': ai_analysis.get('risk_assess', {}).get('risk_factors', []),
'advice': ai_analysis.get('investment_advice', {}).get('advice', '观望'),
'confidence': ai_analysis.get('investment_advice', {}).get('confidence', 50),
'summary': ai_analysis.get('investment_advice', {}).get('summary', ''),
'model_used': 'deepseek-chat',
'analysis_time': ai_analysis.get('analysis_time', 0),
}
self.db.save_ai_analysis(quick_result['snapshot_id'], ai_record)
# 3. 生成交易信号
trading_signals = self._generate_trading_signals(
quick_result.get('flow_data', {}),
quick_result.get('model_data', {}),
quick_result.get('sentiment_data', {}),
ai_analysis
)
duration = time.time() - start_time
logger.info(f"✅ 完整分析完成,耗时 {duration:.2f}")
return {
'success': True,
'snapshot_id': quick_result.get('snapshot_id'),
'flow_data': quick_result.get('flow_data'),
'model_data': quick_result.get('model_data'),
'sentiment_data': quick_result.get('sentiment_data'),
'ai_analysis': ai_analysis,
'trading_signals': trading_signals,
'stock_news': quick_result.get('stock_news'),
'hot_topics': quick_result.get('hot_topics'),
'platforms_data': quick_result.get('platforms_data'),
'fetch_time': quick_result.get('fetch_time'),
'duration': round(duration, 2),
}
except Exception as e:
logger.error(f"❌ 完整分析失败: {e}")
return {'success': False, 'error': str(e)}
def run_alert_check(self) -> Dict:
"""
运行预警检查
Returns:
{
'success': bool,
'alerts': List[Dict],
}
"""
try:
logger.info("⚠️ 开始预警检查...")
if not self.alerts:
return {'success': False, 'error': '预警系统不可用'}
# 获取当前数据
quick_result = self.run_quick_analysis()
if not quick_result['success']:
return {'success': False, 'error': quick_result.get('error')}
# 获取历史数据
history_data = self._get_previous_snapshot()
# 构建检查数据
current_data = {
'flow_data': quick_result.get('flow_data', {}),
'hot_topics': quick_result.get('hot_topics', []),
'viral_k': quick_result.get('model_data', {}).get('viral_k', {}),
'flow_stage': quick_result.get('sentiment_data', {}).get('flow_stage', {}),
}
# 检查预警
alerts = self.alerts.check_alerts(
current_data,
history_data,
quick_result.get('sentiment_data'),
quick_result.get('snapshot_id')
)
logger.info(f"✅ 预警检查完成,触发 {len(alerts)} 个预警")
return {
'success': True,
'alerts': alerts,
'snapshot_id': quick_result.get('snapshot_id'),
}
except Exception as e:
logger.error(f"❌ 预警检查失败: {e}")
return {'success': False, 'error': str(e)}
def get_dashboard_data(self) -> Dict:
"""
获取仪表盘数据
Returns:
{
'latest_snapshot': Dict,
'latest_sentiment': Dict,
'latest_ai_analysis': Dict,
'recent_alerts': List,
'flow_trend': Dict,
'scheduler_status': Dict,
}
"""
try:
data = {}
if self.db:
# 最新快照
data['latest_snapshot'] = self.db.get_latest_snapshot()
# 最新情绪
data['latest_sentiment'] = self.db.get_latest_sentiment()
# 最新AI分析
data['latest_ai_analysis'] = self.db.get_latest_ai_analysis()
# 最近预警
data['recent_alerts'] = self.db.get_alerts(days=1)
# 流量趋势(7天)
data['flow_trend'] = self.get_flow_trend(days=7)
# 调度器状态
try:
from news_flow_scheduler import news_flow_scheduler
data['scheduler_status'] = news_flow_scheduler.get_status()
except:
data['scheduler_status'] = None
return data
except Exception as e:
logger.error(f"获取仪表盘数据失败: {e}")
return {}
def get_flow_trend(self, days: int = 7) -> Dict:
"""获取流量趋势"""
if not self.db:
return {'dates': [], 'scores': [], 'trend': '无数据'}
stats = self.db.get_daily_statistics(days)
if not stats:
return {'dates': [], 'scores': [], 'trend': '无数据', 'analysis': '暂无历史数据'}
# 反转(从旧到新)
stats.reverse()
dates = [s['date'] for s in stats]
avg_scores = [s['avg_score'] for s in stats]
max_scores = [s['max_score'] for s in stats]
min_scores = [s['min_score'] for s in stats]
# 判断趋势
if len(avg_scores) >= 3:
recent_avg = sum(avg_scores[-3:]) / 3
earlier_avg = sum(avg_scores[:3]) / 3
if recent_avg > earlier_avg * 1.2:
trend = '上升'
analysis = f"近期流量持续上升(近3日均值{recent_avg:.0f} > 前3日均值{earlier_avg:.0f}),市场热度升温。"
elif recent_avg < earlier_avg * 0.8:
trend = '下降'
analysis = f"近期流量持续下降(近3日均值{recent_avg:.0f} < 前3日均值{earlier_avg:.0f}),市场热度降温。"
else:
trend = '平稳'
analysis = f"近期流量波动不大(近3日均值{recent_avg:.0f} ≈ 前3日均值{earlier_avg:.0f}),市场处于平衡状态。"
else:
trend = '数据不足'
analysis = '历史数据不足,无法判断趋势'
return {
'dates': dates,
'avg_scores': avg_scores,
'max_scores': max_scores,
'min_scores': min_scores,
'trend': trend,
'analysis': analysis,
}
def _generate_trading_signals(self, flow_data: Dict,
model_data: Dict,
sentiment_data: Dict,
ai_analysis: Dict = None) -> Dict:
"""生成交易信号"""
signals = {
'overall_signal': '观望',
'confidence': 50,
'risk_level': '中等',
'hot_sectors': [],
'operation_advice': '',
'key_message': '',
}
# 获取各项指标
total_score = flow_data.get('total_score', 0)
flow_level = flow_data.get('level', '')
sentiment_index = 50
flow_stage = '未知'
if sentiment_data:
sentiment_index = sentiment_data.get('sentiment', {}).get('sentiment_index', 50)
flow_stage = sentiment_data.get('flow_stage', {}).get('stage_name', '未知')
viral_k = 1.0
if model_data:
viral_k = model_data.get('viral_k', {}).get('k_value', 1.0)
# 核心判断逻辑
if flow_stage in ['一致', 'consensus']:
# 流量高潮 = 逃命时刻
signals['overall_signal'] = '卖出'
signals['confidence'] = 90
signals['risk_level'] = '极高'
signals['key_message'] = '⚠️ 流量高潮 = 价格高潮 = 逃命时刻!立即减仓或清仓!'
signals['operation_advice'] = '立即减仓或清仓,锁定利润。不要贪婪,不要犹豫。'
elif flow_stage in ['退潮', 'decline']:
signals['overall_signal'] = '观望'
signals['confidence'] = 80
signals['risk_level'] = ''
signals['key_message'] = '流量退潮,及时止盈止损'
signals['operation_advice'] = '持仓者及时止盈止损,空仓者继续观望。'
elif flow_stage in ['加速', 'acceleration'] and viral_k > 1.2:
signals['overall_signal'] = '买入'
signals['confidence'] = 75
signals['risk_level'] = '中等'
signals['key_message'] = '流量加速期,可参与龙头'
signals['operation_advice'] = '关注龙头股,轻仓试探。设置止损位(-5%),止盈位(+15%)。'
elif flow_stage in ['启动', 'startup']:
signals['overall_signal'] = '关注'
signals['confidence'] = 65
signals['risk_level'] = ''
signals['key_message'] = '流量启动期,可以关注'
signals['operation_advice'] = '密切关注,等待确认后介入。'
elif flow_level == "极高" and sentiment_index > 85:
signals['overall_signal'] = '观望'
signals['confidence'] = 70
signals['risk_level'] = ''
signals['key_message'] = '流量极高+情绪过热,追高风险大'
signals['operation_advice'] = '不建议追高,等待回调机会。'
else:
signals['overall_signal'] = '观望'
signals['confidence'] = 50
signals['risk_level'] = '中等'
signals['key_message'] = '市场无明确方向,保持观望'
signals['operation_advice'] = '保持观望,等待流量信号明确。'
# 整合AI分析结果
if ai_analysis:
advice = ai_analysis.get('investment_advice', {})
if advice.get('advice'):
signals['ai_advice'] = advice.get('advice')
signals['ai_confidence'] = advice.get('confidence', 50)
signals['ai_summary'] = advice.get('summary', '')
sectors = ai_analysis.get('sector_analysis', {}).get('benefited_sectors', [])
signals['hot_sectors'] = sectors[:3]
return signals
def _get_history_scores(self, hours: int = 24) -> List[int]:
"""获取历史流量得分"""
if not self.db:
return []
scores = self.db.get_recent_scores(hours)
return [s['total_score'] for s in scores]
def _get_history_sentiments(self, limit: int = 10) -> List[Dict]:
"""获取历史情绪记录"""
if not self.db:
return []
return self.db.get_sentiment_history(limit)
def _get_previous_snapshot(self) -> Optional[Dict]:
"""获取上一次快照"""
if not self.db:
return None
snapshots = self.db.get_history_snapshots(limit=2)
if len(snapshots) >= 2:
detail = self.db.get_snapshot_detail(snapshots[1]['id'])
return {
'hot_topics': detail.get('hot_topics', []),
'snapshot': detail.get('snapshot', {}),
}
return None
def compare_with_history(self, current_score: int) -> Dict:
"""与历史数据对比"""
if not self.db:
return {
'percentile': 50,
'level_description': '无历史对比',
'comparison': '暂无足够的历史数据进行对比'
}
stats = self.db.get_daily_statistics(30)
if not stats:
return {
'percentile': 50,
'level_description': '无历史对比',
'comparison': '暂无足够的历史数据进行对比'
}
all_scores = []
for stat in stats:
all_scores.extend([stat['avg_score'], stat['max_score'], stat['min_score']])
all_scores.sort()
lower_count = sum(1 for s in all_scores if s < current_score)
percentile = int(lower_count / len(all_scores) * 100) if all_scores else 50
if percentile >= 90:
level_description = "极高水平"
comparison = f"当前流量得分{current_score}处于历史极高水平(超过{percentile}%的历史记录),流量极度爆发!"
elif percentile >= 70:
level_description = "较高水平"
comparison = f"当前流量得分{current_score}处于历史较高水平(超过{percentile}%的历史记录),流量活跃。"
elif percentile >= 30:
level_description = "正常水平"
comparison = f"当前流量得分{current_score}处于历史正常水平(超过{percentile}%的历史记录)。"
else:
level_description = "较低水平"
comparison = f"当前流量得分{current_score}处于历史较低水平(仅超过{percentile}%的历史记录),流量低迷。"
return {
'percentile': percentile,
'level_description': level_description,
'comparison': comparison
}
# 全局引擎实例
news_flow_engine = NewsFlowEngine()
# 测试代码
if __name__ == "__main__":
print("=== 测试新闻流量分析引擎 ===")
# 运行快速分析
print("\n--- 快速分析 ---")
result = news_flow_engine.run_quick_analysis(category='finance')
if result['success']:
print(f"✅ 分析成功!快照ID: {result.get('snapshot_id')}")
print(f"\n流量得分: {result['flow_data']['total_score']}")
print(f"流量等级: {result['flow_data']['level']}")
print(f"股票相关新闻: {len(result['stock_news'])}")
print(f"热门话题: {len(result['hot_topics'])}")
if result.get('sentiment_data'):
sentiment = result['sentiment_data'].get('sentiment', {})
print(f"\n情绪指数: {sentiment.get('sentiment_index', 'N/A')}")
print(f"情绪分类: {sentiment.get('sentiment_class', 'N/A')}")
flow_stage = result['sentiment_data'].get('flow_stage', {})
print(f"流量阶段: {flow_stage.get('stage_name', 'N/A')}")
else:
print(f"❌ 分析失败: {result.get('error')}")
+557
View File
@@ -0,0 +1,557 @@
"""
新闻流量模型计算模块
核心公式:接盘总量 = 流量 × 转化率 × 客单价
实现流量为王理念的量化分析
"""
import logging
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Tuple
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class NewsFlowModel:
"""新闻流量模型计算器"""
def __init__(self):
# 平台类别权重(用于转化率计算)
self.category_weights = {
'finance': 1.5, # 财经平台转化率高
'social': 1.2, # 社交媒体传播快
'news': 1.0, # 新闻媒体正常
'tech': 0.8, # 科技平台相关性低
}
# 话题类型转化率系数
self.topic_weights = {
# 高转化话题
'政策': 2.0,
'利好': 1.8,
'涨停': 1.8,
'龙头': 1.7,
'机构': 1.6,
'外资': 1.6,
'北向': 1.6,
'重组': 1.5,
'并购': 1.5,
'IPO': 1.5,
# 中转化话题
'业绩': 1.3,
'财报': 1.3,
'板块': 1.2,
'概念': 1.2,
'题材': 1.2,
# 一般话题
'股票': 1.0,
'股市': 1.0,
'A股': 1.0,
}
# 基础转化率(万分之一)
self.base_conversion_rate = 0.0001
# 平均客单价(元)- 散户平均投资金额
self.avg_investment = 50000
# 时效因子衰减系数(每小时衰减)
self.time_decay_rate = 0.95
def calculate_traffic_score(self, platforms_data: List[Dict]) -> Dict:
"""
计算流量分数
流量分数 = Σ(平台权重 × 热度 × 时效因子)
Args:
platforms_data: 平台数据列表
Returns:
{
'total_score': int,
'category_scores': Dict[str, int],
'platform_details': List[Dict],
'normalized_score': int, # 归一化到0-1000
}
"""
category_scores = {
'social': 0,
'news': 0,
'finance': 0,
'tech': 0,
}
platform_details = []
total_raw_score = 0
for platform_data in platforms_data:
if not platform_data.get('success'):
continue
category = platform_data.get('category', 'other')
weight = platform_data.get('weight', 5)
count = platform_data.get('count', 0)
platform_name = platform_data.get('platform_name', '')
# 计算平台得分:权重 × 新闻数量 × 类别权重
category_weight = self.category_weights.get(category, 1.0)
platform_score = weight * count * category_weight
category_scores[category] = category_scores.get(category, 0) + platform_score
total_raw_score += platform_score
platform_details.append({
'platform': platform_data.get('platform', ''),
'platform_name': platform_name,
'category': category,
'count': count,
'weight': weight,
'score': int(platform_score),
})
# 归一化到0-1000
normalized_score = min(int(total_raw_score / 50), 1000)
return {
'total_score': int(total_raw_score),
'normalized_score': normalized_score,
'category_scores': {k: int(v) for k, v in category_scores.items()},
'platform_details': platform_details,
}
def estimate_conversion_rate(self, hot_topics: List[Dict],
category_distribution: Dict[str, int]) -> Dict:
"""
估算转化率
转化率 = 基础转化率 × 话题系数 × 平台系数
Args:
hot_topics: 热门话题列表
category_distribution: 类别得分分布
Returns:
{
'conversion_rate': float,
'topic_factor': float,
'platform_factor': float,
'analysis': str,
}
"""
# 1. 计算话题系数(基于热门话题的类型)
topic_factor = 1.0
matched_topics = []
for topic in hot_topics[:10]: # 只看TOP10热门话题
topic_text = topic.get('topic', '')
topic_heat = topic.get('heat', 0)
for keyword, weight in self.topic_weights.items():
if keyword in topic_text:
# 热度加权
heat_bonus = 1 + (topic_heat / 100) * 0.5
topic_factor = max(topic_factor, weight * heat_bonus)
matched_topics.append({
'topic': topic_text,
'keyword': keyword,
'factor': weight,
})
break
# 2. 计算平台系数(基于类别分布)
total_score = sum(category_distribution.values())
if total_score > 0:
platform_factor = sum(
(score / total_score) * self.category_weights.get(cat, 1.0)
for cat, score in category_distribution.items()
)
else:
platform_factor = 1.0
# 3. 计算最终转化率
conversion_rate = self.base_conversion_rate * topic_factor * platform_factor
# 4. 生成分析
if conversion_rate >= 0.0003:
analysis = f"转化率极高({conversion_rate:.4%})!话题与股市高度相关,投资者关注度极高。"
elif conversion_rate >= 0.0002:
analysis = f"转化率较高({conversion_rate:.4%})。话题具有较强的股市联动性。"
elif conversion_rate >= 0.0001:
analysis = f"转化率正常({conversion_rate:.4%})。话题与股市有一定关联。"
else:
analysis = f"转化率较低({conversion_rate:.4%})。话题与股市关联度不高。"
return {
'conversion_rate': conversion_rate,
'topic_factor': round(topic_factor, 2),
'platform_factor': round(platform_factor, 2),
'matched_topics': matched_topics,
'analysis': analysis,
}
def calculate_potential(self, flow_score: int, conversion_rate: float,
avg_investment: float = None) -> Dict:
"""
计算接盘潜力
核心公式:接盘总量 = 流量 × 转化率 × 客单价
Args:
flow_score: 流量分数(代表潜在触达人数,按比例换算)
conversion_rate: 转化率
avg_investment: 平均客单价(元)
Returns:
{
'potential_volume': float, # 接盘总量(亿元)
'potential_level': str, # 潜力等级
'estimated_participants': int, # 预估参与人数
'analysis': str,
}
"""
if avg_investment is None:
avg_investment = self.avg_investment
# 流量分数换算为潜在触达人数(假设满分1000对应1000万人)
potential_reach = flow_score * 10000 # 分数 × 10000 = 潜在触达人数
# 预估参与人数
estimated_participants = int(potential_reach * conversion_rate)
# 接盘总量 = 参与人数 × 平均投资额(转换为亿元)
potential_volume = (estimated_participants * avg_investment) / 100000000
# 确定潜力等级
if potential_volume >= 100:
potential_level = "超大"
analysis = f"预估接盘资金{potential_volume:.1f}亿元,市场资金充裕,热点题材可能持续发酵。"
elif potential_volume >= 50:
potential_level = ""
analysis = f"预估接盘资金{potential_volume:.1f}亿元,资金量较大,可支撑短期行情。"
elif potential_volume >= 20:
potential_level = ""
analysis = f"预估接盘资金{potential_volume:.1f}亿元,资金量适中,行情可能分化。"
elif potential_volume >= 5:
potential_level = ""
analysis = f"预估接盘资金{potential_volume:.1f}亿元,资金量较小,注意风险。"
else:
potential_level = "极小"
analysis = f"预估接盘资金{potential_volume:.1f}亿元,资金量不足,不建议追高。"
return {
'potential_volume': round(potential_volume, 2),
'potential_level': potential_level,
'potential_reach': potential_reach,
'estimated_participants': estimated_participants,
'avg_investment': avg_investment,
'analysis': analysis,
}
def classify_flow_type(self, history_scores: List[int],
current_score: int) -> Dict:
"""
判断流量类型
存量流量型:出生自带顶流,流量快速到位(政策/大事件)
增量流量型:初始流量小,具备病毒传播能力(话题发酵)
Args:
history_scores: 历史得分列表(从旧到新)
current_score: 当前得分
Returns:
{
'flow_type': str,
'characteristics': List[str],
'time_window': str,
'operation': str,
'confidence': int,
}
"""
if len(history_scores) < 2:
return {
'flow_type': '未知',
'characteristics': ['历史数据不足,无法判断'],
'time_window': '无法判断',
'operation': '继续观察,积累数据',
'confidence': 0,
}
# 计算关键指标
initial_score = history_scores[0]
avg_score = sum(history_scores) / len(history_scores)
max_score = max(history_scores)
# 计算增长率序列
growth_rates = []
for i in range(1, len(history_scores)):
if history_scores[i-1] > 0:
rate = (history_scores[i] - history_scores[i-1]) / history_scores[i-1]
growth_rates.append(rate)
avg_growth = sum(growth_rates) / len(growth_rates) if growth_rates else 0
positive_growth_count = len([r for r in growth_rates if r > 0])
# 判断流量类型
if initial_score >= 500 and (current_score - initial_score) / (initial_score + 1) < 0.3:
# 初始热度高,增长有限 -> 存量流量型
flow_type = "存量流量型"
characteristics = [
f"初始热度高({initial_score}分)",
"流量快速到位",
"可能与政策/大事件相关",
"来得快去得也快",
]
time_window = "时间窗口短(2-3天)"
operation = "快进快出,首日参与最佳,及时止盈"
confidence = 80
elif avg_growth > 0.15 and positive_growth_count >= len(growth_rates) * 0.6:
# 持续增长 -> 增量流量型
flow_type = "增量流量型"
characteristics = [
f"初始热度较低({initial_score}分)",
f"增长率{avg_growth*100:.1f}%",
"逐步攀升,病毒式传播",
"有埋伏机会",
]
time_window = "时间窗口长(5-10天)"
operation = "可以埋伏,等待加速,分批建仓"
confidence = 75
elif avg_growth < -0.1:
# 持续下降 -> 衰退期
flow_type = "流量衰退"
characteristics = [
"热度持续下降",
f"平均跌幅{abs(avg_growth)*100:.1f}%",
"题材热度消退",
]
time_window = "窗口已关闭"
operation = "及时止盈止损,不宜追入"
confidence = 70
else:
# 波动 -> 常规流量
flow_type = "常规流量"
characteristics = [
"热度波动正常",
"无明显趋势",
]
time_window = "无特定窗口"
operation = "保持观望,等待方向明确"
confidence = 50
return {
'flow_type': flow_type,
'characteristics': characteristics,
'time_window': time_window,
'operation': operation,
'confidence': confidence,
'initial_score': initial_score,
'current_score': current_score,
'avg_growth': round(avg_growth * 100, 1),
'history_length': len(history_scores),
}
def calculate_viral_k(self, current_score: int, previous_score: int) -> Dict:
"""
计算病毒系数K值
K值 = 当前传播量 / 上期传播量
K > 1.5: 指数型爆发
K ≈ 1: 线性增长
K < 1: 自然死亡
Returns:
{
'k_value': float,
'trend': str,
'risk_level': str,
'analysis': str,
}
"""
if previous_score == 0:
return {
'k_value': 1.0,
'trend': '无历史数据',
'risk_level': '未知',
'analysis': '首次采集,无法计算K值',
}
k_value = round(current_score / previous_score, 2)
if k_value > 2.0:
trend = "爆发式增长"
risk_level = "高风险"
analysis = f"K值={k_value},流量正在爆发式增长!这是极端情况,可能接近顶部,注意风险。"
elif k_value > 1.5:
trend = "指数型爆发"
risk_level = "中高风险"
analysis = f"K值={k_value},流量指数型增长!题材正在加速发酵,关注龙头但注意追高风险。"
elif k_value > 1.2:
trend = "快速增长"
risk_level = "中风险"
analysis = f"K值={k_value},流量快速增长,题材正在升温,可适度参与。"
elif k_value > 1.0:
trend = "稳步增长"
risk_level = "低风险"
analysis = f"K值={k_value},流量稳步增长,题材处于发酵期。"
elif k_value == 1.0:
trend = "平稳"
risk_level = "低风险"
analysis = f"K值={k_value},流量保持稳定,市场平衡状态。"
elif k_value > 0.8:
trend = "轻微下降"
risk_level = "中风险"
analysis = f"K值={k_value},流量轻微下降,题材热度开始消退。"
else:
trend = "快速衰减"
risk_level = "高风险"
analysis = f"K值={k_value},流量快速衰减!题材进入退潮期,注意及时止盈止损。"
return {
'k_value': k_value,
'current_score': current_score,
'previous_score': previous_score,
'trend': trend,
'risk_level': risk_level,
'analysis': analysis,
}
def run_full_model(self, platforms_data: List[Dict],
hot_topics: List[Dict],
history_scores: List[int] = None) -> Dict:
"""
运行完整的流量模型分析
Returns:
{
'traffic': Dict, # 流量分析
'conversion': Dict, # 转化率分析
'potential': Dict, # 接盘潜力
'flow_type': Dict, # 流量类型
'viral_k': Dict, # K值分析
'summary': str, # 总结
}
"""
# 1. 计算流量分数
traffic = self.calculate_traffic_score(platforms_data)
current_score = traffic['normalized_score']
# 2. 估算转化率
conversion = self.estimate_conversion_rate(
hot_topics,
traffic['category_scores']
)
# 3. 计算接盘潜力
potential = self.calculate_potential(
current_score,
conversion['conversion_rate']
)
# 4. 判断流量类型
if history_scores and len(history_scores) >= 2:
flow_type = self.classify_flow_type(history_scores, current_score)
# 计算K值(与上一次对比)
viral_k = self.calculate_viral_k(current_score, history_scores[-1])
else:
flow_type = {
'flow_type': '未知',
'characteristics': ['历史数据不足'],
'time_window': '无法判断',
'operation': '继续观察',
'confidence': 0,
}
viral_k = {
'k_value': 1.0,
'trend': '无历史数据',
'risk_level': '未知',
'analysis': '首次分析,无法计算K值',
}
# 5. 生成总结
summary = self._generate_summary(traffic, conversion, potential, flow_type, viral_k)
return {
'traffic': traffic,
'conversion': conversion,
'potential': potential,
'flow_type': flow_type,
'viral_k': viral_k,
'summary': summary,
'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
}
def _generate_summary(self, traffic: Dict, conversion: Dict,
potential: Dict, flow_type: Dict, viral_k: Dict) -> str:
"""生成分析总结"""
lines = []
# 流量概况
score = traffic['normalized_score']
if score >= 800:
lines.append(f"【流量】当前流量分数{score},处于极高水平")
elif score >= 500:
lines.append(f"【流量】当前流量分数{score},处于较高水平")
elif score >= 200:
lines.append(f"【流量】当前流量分数{score},处于正常水平")
else:
lines.append(f"【流量】当前流量分数{score},处于低位")
# 转化率
lines.append(f"【转化】{conversion['analysis']}")
# 接盘潜力
lines.append(f"【潜力】{potential['analysis']}")
# 流量类型
if flow_type['flow_type'] != '未知':
lines.append(f"【类型】{flow_type['flow_type']}{flow_type['operation']}")
# K值
if viral_k['k_value'] != 1.0 or viral_k['trend'] != '无历史数据':
lines.append(f"【趋势】K值={viral_k['k_value']}{viral_k['trend']}")
return '\n'.join(lines)
# 全局实例
news_flow_model = NewsFlowModel()
# 测试代码
if __name__ == "__main__":
print("=== 测试新闻流量模型 ===")
# 模拟数据
platforms_data = [
{'success': True, 'platform': 'weibo', 'platform_name': '微博',
'category': 'social', 'weight': 10, 'count': 50},
{'success': True, 'platform': 'eastmoney', 'platform_name': '东方财富',
'category': 'finance', 'weight': 9, 'count': 30},
{'success': True, 'platform': 'baidu', 'platform_name': '百度',
'category': 'news', 'weight': 8, 'count': 40},
]
hot_topics = [
{'topic': 'AI芯片', 'heat': 95, 'count': 50},
{'topic': '新能源政策', 'heat': 80, 'count': 30},
{'topic': '涨停板', 'heat': 75, 'count': 25},
]
history_scores = [300, 350, 420, 500]
# 运行完整分析
result = news_flow_model.run_full_model(platforms_data, hot_topics, history_scores)
print(f"\n流量分数: {result['traffic']['normalized_score']}")
print(f"转化率: {result['conversion']['conversion_rate']:.4%}")
print(f"接盘潜力: {result['potential']['potential_volume']:.1f}亿元 ({result['potential']['potential_level']})")
print(f"流量类型: {result['flow_type']['flow_type']}")
print(f"K值: {result['viral_k']['k_value']} ({result['viral_k']['trend']})")
print(f"\n===总结===\n{result['summary']}")
+491
View File
@@ -0,0 +1,491 @@
"""
新闻流量分析PDF报告生成器
生成包含AI分析结果的PDF格式报告
"""
import io
import os
import tempfile
from datetime import datetime
from typing import Dict, List, Optional
from reportlab.lib.pagesizes import A4
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, PageBreak
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import inch, cm
from reportlab.lib import colors
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_JUSTIFY
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
import logging
logger = logging.getLogger(__name__)
class NewsFlowPDFGenerator:
"""新闻流量分析PDF报告生成器"""
def __init__(self):
self.chinese_font = self._register_chinese_fonts()
self.styles = self._create_styles()
def _register_chinese_fonts(self) -> str:
"""注册中文字体"""
try:
if 'ChineseFont' in pdfmetrics.getRegisteredFontNames():
return 'ChineseFont'
# 字体路径列表
font_paths = [
'C:/Windows/Fonts/simsun.ttc',
'C:/Windows/Fonts/simhei.ttf',
'C:/Windows/Fonts/msyh.ttc',
'/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc',
'/usr/share/fonts/truetype/wqy/wqy-microhei.ttc',
]
for font_path in font_paths:
if os.path.exists(font_path):
try:
pdfmetrics.registerFont(TTFont('ChineseFont', font_path))
return 'ChineseFont'
except:
continue
return 'Helvetica'
except:
return 'Helvetica'
def _create_styles(self) -> Dict:
"""创建PDF样式"""
styles = getSampleStyleSheet()
# 标题样式
styles.add(ParagraphStyle(
name='ChineseTitle',
fontName=self.chinese_font,
fontSize=24,
alignment=TA_CENTER,
spaceAfter=30,
textColor=colors.HexColor('#1a1a2e')
))
# 副标题样式
styles.add(ParagraphStyle(
name='ChineseSubtitle',
fontName=self.chinese_font,
fontSize=14,
alignment=TA_CENTER,
spaceAfter=20,
textColor=colors.HexColor('#666666')
))
# 章节标题
styles.add(ParagraphStyle(
name='ChineseHeading',
fontName=self.chinese_font,
fontSize=16,
spaceBefore=20,
spaceAfter=10,
textColor=colors.HexColor('#2d3436'),
borderPadding=5,
))
# 正文样式
styles.add(ParagraphStyle(
name='ChineseBody',
fontName=self.chinese_font,
fontSize=11,
leading=18,
alignment=TA_JUSTIFY,
spaceBefore=6,
spaceAfter=6,
))
# 小标题
styles.add(ParagraphStyle(
name='ChineseSmallHeading',
fontName=self.chinese_font,
fontSize=12,
spaceBefore=10,
spaceAfter=5,
textColor=colors.HexColor('#0984e3'),
))
# 重点提示
styles.add(ParagraphStyle(
name='ChineseHighlight',
fontName=self.chinese_font,
fontSize=11,
leading=16,
backColor=colors.HexColor('#fff3cd'),
borderPadding=8,
spaceBefore=10,
spaceAfter=10,
))
# 风险警告
styles.add(ParagraphStyle(
name='ChineseWarning',
fontName=self.chinese_font,
fontSize=10,
leading=14,
backColor=colors.HexColor('#f8d7da'),
borderPadding=8,
spaceBefore=10,
spaceAfter=10,
textColor=colors.HexColor('#721c24'),
))
return styles
def generate_report(self, analysis_result: Dict) -> Optional[str]:
"""
生成PDF分析报告
Args:
analysis_result: 完整分析结果
Returns:
PDF文件路径
"""
try:
# 创建临时文件
temp_dir = tempfile.gettempdir()
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
pdf_path = os.path.join(temp_dir, f'news_flow_report_{timestamp}.pdf')
# 创建PDF文档
doc = SimpleDocTemplate(
pdf_path,
pagesize=A4,
rightMargin=50,
leftMargin=50,
topMargin=50,
bottomMargin=50
)
# 构建内容
content = []
# 封面
content.extend(self._build_cover(analysis_result))
content.append(PageBreak())
# 概要
content.extend(self._build_summary(analysis_result))
content.append(Spacer(1, 20))
# 流量分析
content.extend(self._build_flow_analysis(analysis_result))
content.append(Spacer(1, 20))
# AI分析结果
content.extend(self._build_ai_analysis(analysis_result))
content.append(PageBreak())
# 板块深度分析
content.extend(self._build_sector_analysis(analysis_result))
content.append(Spacer(1, 20))
# 股票推荐
content.extend(self._build_stock_recommendations(analysis_result))
content.append(Spacer(1, 20))
# 风险提示
content.extend(self._build_risk_warning(analysis_result))
# 生成PDF
doc.build(content)
logger.info(f"✅ PDF报告生成成功: {pdf_path}")
return pdf_path
except Exception as e:
logger.error(f"❌ PDF生成失败: {e}")
return None
def _build_cover(self, result: Dict) -> List:
"""构建封面"""
content = []
content.append(Spacer(1, 100))
content.append(Paragraph("新闻流量分析报告", self.styles['ChineseTitle']))
content.append(Spacer(1, 30))
# 生成时间
fetch_time = result.get('fetch_time', datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
content.append(Paragraph(f"生成时间:{fetch_time}", self.styles['ChineseSubtitle']))
# 分析耗时
duration = result.get('duration', 0)
content.append(Paragraph(f"分析耗时:{duration}", self.styles['ChineseSubtitle']))
content.append(Spacer(1, 50))
# 核心指标概览
flow_data = result.get('flow_data', {})
sentiment_data = result.get('sentiment_data', {})
overview_text = f"""
流量得分:{flow_data.get('total_score', 0)}/1000 ({flow_data.get('level', '')})<br/>
情绪指数:{sentiment_data.get('sentiment', {}).get('sentiment_index', 50)}/100<br/>
流量阶段:{sentiment_data.get('flow_stage', {}).get('stage_name', '未知')}
"""
content.append(Paragraph(overview_text, self.styles['ChineseBody']))
return content
def _build_summary(self, result: Dict) -> List:
"""构建分析概要"""
content = []
content.append(Paragraph("📊 分析概要", self.styles['ChineseHeading']))
trading_signals = result.get('trading_signals', {})
ai_analysis = result.get('ai_analysis', {})
# 交易信号
signal = trading_signals.get('overall_signal', '观望')
confidence = trading_signals.get('confidence', 50)
content.append(Paragraph(
f"【AI建议】{signal}(置信度:{confidence}%",
self.styles['ChineseHighlight']
))
# 核心提示
key_message = trading_signals.get('key_message', '')
if key_message:
content.append(Paragraph(f"核心提示:{key_message}", self.styles['ChineseBody']))
# AI总结
advice = ai_analysis.get('investment_advice', {})
summary = advice.get('summary', '')
if summary:
content.append(Paragraph(f"AI总结:{summary}", self.styles['ChineseBody']))
return content
def _build_flow_analysis(self, result: Dict) -> List:
"""构建流量分析"""
content = []
content.append(Paragraph("📈 流量分析", self.styles['ChineseHeading']))
flow_data = result.get('flow_data', {})
model_data = result.get('model_data', {})
# 流量得分表格
flow_table_data = [
['指标', '数值', '等级'],
['总流量得分', str(flow_data.get('total_score', 0)), flow_data.get('level', '')],
['社交媒体', str(flow_data.get('social_score', 0)), '-'],
['财经平台', str(flow_data.get('finance_score', 0)), '-'],
['新闻媒体', str(flow_data.get('news_score', 0)), '-'],
]
table = Table(flow_table_data, colWidths=[150, 100, 100])
table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#4a69bd')),
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
('FONTNAME', (0, 0), (-1, -1), self.chinese_font),
('FONTSIZE', (0, 0), (-1, -1), 10),
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('GRID', (0, 0), (-1, -1), 1, colors.grey),
('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.whitesmoke, colors.white]),
]))
content.append(table)
# K值分析
viral_k = model_data.get('viral_k', {})
if viral_k:
k_value = viral_k.get('k_value', 1.0)
trend = viral_k.get('trend', '稳定')
content.append(Spacer(1, 10))
content.append(Paragraph(
f"K值(病毒系数):{k_value:.2f} - {trend}",
self.styles['ChineseBody']
))
return content
def _build_ai_analysis(self, result: Dict) -> List:
"""构建AI分析结果"""
content = []
content.append(Paragraph("🤖 AI智能分析", self.styles['ChineseHeading']))
ai_analysis = result.get('ai_analysis', {})
if not ai_analysis:
content.append(Paragraph("暂无AI分析数据", self.styles['ChineseBody']))
return content
# 受益板块
sector_analysis = ai_analysis.get('sector_analysis', {})
benefited_sectors = sector_analysis.get('benefited_sectors', [])
if benefited_sectors:
content.append(Paragraph("受益板块分析", self.styles['ChineseSmallHeading']))
sector_table_data = [['板块', '置信度', '分析理由']]
for sector in benefited_sectors[:5]:
sector_table_data.append([
sector.get('name', ''),
f"{sector.get('confidence', 0)}%",
sector.get('reason', '')[:40] + '...' if len(sector.get('reason', '')) > 40 else sector.get('reason', '')
])
table = Table(sector_table_data, colWidths=[100, 60, 280])
table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#00b894')),
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
('FONTNAME', (0, 0), (-1, -1), self.chinese_font),
('FONTSIZE', (0, 0), (-1, -1), 9),
('ALIGN', (0, 0), (1, -1), 'CENTER'),
('ALIGN', (2, 0), (2, -1), 'LEFT'),
('GRID', (0, 0), (-1, -1), 1, colors.grey),
('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.whitesmoke, colors.white]),
]))
content.append(table)
# 热门题材
hot_themes = sector_analysis.get('hot_themes', [])
if hot_themes:
content.append(Spacer(1, 15))
content.append(Paragraph("今日热门题材", self.styles['ChineseSmallHeading']))
themes_text = []
for theme in hot_themes[:5]:
themes_text.append(f"{theme.get('theme', '')} ({theme.get('heat_level', '')})")
content.append(Paragraph('<br/>'.join(themes_text), self.styles['ChineseBody']))
return content
def _build_sector_analysis(self, result: Dict) -> List:
"""构建板块深度分析"""
content = []
ai_analysis = result.get('ai_analysis', {})
multi_sector = ai_analysis.get('multi_sector', {})
sector_analyses = multi_sector.get('sector_analyses', [])
if not sector_analyses:
return content
content.append(Paragraph("🎯 板块深度分析", self.styles['ChineseHeading']))
content.append(Paragraph(
f"共分析 {len(sector_analyses)} 个热门板块",
self.styles['ChineseBody']
))
for sector in sector_analyses[:5]:
sector_name = sector.get('sector_name', '未知')
heat_level = sector.get('heat_level', '')
outlook = sector.get('short_term_outlook', '震荡')
content.append(Spacer(1, 10))
content.append(Paragraph(
f"{sector_name}】热度:{heat_level} | 短期预判:{outlook}",
self.styles['ChineseSmallHeading']
))
# 预判理由
outlook_reason = sector.get('outlook_reason', '')
if outlook_reason:
content.append(Paragraph(f"预判理由:{outlook_reason}", self.styles['ChineseBody']))
# 龙头股
leaders = sector.get('leader_stocks', [])
if leaders:
leader_names = [f"{s.get('code', '')}{s.get('name', '')}" for s in leaders[:3]]
content.append(Paragraph(f"龙头股:{', '.join(leader_names)}", self.styles['ChineseBody']))
# 投资建议
advice = sector.get('investment_advice', '')
if advice:
content.append(Paragraph(f"建议:{advice}", self.styles['ChineseBody']))
return content
def _build_stock_recommendations(self, result: Dict) -> List:
"""构建股票推荐"""
content = []
ai_analysis = result.get('ai_analysis', {})
stock_recommend = ai_analysis.get('stock_recommend', {})
recommended_stocks = stock_recommend.get('recommended_stocks', [])
if not recommended_stocks:
return content
content.append(Paragraph("💰 AI选股推荐", self.styles['ChineseHeading']))
# 股票推荐表格
stock_table_data = [['代码', '名称', '板块', '风险', '推荐理由']]
for stock in recommended_stocks[:8]:
stock_table_data.append([
stock.get('code', ''),
stock.get('name', ''),
stock.get('sector', ''),
stock.get('risk_level', ''),
stock.get('reason', '')[:30] + '...' if len(stock.get('reason', '')) > 30 else stock.get('reason', '')
])
table = Table(stock_table_data, colWidths=[60, 70, 70, 40, 200])
table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#e17055')),
('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
('FONTNAME', (0, 0), (-1, -1), self.chinese_font),
('FONTSIZE', (0, 0), (-1, -1), 9),
('ALIGN', (0, 0), (3, -1), 'CENTER'),
('ALIGN', (4, 0), (4, -1), 'LEFT'),
('GRID', (0, 0), (-1, -1), 1, colors.grey),
('ROWBACKGROUNDS', (0, 1), (-1, -1), [colors.whitesmoke, colors.white]),
]))
content.append(table)
# 整体策略
overall_strategy = stock_recommend.get('overall_strategy', '')
if overall_strategy:
content.append(Spacer(1, 10))
content.append(Paragraph(f"整体策略:{overall_strategy}", self.styles['ChineseBody']))
return content
def _build_risk_warning(self, result: Dict) -> List:
"""构建风险提示"""
content = []
content.append(Paragraph("⚠️ 风险提示", self.styles['ChineseHeading']))
ai_analysis = result.get('ai_analysis', {})
risk_assess = ai_analysis.get('risk_assess', {})
risk_level = risk_assess.get('risk_level', '中等')
risk_score = risk_assess.get('risk_score', 50)
risk_factors = risk_assess.get('risk_factors', [])
content.append(Paragraph(
f"风险等级:{risk_level}(分数:{risk_score}/100",
self.styles['ChineseBody']
))
if risk_factors:
factors_text = '<br/>'.join([f"{f}" for f in risk_factors[:5]])
content.append(Paragraph(f"风险因素:<br/>{factors_text}", self.styles['ChineseBody']))
# 免责声明
disclaimer = """
【免责声明】
本报告由AI自动生成,仅供参考,不构成任何投资建议。
股市有风险,投资需谨慎。请投资者根据自身情况独立判断,
理性投资,自负盈亏。本报告作者及生成系统不对投资决策
产生的任何损失承担责任。
"""
content.append(Spacer(1, 20))
content.append(Paragraph(disclaimer, self.styles['ChineseWarning']))
return content
+383
View File
@@ -0,0 +1,383 @@
"""
新闻流量定时任务调度模块
实现三种定时任务:热点同步(30分钟)、预警生成(1小时)、深度分析(2小时)
"""
import schedule
import threading
import time
import logging
from datetime import datetime
from typing import Dict, List, Optional, Callable
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class NewsFlowScheduler:
"""新闻流量定时任务调度器"""
# 任务类型定义
TASK_TYPES = {
'sync_hotspots': {
'name': '热点同步',
'interval': 30, # 分钟
'description': '同步22个平台的热点数据',
},
'generate_alerts': {
'name': '预警生成',
'interval': 60, # 分钟
'description': '检查预警条件并生成预警',
},
'deep_analysis': {
'name': '深度分析',
'interval': 120, # 分钟
'description': '运行完整的AI分析',
},
}
def __init__(self):
"""初始化调度器"""
self.running = False
self.thread = None
self.lock = threading.Lock()
# 任务配置
self.task_enabled = {
'sync_hotspots': True,
'generate_alerts': True,
'deep_analysis': True,
}
# 任务间隔(分钟)
self.task_intervals = {
'sync_hotspots': 30,
'generate_alerts': 60,
'deep_analysis': 120,
}
# 上次运行时间
self.last_run_times = {}
# 依赖模块
self.engine = None
self.db = None
self.alert_system = None
self._init_dependencies()
logger.info("[新闻流量] 调度器初始化完成")
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 news_flow_alert import news_flow_alert_system
self.alert_system = news_flow_alert_system
except Exception as e:
logger.warning(f"预警系统初始化失败: {e}")
def _get_engine(self):
"""延迟加载引擎(避免循环导入)"""
if self.engine is None:
try:
from news_flow_engine import news_flow_engine
self.engine = news_flow_engine
except Exception as e:
logger.error(f"引擎模块初始化失败: {e}")
return self.engine
def start(self):
"""启动调度器"""
if self.running:
logger.warning("[新闻流量] 调度器已在运行")
return
with self.lock:
# 清除旧任务
self._clear_jobs()
# 注册任务
if self.task_enabled.get('sync_hotspots'):
interval = self.task_intervals.get('sync_hotspots', 30)
job = schedule.every(interval).minutes.do(self._run_sync_hotspots)
job.tag('news_flow', 'sync_hotspots')
logger.info(f"[新闻流量] 注册热点同步任务,间隔{interval}分钟")
if self.task_enabled.get('generate_alerts'):
interval = self.task_intervals.get('generate_alerts', 60)
job = schedule.every(interval).minutes.do(self._run_generate_alerts)
job.tag('news_flow', 'generate_alerts')
logger.info(f"[新闻流量] 注册预警生成任务,间隔{interval}分钟")
if self.task_enabled.get('deep_analysis'):
interval = self.task_intervals.get('deep_analysis', 120)
job = schedule.every(interval).minutes.do(self._run_deep_analysis)
job.tag('news_flow', 'deep_analysis')
logger.info(f"[新闻流量] 注册深度分析任务,间隔{interval}分钟")
# 启动调度线程
self.running = True
self.thread = threading.Thread(target=self._schedule_loop, daemon=True)
self.thread.start()
logger.info("[新闻流量] 调度器已启动")
def stop(self):
"""停止调度器"""
with self.lock:
self.running = False
self._clear_jobs()
logger.info("[新闻流量] 调度器已停止")
def _clear_jobs(self):
"""清除本模块的任务"""
jobs_to_remove = [job for job in schedule.jobs if 'news_flow' in job.tags]
for job in jobs_to_remove:
schedule.cancel_job(job)
def _schedule_loop(self):
"""调度循环"""
while self.running:
try:
schedule.run_pending()
except Exception as e:
logger.error(f"[新闻流量] 调度循环异常: {e}")
time.sleep(30) # 每30秒检查一次
def _log_task(self, task_name: str, task_type: str,
status: str, message: str = '',
duration: float = 0, snapshot_id: int = None):
"""记录任务日志"""
try:
if self.db:
self.db.save_scheduler_log(
task_name, task_type, status, message, duration, snapshot_id
)
except Exception as e:
logger.error(f"记录任务日志失败: {e}")
def _run_sync_hotspots(self):
"""运行热点同步任务"""
task_type = 'sync_hotspots'
task_name = self.TASK_TYPES[task_type]['name']
logger.info(f"[新闻流量] 开始执行: {task_name}")
start_time = time.time()
try:
engine = self._get_engine()
if not engine:
raise Exception("引擎模块不可用")
# 运行快速分析(仅数据同步和基础计算)
result = engine.run_quick_analysis()
duration = time.time() - start_time
if result.get('success'):
snapshot_id = result.get('snapshot_id')
message = f"成功同步{result.get('success_count', 0)}个平台数据"
self._log_task(task_name, task_type, 'success', message, duration, snapshot_id)
logger.info(f"[新闻流量] {task_name}完成: {message}")
else:
message = result.get('error', '未知错误')
self._log_task(task_name, task_type, 'failed', message, duration)
logger.error(f"[新闻流量] {task_name}失败: {message}")
self.last_run_times[task_type] = datetime.now()
except Exception as e:
duration = time.time() - start_time
message = str(e)
self._log_task(task_name, task_type, 'error', message, duration)
logger.error(f"[新闻流量] {task_name}异常: {e}")
def _run_generate_alerts(self):
"""运行预警生成任务"""
task_type = 'generate_alerts'
task_name = self.TASK_TYPES[task_type]['name']
logger.info(f"[新闻流量] 开始执行: {task_name}")
start_time = time.time()
try:
engine = self._get_engine()
if not engine:
raise Exception("引擎模块不可用")
# 运行预警检查
result = engine.run_alert_check()
duration = time.time() - start_time
if result.get('success'):
alert_count = len(result.get('alerts', []))
message = f"生成{alert_count}个预警"
self._log_task(task_name, task_type, 'success', message, duration)
logger.info(f"[新闻流量] {task_name}完成: {message}")
# 发送通知
if alert_count > 0 and self.alert_system:
self.alert_system.send_notification(result['alerts'])
else:
message = result.get('error', '未知错误')
self._log_task(task_name, task_type, 'failed', message, duration)
logger.error(f"[新闻流量] {task_name}失败: {message}")
self.last_run_times[task_type] = datetime.now()
except Exception as e:
duration = time.time() - start_time
message = str(e)
self._log_task(task_name, task_type, 'error', message, duration)
logger.error(f"[新闻流量] {task_name}异常: {e}")
def _run_deep_analysis(self):
"""运行深度分析任务"""
task_type = 'deep_analysis'
task_name = self.TASK_TYPES[task_type]['name']
logger.info(f"[新闻流量] 开始执行: {task_name}")
start_time = time.time()
try:
engine = self._get_engine()
if not engine:
raise Exception("引擎模块不可用")
# 运行完整分析(包含AI
result = engine.run_full_analysis(include_ai=True)
duration = time.time() - start_time
if result.get('success'):
snapshot_id = result.get('snapshot_id')
advice = result.get('ai_analysis', {}).get('investment_advice', {}).get('advice', 'N/A')
message = f"深度分析完成,建议:{advice}"
self._log_task(task_name, task_type, 'success', message, duration, snapshot_id)
logger.info(f"[新闻流量] {task_name}完成: {message}")
else:
message = result.get('error', '未知错误')
self._log_task(task_name, task_type, 'failed', message, duration)
logger.error(f"[新闻流量] {task_name}失败: {message}")
self.last_run_times[task_type] = datetime.now()
except Exception as e:
duration = time.time() - start_time
message = str(e)
self._log_task(task_name, task_type, 'error', message, duration)
logger.error(f"[新闻流量] {task_name}异常: {e}")
# ==================== 手动触发方法 ====================
def run_sync_now(self) -> Dict:
"""立即执行热点同步"""
logger.info("[新闻流量] 手动触发热点同步")
self._run_sync_hotspots()
return {'success': True, 'message': '热点同步已执行'}
def run_alerts_now(self) -> Dict:
"""立即执行预警生成"""
logger.info("[新闻流量] 手动触发预警生成")
self._run_generate_alerts()
return {'success': True, 'message': '预警生成已执行'}
def run_analysis_now(self) -> Dict:
"""立即执行深度分析"""
logger.info("[新闻流量] 手动触发深度分析")
self._run_deep_analysis()
return {'success': True, 'message': '深度分析已执行'}
# ==================== 配置方法 ====================
def set_task_enabled(self, task_type: str, enabled: bool):
"""设置任务开关"""
if task_type in self.task_enabled:
self.task_enabled[task_type] = enabled
logger.info(f"[新闻流量] 任务 {task_type} {'启用' if enabled else '禁用'}")
# 如果调度器正在运行,重新注册任务
if self.running:
self.stop()
self.start()
def set_task_interval(self, task_type: str, interval: int):
"""设置任务间隔(分钟)"""
if task_type in self.task_intervals:
self.task_intervals[task_type] = interval
logger.info(f"[新闻流量] 任务 {task_type} 间隔设置为 {interval} 分钟")
# 如果调度器正在运行,重新注册任务
if self.running:
self.stop()
self.start()
def get_status(self) -> Dict:
"""获取调度器状态"""
return {
'running': self.running,
'task_enabled': self.task_enabled.copy(),
'task_intervals': self.task_intervals.copy(),
'last_run_times': {
k: v.strftime('%Y-%m-%d %H:%M:%S') if v else None
for k, v in self.last_run_times.items()
},
'next_run_times': self._get_next_run_times(),
}
def _get_next_run_times(self) -> Dict:
"""获取下次运行时间"""
next_times = {}
for job in schedule.jobs:
if 'news_flow' in job.tags:
for tag in job.tags:
if tag in self.TASK_TYPES:
next_times[tag] = str(job.next_run)
return next_times
def get_task_logs(self, days: int = 7, task_type: str = None) -> List[Dict]:
"""获取任务日志"""
if self.db:
return self.db.get_scheduler_logs(days, task_type)
return []
# 全局实例
news_flow_scheduler = NewsFlowScheduler()
# 测试代码
if __name__ == "__main__":
print("=== 测试新闻流量调度器 ===")
# 获取状态
status = news_flow_scheduler.get_status()
print(f"\n调度器状态:")
print(f" 运行中: {status['running']}")
print(f" 任务配置: {status['task_enabled']}")
print(f" 任务间隔: {status['task_intervals']}")
# 启动调度器
print("\n启动调度器...")
news_flow_scheduler.start()
# 再次获取状态
status = news_flow_scheduler.get_status()
print(f"\n调度器状态:")
print(f" 运行中: {status['running']}")
print(f" 下次运行: {status['next_run_times']}")
# 等待一会儿
print("\n等待5秒...")
time.sleep(5)
# 停止调度器
print("\n停止调度器...")
news_flow_scheduler.stop()
print("\n测试完成")
+562
View File
@@ -0,0 +1,562 @@
"""
新闻流量情绪分析模块
实现情绪指数、情绪分类、流量阶段判断、情绪动量计算
"""
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']}")
+1229
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -13,4 +13,5 @@ ta>=0.10.2
reportlab>=4.0.0 reportlab>=4.0.0
peewee>=3.17.0 peewee>=3.17.0
schedule>=1.2.0 schedule>=1.2.0
pywencai>=0.7.0 pywencai>=0.7.0
jieba>=0.42.1
File diff suppressed because one or more lines are too long
+666
View File
@@ -0,0 +1,666 @@
# 每日热点新闻 API - 调用说明文档
## 📋 目录
- [基础信息](#基础信息)
- [快速开始](#快速开始)
- [接口说明](#接口说明)
- [支持的平台](#支持的平台)
- [调用示例](#调用示例)
- [错误处理](#错误处理)
- [注意事项](#注意事项)
- [常见问题](#常见问题)
---
## 基础信息
### API 地址
```
生产环境:https://newsapi.ws4.cn/api/v1/dailynews/
```
### 接口说明
获取各大平台的热点新闻数据,包括微博热搜、百度热搜、知乎热榜等20+个平台。
### 特性
- ✅ 支持多种主流平台
- ✅ 实时更新数据
- ✅ 无需认证即可使用
- ✅ JSON 格式返回
- ✅ 支持跨平台聚合查询
---
## 快速开始
### 最简单的调用
```bash
curl "https://newsapi.ws4.cn/api/v1/dailynews/?platform=baidu"
```
### Python 快速示例
```python
import requests
response = requests.get("https://newsapi.ws4.cn/api/v1/dailynews/?platform=baidu")
data = response.json()
print(f"获取到 {len(data['data'])} 条新闻")
```
---
## 接口说明
### 请求信息
- **方法**: GET
- **Content-Type**: application/json
- **编码**: UTF-8
### 请求参数
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|--------|------|------|------|------|
| platform | String | 是 | 平台代码,支持多个平台用逗号分隔 | `baidu``baidu,weibo,zhihu` |
### 返回格式
#### 成功响应
```json
{
"status": "200",
"data": [
{
"title": "新闻标题",
"url": "新闻链接",
"content": "新闻内容或描述",
"source": "平台名称",
"publish_time": "2026-01-24 11:52:52"
}
],
"msg": "success"
}
```
#### 字段说明
| 字段 | 类型 | 说明 |
|------|------|------|
| status | String | 状态码,200 表示成功 |
| data | Array | 新闻数据列表 |
| data[].title | String | 新闻标题 |
| data[].url | String | 新闻链接 |
| data[].content | String | 新闻内容或描述 |
| data[].source | String | 平台来源 |
| data[].publish_time | String | 发布时间 |
| msg | String | 响应消息 |
---
## 支持的平台
| 序号 | 平台名称 | 平台代码 | 说明 |
|------|----------|----------|------|
| 1 | 百度热搜 | `baidu` | 社会热点、娱乐、事件 |
| 2 | 少数派 | `sspai` | 科技、数码、生活方式 |
| 3 | 微博热搜 | `weibo` | 社交媒体热点 |
| 4 | 知乎热榜 | `zhihu` | 问答、深度内容 |
| 5 | 36氪 | `tskr` | 科技创业、商业资讯 |
| 6 | 吾爱破解 | `ftpojie` | 技术、软件、安全 |
| 7 | 哔哩哔哩 | `bilibili` | 视频、动漫、游戏 |
| 8 | 豆瓣 | `douban` | 书影音、文化 |
| 9 | 虎扑 | `hupu` | 体育、游戏 |
| 10 | 百度贴吧 | `tieba` | 兴趣社区 |
| 11 | 掘金 | `juejin` | 编程、技术 |
| 12 | 抖音 | `douyin` | 短视频热点 |
| 13 | V2EX | `v2ex` | 技术、编程 |
| 14 | 今日头条 | `jinritoutiao` | 新闻热点 |
| 15 | Stack Overflow | `stackoverflow` | 编程问答 |
| 16 | GitHub Trending | `github` | 开源项目 |
| 17 | Hacker News | `hackernews` | 科技新闻 |
| 18 | 新浪财经 | `sina_finance` | 财经新闻 |
| 19 | 东方财富 | `eastmoney` | 财经资讯 |
| 20 | 雪球 | `xueqiu` | 股票投资 |
| 21 | 财联社 | `cls` | 财经快讯 |
| 22 | 腾讯网 | `tenxunwang` | 综合新闻 |
---
## 调用示例
### 1. cURL
#### 获取单个平台
```bash
curl "https://newsapi.ws4.cn/api/v1/dailynews/?platform=baidu"
```
#### 获取多个平台
```bash
curl "https://newsapi.ws4.cn/api/v1/dailynews/?platform=baidu,weibo,zhihu"
```
#### 完整示例
```bash
curl -X GET "https://newsapi.ws4.cn/api/v1/dailynews/?platform=baidu" \
-H "Content-Type: application/json"
```
---
### 2. Python
#### 使用 requests 库
```python
import requests
def get_news(platform="baidu"):
url = "https://newsapi.ws4.cn/api/v1/dailynews/"
params = {"platform": platform}
try:
response = requests.get(url, params=params)
data = response.json()
if data['status'] == '200':
print(f"获取到 {len(data['data'])} 条新闻")
return data['data']
else:
print(f"请求失败: {data['msg']}")
return []
except Exception as e:
print(f"发生错误: {e}")
return []
# 使用示例
news_list = get_news("baidu")
for news in news_list[:5]:
print(f"标题: {news['title']}")
print(f"链接: {news['url']}")
print(f"时间: {news['publish_time']}\n")
```
#### 获取多个平台
```python
def get_multiple_news(platforms):
platforms_str = ",".join(platforms)
url = f"https://newsapi.ws4.cn/api/v1/dailynews/?platform={platforms_str}"
response = requests.get(url)
return response.json()
# 使用示例
data = get_multiple_news(["baidu", "weibo", "zhihu"])
print(f"总共获取到 {len(data['data'])} 条新闻")
```
#### 异步请求(多个平台)
```python
import asyncio
import aiohttp
async def fetch_news(session, platform):
url = "https://newsapi.ws4.cn/api/v1/dailynews/"
async with session.get(url, params={"platform": platform}) as response:
return await response.json()
async def get_news_async(platforms):
async with aiohttp.ClientSession() as session:
tasks = [fetch_news(session, p) for p in platforms]
results = await asyncio.gather(*tasks)
return results
# 使用示例
import asyncio
platforms = ["baidu", "weibo", "zhihu"]
results = asyncio.run(get_news_async(platforms))
for i, result in enumerate(results):
print(f"{platforms[i]}: {len(result['data'])} 条新闻")
```
---
### 3. JavaScript / Node.js
#### 使用 fetch(浏览器或现代 Node.js
```javascript
async function getNews(platform = 'baidu') {
const url = `https://newsapi.ws4.cn/api/v1/dailynews/?platform=${platform}`;
try {
const response = await fetch(url);
const data = await response.json();
if (data.status === '200') {
console.log(`获取到 ${data.data.length} 条新闻`);
return data.data;
} else {
console.log('请求失败:', data.msg);
return [];
}
} catch (error) {
console.log('发生错误:', error);
return [];
}
}
// 使用示例
getNews('baidu').then(newsList => {
newsList.slice(0, 5).forEach(news => {
console.log(`标题: ${news.title}`);
console.log(`链接: ${news.url}\n`);
});
});
```
#### 使用 axios
```javascript
const axios = require('axios');
async function getNews(platform) {
try {
const response = await axios.get('https://newsapi.ws4.cn/api/v1/dailynews/', {
params: { platform }
});
console.log(`获取到 ${response.data.data.length} 条新闻`);
return response.data.data;
} catch (error) {
console.error('发生错误:', error);
return [];
}
}
// 使用示例
getNews('baidu').then(newsList => {
newsList.forEach((news, index) => {
console.log(`${index + 1}. ${news.title}`);
});
});
```
#### Node.js 使用 https 模块
```javascript
const https = require('https');
function getNews(platform, callback) {
const url = `https://newsapi.ws4.cn/api/v1/dailynews/?platform=${platform}`;
https.get(url, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
try {
const result = JSON.parse(data);
callback(null, result);
} catch (error) {
callback(error, null);
}
});
}).on('error', (error) => {
callback(error, null);
});
}
// 使用示例
getNews('baidu', (error, result) => {
if (error) {
console.error('请求失败:', error);
return;
}
console.log(`获取到 ${result.data.length} 条新闻`);
});
```
---
### 4. Java
#### 使用 HttpURLConnection
```java
import java.io.*;
import java.net.*;
import java.util.*;
public class NewsApiClient {
public static String getNews(String platform) {
try {
String url = "https://newsapi.ws4.cn/api/v1/dailynews/?platform=" + platform;
URL urlObj = new URL(url);
HttpURLConnection conn = (HttpURLConnection) urlObj.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Content-Type", "application/json");
int responseCode = conn.getResponseCode();
BufferedReader in = new BufferedReader(
new InputStreamReader(conn.getInputStream())
);
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
return response.toString();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args) {
String jsonResponse = getNews("baidu");
System.out.println(jsonResponse);
}
}
```
#### 使用 OkHttp
```java
import okhttp3.*;
import java.io.IOException;
public class NewsApi {
private static final OkHttpClient client = new OkHttpClient();
public static void getNews(String platform) throws IOException {
HttpUrl url = HttpUrl.parse("https://newsapi.ws4.cn/api/v1/dailynews/")
.newBuilder()
.addQueryParameter("platform", platform)
.build();
Request request = new Request.Builder()
.url(url)
.get()
.build();
try (Response response = client.newCall(request).execute()) {
String jsonData = response.body().string();
System.out.println(jsonData);
}
}
public static void main(String[] args) throws IOException {
getNews("baidu");
}
}
```
---
### 5. Go
```go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
type News struct {
Title string `json:"title"`
URL string `json:"url"`
Content string `json:"content"`
Source string `json:"source"`
PublishTime string `json:"publish_time"`
}
type Response struct {
Status string `json:"status"`
Data []News `json:"data"`
Msg string `json:"msg"`
}
func getNews(platform string) (*Response, error) {
url := fmt.Sprintf("https://newsapi.ws4.cn/api/v1/dailynews/?platform=%s", platform)
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var result Response
if err := json.Unmarshal(body, &result); err != nil {
return nil, err
}
return &result, nil
}
func main() {
data, err := getNews("baidu")
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("获取到 %d 条新闻\n", len(data.Data))
for i, news := range data.Data {
if i >= 5 {
break
}
fmt.Printf("%d. %s\n", i+1, news.Title)
}
}
```
---
### 6. PHP
```php
<?php
function getNews($platform = 'baidu') {
$url = 'https://newsapi.ws4.cn/api/v1/dailynews/?platform=' . $platform;
$response = file_get_contents($url);
if ($response === false) {
return null;
}
return json_decode($response, true);
}
// 使用示例
$newsData = getNews('baidu');
if ($newsData && $newsData['status'] === '200') {
echo "获取到 " . count($newsData['data']) . " 条新闻\n";
foreach (array_slice($newsData['data'], 0, 5) as $news) {
echo "标题: " . $news['title'] . "\n";
echo "链接: " . $news['url'] . "\n\n";
}
}
?>
```
---
### 7. Ruby
```ruby
require 'net/http'
require 'json'
require 'uri'
def get_news(platform = 'baidu')
uri = URI.parse("https://newsapi.ws4.cn/api/v1/dailynews/?platform=#{platform}")
response = Net::HTTP.get_response(uri)
if response.is_a?(Net::HTTPSuccess)
data = JSON.parse(response.body)
return data
else
puts "请求失败: #{response.code}"
return nil
end
end
# 使用示例
result = get_news('baidu')
if result && result['status'] == '200'
puts "获取到 #{result['data'].size} 条新闻"
result['data'].first(5).each_with_index do |news, index|
puts "#{index + 1}. #{news['title']}"
end
end
```
---
## 错误处理
### 常见错误码
| 状态码 | 说明 | 解决方案 |
|--------|------|----------|
| 200 | 成功 | 正常处理数据 |
| 404 | 平台代码错误 | 检查平台代码是否正确 |
| 500 | 服务器内部错误 | 稍后重试 |
### 错误处理示例(Python
```python
import requests
import time
def get_news_with_retry(platform, max_retries=3):
url = "https://newsapi.ws4.cn/api/v1/dailynews/"
for attempt in range(max_retries):
try:
response = requests.get(url, params={"platform": platform}, timeout=10)
if response.status_code == 200:
data = response.json()
if data.get('status') == '200':
return data.get('data', [])
else:
print(f"API返回错误: {data.get('msg')}")
return []
elif response.status_code == 404:
print(f"平台代码错误: {platform}")
return []
elif response.status_code == 500:
print(f"服务器错误,第 {attempt + 1} 次重试...")
time.sleep(2)
else:
print(f"未知错误: {response.status_code}")
return []
except requests.exceptions.Timeout:
print(f"请求超时,第 {attempt + 1} 次重试...")
time.sleep(2)
except requests.exceptions.ConnectionError:
print(f"连接错误,第 {attempt + 1} 次重试...")
time.sleep(2)
except Exception as e:
print(f"发生未知错误: {e}")
return []
print("达到最大重试次数,请求失败")
return []
```
---
## 注意事项
### 1. 数据时效性
- 数据实时更新,建议不要频繁请求
- 推荐缓存时间:5-10 分钟
- 不同平台更新频率不同
### 2. 请求频率限制
- 目前没有明确的速率限制
- 建议合理使用,避免过度请求
- 生产环境建议加入请求队列
### 3. 数据准确性
- 数据仅供参考,不应作为新闻的主要来源
- 建议与官方平台数据交叉验证
### 4. 合法使用
- API 仅供合法使用
- 任何非法使用均不受支持
- 用户需自行承担使用责任
### 5. HTTPS 要求
- 必须使用 HTTPS 协议
- HTTP 请求可能会被拒绝
---
## 常见问题
### Q1: 如何获取多个平台的数据?
**A**: 使用逗号分隔多个平台代码
```bash
curl "https://newsapi.ws4.cn/api/v1/dailynews/?platform=baidu,weibo,zhihu"
```
### Q2: 数据多久更新一次?
**A**: 不同平台更新频率不同,一般为 30 分钟到 2 小时不等。建议缓存数据,避免频繁请求。
### Q3: 是否需要 API 密钥?
**A**: 目前不需要,接口可以直接调用。
### Q4: 支持哪些编程语言?
**A**: 支持所有支持 HTTP 请求的编程语言,本文档提供了 Python、JavaScript、Java、Go、PHP、Ruby 等示例。
### Q5: 接口限流吗?
**A**: 目前没有明确的限流策略,但建议合理使用。
### Q6: 如何处理中文编码?
**A**: 接口返回的是 UTF-8 编码的 JSON,大多数现代语言都能自动处理。如果遇到乱码,请确保使用 UTF-8 解码。
### Q7: 如何获取新闻的详细内容?
**A**: API 只返回标题和链接,详细内容需要访问链接页面获取。
### Q8: 数据可以商用吗?
**A**: 数据来源于各公开平台,使用时请遵守各平台的使用协议和相关法律法规。
---
## 技术支持
- **API 文档**: https://newsapi.ws4.cn/docs
- **健康检查**: https://newsapi.ws4.cn/health
---
## 更新日志
| 日期 | 版本 | 说明 |
|------|------|------|
| 2026-01-24 | 1.0.0 | 初始版本,支持 20+ 个平台 |
---
**免责声明**: 本 API 提供的信息仅供参考,使用者应从其他平台验证信息的准确性和时效性。