""" 智策UI界面模块 展示板块分析结果和预测 """ import streamlit as st import time 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 import json from sector_strategy_data import SectorStrategyDataFetcher from sector_strategy_engine import SectorStrategyEngine from sector_strategy_pdf import SectorStrategyPDFGenerator from sector_strategy_db import SectorStrategyDatabase from sector_strategy_scheduler import sector_strategy_scheduler def _parse_json_field(value, default): """将可能的JSON字符串安全转换为Python对象""" try: if isinstance(value, (dict, list)): return value if value is None: return default if isinstance(value, str): v = value.strip() if not v: return default return json.loads(v) return default except Exception: return default def display_sector_strategy(): """显示智策板块分析主界面""" st.markdown("""

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

""", unsafe_allow_html=True) st.markdown("---") # 创建标签页 tab1, tab2 = st.tabs(["📊 智策分析", "📋 历史报告"]) with tab1: display_analysis_tab() with tab2: display_history_tab() def display_analysis_tab(): """显示分析标签页""" # 定时任务设置区域 display_scheduler_settings() # 功能说明 with st.expander("💡 智策系统介绍", expanded=False): st.markdown(""" ### 🌟 系统特色 **智策**是基于多AI智能体的板块策略分析系统,通过四位专业分析师的协同工作,为您提供全方位的板块投资决策支持。 ### 🤖 AI智能体团队 1. **🌐 宏观策略师** - 分析宏观经济形势和政策导向 - 解读财经新闻对市场的影响 - 识别行业发展趋势 2. **📊 板块诊断师** - 深入分析板块走势和估值 - 评估板块基本面和成长性 - 预判板块轮动方向 3. **💰 资金流向分析师** - 跟踪主力资金的板块流向 - 分析北向资金的偏好 - 识别资金轮动信号 4. **📈 市场情绪解码员** - 量化市场情绪指标 - 识别恐慌贪婪信号 - 评估板块热度 ### 📊 核心预测 - **板块多空**: 看多/看空板块推荐 - **板块轮动**: 强势/潜力/衰退板块识别 - **板块热度**: 热度排行和升降温趋势 ### 📈 数据来源 所有数据来自**AKShare**开源库,包括: - 行业板块和概念板块行情 - 板块资金流向数据 - 北向资金数据 - 市场统计数据 - 财经新闻数据 """) st.markdown("---") # 操作按钮 col1, col2 = st.columns([2, 2]) with col1: analyze_button = st.button("🚀 开始智策分析", type="primary", width='content') with col2: if st.button("🔄 清除结果", width='content'): if 'sector_strategy_result' in st.session_state: del st.session_state.sector_strategy_result st.success("已清除分析结果") st.rerun() st.markdown("---") # 开始分析(使用.env中配置的默认模型) if analyze_button: # 清除之前的结果 if 'sector_strategy_result' in st.session_state: del st.session_state.sector_strategy_result run_sector_strategy_analysis() # 显示分析结果 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 display_history_tab(): """显示历史报告标签页""" st.markdown("### 📋 智策历史报告") st.markdown("查看和管理历史分析报告") try: # 初始化引擎以获取历史报告 engine = SectorStrategyEngine() # 获取历史报告 reports = engine.get_historical_reports(limit=20) if reports.empty: st.info("📝 暂无历史报告") st.markdown(""" **提示**: - 运行智策分析后,报告将自动保存到历史记录中 - 您可以在此查看和管理所有历史分析报告 """) return st.success(f"📊 共找到 {len(reports)} 份历史报告") # 报告列表(精简摘要展示) for i, report in reports.iterrows(): report_id = report['id'] if 'id' in report else None created_at = report['created_at'] if 'created_at' in report else '' data_date_range = report['data_date_range'] if 'data_date_range' in report else '' summary = report['summary'] if 'summary' in report else '智策板块分析报告' confidence_score = report['confidence_score'] if 'confidence_score' in report else 0 risk_level = report['risk_level'] if 'risk_level' in report else '中等' market_outlook = report['market_outlook'] if 'market_outlook' in report else '谨慎乐观' with st.container(): st.markdown(f"**📊 报告 #{report_id}**") st.caption(f"生成时间: {created_at} | 数据区间: {data_date_range}") col1, col2, col3 = st.columns([1, 1, 1]) with col1: st.metric("置信度", f"{confidence_score:.1%}") with col2: st.metric("风险等级", risk_level) with col3: st.metric("市场展望", market_outlook) # 操作区:加载到分析视图 / 删除 op1, op2 = st.columns([1, 1]) with op1: if st.button("📥 加载到分析视图", key=f"load_{report_id}"): # 获取报告详情并写入session以展示到分析视图 detail = engine.get_report_detail(report_id) if detail and isinstance(detail.get('analysis_content_parsed'), dict): st.session_state.sector_strategy_result = detail['analysis_content_parsed'] st.session_state.sector_strategy_result_source = 'from_history' st.session_state.loaded_report_id = report_id st.success("✅ 已加载到分析视图,请切换到‘智策分析’标签查看") time.sleep(0.5) st.rerun() else: st.error("❌ 加载失败:报告内容缺失") with op2: if st.button(f"🗑️ 删除", key=f"delete_{report_id}"): if engine.delete_report(report_id): st.success("报告已删除") st.rerun() else: st.error("删除失败") # 改进的摘要展示逻辑,突出看多板块信息 st.markdown("**📝 报告摘要**") summary_text = summary or "智策板块分析报告" # 解析摘要中的看多板块信息 if "看多板块:" in summary_text: parts = summary_text.split(",看多板块:") main_summary = parts[0] bullish_info = parts[1] if len(parts) > 1 else "" # 显示主要摘要信息 st.markdown(f"🔹 {main_summary}") # 特别突出显示看多板块 if bullish_info: st.markdown(f"📈 **看多板块**: :green[{bullish_info}]") else: # 原有的简单展示方式 short = summary_text if len(summary_text) <= 120 else (summary_text[:120] + "...") with st.expander(f"{short}", expanded=False): st.write(summary_text) st.markdown("-") except Exception as e: st.error(f"❌ 加载历史报告失败: {e}") def display_report_detail(report_id): """详细报告页面已移除:保留占位以避免旧调用报错""" st.info("当前版本仅提供报告摘要,详细页面已移除。") def run_sector_strategy_analysis(model=None): """运行智策分析""" import config model = model or config.DEFAULT_MODEL_NAME # 进度显示 progress_bar = st.progress(0) status_text = st.empty() try: # 1. 获取数据 status_text.text("📊 正在获取市场数据...") progress_bar.progress(10) fetcher = SectorStrategyDataFetcher() # 使用带缓存回退的获取逻辑 data = fetcher.get_cached_data_with_fallback() 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) # 传递缓存元信息到结果以便页面提示 if data.get("from_cache") or data.get("cache_warning"): result["cache_meta"] = { "from_cache": bool(data.get("from_cache")), "cache_warning": data.get("cache_warning", ""), "data_timestamp": data.get("timestamp") } 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("📊 市场数据概览") # 缓存提示横幅 if data.get("from_cache") or data.get("cache_warning"): st.warning(data.get("cache_warning", "当前数据来自缓存,可能不是最新信息")) 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_saved_report_summary(saved_report: dict): """在主页面显示保存的报告摘要(标题、时间、关键指标)""" st.subheader("📝 报告摘要") summary = saved_report.get('summary', '智策板块分析报告') created_at = saved_report.get('created_at', '') data_date_range = saved_report.get('data_date_range', '') confidence_score = saved_report.get('confidence_score', 0) risk_level = saved_report.get('risk_level', '中等') market_outlook = saved_report.get('market_outlook', '谨慎乐观') st.caption(f"生成时间: {created_at} | 数据区间: {data_date_range}") # 使用改进的摘要展示逻辑,突出看多板块信息 summary_text = summary or "智策板块分析报告" # 解析摘要中的看多板块信息 if "看多板块:" in summary_text: parts = summary_text.split(",看多板块:") main_summary = parts[0] bullish_info = parts[1] if len(parts) > 1 else "" # 显示主要摘要信息 st.markdown(f"🔹 {main_summary}") # 特别突出显示看多板块 if bullish_info: st.markdown(f"📈 **看多板块**: :green[{bullish_info}]") else: # 原有的简单展示方式 st.info(summary_text) col1, col2, col3 = st.columns(3) with col1: st.metric("置信度", f"{confidence_score:.1%}") with col2: st.metric("风险等级", risk_level) with col3: st.metric("市场展望", market_outlook) def display_analysis_results(result): """显示分析结果""" st.success("✅ 智策分析完成!") st.info(f"📅 分析时间: {result.get('timestamp', 'N/A')}") # 显示缓存提示(如果本次分析使用了缓存数据) cache_meta = result.get("cache_meta") if cache_meta and (cache_meta.get("from_cache") or cache_meta.get("cache_warning")): st.warning(cache_meta.get("cache_warning", "当前分析基于缓存数据,可能不是最新信息")) # 如果内容源自历史报告,给出返回入口 if st.session_state.get('sector_strategy_result_source') == 'from_history': loaded_id = st.session_state.get('loaded_report_id') st.info(f"🗂️ 当前展示为历史报告内容(ID: {loaded_id})") if st.button("↩️ 返回历史报告列表"): # 清除已加载的历史报告并返回 for key in ['sector_strategy_result', 'sector_strategy_result_source', 'loaded_report_id']: if key in st.session_state: del st.session_state[key] st.rerun() # 显示引擎回传的保存报告摘要(用于主页面动态更新) saved_report = result.get("saved_report") if saved_report: display_saved_report_summary(saved_report) # 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, config={'responsive': 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, config={'responsive': True}, key="sector_heat") def display_pdf_export_section(result): """显示PDF导出部分""" st.subheader("📄 导出报告") col1, col2, col3, col4 = st.columns([2, 1, 1, 1]) with col1: st.write("将分析报告导出为PDF或Markdown文件,方便保存和分享") with col2: if st.button("📥 生成PDF报告", type="primary", width='content'): 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: if st.button("📝 生成Markdown", type="secondary", width='content'): with st.spinner("正在生成Markdown报告..."): try: # 生成Markdown内容 markdown_content = generate_sector_markdown_report(result) # 保存到session_state st.session_state.sector_markdown_data = markdown_content st.session_state.sector_markdown_filename = f"智策报告_{result.get('timestamp', datetime.now().strftime('%Y%m%d_%H%M%S')).replace(':', '').replace(' ', '_')}.md" st.success("✅ Markdown报告生成成功!") st.rerun() except Exception as e: st.error(f"❌ Markdown生成失败: {str(e)}") with col4: # 如果已经生成了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", width='content' ) # 如果已经生成了Markdown,显示下载按钮 if 'sector_markdown_data' in st.session_state: st.download_button( label="💾 下载Markdown", data=st.session_state.sector_markdown_data, file_name=st.session_state.sector_markdown_filename, mime="text/markdown", width='content' ) def generate_sector_markdown_report(result_data: dict) -> str: """生成智策分析Markdown报告""" # 获取当前时间 current_time = datetime.now().strftime("%Y年%m月%d日 %H:%M:%S") # 标题页 markdown_content = f"""# 智策板块策略分析报告 **AI驱动的多维度板块投资决策支持系统** --- ## 📊 报告信息 - **生成时间**: {current_time} - **分析周期**: 当日市场数据 - **AI模型**: DeepSeek Multi-Agent System - **分析维度**: 宏观·板块·资金·情绪 > ⚠️ 本报告由AI系统自动生成,仅供参考,不构成投资建议。投资有风险,决策需谨慎。 --- ## 📈 市场概况 本报告基于{result_data.get('timestamp', 'N/A')}的实时市场数据, 通过四位AI智能体的多维度分析,为您提供板块投资策略建议。 ### 分析师团队: - **宏观策略师** - 分析宏观经济、政策导向、新闻事件 - **板块诊断师** - 分析板块走势、估值水平、轮动特征 - **资金流向分析师** - 分析主力资金、北向资金流向 - **市场情绪解码员** - 分析市场情绪、热度、赚钱效应 """ # 核心预测 predictions = result_data.get('final_predictions', {}) if predictions.get('prediction_text'): # 文本格式预测 markdown_content += f""" ## 🎯 核心预测 {predictions.get('prediction_text', '')} """ else: # JSON格式预测 markdown_content += "## 🎯 核心预测\n\n" # 1. 板块多空预测 long_short = predictions.get('long_short', {}) bullish = long_short.get('bullish', []) bearish = long_short.get('bearish', []) markdown_content += "### 📊 板块多空预测\n\n" if bullish: markdown_content += "#### 🟢 看多板块\n\n" for idx, item in enumerate(bullish, 1): markdown_content += f"{idx}. **{item.get('sector', 'N/A')}** (信心度: {item.get('confidence', 0)}/10)\n" markdown_content += f" - 理由: {item.get('reason', 'N/A')}\n" markdown_content += f" - 风险: {item.get('risk', 'N/A')}\n\n" if bearish: markdown_content += "#### 🔴 看空板块\n\n" for idx, item in enumerate(bearish, 1): markdown_content += f"{idx}. **{item.get('sector', 'N/A')}** (信心度: {item.get('confidence', 0)}/10)\n" markdown_content += f" - 理由: {item.get('reason', 'N/A')}\n" markdown_content += f" - 风险: {item.get('risk', 'N/A')}\n\n" # 2. 板块轮动预测 rotation = predictions.get('rotation', {}) current_strong = rotation.get('current_strong', []) potential = rotation.get('potential', []) declining = rotation.get('declining', []) markdown_content += "### 🔄 板块轮动预测\n\n" if current_strong: markdown_content += "#### 💪 当前强势板块\n\n" for item in current_strong: markdown_content += f"- **{item.get('sector', 'N/A')}**\n" markdown_content += f" - 轮动逻辑: {item.get('logic', 'N/A')}\n" markdown_content += f" - 时间窗口: {item.get('time_window', 'N/A')}\n" markdown_content += f" - 操作建议: {item.get('advice', 'N/A')}\n\n" if potential: markdown_content += "#### 🌱 潜力接力板块\n\n" for item in potential: markdown_content += f"- **{item.get('sector', 'N/A')}**\n" markdown_content += f" - 轮动逻辑: {item.get('logic', 'N/A')}\n" markdown_content += f" - 时间窗口: {item.get('time_window', 'N/A')}\n" markdown_content += f" - 操作建议: {item.get('advice', 'N/A')}\n\n" if declining: markdown_content += "#### 📉 衰退板块\n\n" for item in declining: markdown_content += f"- **{item.get('sector', 'N/A')}**\n" markdown_content += f" - 轮动逻辑: {item.get('logic', 'N/A')}\n" markdown_content += f" - 时间窗口: {item.get('time_window', 'N/A')}\n" markdown_content += f" - 操作建议: {item.get('advice', 'N/A')}\n\n" # 3. 板块热度排行 heat = predictions.get('heat', {}) hottest = heat.get('hottest', []) heating = heat.get('heating', []) cooling = heat.get('cooling', []) markdown_content += "### 🔥 板块热度排行\n\n" if hottest: markdown_content += "#### 最热板块\n\n| 排名 | 板块 | 热度评分 | 趋势 | 持续性 |\n|------|------|----------|------|--------|\n" for idx, item in enumerate(hottest[:10], 1): markdown_content += f"| {idx} | {item.get('sector', 'N/A')} | {item.get('score', 0)} | {item.get('trend', 'N/A')} | {item.get('sustainability', 'N/A')} |\n" markdown_content += "\n" if heating: markdown_content += "#### 升温板块\n\n" for idx, item in enumerate(heating[:5], 1): markdown_content += f"{idx}. {item.get('sector', 'N/A')} (评分: {item.get('score', 0)})\n" markdown_content += "\n" if cooling: markdown_content += "#### 降温板块\n\n" for idx, item in enumerate(cooling[:5], 1): markdown_content += f"{idx}. {item.get('sector', 'N/A')} (评分: {item.get('score', 0)})\n" markdown_content += "\n" # 4. 策略总结 summary = predictions.get('summary', {}) if summary: markdown_content += "### 📝 策略总结\n\n" if summary.get('market_view'): markdown_content += f"**市场观点:** {summary.get('market_view', '')}\n\n" if summary.get('key_opportunity'): markdown_content += f"**核心机会:** {summary.get('key_opportunity', '')}\n\n" if summary.get('major_risk'): markdown_content += f"**主要风险:** {summary.get('major_risk', '')}\n\n" if summary.get('strategy'): markdown_content += f"**整体策略:** {summary.get('strategy', '')}\n\n" # AI智能体分析 agents_analysis = result_data.get('agents_analysis', {}) if agents_analysis: markdown_content += "## 🤖 AI智能体分析\n\n" for key, agent_data in agents_analysis.items(): agent_name = agent_data.get('agent_name', '未知分析师') agent_role = agent_data.get('agent_role', '') focus_areas = ', '.join(agent_data.get('focus_areas', [])) analysis = agent_data.get('analysis', '') markdown_content += f"### {agent_name}\n\n" markdown_content += f"- **职责**: {agent_role}\n" markdown_content += f"- **关注领域**: {focus_areas}\n\n" markdown_content += f"{analysis}\n\n" markdown_content += "---\n\n" # 综合研判 comprehensive_report = result_data.get('comprehensive_report', '') if comprehensive_report: markdown_content += "## 📊 综合研判\n\n" markdown_content += f"{comprehensive_report}\n\n" markdown_content += """ --- *报告由智策AI系统自动生成* """ return markdown_content 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("▶️ 启动", width='content', 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("⏹️ 停止", width='content'): if sector_strategy_scheduler.stop(): st.success("✅ 定时任务已停止") time.sleep(1) st.rerun() else: st.error("❌ 停止失败") with col_b: if st.button("🔄 立即运行", width='content'): with st.spinner("正在运行分析..."): sector_strategy_scheduler.manual_run() st.success("✅ 手动分析完成!") with col_c: if st.button("📧 测试邮件", width='content'): 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()