Files
oficcejo f3af15ddc8 update
2026-02-27 12:08:00 +08:00

1630 lines
63 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
智瞰龙虎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
import config
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():
"""显示分析标签页"""
# 检查是否触发批量分析(不立即删除标志)
if st.session_state.get('longhubang_batch_trigger'):
run_longhubang_batch_analysis()
return
st.subheader("🔍 龙虎榜综合分析")
# 参数设置
col1, col2 = st.columns([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天的龙虎榜数据"
)
# 分析按钮
col1, col2 = st.columns([2, 2])
with col1:
analyze_button = st.button("🚀 开始分析", type="primary", width='stretch')
with col2:
if st.button("🔄 清除结果", width='stretch'):
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
# 准备参数(使用.env中配置的默认模型)
if analysis_mode == "指定日期":
date_str = selected_date.strftime('%Y-%m-%d')
run_longhubang_analysis(date=date_str)
else:
run_longhubang_analysis(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=None, date=None, days=1):
"""运行龙虎榜分析"""
import config
model = model or config.DEFAULT_MODEL_NAME
# 进度显示
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 综合评分排名")
# 兼容历史数据与类型统一,避免 Arrow 序列化错误
if isinstance(scoring_df, list):
scoring_df = pd.DataFrame(scoring_df)
numeric_cols = ['排名','综合评分','资金含金量','净买入额','卖出压力','机构共振','加分项','顶级游资','买方数','净流入']
for col in numeric_cols:
if col in scoring_df.columns:
scoring_df[col] = pd.to_numeric(scoring_df[col], errors='coerce')
text_cols = ['股票名称','股票代码','机构参与']
for col in text_cols:
if col in scoring_df.columns:
scoring_df[col] = scoring_df[col].astype(str)
top10_df = scoring_df.head(10).copy()
if '排名' in top10_df.columns:
top10_df['排名'] = pd.to_numeric(top10_df['排名'], errors='coerce').fillna(0).astype(int)
# 格式化显示
st.dataframe(
top10_df,
column_config={
"排名": st.column_config.NumberColumn("排名", format="%d", 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,
width='stretch'
)
# 一键批量分析功能
st.markdown("---")
col_batch1, col_batch2, col_batch3 = st.columns([2, 1, 1])
with col_batch1:
st.markdown("#### 🚀 批量深度分析")
st.caption("对TOP10股票进行完整的AI团队分析,获取投资评级和关键价位")
with col_batch2:
batch_count = st.selectbox(
"分析数量",
options=[3, 5, 10],
index=0,
help="选择分析前N只股票",
key="batch_count_selector"
)
# 同步更新session_state中的batch_count
st.session_state.batch_count = batch_count
with col_batch3:
st.write("") # 占位
if st.button("🚀 开始批量分析", type="primary", width='stretch'):
# 提取股票代码
stock_codes = top10_df.head(batch_count)['股票代码'].tolist()
# 存储到session_state,触发批量分析
st.session_state.longhubang_batch_codes = stock_codes
st.session_state.longhubang_batch_trigger = True
st.rerun()
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, config={'displayModeBar': False}, use_container_width=True)
with col2:
# 五维评分雷达图(显示批量分析数量的股票)
if len(top10_df) > 0:
display_count = min(5, len(top10_df))
fig2 = go.Figure()
# 为每只股票添加雷达图
colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7']
for i in range(display_count):
stock = top10_df.iloc[i]
fig2.add_trace(go.Scatterpolar(
r=[
stock['资金含金量'] / 30 * 100,
stock['净买入额'] / 25 * 100,
stock['卖出压力'] / 20 * 100,
stock['机构共振'] / 15 * 100,
stock['加分项'] / 10 * 100
],
theta=['资金含金量', '净买入额', '卖出压力', '机构共振', '加分项'],
fill='toself',
name=f"{stock['股票名称']}",
line_color=colors[i % len(colors)],
fillcolor=colors[i % len(colors)],
opacity=0.6
))
fig2.update_layout(
polar=dict(
radialaxis=dict(
visible=True,
range=[0, 100]
)
),
showlegend=True,
title=f"🏆 TOP{display_count} 五维评分对比",
height=400,
legend=dict(
orientation="h",
yanchor="auto",
y=-0.2,
xanchor="center",
x=0.5
)
)
st.plotly_chart(fig2, config={'displayModeBar': False}, use_container_width=True)
st.markdown("---")
# 完整排名表格
st.markdown("### 📋 完整评分排名")
st.dataframe(
scoring_df,
column_config={
"排名": st.column_config.NumberColumn("排名", format="%d", 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,
width='stretch'
)
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,
width='stretch'
)
# 详细推荐理由
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,
width='stretch'
)
# 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,
width='stretch'
)
# 热门概念
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,
width='stretch'
)
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, config={'displayModeBar': False}, 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, config={'displayModeBar': False}, use_container_width=True)
def display_pdf_export_section(result):
"""显示PDF导出功能"""
st.markdown("### 📄 导出报告")
col1, col2, col3 = st.columns([2, 1, 1])
with col1:
st.info("💡 点击按钮生成并下载专业分析报告")
with col2:
if st.button("📥 生成PDF", type="primary", width='stretch'):
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",
width='stretch'
)
st.success("✅ PDF报告生成成功!")
except Exception as e:
st.error(f"❌ PDF生成失败: {str(e)}")
with col3:
if st.button("📝 生成Markdown", type="secondary", width='stretch'):
with st.spinner("正在生成Markdown报告..."):
try:
# 生成Markdown内容
markdown_content = generate_markdown_report(result)
# 提供下载
st.download_button(
label="📥 下载Markdown报告",
data=markdown_content,
file_name=f"智瞰龙虎报告_{datetime.now().strftime('%Y%m%d_%H%M%S')}.md",
mime="text/markdown",
width='stretch'
)
st.success("✅ Markdown报告生成成功!")
except Exception as e:
st.error(f"❌ Markdown生成失败: {str(e)}")
def generate_markdown_report(result_data: dict) -> str:
"""生成龙虎榜分析Markdown报告"""
# 获取当前时间
current_time = datetime.now().strftime("%Y年%m月%d日 %H:%M:%S")
# 标题页
markdown_content = f"""# 智瞰龙虎榜分析报告
**AI驱动的龙虎榜多维度分析系统**
---
## 📊 报告概览
- **生成时间**: {current_time}
- **数据记录**: {result_data.get('data_info', {}).get('total_records', 0)}
- **涉及股票**: {result_data.get('data_info', {}).get('total_stocks', 0)}
- **涉及游资**: {result_data.get('data_info', {}).get('total_youzi', 0)}
- **AI分析师**: 5位专业分析师团队
- **分析模型**: DeepSeek AI Multi-Agent System
> ⚠️ 本报告由AI系统基于龙虎榜公开数据自动生成,仅供参考,不构成投资建议。市场有风险,投资需谨慎。
---
## 📈 数据概况
本次分析共涵盖 **{result_data.get('data_info', {}).get('total_records', 0)}** 条龙虎榜记录,
涉及 **{result_data.get('data_info', {}).get('total_stocks', 0)}** 只股票和
**{result_data.get('data_info', {}).get('total_youzi', 0)}** 个游资席位。
"""
# 资金概况
summary = result_data.get('data_info', {}).get('summary', {})
markdown_content += f"""
### 💰 资金概况
- **总买入金额**: {summary.get('total_buy_amount', 0):,.2f}
- **总卖出金额**: {summary.get('total_sell_amount', 0):,.2f}
- **净流入金额**: {summary.get('total_net_inflow', 0):,.2f}
"""
# TOP游资
if summary.get('top_youzi'):
markdown_content += "### 🏆 活跃游资 TOP10\n\n| 排名 | 游资名称 | 净流入金额(元) |\n|------|----------|---------------|\n"
for idx, (name, amount) in enumerate(list(summary['top_youzi'].items())[:10], 1):
markdown_content += f"| {idx} | {name} | {amount:,.2f} |\n"
markdown_content += "\n"
# TOP股票
if summary.get('top_stocks'):
markdown_content += "### 📈 资金净流入 TOP20 股票\n\n| 排名 | 股票代码 | 股票名称 | 净流入金额(元) |\n|------|----------|----------|---------------|\n"
for idx, stock in enumerate(summary['top_stocks'][:20], 1):
markdown_content += f"| {idx} | {stock['code']} | {stock['name']} | {stock['net_inflow']:,.2f} |\n"
markdown_content += "\n"
# 热门概念
if summary.get('hot_concepts'):
markdown_content += "### 🔥 热门概念 TOP15\n\n"
for idx, (concept, count) in enumerate(list(summary['hot_concepts'].items())[:15], 1):
markdown_content += f"{idx}. {concept} ({count}次) \n"
markdown_content += "\n"
# 推荐股票
recommended = result_data.get('recommended_stocks', [])
if recommended:
markdown_content += f"""
## 🎯 AI推荐股票
基于5位AI分析师的综合分析,系统识别出以下 **{len(recommended)}** 只潜力股票,
这些股票在资金流向、游资关注度、题材热度等多个维度表现突出。
### 推荐股票清单
| 排名 | 股票代码 | 股票名称 | 净流入金额 | 确定性 | 持有周期 |
|------|----------|----------|------------|--------|----------|
"""
for stock in recommended[:10]:
markdown_content += f"| {stock.get('rank', '-')} | {stock.get('code', '-')} | {stock.get('name', '-')} | {stock.get('net_inflow', 0):,.0f} | {stock.get('confidence', '-')} | {stock.get('hold_period', '-')} |\n"
markdown_content += "\n### 推荐理由详解\n\n"
for stock in recommended[:5]: # 只详细展示前5只
markdown_content += f"**{stock.get('rank', '-')}. {stock.get('name', '-')} ({stock.get('code', '-')})**\n\n"
markdown_content += f"- 推荐理由: {stock.get('reason', '暂无')}\n"
markdown_content += f"- 确定性: {stock.get('confidence', '-')}\n"
markdown_content += f"- 持有周期: {stock.get('hold_period', '-')}\n\n"
# AI分析师报告
agents_analysis = result_data.get('agents_analysis', {})
if agents_analysis:
markdown_content += "## 🤖 AI分析师报告\n\n"
markdown_content += "本报告由5位AI专业分析师从不同维度进行分析,综合形成投资建议:\n\n"
markdown_content += "- **游资行为分析师** - 分析游资操作特征和意图\n"
markdown_content += "- **个股潜力分析师** - 挖掘次日大概率上涨的股票\n"
markdown_content += "- **题材追踪分析师** - 识别热点题材和轮动机会\n"
markdown_content += "- **风险控制专家** - 识别高风险股票和市场陷阱\n"
markdown_content += "- **首席策略师** - 综合研判并给出最终建议\n\n"
agent_titles = {
'youzi': '游资行为分析师',
'stock': '个股潜力分析师',
'theme': '题材追踪分析师',
'risk': '风险控制专家',
'chief': '首席策略师综合研判'
}
for agent_key, agent_title in agent_titles.items():
agent_data = agents_analysis.get(agent_key, {})
if agent_data:
markdown_content += f"### {agent_title}\n\n"
analysis_text = agent_data.get('analysis', '暂无分析')
# 处理文本中的换行
analysis_text = analysis_text.replace('\n', '\n\n')
markdown_content += f"{analysis_text}\n\n"
markdown_content += """
---
*报告由智瞰龙虎AI系统自动生成*
"""
return markdown_content
def display_history_tab():
"""显示历史报告标签页(增强版)"""
st.subheader("📚 历史分析报告")
try:
engine = LonghubangEngine()
reports_df = engine.get_historical_reports(limit=50)
if reports_df.empty:
st.info("暂无历史报告")
return
st.info(f"💾 共有 {len(reports_df)} 条历史报告")
# 显示报告列表
st.markdown("### 📋 报告列表")
# 为每条报告创建展开面板
for idx, row in reports_df.iterrows():
report_id = row['id']
analysis_date = row['analysis_date']
data_date_range = row['data_date_range']
summary = row['summary']
# 创建展开面板
with st.expander(
f"📄 报告 #{report_id} | {analysis_date} | 数据范围: {data_date_range}",
expanded=False
):
# 获取完整报告详情
report_detail = engine.get_report_detail(report_id)
if not report_detail:
st.warning("无法加载报告详情")
continue
# 显示摘要
st.markdown("#### 📝 报告摘要")
st.info(summary)
st.markdown("---")
# 显示推荐股票
recommended_stocks = report_detail.get('recommended_stocks', [])
if recommended_stocks:
st.markdown(f"#### 🎯 推荐股票 ({len(recommended_stocks)}只)")
# 创建DataFrame显示
df_stocks = pd.DataFrame(recommended_stocks)
st.dataframe(
df_stocks,
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"),
"reason": st.column_config.TextColumn("推荐理由"),
"confidence": st.column_config.TextColumn("确定性"),
"hold_period": st.column_config.TextColumn("持有周期")
},
hide_index=True,
width='stretch'
)
st.markdown("---")
# 尝试解析完整分析内容
analysis_content_parsed = report_detail.get('analysis_content_parsed')
if analysis_content_parsed and isinstance(analysis_content_parsed, dict):
# 显示AI分析师团队报告
agents_analysis = analysis_content_parsed.get('agents_analysis', {})
if agents_analysis:
st.markdown("#### 🤖 AI分析师团队报告")
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=False):
analysis = agent_data.get('analysis', '暂无分析')
st.markdown(analysis)
st.caption(f"分析时间: {agent_data.get('timestamp', 'N/A')}")
# 显示AI评分排名
scoring_ranking = analysis_content_parsed.get('scoring_ranking', [])
if scoring_ranking:
st.markdown("---")
st.markdown("#### 🏆 AI智能评分排名 (TOP10)")
df_scoring = pd.DataFrame(scoring_ranking[:10])
# 类型统一,避免Arrow序列化错误
numeric_cols = ['排名','综合评分','资金含金量','净买入额','卖出压力','机构共振','加分项','顶级游资','买方数','净流入']
for col in numeric_cols:
if col in df_scoring.columns:
df_scoring[col] = pd.to_numeric(df_scoring[col], errors='coerce')
text_cols = ['股票名称','股票代码','机构参与']
for col in text_cols:
if col in df_scoring.columns:
df_scoring[col] = df_scoring[col].astype(str)
if '排名' in df_scoring.columns:
df_scoring['排名'] = pd.to_numeric(df_scoring['排名'], errors='coerce').fillna(0).astype(int)
# 显示完整的评分表格
st.dataframe(
df_scoring,
column_config={
"排名": st.column_config.NumberColumn("排名", format="%d"),
"股票名称": 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,
width='stretch'
)
# 显示评分说明
with st.expander("📖 评分维度说明", expanded=False):
st.markdown("""
**AI智能评分体系 (总分100分)**
- **资金含金量** (0-30分):顶级游资+10分,知名游资+5分,普通游资+1.5分
- **净买入额** (0-25分):根据净流入金额大小评分
- **卖出压力** (0-20分):卖出比例越低得分越高
- **机构共振** (0-15分):机构+游资共振15分最高
- **加分项** (0-10分):主力集中度、热门概念、连续上榜等
💡 评分越高,表示该股票受到资金青睐程度越高!
""")
# 显示数据概况
data_info = analysis_content_parsed.get('data_info', {})
if data_info:
st.markdown("---")
st.markdown("#### 📊 数据概况")
col1, col2, col3 = st.columns(3)
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)} 个")
else:
# 如果无法解析,显示原始内容
st.markdown("#### 📄 原始分析内容")
analysis_content = report_detail.get('analysis_content', '')
if analysis_content:
st.text_area("原始分析内容", value=analysis_content[:2000], height=200, disabled=True)
if len(analysis_content) > 2000:
st.caption("(内容过长,仅显示前2000字符)")
# 操作按钮
st.markdown("---")
col_export1, col_export2, col_export3 = st.columns(3)
with col_export1:
if st.button(f"📥 导出为PDF", key=f"export_pdf_{report_id}"):
st.info("PDF导出功能开发中...")
with col_export2:
# 使用session_state来管理按钮状态,避免需要点击两次的问题
load_key = f"load_report_{report_id}"
if st.button(f"📋 加载到分析页", key=load_key):
# 将历史报告加载到当前分析结果中
if analysis_content_parsed:
# 重建完整的result结构
scoring_data = analysis_content_parsed.get('scoring_ranking', [])
if scoring_data:
df_scoring = pd.DataFrame(scoring_data)
# 类型统一,避免Arrow序列化错误
numeric_cols = ['排名','综合评分','资金含金量','净买入额','卖出压力','机构共振','加分项','顶级游资','买方数','净流入']
for col in numeric_cols:
if col in df_scoring.columns:
df_scoring[col] = pd.to_numeric(df_scoring[col], errors='coerce')
text_cols = ['股票名称','股票代码','机构参与']
for col in text_cols:
if col in df_scoring.columns:
df_scoring[col] = df_scoring[col].astype(str)
if '排名' in df_scoring.columns:
df_scoring['排名'] = pd.to_numeric(df_scoring['排名'], errors='coerce').fillna(0).astype(int)
else:
df_scoring = None
loaded_result = {
"success": True,
"timestamp": report_detail.get('analysis_date', ''),
"data_info": analysis_content_parsed.get('data_info', {}),
"agents_analysis": analysis_content_parsed.get('agents_analysis', {}),
"scoring_ranking": df_scoring,
"final_report": analysis_content_parsed.get('final_report', {}),
"recommended_stocks": report_detail.get('recommended_stocks', [])
}
st.session_state.longhubang_result = loaded_result
# 使用rerun来立即刷新页面状态
st.success('✅ 报告已加载到分析页面,请切换到"龙虎榜分析"标签查看')
st.rerun()
with col_export3:
# 删除按钮
delete_key = f"delete_report_{report_id}"
if st.button(f"🗑️ 删除报告", key=delete_key, type="secondary"):
# 使用session_state来管理删除确认状态
st.session_state[f"confirm_delete_{report_id}"] = True
st.rerun()
# 删除确认对话框
if st.session_state.get(f"confirm_delete_{report_id}", False):
st.warning(f"⚠️ 确认删除报告 #{report_id}?此操作不可撤销!")
col_confirm1, col_confirm2 = st.columns(2)
with col_confirm1:
if st.button(f"✅ 确认删除", key=f"confirm_delete_yes_{report_id}", type="primary"):
try:
# 调用数据库删除方法 - 修复属性名
engine.database.delete_analysis_report(report_id)
st.success(f"✅ 报告 #{report_id} 已成功删除")
# 清除确认状态并刷新页面
if f"confirm_delete_{report_id}" in st.session_state:
del st.session_state[f"confirm_delete_{report_id}"]
st.rerun()
except Exception as e:
st.error(f"❌ 删除失败: {str(e)}")
with col_confirm2:
if st.button(f"❌ 取消", key=f"confirm_delete_no_{report_id}"):
# 清除确认状态
if f"confirm_delete_{report_id}" in st.session_state:
del st.session_state[f"confirm_delete_{report_id}"]
st.rerun()
except Exception as e:
st.error(f"❌ 加载历史报告失败: {str(e)}")
import traceback
st.code(traceback.format_exc())
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,
width='stretch'
)
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,
width='stretch'
)
except Exception as e:
st.error(f"❌ 加载统计数据失败: {str(e)}")
def run_longhubang_batch_analysis():
"""执行龙虎榜TOP股票批量分析(遵循统一调用规范)"""
st.markdown("## 🚀 龙虎榜TOP股票批量分析")
st.markdown("---")
# 检查是否已有分析结果
if st.session_state.get('longhubang_batch_results'):
display_longhubang_batch_results(st.session_state.longhubang_batch_results)
# 返回按钮
col_back, col_clear = st.columns(2)
with col_back:
if st.button("🔙 返回龙虎榜分析", width='stretch'):
# 清除所有批量分析相关状态
if 'longhubang_batch_trigger' in st.session_state:
del st.session_state.longhubang_batch_trigger
if 'longhubang_batch_codes' in st.session_state:
del st.session_state.longhubang_batch_codes
if 'longhubang_batch_results' in st.session_state:
del st.session_state.longhubang_batch_results
st.rerun()
with col_clear:
if st.button("🔄 重新分析", width='stretch'):
# 清除结果,保留触发标志和代码
if 'longhubang_batch_results' in st.session_state:
del st.session_state.longhubang_batch_results
st.rerun()
return
# 获取股票代码列表
stock_codes = st.session_state.get('longhubang_batch_codes', [])
if not stock_codes:
st.error("未找到股票代码列表")
# 清除触发标志
if 'longhubang_batch_trigger' in st.session_state:
del st.session_state.longhubang_batch_trigger
return
st.info(f"即将分析 {len(stock_codes)} 只股票:{', '.join(stock_codes)}")
# 返回按钮
if st.button("🔙 取消返回", type="secondary"):
# 清除所有批量分析相关状态
if 'longhubang_batch_trigger' in st.session_state:
del st.session_state.longhubang_batch_trigger
if 'longhubang_batch_codes' in st.session_state:
del st.session_state.longhubang_batch_codes
st.rerun()
st.markdown("---")
# 分析选项
col1, col2 = st.columns(2)
with col1:
analysis_mode = st.selectbox(
"分析模式",
options=["sequential", "parallel"],
format_func=lambda x: "顺序分析(稳定)" if x == "sequential" else "并行分析(快速)",
help="顺序分析较慢但稳定,并行分析更快但消耗更多资源"
)
with col2:
if analysis_mode == "parallel":
max_workers = st.number_input(
"并行线程数",
min_value=2,
max_value=5,
value=3,
help="同时分析的股票数量"
)
else:
max_workers = 1
st.markdown("---")
# 开始分析按钮
col_confirm, col_cancel = st.columns(2)
start_analysis = False
with col_confirm:
if st.button("🚀 确认开始分析", type="primary", width='stretch'):
start_analysis = True
with col_cancel:
if st.button("❌ 取消", type="secondary", width='stretch'):
# 清除所有批量分析相关状态
if 'longhubang_batch_trigger' in st.session_state:
del st.session_state.longhubang_batch_trigger
if 'longhubang_batch_codes' in st.session_state:
del st.session_state.longhubang_batch_codes
st.rerun()
if start_analysis:
# 导入统一分析函数(遵循统一规范)
from app import analyze_single_stock_for_batch
import concurrent.futures
import time
st.markdown("---")
st.info("⏳ 正在执行批量分析,请稍候...")
# 进度显示
progress_bar = st.progress(0)
status_text = st.empty()
results = []
start_time = time.time()
if analysis_mode == "sequential":
# 顺序分析
for i, code in enumerate(stock_codes):
status_text.text(f"正在分析 {code} ({i+1}/{len(stock_codes)})")
progress_bar.progress((i + 1) / len(stock_codes))
try:
# 调用统一分析函数
result = analyze_single_stock_for_batch(
symbol=code,
period="1y",
enabled_analysts_config={
'technical': True,
'fundamental': True,
'fund_flow': True,
'risk': True,
'sentiment': False,
'news': False
},
selected_model=config.DEFAULT_MODEL_NAME
)
results.append({
"code": code,
"result": result
})
except Exception as e:
results.append({
"code": code,
"result": {"success": False, "error": str(e)}
})
else:
# 并行分析
status_text.text(f"并行分析 {len(stock_codes)} 只股票...")
def analyze_one(code):
try:
result = analyze_single_stock_for_batch(
symbol=code,
period="1y",
enabled_analysts_config={
'technical': True,
'fundamental': True,
'fund_flow': True,
'risk': True,
'sentiment': False,
'news': False
},
selected_model=config.DEFAULT_MODEL_NAME
)
return {"code": code, "result": result}
except Exception as e:
return {"code": code, "result": {"success": False, "error": str(e)}}
with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
futures = {executor.submit(analyze_one, code): code for code in stock_codes}
completed = 0
for future in concurrent.futures.as_completed(futures):
completed += 1
progress_bar.progress(completed / len(stock_codes))
status_text.text(f"已完成 {completed}/{len(stock_codes)}")
results.append(future.result())
# 清除进度
progress_bar.empty()
status_text.empty()
# 计算统计
elapsed_time = time.time() - start_time
success_count = sum(1 for r in results if r.get("result", {}).get("success"))
failed_count = len(results) - success_count
st.success(f"✅ 批量分析完成!成功 {success_count} 只,失败 {failed_count} 只,耗时 {elapsed_time:.1f}秒")
# 保存结果到session_state
st.session_state.longhubang_batch_results = {
"results": results,
"total": len(results),
"success": success_count,
"failed": failed_count,
"elapsed_time": elapsed_time
}
time.sleep(0.5)
st.rerun()
def display_longhubang_batch_results(batch_results: dict):
"""显示龙虎榜批量分析结果"""
st.markdown("### 📊 批量分析结果")
results = batch_results.get("results", [])
total = batch_results.get("total", 0)
success = batch_results.get("success", 0)
failed = batch_results.get("failed", 0)
elapsed_time = batch_results.get("elapsed_time", 0)
# 统计信息
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("总计", total)
with col2:
st.metric("成功", success)
with col3:
st.metric("失败", failed)
with col4:
st.metric("耗时", f"{elapsed_time:.1f}秒")
st.markdown("---")
# 失败的股票
failed_results = [r for r in results if not r.get("result", {}).get("success")]
if failed_results:
with st.expander(f"❌ 失败股票 ({len(failed_results)}只)", expanded=False):
for item in failed_results:
code = item.get("code", "")
error = item.get("result", {}).get("error", "未知错误")
st.error(f"**{code}**: {error}")
# 成功的股票
success_results = [r for r in results if r.get("result", {}).get("success")]
if not success_results:
st.warning("⚠️ 没有成功分析的股票")
return
st.markdown("### 🎯 分析结果详情")
# 显示每只股票的分析结果(使用统一字段名)
for item in success_results:
code = item.get("code", "")
result = item.get("result", {})
final_decision = result.get("final_decision", {})
stock_info = result.get("stock_info", {})
# 使用统一字段名
rating = final_decision.get("rating", "未知")
confidence = final_decision.get("confidence_level", "N/A")
entry_range = final_decision.get("entry_range", "N/A")
take_profit = final_decision.get("take_profit", "N/A")
stop_loss = final_decision.get("stop_loss", "N/A")
target_price = final_decision.get("target_price", "N/A")
advice = final_decision.get("advice", "")
# 评级颜色
if "强烈买入" in rating or "买入" in rating:
rating_color = "🟢"
elif "卖出" in rating:
rating_color = "🔴"
else:
rating_color = "🟡"
with st.expander(f"{rating_color} {code} {stock_info.get('name', '')} - {rating} (信心度: {confidence})", expanded=False):
col1, col2, col3 = st.columns(3)
with col1:
st.markdown("**基本信息**")
st.write(f"当前价: {stock_info.get('current_price', 'N/A')}")
st.write(f"目标价: {target_price}")
with col2:
st.markdown("**进出场位置**")
st.write(f"进场区间: {entry_range}")
st.write(f"止盈位: {take_profit}")
with col3:
st.markdown("**风控**")
st.write(f"止损位: {stop_loss}")
st.write(f"评级: {rating}")
if advice:
st.markdown("**投资建议**")
st.info(advice)
# 添加到监测按钮
if st.button(f" 加入监测", key=f"add_monitor_{code}"):
add_to_monitor_from_longhubang(code, stock_info.get('name', ''), final_decision)
def add_to_monitor_from_longhubang(code: str, name: str, final_decision: dict):
"""从龙虎榜分析结果添加到监测列表"""
try:
from monitor_db import monitor_db
import re
# 提取数据(使用统一字段名和解析逻辑)
rating = final_decision.get("rating", "持有")
entry_range = final_decision.get("entry_range", "")
take_profit_str = final_decision.get("take_profit", "")
stop_loss_str = final_decision.get("stop_loss", "")
# 解析进场区间
entry_min, entry_max = None, None
if entry_range and isinstance(entry_range, str) and "-" in entry_range:
try:
parts = entry_range.split("-")
entry_min = float(parts[0].strip())
entry_max = float(parts[1].strip())
except:
pass
# 解析止盈止损
take_profit, stop_loss = None, None
if take_profit_str:
try:
numbers = re.findall(r'\d+\.?\d*', str(take_profit_str))
if numbers:
take_profit = float(numbers[0])
except:
pass
if stop_loss_str:
try:
numbers = re.findall(r'\d+\.?\d*', str(stop_loss_str))
if numbers:
stop_loss = float(numbers[0])
except:
pass
# 验证必需参数
if not all([entry_min, entry_max, take_profit, stop_loss]):
st.error("❌ 分析结果缺少完整的进场区间和止盈止损信息")
return
# 添加到监测
monitor_db.add_monitored_stock(
symbol=code,
name=name,
rating=rating,
entry_range={"min": entry_min, "max": entry_max},
take_profit=take_profit,
stop_loss=stop_loss,
check_interval=60,
notification_enabled=True
)
st.success(f"✅ {code} 已成功加入监测列表!")
except Exception as e:
st.error(f"❌ 添加监测失败: {str(e)}")
# 测试函数
if __name__ == "__main__":
st.set_page_config(
page_title="智瞰龙虎",
page_icon="🎯",
layout="wide"
)
display_longhubang()