Files
aiagents-stock/longhubang_ui.py
T
2025-10-17 19:26:13 +08:00

803 lines
26 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
智瞰龙虎UI界面模块
展示龙虎榜分析结果和推荐股票
"""
import streamlit as st
import plotly.graph_objects as go
import plotly.express as px
import pandas as pd
from datetime import datetime, timedelta
import time
import base64
from longhubang_engine import LonghubangEngine
from longhubang_pdf import LonghubangPDFGenerator
def display_longhubang():
"""显示智瞰龙虎主界面"""
st.markdown("""
<div class="top-nav">
<h1 class="nav-title">🎯 智瞰龙虎 - AI驱动的龙虎榜分析</h1>
<p class="nav-subtitle">Multi-Agent Dragon Tiger Analysis | 游资·个股·题材·风险多维分析</p>
</div>
""", unsafe_allow_html=True)
st.markdown("---")
# 功能说明
with st.expander("💡 智瞰龙虎系统介绍", expanded=False):
st.markdown("""
### 🌟 系统特色
**智瞰龙虎**是基于多AI智能体的龙虎榜深度分析系统,通过5位专业分析师的协同工作,
为您挖掘次日大概率上涨的潜力股票。
### 🤖 AI分析师团队
1. **🎯 游资行为分析师**
- 识别活跃游资及其操作风格
- 分析游资席位的进出特征
- 研判游资对个股的态度
2. **📈 个股潜力分析师**
- 从龙虎榜数据挖掘潜力股
- 识别次日大概率上涨的股票
- 分析资金动向和技术形态
3. **🔥 题材追踪分析师**
- 识别当前热点题材和概念
- 分析题材的炒作周期
- 预判题材的持续性
4. **⚠️ 风险控制专家**
- 识别高风险股票和陷阱
- 分析游资出货信号
- 提供风险管理建议
5. **👔 首席策略师**
- 综合所有分析师意见
- 给出最终推荐股票清单
- 提供具体操作策略
### 📊 数据来源
数据来自**StockAPI龙虎榜接口**,包括:
- 游资上榜交割单历史数据
- 股票买卖金额和净流入
- 热门概念和题材
- 更新时间:交易日下午5点40
### 🎯 核心功能
- ✅ **潜力股挖掘** - AI识别次日大概率上涨股票
- ✅ **游资追踪** - 跟踪活跃游资的操作
- ✅ **题材识别** - 发现热点题材和龙头股
- ✅ **风险提示** - 识别高风险股票和陷阱
- ✅ **历史记录** - 存储所有龙虎榜数据
- ✅ **PDF报告** - 生成专业分析报告
""")
st.markdown("---")
# 创建标签页
tab1, tab2, tab3 = st.tabs([
"📊 龙虎榜分析",
"📚 历史报告",
"📈 数据统计"
])
with tab1:
display_analysis_tab()
with tab2:
display_history_tab()
with tab3:
display_statistics_tab()
def display_analysis_tab():
"""显示分析标签页"""
st.subheader("🔍 龙虎榜综合分析")
# 参数设置
col1, col2, col3 = st.columns([2, 2, 2])
with col1:
analysis_mode = st.selectbox(
"分析模式",
["指定日期", "最近N天"],
help="选择分析特定日期还是最近几天的数据"
)
with col2:
if analysis_mode == "指定日期":
selected_date = st.date_input(
"选择日期",
value=datetime.now() - timedelta(days=1),
help="选择要分析的龙虎榜日期"
)
else:
days = st.number_input(
"最近天数",
min_value=1,
max_value=10,
value=1,
help="分析最近N天的龙虎榜数据"
)
with col3:
selected_model = st.selectbox(
"AI模型",
["deepseek-chat", "deepseek-reasoner"],
help="Reasoner模型提供更强的推理能力"
)
# 分析按钮
col1, col2, col3 = st.columns([2, 2, 2])
with col1:
analyze_button = st.button("🚀 开始分析", type="primary", use_container_width=True)
with col2:
if st.button("🔄 清除结果", use_container_width=True):
if 'longhubang_result' in st.session_state:
del st.session_state.longhubang_result
st.success("已清除分析结果")
st.rerun()
st.markdown("---")
# 开始分析
if analyze_button:
# 清除之前的结果
if 'longhubang_result' in st.session_state:
del st.session_state.longhubang_result
# 准备参数
if analysis_mode == "指定日期":
date_str = selected_date.strftime('%Y-%m-%d')
run_longhubang_analysis(model=selected_model, date=date_str)
else:
run_longhubang_analysis(model=selected_model, days=days)
# 显示分析结果
if 'longhubang_result' in st.session_state:
result = st.session_state.longhubang_result
if result.get("success"):
display_analysis_results(result)
else:
st.error(f"❌ 分析失败: {result.get('error', '未知错误')}")
def run_longhubang_analysis(model="deepseek-chat", date=None, days=1):
"""运行龙虎榜分析"""
# 进度显示
progress_bar = st.progress(0)
status_text = st.empty()
try:
status_text.text("🚀 初始化分析引擎...")
progress_bar.progress(5)
engine = LonghubangEngine(model=model)
status_text.text("📊 正在获取龙虎榜数据...")
progress_bar.progress(15)
# 运行分析
result = engine.run_comprehensive_analysis(date=date, days=days)
progress_bar.progress(90)
if result.get("success"):
# 保存结果
st.session_state.longhubang_result = result
progress_bar.progress(100)
status_text.text("✅ 分析完成!")
time.sleep(1)
status_text.empty()
progress_bar.empty()
# 自动刷新显示结果
st.rerun()
else:
st.error(f"❌ 分析失败: {result.get('error', '未知错误')}")
except Exception as e:
st.error(f"❌ 分析过程出错: {str(e)}")
import traceback
st.code(traceback.format_exc())
finally:
progress_bar.empty()
status_text.empty()
def display_analysis_results(result):
"""显示分析结果"""
st.success("✅ 龙虎榜分析完成!")
st.info(f"📅 分析时间: {result.get('timestamp', 'N/A')}")
# 数据概况
data_info = result.get('data_info', {})
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("龙虎榜记录", f"{data_info.get('total_records', 0)} 条")
with col2:
st.metric("涉及股票", f"{data_info.get('total_stocks', 0)} 只")
with col3:
st.metric("涉及游资", f"{data_info.get('total_youzi', 0)} 个")
with col4:
recommended = result.get('recommended_stocks', [])
st.metric("推荐股票", f"{len(recommended)} 只", delta="AI筛选")
# PDF导出功能
display_pdf_export_section(result)
st.markdown("---")
# 创建子标签页
tab1, tab2, tab3, tab4, tab5 = st.tabs([
"🏆 AI评分排名",
"🎯 推荐股票",
"🤖 AI分析师报告",
"📊 数据详情",
"📈 可视化图表"
])
with tab1:
display_scoring_ranking(result)
with tab2:
display_recommended_stocks(result)
with tab3:
display_agents_reports(result)
with tab4:
display_data_details(result)
with tab5:
display_visualizations(result)
def display_scoring_ranking(result):
"""显示AI智能评分排名"""
st.subheader("🏆 AI智能评分排名")
scoring_df = result.get('scoring_ranking')
if scoring_df is None or (hasattr(scoring_df, 'empty') and scoring_df.empty):
st.warning("暂无评分数据")
return
# 评分说明
with st.expander("📖 评分维度说明", expanded=False):
st.markdown("""
### 📊 AI智能评分体系 (总分100分)
#### 1️⃣ 买入资金含金量 (0-30分)
- **顶级游资**(赵老哥、章盟主、92科比等):每个 +10分
- **知名游资**(深股通、中信证券等):每个 +5分
- **普通游资**:每个 +1.5分
#### 2️⃣ 净买入额评分 (0-25分)
- 净流入 < 1000万:0-10分
- 净流入 1000-5000万:10-18分
- 净流入 5000万-1亿:18-22分
- 净流入 > 1亿:22-25分
#### 3️⃣ 卖出压力评分 (0-20分)
- 卖出比例 0-10%:20分 ✨(压力极小)
- 卖出比例 10-30%15-20分(压力较小)
- 卖出比例 30-50%10-15分(压力中等)
- 卖出比例 50-80%:5-10分(压力较大)
- 卖出比例 > 80%:0-5分(压力极大)
#### 4️⃣ 机构共振评分 (0-15分)
- **机构+游资共振**:15分 ⭐(最强信号)
- 仅机构买入:8-12分
- 仅游资买入:5-10分
#### 5️⃣ 其他加分项 (0-10分)
- **主力集中度**:席位越少越集中 (+1-3分)
- **热门概念**:AI、新能源、芯片等 (+0-3分)
- **连续上榜**:连续多日上榜 (+0-2分)
- **买卖比例优秀**:买入远大于卖出 (+0-2分)
---
💡 **评分越高,表示该股票受到资金青睐程度越高!**
⚠️ **但仍需结合市场环境、技术面等因素综合判断!**
""")
st.markdown("---")
# 显示TOP10评分表格
st.markdown("### 🥇 TOP10 综合评分排名")
top10_df = scoring_df.head(10).copy()
# 格式化显示
st.dataframe(
top10_df,
column_config={
"排名": st.column_config.TextColumn("排名", width="small"),
"股票名称": st.column_config.TextColumn("股票名称", width="medium"),
"股票代码": st.column_config.TextColumn("代码", width="small"),
"综合评分": st.column_config.NumberColumn(
"综合评分",
format="%.1f",
help="总分100分"
),
"资金含金量": st.column_config.ProgressColumn(
"资金含金量",
format="%d分",
min_value=0,
max_value=30
),
"净买入额": st.column_config.ProgressColumn(
"净买入额",
format="%d分",
min_value=0,
max_value=25
),
"卖出压力": st.column_config.ProgressColumn(
"卖出压力",
format="%d分",
min_value=0,
max_value=20
),
"机构共振": st.column_config.ProgressColumn(
"机构共振",
format="%d分",
min_value=0,
max_value=15
),
"加分项": st.column_config.ProgressColumn(
"加分项",
format="%d分",
min_value=0,
max_value=10
),
"顶级游资": st.column_config.NumberColumn("顶级游资", format="%d家"),
"买方数": st.column_config.NumberColumn("买方数", format="%d家"),
"机构参与": st.column_config.TextColumn("机构参与"),
"净流入": st.column_config.NumberColumn("净流入(元)", format="%.2f")
},
hide_index=True,
use_container_width=True
)
st.markdown("---")
# 评分分布图表
st.markdown("### 📊 评分分布可视化")
col1, col2 = st.columns(2)
with col1:
# 综合评分柱状图
fig1 = px.bar(
top10_df,
x='股票名称',
y='综合评分',
title='TOP10 综合评分对比',
text='综合评分',
color='综合评分',
color_continuous_scale='RdYlGn'
)
fig1.update_traces(texttemplate='%{text:.1f}分', textposition='outside')
fig1.update_layout(
xaxis_tickangle=-45,
showlegend=False,
height=400
)
st.plotly_chart(fig1, use_container_width=True)
with col2:
# 五维评分雷达图(显示第一名)
if len(top10_df) > 0:
first_place = top10_df.iloc[0]
fig2 = go.Figure(data=go.Scatterpolar(
r=[
first_place['资金含金量'] / 30 * 100,
first_place['净买入额'] / 25 * 100,
first_place['卖出压力'] / 20 * 100,
first_place['机构共振'] / 15 * 100,
first_place['加分项'] / 10 * 100
],
theta=['资金含金量', '净买入额', '卖出压力', '机构共振', '加分项'],
fill='toself',
name=first_place['股票名称']
))
fig2.update_layout(
polar=dict(
radialaxis=dict(
visible=True,
range=[0, 100]
)
),
showlegend=True,
title=f"🥇 {first_place['股票名称']} 五维评分",
height=400
)
st.plotly_chart(fig2, use_container_width=True)
st.markdown("---")
# 完整排名表格
st.markdown("### 📋 完整评分排名")
st.dataframe(
scoring_df,
column_config={
"排名": st.column_config.TextColumn("排名", width="small"),
"股票名称": st.column_config.TextColumn("股票名称"),
"股票代码": st.column_config.TextColumn("代码"),
"综合评分": st.column_config.NumberColumn("综合评分", format="%.1f"),
"顶级游资": st.column_config.NumberColumn("顶级游资", format="%d家"),
"买方数": st.column_config.NumberColumn("买方数", format="%d家"),
"机构参与": st.column_config.TextColumn("机构"),
"净流入": st.column_config.NumberColumn("净流入(元)", format="%.2f")
},
hide_index=True,
use_container_width=True
)
def display_recommended_stocks(result):
"""显示推荐股票"""
st.subheader("🎯 AI推荐股票")
recommended = result.get('recommended_stocks', [])
if not recommended:
st.warning("暂无推荐股票")
return
st.info(f"💡 基于5位AI分析师的综合分析,系统识别出以下 **{len(recommended)}** 只潜力股票")
# 创建DataFrame
df_recommended = pd.DataFrame(recommended)
# 显示表格
st.dataframe(
df_recommended,
column_config={
"rank": st.column_config.NumberColumn("排名", format="%d"),
"code": st.column_config.TextColumn("股票代码"),
"name": st.column_config.TextColumn("股票名称"),
"net_inflow": st.column_config.NumberColumn("净流入金额", format="%.2f"),
"confidence": st.column_config.TextColumn("确定性"),
"hold_period": st.column_config.TextColumn("持有周期"),
"reason": st.column_config.TextColumn("推荐理由")
},
hide_index=True,
use_container_width=True
)
# 详细推荐理由
st.markdown("### 📝 详细推荐理由")
for stock in recommended[:5]: # 只显示前5只
with st.expander(f"**{stock.get('rank', '-')}. {stock.get('name', '-')} ({stock.get('code', '-')})**"):
col1, col2 = st.columns([2, 1])
with col1:
st.markdown(f"**推荐理由:** {stock.get('reason', '暂无')}")
st.markdown(f"**净流入:** {stock.get('net_inflow', 0):,.2f} 元")
with col2:
st.markdown(f"**确定性:** {stock.get('confidence', '-')}")
st.markdown(f"**持有周期:** {stock.get('hold_period', '-')}")
def display_agents_reports(result):
"""显示AI分析师报告"""
st.subheader("🤖 AI分析师团队报告")
agents_analysis = result.get('agents_analysis', {})
if not agents_analysis:
st.warning("暂无分析报告")
return
# 各分析师报告
agent_info = {
'youzi': {'title': '🎯 游资行为分析师', 'icon': '🎯'},
'stock': {'title': '📈 个股潜力分析师', 'icon': '📈'},
'theme': {'title': '🔥 题材追踪分析师', 'icon': '🔥'},
'risk': {'title': '⚠️ 风险控制专家', 'icon': '⚠️'},
'chief': {'title': '👔 首席策略师综合研判', 'icon': '👔'}
}
for agent_key, info in agent_info.items():
agent_data = agents_analysis.get(agent_key, {})
if agent_data:
with st.expander(f"{info['icon']} {info['title']}", expanded=(agent_key == 'chief')):
analysis = agent_data.get('analysis', '暂无分析')
st.markdown(analysis)
st.markdown(f"*{agent_data.get('agent_role', '')}*")
st.caption(f"分析时间: {agent_data.get('timestamp', 'N/A')}")
def display_data_details(result):
"""显示数据详情"""
st.subheader("📊 龙虎榜数据详情")
data_info = result.get('data_info', {})
summary = data_info.get('summary', {})
# TOP游资
if summary.get('top_youzi'):
st.markdown("### 🏆 活跃游资 TOP10")
youzi_data = [
{'排名': idx, '游资名称': name, '净流入金额': amount}
for idx, (name, amount) in enumerate(list(summary['top_youzi'].items())[:10], 1)
]
df_youzi = pd.DataFrame(youzi_data)
st.dataframe(
df_youzi,
column_config={
"排名": st.column_config.NumberColumn("排名", format="%d"),
"游资名称": st.column_config.TextColumn("游资名称"),
"净流入金额": st.column_config.NumberColumn("净流入金额(元)", format="%.2f")
},
hide_index=True,
use_container_width=True
)
# TOP股票
if summary.get('top_stocks'):
st.markdown("### 📈 资金净流入 TOP20 股票")
df_stocks = pd.DataFrame(summary['top_stocks'][:20])
st.dataframe(
df_stocks,
column_config={
"code": st.column_config.TextColumn("股票代码"),
"name": st.column_config.TextColumn("股票名称"),
"net_inflow": st.column_config.NumberColumn("净流入金额(元)", format="%.2f")
},
hide_index=True,
use_container_width=True
)
# 热门概念
if summary.get('hot_concepts'):
st.markdown("### 🔥 热门概念 TOP20")
concepts_data = [
{'排名': idx, '概念名称': concept, '出现次数': count}
for idx, (concept, count) in enumerate(list(summary['hot_concepts'].items())[:20], 1)
]
df_concepts = pd.DataFrame(concepts_data)
st.dataframe(
df_concepts,
column_config={
"排名": st.column_config.NumberColumn("排名", format="%d"),
"概念名称": st.column_config.TextColumn("概念名称"),
"出现次数": st.column_config.NumberColumn("出现次数", format="%d")
},
hide_index=True,
use_container_width=True
)
def display_visualizations(result):
"""显示可视化图表"""
st.subheader("📈 数据可视化")
data_info = result.get('data_info', {})
summary = data_info.get('summary', {})
# 资金流向图表
if summary.get('top_stocks'):
st.markdown("### 💰 TOP20 股票资金净流入")
stocks = summary['top_stocks'][:20]
df_chart = pd.DataFrame(stocks)
fig = px.bar(
df_chart,
x='name',
y='net_inflow',
title='TOP20 股票资金净流入金额',
labels={'name': '股票名称', 'net_inflow': '净流入金额(元)'}
)
fig.update_layout(xaxis_tickangle=-45)
st.plotly_chart(fig, use_container_width=True)
# 热门概念图表
if summary.get('hot_concepts'):
st.markdown("### 🔥 热门概念分布")
concepts = list(summary['hot_concepts'].items())[:15]
df_concepts = pd.DataFrame(concepts, columns=['概念', '次数'])
fig = px.pie(
df_concepts,
values='次数',
names='概念',
title='热门概念出现次数分布'
)
st.plotly_chart(fig, use_container_width=True)
def display_pdf_export_section(result):
"""显示PDF导出功能"""
st.markdown("### 📄 导出PDF报告")
col1, col2 = st.columns([3, 1])
with col1:
st.info("💡 点击按钮生成并下载专业的PDF分析报告")
with col2:
if st.button("📥 生成PDF", type="primary", use_container_width=True):
with st.spinner("正在生成PDF报告..."):
try:
generator = LonghubangPDFGenerator()
pdf_path = generator.generate_pdf(result)
# 读取PDF文件
with open(pdf_path, "rb") as f:
pdf_bytes = f.read()
# 提供下载
st.download_button(
label="📥 下载PDF报告",
data=pdf_bytes,
file_name=f"智瞰龙虎报告_{datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf",
mime="application/pdf",
use_container_width=True
)
st.success("✅ PDF报告生成成功!")
except Exception as e:
st.error(f"❌ PDF生成失败: {str(e)}")
def display_history_tab():
"""显示历史报告标签页"""
st.subheader("📚 历史分析报告")
try:
engine = LonghubangEngine()
reports_df = engine.get_historical_reports(limit=20)
if reports_df.empty:
st.info("暂无历史报告")
return
st.dataframe(
reports_df,
column_config={
"id": st.column_config.NumberColumn("ID", format="%d"),
"analysis_date": st.column_config.TextColumn("分析时间"),
"data_date_range": st.column_config.TextColumn("数据日期范围"),
"summary": st.column_config.TextColumn("摘要")
},
hide_index=True,
use_container_width=True
)
except Exception as e:
st.error(f"❌ 加载历史报告失败: {str(e)}")
def display_statistics_tab():
"""显示数据统计标签页"""
st.subheader("📈 数据统计")
try:
engine = LonghubangEngine()
stats = engine.get_statistics()
# 基本统计
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("总记录数", f"{stats.get('total_records', 0):,}")
with col2:
st.metric("股票总数", f"{stats.get('total_stocks', 0):,}")
with col3:
st.metric("游资总数", f"{stats.get('total_youzi', 0):,}")
with col4:
st.metric("分析报告", f"{stats.get('total_reports', 0):,}")
# 日期范围
date_range = stats.get('date_range', {})
if date_range:
st.info(f"📅 数据日期范围: {date_range.get('start', 'N/A')}{date_range.get('end', 'N/A')}")
st.markdown("---")
# 活跃游资排名
st.markdown("### 🏆 历史活跃游资排名 (近30天)")
end_date = datetime.now().strftime('%Y-%m-%d')
start_date = (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d')
top_youzi_df = engine.get_top_youzi(start_date, end_date, limit=20)
if not top_youzi_df.empty:
st.dataframe(
top_youzi_df,
column_config={
"youzi_name": st.column_config.TextColumn("游资名称"),
"trade_count": st.column_config.NumberColumn("交易次数", format="%d"),
"total_net_inflow": st.column_config.NumberColumn("总净流入(元)", format="%.2f")
},
hide_index=True,
use_container_width=True
)
st.markdown("---")
# 热门股票排名
st.markdown("### 📈 历史热门股票排名 (近30天)")
top_stocks_df = engine.get_top_stocks(start_date, end_date, limit=20)
if not top_stocks_df.empty:
st.dataframe(
top_stocks_df,
column_config={
"stock_code": st.column_config.TextColumn("股票代码"),
"stock_name": st.column_config.TextColumn("股票名称"),
"youzi_count": st.column_config.NumberColumn("游资数量", format="%d"),
"total_net_inflow": st.column_config.NumberColumn("总净流入(元)", format="%.2f")
},
hide_index=True,
use_container_width=True
)
except Exception as e:
st.error(f"❌ 加载统计数据失败: {str(e)}")
# 测试函数
if __name__ == "__main__":
st.set_page_config(
page_title="智瞰龙虎",
page_icon="🎯",
layout="wide"
)
display_longhubang()