""" 智策UI界面模块 展示板块分析结果和预测 """ import streamlit as st import plotly.graph_objects as go import plotly.express as px import pandas as pd from datetime import datetime, time as dt_time import time import base64 from sector_strategy_data import SectorStrategyDataFetcher from sector_strategy_engine import SectorStrategyEngine from sector_strategy_pdf import SectorStrategyPDFGenerator from sector_strategy_scheduler import sector_strategy_scheduler def display_sector_strategy(): """显示智策板块分析主界面""" st.markdown("""

🎯 智策 - AI驱动的板块策略分析

""", unsafe_allow_html=True) st.markdown("---") # 定时任务设置区域 display_scheduler_settings() # 功能说明 with st.expander("💡 智策系统介绍", expanded=False): st.markdown(""" ### 🌟 系统特色 **智策**是基于多AI智能体的板块策略分析系统,通过四位专业分析师的协同工作,为您提供全方位的板块投资决策支持。 ### 🤖 AI智能体团队 1. **🌐 宏观策略师** - 分析宏观经济形势和政策导向 - 解读财经新闻对市场的影响 - 识别行业发展趋势 2. **📊 板块诊断师** - 深入分析板块走势和估值 - 评估板块基本面和成长性 - 预判板块轮动方向 3. **💰 资金流向分析师** - 跟踪主力资金的板块流向 - 分析北向资金的偏好 - 识别资金轮动信号 4. **📈 市场情绪解码员** - 量化市场情绪指标 - 识别恐慌贪婪信号 - 评估板块热度 ### 📊 核心预测 - **板块多空**: 看多/看空板块推荐 - **板块轮动**: 强势/潜力/衰退板块识别 - **板块热度**: 热度排行和升降温趋势 ### 📈 数据来源 所有数据来自**AKShare**开源库,包括: - 行业板块和概念板块行情 - 板块资金流向数据 - 北向资金数据 - 市场统计数据 - 财经新闻数据 """) st.markdown("---") # 模型选择 col1, col2, col3 = st.columns([2, 2, 2]) with col1: selected_model = st.selectbox( "选择AI模型", ["deepseek-chat", "deepseek-reasoner"], help="Reasoner模型提供更强的推理能力" ) with col2: st.write("") st.write("") analyze_button = st.button("🚀 开始智策分析", type="primary", use_container_width=True) with col3: st.write("") st.write("") if st.button("🔄 清除结果", use_container_width=True): if 'sector_strategy_result' in st.session_state: del st.session_state.sector_strategy_result st.success("已清除分析结果") st.rerun() st.markdown("---") # 开始分析 if analyze_button: # 清除之前的结果 if 'sector_strategy_result' in st.session_state: del st.session_state.sector_strategy_result run_sector_strategy_analysis(selected_model) # 显示分析结果 if 'sector_strategy_result' in st.session_state: result = st.session_state.sector_strategy_result if result.get("success"): display_analysis_results(result) else: st.error(f"❌ 分析失败: {result.get('error', '未知错误')}") def run_sector_strategy_analysis(model="deepseek-chat"): """运行智策分析""" # 进度显示 progress_bar = st.progress(0) status_text = st.empty() try: # 1. 获取数据 status_text.text("📊 正在获取市场数据...") progress_bar.progress(10) fetcher = SectorStrategyDataFetcher() data = fetcher.get_all_sector_data() if not data.get("success"): st.error("❌ 数据获取失败") return progress_bar.progress(30) status_text.text("✓ 数据获取完成") # 显示数据摘要 display_data_summary(data) # 2. 运行AI分析 status_text.text("🤖 AI智能体团队正在分析,预计需要10分钟...") progress_bar.progress(40) engine = SectorStrategyEngine(model=model) result = engine.run_comprehensive_analysis(data) progress_bar.progress(90) if result.get("success"): # 保存结果 st.session_state.sector_strategy_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_data_summary(data): """显示数据摘要""" st.subheader("📊 市场数据概览") col1, col2, col3, col4 = st.columns(4) market = data.get("market_overview", {}) with col1: if market.get("sh_index"): sh = market["sh_index"] st.metric( "上证指数", f"{sh['close']:.2f}", f"{sh['change_pct']:+.2f}%" ) with col2: if market.get("up_count"): st.metric( "上涨股票", market['up_count'], f"{market['up_ratio']:.1f}%" ) with col3: sectors_count = len(data.get("sectors", {})) st.metric("行业板块", sectors_count) with col4: concepts_count = len(data.get("concepts", {})) st.metric("概念板块", concepts_count) def display_analysis_results(result): """显示分析结果""" st.success("✅ 智策分析完成!") st.info(f"📅 分析时间: {result.get('timestamp', 'N/A')}") # PDF导出功能 display_pdf_export_section(result) st.markdown("---") # 创建标签页 tab1, tab2, tab3, tab4 = st.tabs([ "📋 核心预测", "🤖 智能体分析", "📊 综合研判", "📈 数据可视化" ]) # Tab 1: 核心预测 with tab1: display_predictions(result.get("final_predictions", {})) # Tab 2: 智能体分析 with tab2: display_agents_reports(result.get("agents_analysis", {})) # Tab 3: 综合研判 with tab3: display_comprehensive_report(result.get("comprehensive_report", "")) # Tab 4: 数据可视化 with tab4: display_visualizations(result.get("final_predictions", {})) def display_predictions(predictions): """显示核心预测""" st.subheader("🎯 智策核心预测") if not predictions or predictions.get("prediction_text"): # 文本格式 st.markdown("### 预测报告") st.write(predictions.get("prediction_text", "暂无预测")) return # JSON格式预测 # 1. 板块多空 st.markdown("### 📊 板块多空预测") col1, col2 = st.columns(2) with col1: st.markdown("#### 🟢 看多板块") bullish = predictions.get("long_short", {}).get("bullish", []) if bullish: for item in bullish: st.markdown(f"""

{item.get('sector', 'N/A')}

信心度: {item.get('confidence', 0)}/10

理由: {item.get('reason', '')}

风险: {item.get('risk', '')}

""", unsafe_allow_html=True) else: st.info("暂无看多板块") with col2: st.markdown("#### 🔴 看空板块") bearish = predictions.get("long_short", {}).get("bearish", []) if bearish: for item in bearish: st.markdown(f"""

{item.get('sector', 'N/A')}

信心度: {item.get('confidence', 0)}/10

理由: {item.get('reason', '')}

风险: {item.get('risk', '')}

""", unsafe_allow_html=True) else: st.info("暂无看空板块") st.markdown("---") # 2. 板块轮动 st.markdown("### 🔄 板块轮动预测") rotation = predictions.get("rotation", {}) col1, col2, col3 = st.columns(3) with col1: st.markdown("#### 💪 当前强势") current_strong = rotation.get("current_strong", []) for item in current_strong: st.markdown(f""" **{item.get('sector', 'N/A')}** - 时间窗口: {item.get('time_window', 'N/A')} - 逻辑: {item.get('logic', '')[:50]}... - 建议: {item.get('advice', '')} """) with col2: st.markdown("#### 🌱 潜力接力") potential = rotation.get("potential", []) for item in potential: st.markdown(f""" **{item.get('sector', 'N/A')}** - 时间窗口: {item.get('time_window', 'N/A')} - 逻辑: {item.get('logic', '')[:50]}... - 建议: {item.get('advice', '')} """) with col3: st.markdown("#### 📉 衰退板块") declining = rotation.get("declining", []) for item in declining: st.markdown(f""" **{item.get('sector', 'N/A')}** - 时间窗口: {item.get('time_window', 'N/A')} - 逻辑: {item.get('logic', '')[:50]}... - 建议: {item.get('advice', '')} """) st.markdown("---") # 3. 板块热度 st.markdown("### 🔥 板块热度排行") heat = predictions.get("heat", {}) col1, col2, col3 = st.columns(3) with col1: st.markdown("#### 🔥 最热板块") hottest = heat.get("hottest", []) for idx, item in enumerate(hottest, 1): st.metric( f"{idx}. {item.get('sector', 'N/A')}", f"{item.get('score', 0)}分", f"{item.get('trend', 'N/A')}" ) with col2: st.markdown("#### 📈 升温板块") heating = heat.get("heating", []) for idx, item in enumerate(heating, 1): st.metric( f"{idx}. {item.get('sector', 'N/A')}", f"{item.get('score', 0)}分", "↗️ 升温" ) with col3: st.markdown("#### 📉 降温板块") cooling = heat.get("cooling", []) for idx, item in enumerate(cooling, 1): st.metric( f"{idx}. {item.get('sector', 'N/A')}", f"{item.get('score', 0)}分", "↘️ 降温" ) st.markdown("---") # 4. 总结建议 summary = predictions.get("summary", {}) if summary: st.markdown("### 📝 策略总结") col1, col2 = st.columns(2) with col1: st.markdown(f"""

💡 市场观点

{summary.get('market_view', 'N/A')}

""", unsafe_allow_html=True) st.markdown(f"""

🎯 核心机会

{summary.get('key_opportunity', 'N/A')}

""", unsafe_allow_html=True) with col2: st.markdown(f"""

⚠️ 主要风险

{summary.get('major_risk', 'N/A')}

""", unsafe_allow_html=True) st.markdown(f"""

📋 整体策略

{summary.get('strategy', 'N/A')}

""", unsafe_allow_html=True) def display_agents_reports(agents_analysis): """显示智能体分析报告""" st.subheader("🤖 AI智能体分析报告") if not agents_analysis: st.info("暂无智能体分析数据") return # 创建子标签页 agent_names = [] agent_data = [] for key, value in agents_analysis.items(): agent_names.append(value.get("agent_name", "未知分析师")) agent_data.append(value) tabs = st.tabs(agent_names) for idx, tab in enumerate(tabs): with tab: agent = agent_data[idx] st.markdown(f"""

👨‍💼 {agent.get('agent_name', '未知')}

职责: {agent.get('agent_role', '未知')}

关注领域: {', '.join(agent.get('focus_areas', []))}

分析时间: {agent.get('timestamp', '未知')}

""", unsafe_allow_html=True) st.markdown("---") st.markdown("### 📄 分析报告") st.write(agent.get("analysis", "暂无分析")) def display_comprehensive_report(report): """显示综合研判报告""" st.subheader("📊 综合研判报告") if not report: st.info("暂无综合研判数据") return st.markdown("""

🎯 智策综合研判

基于四位专业分析师的深度分析,形成的全面市场和板块研判

""", unsafe_allow_html=True) st.markdown("---") st.write(report) def display_visualizations(predictions): """显示数据可视化""" st.subheader("📈 数据可视化") if not predictions or predictions.get("prediction_text"): st.info("暂无可视化数据") return # 1. 板块多空雷达图 st.markdown("### 📊 板块多空信心度对比") bullish = predictions.get("long_short", {}).get("bullish", []) bearish = predictions.get("long_short", {}).get("bearish", []) if bullish or bearish: # 准备数据 sectors = [] confidence = [] types = [] for item in bullish[:5]: sectors.append(item.get('sector', 'N/A')) confidence.append(item.get('confidence', 0)) types.append('看多') for item in bearish[:5]: sectors.append(item.get('sector', 'N/A')) confidence.append(-item.get('confidence', 0)) # 负值表示看空 types.append('看空') # 创建条形图 df = pd.DataFrame({ '板块': sectors, '信心度': confidence, '类型': types }) fig = px.bar(df, x='板块', y='信心度', color='类型', color_discrete_map={'看多': '#4caf50', '看空': '#f44336'}, title='板块多空信心度对比') fig.update_layout(height=400) st.plotly_chart(fig, use_container_width=True, key="sector_confidence") st.markdown("---") # 2. 板块热度分布 st.markdown("### 🔥 板块热度分布") heat = predictions.get("heat", {}) hottest = heat.get("hottest", []) heating = heat.get("heating", []) if hottest or heating: sectors = [] scores = [] trends = [] for item in hottest: sectors.append(item.get('sector', 'N/A')) scores.append(item.get('score', 0)) trends.append('最热') for item in heating: sectors.append(item.get('sector', 'N/A')) scores.append(item.get('score', 0)) trends.append('升温') df = pd.DataFrame({ '板块': sectors, '热度': scores, '趋势': trends }) fig = px.scatter(df, x='板块', y='热度', size='热度', color='趋势', color_discrete_map={'最热': '#ff5722', '升温': '#ff9800'}, title='板块热度分布图') fig.update_layout(height=400) st.plotly_chart(fig, use_container_width=True, key="sector_heat") def display_pdf_export_section(result): """显示PDF导出部分""" st.subheader("📄 导出报告") col1, col2, col3 = st.columns([2, 1, 1]) with col1: st.write("将分析报告导出为PDF文件,方便保存和分享") with col2: if st.button("📥 生成PDF报告", type="primary", use_container_width=True): with st.spinner("正在生成PDF报告..."): try: # 生成PDF generator = SectorStrategyPDFGenerator() pdf_path = generator.generate_pdf(result) # 读取PDF文件 with open(pdf_path, "rb") as f: pdf_bytes = f.read() # 保存到session_state st.session_state.sector_pdf_data = pdf_bytes st.session_state.sector_pdf_filename = f"智策报告_{result.get('timestamp', datetime.now().strftime('%Y%m%d_%H%M%S')).replace(':', '').replace(' ', '_')}.pdf" st.success("✅ PDF报告生成成功!") st.rerun() except Exception as e: st.error(f"❌ PDF生成失败: {str(e)}") with col3: # 如果已经生成了PDF,显示下载按钮 if 'sector_pdf_data' in st.session_state: st.download_button( label="💾 下载PDF", data=st.session_state.sector_pdf_data, file_name=st.session_state.sector_pdf_filename, mime="application/pdf", use_container_width=True ) def display_scheduler_settings(): """显示定时任务设置""" with st.expander("⏰ 定时分析设置", expanded=False): st.markdown(""" **定时分析功能** 开启后,系统将在每天指定时间自动运行智策分析,并将核心结果通过邮件发送。 **前提条件:** - 需要在 `.env` 文件中配置邮件设置 - 配置项:`EMAIL_ENABLED`, `SMTP_SERVER`, `EMAIL_FROM`, `EMAIL_PASSWORD`, `EMAIL_TO` """) # 获取当前状态 status = sector_strategy_scheduler.get_status() col1, col2 = st.columns([1, 1]) with col1: # 显示当前状态 if status['running']: st.success(f"✅ 定时任务运行中") st.info(f"⏰ 定时时间: {status['schedule_time']}") if status['next_run_time']: st.info(f"📅 下次运行: {status['next_run_time']}") if status['last_run_time']: st.info(f"📊 上次运行: {status['last_run_time']}") else: st.warning("⏸️ 定时任务未运行") with col2: # 时间设置 schedule_time = st.time_input( "设置定时时间", value=dt_time(9, 0), # 默认9:00 help="系统将在每天此时间自动运行分析" ) schedule_time_str = schedule_time.strftime("%H:%M") # 控制按钮 col_a, col_b, col_c = st.columns(3) with col_a: if not status['running']: if st.button("▶️ 启动", use_container_width=True, type="primary"): if sector_strategy_scheduler.start(schedule_time_str): st.success(f"✅ 定时任务已启动!每天 {schedule_time_str} 运行") time.sleep(1) st.rerun() else: st.error("❌ 启动失败") else: if st.button("⏹️ 停止", use_container_width=True): if sector_strategy_scheduler.stop(): st.success("✅ 定时任务已停止") time.sleep(1) st.rerun() else: st.error("❌ 停止失败") with col_b: if st.button("🔄 立即运行", use_container_width=True): with st.spinner("正在运行分析..."): sector_strategy_scheduler.manual_run() st.success("✅ 手动分析完成!") with col_c: if st.button("📧 测试邮件", use_container_width=True): test_email_notification() # 邮件配置检查 st.markdown("---") check_email_config() def check_email_config(): """检查邮件配置""" st.markdown("**📧 邮件配置检查**") import os from dotenv import load_dotenv load_dotenv() email_enabled = os.getenv('EMAIL_ENABLED', 'false').lower() == 'true' smtp_server = os.getenv('SMTP_SERVER', '') email_from = os.getenv('EMAIL_FROM', '') email_password = os.getenv('EMAIL_PASSWORD', '') email_to = os.getenv('EMAIL_TO', '') col1, col2 = st.columns(2) with col1: st.write("**配置项**") st.write(f"✅ 邮件功能: {'已启用' if email_enabled else '❌ 未启用'}") st.write(f"{'✅' if smtp_server else '❌'} SMTP服务器: {smtp_server or '未配置'}") st.write(f"{'✅' if email_from else '❌'} 发件邮箱: {email_from or '未配置'}") with col2: st.write("**状态**") st.write(f"{'✅' if email_password else '❌'} 邮箱密码: {'已配置' if email_password else '未配置'}") st.write(f"{'✅' if email_to else '❌'} 收件邮箱: {email_to or '未配置'}") config_complete = all([email_enabled, smtp_server, email_from, email_password, email_to]) if config_complete: st.success("✅ 邮件配置完整") else: st.warning("⚠️ 邮件配置不完整,请在 .env 文件中配置") def test_email_notification(): """测试邮件通知""" try: from notification_service import notification_service # 使用notification_service的send_test_email方法 success, message = notification_service.send_test_email() if success: st.success(f"✅ {message}") st.balloons() else: st.error(f"❌ {message}") except Exception as e: st.error(f"❌ 发送测试邮件时出错: {str(e)}") import traceback st.code(traceback.format_exc()) # 主入口 if __name__ == "__main__": display_sector_strategy()