diff --git a/README.md b/README.md index 65c3c09..3eb5b11 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,16 @@ ## ✨ 更新说明 +### 最新更新:增加批量分析功能 ⭐️ +- **批量输入**:支持同时输入多个股票代码(每行一个或逗号分隔) +- **双模式分析**: + - 顺序分析:按次序逐个分析,稳定可靠 + - 多线程并行:同时分析多只股票,速度更快 +- **对比视图**:横向对比多只股票的关键指标和投资评级 +- **详细卡片**:逐个查看每只股票的完整分析报告 +- **智能筛选**:按评级、涨跌幅、信心度等条件快速筛选 +- **自动保存**:所有分析结果自动保存到历史记录 + ### 1006增加跟踪主力资金mcp,增加环境配置功能 ### 1005增加股票监测功能,增加历史记录中个股导入到监测板块 @@ -38,6 +48,19 @@ 5. 📋 最终投资决策 6. 🎯 操作建议和风险提示 7. 📄 PDF报告导出 + +### 🔥 批量分析功能(新增)⭐️ +- **批量输入支持**:一次性输入多只股票代码 +- **两种分析模式**: + - 顺序分析:稳定可靠,适合少量股票 + - 多线程并行:快速高效,适合大量股票(最多3个并发) +- **实时进度显示**:清晰展示每只股票的分析进度和状态 +- **对比分析表格**: + - 横向对比多只股票的关键指标 + - 投资评级可视化(买入/持有/卖出) + - 智能筛选和排序功能 +- **详细卡片视图**:逐个查看完整的分析报告 +- **批量保存**:所有分析结果自动保存到数据库 image image image @@ -111,7 +134,7 @@ Copy-Item .env.example .env cp .env.example .env ``` -2. 编辑 `.env` 文件,设置您的配置: +2. 编辑 `.env` 文件,设置您的配置(也可在前端web界面-环境配置中设置): ```env # DeepSeek API配置(必需) DEEPSEEK_API_KEY=your_actual_deepseek_api_key_here @@ -157,13 +180,71 @@ streamlit run app.py - **美股**:AAPL, MSFT, GOOGL, TSLA, NVDA - **A股**:000001, 600036, 000002, 600519 -### 分析流程 -1. 在输入框中输入股票代码 -2. 点击"开始分析"按钮 -3. 等待AI分析师团队完成分析 -4. 查看各维度分析报告 -5. 阅读团队讨论结果 -6. 获取最终投资决策 +### 单个股票分析流程 +1. 选择"单个分析"模式 +2. 在输入框中输入股票代码 +3. 选择要参与分析的分析师团队 +4. 点击"开始分析"按钮 +5. 等待AI分析师团队完成分析 +6. 查看各维度分析报告 +7. 阅读团队讨论结果 +8. 获取最终投资决策 + +### 批量股票分析流程 ⭐️ 新增 +1. **选择批量分析模式** + - 点击"批量分析"单选按钮 + - 选择分析模式:顺序分析 或 多线程并行 + +2. **输入股票代码** + - 支持多种格式: + ``` + # 每行一个代码 + 000001 + 600036 + 600519 + + # 或逗号分隔 + 000001, 600036, 600519 + + # 或空格分隔 + 000001 600036 600519 + ``` + +3. **配置分析参数** + - 选择数据周期(1y, 6mo, 3mo, 1mo) + - 选择要参与的分析师团队 + +4. **开始批量分析** + - 点击"开始批量分析"按钮 + - 系统会实时显示分析进度 + - 每只股票的分析状态都会更新 + +5. **查看分析结果** + - **对比表格模式**: + - 横向对比所有股票的关键指标 + - 投资评级用颜色标识(绿色=买入,黄色=持有,红色=卖出) + - 可按评级筛选 + - 可按涨跌幅、信心度、RSI等排序 + + - **详细卡片模式**: + - 从下拉列表选择要查看的股票 + - 查看完整的分析报告 + - 包含图表、各分析师意见、团队讨论、最终决策 + +6. **结果管理** + - 所有分析结果自动保存到历史记录 + - 点击"清除结果"可清空当前批量分析结果 + - 失败的股票会单独列出,显示失败原因 + +### 批量分析使用技巧 +- **建议数量**:一次分析不超过20只股票 +- **模式选择**: + - 3-5只股票:建议使用顺序分析,更稳定 + - 6只以上:建议使用多线程并行,更快速 +- **注意事项**: + - 多线程模式最多3个并发,避免API限流 + - 批量分析时间较长,请耐心等待 + - 可在历史记录中查看所有分析结果 ### 结果解读 - **投资评级**:买入/持有/卖出 diff --git a/app.py b/app.py index d420de5..497fbd6 100644 --- a/app.py +++ b/app.py @@ -399,22 +399,68 @@ def main(): return # 主界面 - col1, col2, col3 = st.columns([2, 1, 1]) - - with col1: - stock_input = st.text_input( - "🔍 请输入股票代码或名称", - placeholder="例如: AAPL, 000001, 600036", - help="支持美股代码(如AAPL)和A股代码(如000001)" + # 添加单个/批量分析切换 + col_mode1, col_mode2 = st.columns([1, 3]) + with col_mode1: + analysis_mode = st.radio( + "分析模式", + ["单个分析", "批量分析"], + horizontal=True, + help="单个分析:分析单只股票;批量分析:同时分析多只股票" ) - with col2: - analyze_button = st.button("🚀 开始分析", type="primary", use_container_width=True) + with col_mode2: + if analysis_mode == "批量分析": + batch_mode = st.radio( + "批量模式", + ["顺序分析", "多线程并行"], + horizontal=True, + help="顺序分析:按次序分析,稳定但较慢;多线程并行:同时分析多只,快速但消耗资源" + ) + st.session_state.batch_mode = batch_mode - with col3: - if st.button("🔄 清除缓存", use_container_width=True): - st.cache_data.clear() - st.success("缓存已清除") + st.markdown("---") + + if analysis_mode == "单个分析": + # 单个股票分析界面 + col1, col2, col3 = st.columns([2, 1, 1]) + + with col1: + stock_input = st.text_input( + "🔍 请输入股票代码或名称", + placeholder="例如: AAPL, 000001, 600036", + help="支持美股代码(如AAPL)和A股代码(如000001)" + ) + + with col2: + analyze_button = st.button("🚀 开始分析", type="primary", use_container_width=True) + + with col3: + if st.button("🔄 清除缓存", use_container_width=True): + st.cache_data.clear() + st.success("缓存已清除") + + else: + # 批量股票分析界面 + stock_input = st.text_area( + "🔍 请输入多个股票代码(每行一个或用逗号分隔)", + placeholder="例如:\n000001\n600036\n600519\n\n或者: 000001, 600036, 600519", + height=120, + help="支持多种格式:每行一个代码或用逗号分隔" + ) + + col1, col2, col3 = st.columns(3) + with col1: + analyze_button = st.button("🚀 开始批量分析", type="primary", use_container_width=True) + with col2: + if st.button("🔄 清除缓存", use_container_width=True): + st.cache_data.clear() + st.success("缓存已清除") + with col3: + if st.button("🗑️ 清除结果", use_container_width=True): + if 'batch_analysis_results' in st.session_state: + del st.session_state.batch_analysis_results + st.success("已清除批量分析结果") # 分析师团队选择 st.markdown("---") @@ -480,21 +526,47 @@ def main(): st.error("❌ 请至少选择一位分析师参与分析") return - # 清除之前的分析结果 - if 'analysis_completed' in st.session_state: - del st.session_state.analysis_completed - if 'stock_info' in st.session_state: - del st.session_state.stock_info - if 'agents_results' in st.session_state: - del st.session_state.agents_results - if 'discussion_result' in st.session_state: - del st.session_state.discussion_result - if 'final_decision' in st.session_state: - del st.session_state.final_decision + if analysis_mode == "单个分析": + # 单个股票分析 + # 清除之前的分析结果 + if 'analysis_completed' in st.session_state: + del st.session_state.analysis_completed + if 'stock_info' in st.session_state: + del st.session_state.stock_info + if 'agents_results' in st.session_state: + del st.session_state.agents_results + if 'discussion_result' in st.session_state: + del st.session_state.discussion_result + if 'final_decision' in st.session_state: + del st.session_state.final_decision + + run_stock_analysis(stock_input, period) + + else: + # 批量股票分析 + # 解析股票代码列表 + stock_list = parse_stock_list(stock_input) - run_stock_analysis(stock_input, period) + if not stock_list: + st.error("❌ 请输入有效的股票代码") + return + + if len(stock_list) > 20: + st.warning(f"⚠️ 检测到 {len(stock_list)} 只股票,建议一次分析不超过20只") + + st.info(f"📊 准备分析 {len(stock_list)} 只股票: {', '.join(stock_list)}") + + # 清除之前的批量分析结果 + if 'batch_analysis_results' in st.session_state: + del st.session_state.batch_analysis_results + + # 获取批量模式 + batch_mode = st.session_state.get('batch_mode', '顺序分析') + + # 运行批量分析 + run_batch_analysis(stock_list, period, batch_mode) - # 检查是否有已完成的分析结果 + # 检查是否有已完成的单个分析结果 if 'analysis_completed' in st.session_state and st.session_state.analysis_completed: # 重新显示分析结果 stock_info = st.session_state.stock_info @@ -521,6 +593,10 @@ def main(): # 显示最终决策 display_final_decision(final_decision, stock_info, agents_results, discussion_result) + # 检查是否有已完成的批量分析结果 + elif 'batch_analysis_results' in st.session_state and st.session_state.batch_analysis_results: + display_batch_analysis_results(st.session_state.batch_analysis_results, period) + # 示例和说明 elif not stock_input: show_example_interface() @@ -548,6 +624,287 @@ def get_stock_data(symbol, period): return stock_info, stock_data_with_indicators, indicators +def parse_stock_list(stock_input): + """解析股票代码列表 + + 支持的格式: + - 每行一个代码 + - 逗号分隔 + - 空格分隔 + """ + if not stock_input or not stock_input.strip(): + return [] + + # 先按换行符分割 + lines = stock_input.strip().split('\n') + + # 处理每一行 + stock_list = [] + for line in lines: + line = line.strip() + if not line: + continue + + # 检查是否包含逗号 + if ',' in line: + codes = [code.strip() for code in line.split(',')] + stock_list.extend([code for code in codes if code]) + # 检查是否包含空格 + elif ' ' in line: + codes = [code.strip() for code in line.split()] + stock_list.extend([code for code in codes if code]) + else: + stock_list.append(line) + + # 去重并保持顺序 + seen = set() + unique_list = [] + for code in stock_list: + if code not in seen: + seen.add(code) + unique_list.append(code) + + return unique_list + +def analyze_single_stock_for_batch(symbol, period, enabled_analysts_config=None, selected_model='deepseek-chat'): + """单个股票分析(用于批量分析) + + Args: + symbol: 股票代码 + period: 数据周期 + enabled_analysts_config: 分析师配置字典 + selected_model: 选择的AI模型 + + 返回分析结果或错误信息 + """ + try: + # 使用默认配置 + if enabled_analysts_config is None: + enabled_analysts_config = { + 'technical': True, + 'fundamental': True, + 'fund_flow': True, + 'risk': True, + 'sentiment': False, + 'news': False + } + + # 1. 获取股票数据 + stock_info, stock_data, indicators = get_stock_data(symbol, period) + + if "error" in stock_info: + return {"symbol": symbol, "error": stock_info['error'], "success": False} + + if stock_data is None: + return {"symbol": symbol, "error": "无法获取股票历史数据", "success": False} + + # 2. 获取财务数据 + fetcher = StockDataFetcher() + financial_data = fetcher.get_financial_data(symbol) + + # 获取分析师选择状态(从参数而不是session_state) + enable_fund_flow = enabled_analysts_config.get('fund_flow', True) + enable_sentiment = enabled_analysts_config.get('sentiment', False) + enable_news = enabled_analysts_config.get('news', False) + + # 3. 获取资金流向数据(可选) + fund_flow_data = None + if enable_fund_flow and fetcher._is_chinese_stock(symbol): + try: + fund_flow_data = fetcher.get_fund_flow_data(symbol) + except: + pass + + # 4. 获取市场情绪数据(可选) + sentiment_data = None + if enable_sentiment and fetcher._is_chinese_stock(symbol): + try: + from market_sentiment_data import MarketSentimentDataFetcher + sentiment_fetcher = MarketSentimentDataFetcher() + sentiment_data = sentiment_fetcher.get_market_sentiment_data(symbol, stock_data) + except: + pass + + # 5. 获取新闻公告数据(可选) + news_announcement_data = None + if enable_news and fetcher._is_chinese_stock(symbol): + try: + from news_announcement_data import NewsAnnouncementDataFetcher + news_fetcher = NewsAnnouncementDataFetcher() + news_announcement_data = news_fetcher.get_news_and_announcements(symbol) + except: + pass + + # 6. 初始化AI分析系统 + agents = StockAnalysisAgents(model=selected_model) + + # 使用传入的分析师配置 + enabled_analysts = enabled_analysts_config + + # 7. 运行多智能体分析 + agents_results = agents.run_multi_agent_analysis( + stock_info, stock_data, indicators, financial_data, + fund_flow_data, sentiment_data, news_announcement_data, + enabled_analysts=enabled_analysts + ) + + # 8. 团队讨论 + discussion_result = agents.conduct_team_discussion(agents_results, stock_info) + + # 9. 最终决策 + final_decision = agents.make_final_decision(discussion_result, stock_info, indicators) + + # 保存到数据库 + try: + db.save_analysis( + symbol=stock_info.get('symbol', ''), + stock_name=stock_info.get('name', ''), + period=period, + stock_info=stock_info, + agents_results=agents_results, + discussion_result=discussion_result, + final_decision=final_decision + ) + except Exception as e: + print(f"保存到数据库时出现错误: {str(e)}") + + return { + "symbol": symbol, + "success": True, + "stock_info": stock_info, + "indicators": indicators, + "agents_results": agents_results, + "discussion_result": discussion_result, + "final_decision": final_decision + } + + except Exception as e: + return {"symbol": symbol, "error": str(e), "success": False} + +def run_batch_analysis(stock_list, period, batch_mode="顺序分析"): + """运行批量股票分析""" + import concurrent.futures + import threading + + # 在开始分析前获取配置(从session_state) + enabled_analysts_config = { + 'technical': st.session_state.get('enable_technical', True), + 'fundamental': st.session_state.get('enable_fundamental', True), + 'fund_flow': st.session_state.get('enable_fund_flow', True), + 'risk': st.session_state.get('enable_risk', True), + 'sentiment': st.session_state.get('enable_sentiment', False), + 'news': st.session_state.get('enable_news', False) + } + selected_model = st.session_state.get('selected_model', 'deepseek-chat') + + # 创建进度显示 + st.subheader(f"📊 批量分析进行中 ({batch_mode})") + + progress_bar = st.progress(0) + status_text = st.empty() + + # 存储结果 + results = [] + total = len(stock_list) + + if batch_mode == "多线程并行": + # 多线程并行分析 + status_text.text(f"🚀 使用多线程并行分析 {total} 只股票...") + + # 创建线程锁用于更新进度 + lock = threading.Lock() + completed = [0] # 使用列表以便在闭包中修改 + progress_status = [{}] # 存储进度状态 + + def analyze_with_progress(symbol): + """包装分析函数,不在线程中访问Streamlit上下文""" + try: + result = analyze_single_stock_for_batch(symbol, period, enabled_analysts_config, selected_model) + with lock: + completed[0] += 1 + progress_status[0][symbol] = result + return result + except Exception as e: + with lock: + completed[0] += 1 + error_result = {"symbol": symbol, "error": str(e), "success": False} + progress_status[0][symbol] = error_result + return error_result + + # 使用线程池执行,限制最大并发数为3以避免API限流 + with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor: + future_to_symbol = {executor.submit(analyze_with_progress, symbol): symbol + for symbol in stock_list} + + for future in concurrent.futures.as_completed(future_to_symbol): + symbol = future_to_symbol[future] + try: + result = future.result(timeout=300) # 5分钟超时 + results.append(result) + + # 在主线程中更新UI + progress = len(results) / total + progress_bar.progress(progress) + + if result['success']: + status_text.text(f"✅ [{len(results)}/{total}] {symbol} 分析完成") + else: + status_text.text(f"❌ [{len(results)}/{total}] {symbol} 分析失败: {result.get('error', '未知错误')}") + + except concurrent.futures.TimeoutError: + results.append({"symbol": symbol, "error": "分析超时(5分钟)", "success": False}) + progress_bar.progress(len(results) / total) + status_text.text(f"⏱️ [{len(results)}/{total}] {symbol} 分析超时") + except Exception as e: + results.append({"symbol": symbol, "error": str(e), "success": False}) + progress_bar.progress(len(results) / total) + status_text.text(f"❌ [{len(results)}/{total}] {symbol} 出现错误") + + else: + # 顺序分析 + status_text.text(f"📝 按顺序分析 {total} 只股票...") + + for i, symbol in enumerate(stock_list, 1): + status_text.text(f"🔍 [{i}/{total}] 正在分析 {symbol}...") + + try: + result = analyze_single_stock_for_batch(symbol, period, enabled_analysts_config, selected_model) + except Exception as e: + result = {"symbol": symbol, "error": str(e), "success": False} + + results.append(result) + + # 更新进度 + progress = i / total + progress_bar.progress(progress) + + if result['success']: + status_text.text(f"✅ [{i}/{total}] {symbol} 分析完成") + else: + status_text.text(f"❌ [{i}/{total}] {symbol} 分析失败: {result.get('error', '未知错误')}") + + # 完成 + progress_bar.progress(1.0) + + # 统计结果 + success_count = sum(1 for r in results if r['success']) + failed_count = total - success_count + + if success_count > 0: + status_text.success(f"✅ 批量分析完成!成功 {success_count} 只,失败 {failed_count} 只") + else: + status_text.error(f"❌ 批量分析完成,但所有股票都分析失败") + + # 保存结果到session_state + st.session_state.batch_analysis_results = results + st.session_state.batch_analysis_mode = batch_mode + + time.sleep(2) + progress_bar.empty() + + # 自动显示结果 + st.rerun() + def run_stock_analysis(symbol, period): """运行股票分析""" @@ -1682,5 +2039,190 @@ MINIQMT_HOST="{current_config.get('MINIQMT_HOST', '127.0.0.1')}" MINIQMT_PORT="{current_config.get('MINIQMT_PORT', '58610')}" """, language="bash") +def display_batch_analysis_results(results, period): + """显示批量分析结果(对比视图)""" + + st.subheader("📊 批量分析结果对比") + + # 统计信息 + total = len(results) + success_results = [r for r in results if r['success']] + failed_results = [r for r in results if not r['success']] + + # 显示统计 + col1, col2, col3 = st.columns(3) + with col1: + st.metric("总数", total) + with col2: + st.metric("成功", len(success_results), delta=None, delta_color="normal") + with col3: + st.metric("失败", len(failed_results), delta=None, delta_color="inverse") + + st.markdown("---") + + # 失败的股票列表 + if failed_results: + with st.expander(f"❌ 查看失败的 {len(failed_results)} 只股票", expanded=False): + for result in failed_results: + st.error(f"**{result['symbol']}**: {result.get('error', '未知错误')}") + + # 成功的股票分析结果 + if not success_results: + st.warning("⚠️ 没有成功分析的股票") + return + + # 创建对比视图选项 + view_mode = st.radio( + "显示模式", + ["对比表格", "详细卡片"], + horizontal=True, + help="对比表格:横向对比多只股票;详细卡片:逐个查看详细分析" + ) + + if view_mode == "对比表格": + # 表格对比视图 + display_comparison_table(success_results) + else: + # 详细卡片视图 + display_detailed_cards(success_results, period) + +def display_comparison_table(results): + """显示对比表格""" + import pandas as pd + + st.subheader("📋 股票对比表格") + + # 构建对比数据 + comparison_data = [] + for result in results: + stock_info = result['stock_info'] + indicators = result.get('indicators', {}) + final_decision = result['final_decision'] + + # 解析评级 + if isinstance(final_decision, dict): + rating = final_decision.get('rating', 'N/A') + confidence = final_decision.get('confidence_level', 'N/A') + target_price = final_decision.get('target_price', 'N/A') + else: + rating = 'N/A' + confidence = 'N/A' + target_price = 'N/A' + + row = { + '股票代码': stock_info.get('symbol', 'N/A'), + '股票名称': stock_info.get('name', 'N/A'), + '当前价格': stock_info.get('current_price', 'N/A'), + '涨跌幅(%)': stock_info.get('change_percent', 'N/A'), + '市盈率': stock_info.get('pe_ratio', 'N/A'), + '市净率': stock_info.get('pb_ratio', 'N/A'), + 'RSI': indicators.get('rsi', 'N/A'), + 'MACD': indicators.get('macd', 'N/A'), + '投资评级': rating, + '信心度': confidence, + '目标价格': target_price + } + comparison_data.append(row) + + # 创建DataFrame + df = pd.DataFrame(comparison_data) + + # 应用样式 + def highlight_rating(val): + if val == '买入' or val == '强烈买入': + return 'background-color: #c8e6c9; color: #2e7d32;' + elif val == '持有': + return 'background-color: #fff9c4; color: #f57f17;' + elif val == '卖出' or val == '强烈卖出': + return 'background-color: #ffcdd2; color: #c62828;' + return '' + + # 显示表格 + st.dataframe( + df.style.applymap(highlight_rating, subset=['投资评级']), + use_container_width=True, + height=400 + ) + + # 添加筛选功能 + st.markdown("---") + st.subheader("🔍 快速筛选") + + col1, col2 = st.columns(2) + with col1: + rating_filter = st.multiselect( + "按评级筛选", + options=df['投资评级'].unique().tolist(), + default=df['投资评级'].unique().tolist() + ) + + with col2: + # 按涨跌幅排序 + sort_by = st.selectbox( + "排序方式", + ["默认", "涨跌幅降序", "涨跌幅升序", "信心度降序", "RSI降序"] + ) + + # 应用筛选 + filtered_df = df[df['投资评级'].isin(rating_filter)] + + # 应用排序 + if sort_by == "涨跌幅降序": + filtered_df = filtered_df.sort_values('涨跌幅(%)', ascending=False) + elif sort_by == "涨跌幅升序": + filtered_df = filtered_df.sort_values('涨跌幅(%)', ascending=True) + elif sort_by == "信心度降序": + filtered_df = filtered_df.sort_values('信心度', ascending=False) + elif sort_by == "RSI降序": + filtered_df = filtered_df.sort_values('RSI', ascending=False) + + if not filtered_df.empty: + st.dataframe(filtered_df, use_container_width=True) + else: + st.info("没有符合条件的股票") + +def display_detailed_cards(results, period): + """显示详细卡片视图""" + + st.subheader("📇 详细分析卡片") + + # 选择要查看的股票 + stock_options = [f"{r['stock_info']['symbol']} - {r['stock_info']['name']}" for r in results] + selected_stock = st.selectbox("选择股票", options=stock_options) + + # 找到对应的结果 + selected_index = stock_options.index(selected_stock) + result = results[selected_index] + + # 显示详细分析 + stock_info = result['stock_info'] + indicators = result['indicators'] + agents_results = result['agents_results'] + discussion_result = result['discussion_result'] + final_decision = result['final_decision'] + + # 获取股票数据用于显示图表 + try: + stock_info_current, stock_data, _ = get_stock_data(stock_info['symbol'], period) + + # 显示股票基本信息 + display_stock_info(stock_info, indicators) + + # 显示股票图表 + if stock_data is not None: + display_stock_chart(stock_data, stock_info) + + # 显示各分析师报告 + display_agents_analysis(agents_results) + + # 显示团队讨论 + display_team_discussion(discussion_result) + + # 显示最终决策 + display_final_decision(final_decision, stock_info, agents_results, discussion_result) + + except Exception as e: + st.error(f"显示详细信息时出错: {str(e)}") + if __name__ == "__main__": main() \ No newline at end of file diff --git a/批量分析功能说明.md b/批量分析功能说明.md new file mode 100644 index 0000000..e4c2f71 --- /dev/null +++ b/批量分析功能说明.md @@ -0,0 +1,290 @@ +# 📊 批量分析功能使用指南 + +## 功能概述 + +批量分析功能允许您一次性分析多只股票,并提供横向对比视图,快速筛选出最有投资价值的股票。 + +## ✨ 核心特性 + +### 1. 灵活的输入方式 +支持多种股票代码输入格式: +- **每行一个代码** +- **逗号分隔** +- **空格分隔** +- **混合格式** + +### 2. 双模式分析 +- **顺序分析**:按次序逐个分析,稳定可靠 + - 适用场景:3-5只股票,网络不稳定时 + - 优点:可靠性高,不会因并发导致问题 + - 缺点:速度较慢 + +- **多线程并行**:同时分析多只股票 + - 适用场景:6只以上股票,网络稳定时 + - 优点:速度快,效率高 + - 缺点:资源消耗较大 + - 限制:最多3个并发(避免API限流) + +### 3. 实时进度追踪 +- 显示当前分析进度(X/总数) +- 实时更新每只股票的状态 +- ✅ 成功标记 +- ❌ 失败标记及原因 + +### 4. 智能结果展示 + +#### 对比表格模式 +横向对比所有股票的关键指标: +- **基本信息**:代码、名称、价格、涨跌幅 +- **估值指标**:市盈率、市净率 +- **技术指标**:RSI、MACD +- **投资建议**:评级、信心度、目标价格 + +**颜色编码**: +- 🟢 绿色:买入/强烈买入 +- 🟡 黄色:持有 +- 🔴 红色:卖出/强烈卖出 + +**筛选和排序**: +- 按投资评级筛选 +- 按涨跌幅排序 +- 按信心度排序 +- 按RSI排序 + +#### 详细卡片模式 +逐个查看每只股票的完整分析: +- 基本信息和技术指标 +- K线图和成交量图 +- 各分析师详细报告 +- 团队综合讨论 +- 最终投资决策 +- 操作建议和风险提示 + +## 📖 使用步骤 + +### Step 1: 选择批量分析模式 +1. 在主界面顶部选择"批量分析" +2. 选择分析模式: + - "顺序分析":稳定但较慢 + - "多线程并行":快速但需要稳定网络 + +### Step 2: 输入股票代码 +在文本框中输入股票代码,支持以下格式: + +``` +# 格式1:每行一个(推荐) +000001 +600036 +600519 +000858 + +# 格式2:逗号分隔 +000001, 600036, 600519, 000858 + +# 格式3:空格分隔 +000001 600036 600519 000858 + +# 格式4:混合使用 +000001, 600036 +600519 000858 +``` + +### Step 3: 配置分析参数 +1. **选择数据周期**: + - 1y(一年)- 推荐,数据更完整 + - 6mo(半年) + - 3mo(三个月) + - 1mo(一个月) + +2. **选择分析师团队**: + - ✅ 技术分析师(推荐) + - ✅ 基本面分析师(推荐) + - ✅ 资金面分析师(推荐) + - ✅ 风险管理师(推荐) + - ⬜ 市场情绪分析师(可选,仅A股) + - ⬜ 新闻公告分析师(可选,仅A股) + +### Step 4: 开始分析 +1. 点击"🚀 开始批量分析"按钮 +2. 系统会显示实时进度: + ``` + 📊 批量分析进行中 (多线程并行) + ✅ [1/5] 000001 分析完成 + 🔍 [2/5] 正在分析 600036... + ``` +3. 等待所有股票分析完成 + +### Step 5: 查看结果 + +#### 对比表格视图 +1. 自动显示对比表格 +2. 查看所有股票的关键指标 +3. 使用筛选功能: + - 按评级筛选(只看"买入"的股票) + - 按涨跌幅排序(找涨幅最大的) + - 按信心度排序(找最有把握的) + +#### 详细卡片视图 +1. 切换到"详细卡片"模式 +2. 从下拉列表选择要查看的股票 +3. 查看完整的分析报告 +4. 查看K线图和技术指标 +5. 阅读各分析师的详细意见 + +## 💡 使用技巧 + +### 最佳实践 + +1. **选股策略** + ``` + 第一步:输入感兴趣的板块所有股票(10-20只) + 第二步:使用批量分析快速筛选 + 第三步:在对比表格中按评级筛选 + 第四步:在详细卡片中深入研究候选股票 + ``` + +2. **分析数量建议** + - **3-5只**:使用顺序分析 + - **6-10只**:使用多线程并行 + - **11-20只**:使用多线程并行,但需要耐心等待 + - **20只以上**:分批次分析 + +3. **时间估算** + - 单只股票分析:约1-2分钟 + - 顺序分析5只:约5-10分钟 + - 多线程分析10只:约4-7分钟(3个并发) + +4. **结果筛选技巧** + - **保守型投资者**:筛选"买入"评级且信心度≥7 + - **激进型投资者**:按涨跌幅降序,关注强势股 + - **价值投资者**:关注低市盈率+高ROE的股票 + - **技术派**:按RSI筛选超卖或超买状态 + +### 注意事项 + +⚠️ **重要提示**: +1. 批量分析时间较长,请耐心等待 +2. 多线程模式会消耗更多API调用额度 +3. 建议在非交易时段进行批量分析 +4. 分析过程中不要关闭浏览器 +5. 所有结果会自动保存到历史记录 + +⚠️ **故障处理**: +- 如果某只股票分析失败,不会影响其他股票 +- 失败的股票会单独列出,显示失败原因 +- 可以在历史记录中单独重新分析失败的股票 + +## 📈 应用场景 + +### 场景1:板块轮动分析 +``` +1. 识别热门板块(如新能源、半导体) +2. 批量输入该板块10-15只股票 +3. 使用对比表格筛选出评级最高的3-5只 +4. 深入研究这些候选股票 +``` + +### 场景2:持仓股票全面体检 +``` +1. 输入所有持仓股票代码 +2. 批量分析获取最新评级 +3. 识别需要止损的股票(评级"卖出") +4. 识别可以加仓的股票(评级"买入") +``` + +### 场景3:每日选股 +``` +1. 选择10-20只活跃度高的股票 +2. 批量分析获取当日评级 +3. 筛选出"买入"评级的股票 +4. 结合个人风险偏好做出决策 +``` + +### 场景4:龙头股对比 +``` +1. 输入同行业的龙头公司(如茅台、五粮液、泸州老窖) +2. 批量分析进行横向对比 +3. 对比关键指标:市盈率、ROE、涨跌幅 +4. 选择综合表现最佳的股票 +``` + +## 🎯 结果解读 + +### 投资评级 +- **强烈买入**:多位分析师一致看好,建议重点关注 +- **买入**:综合评价积极,可以考虑买入 +- **持有**:观望为主,已持有可继续持有 +- **卖出**:风险较大,建议卖出或不买入 +- **强烈卖出**:多位分析师一致看空,建议立即卖出 + +### 信心度(1-10分) +- **8-10分**:高度确定,分析师共识强 +- **6-7分**:较为确定,可以参考 +- **4-5分**:不确定,需谨慎对待 +- **1-3分**:非常不确定,建议忽略 + +### 技术指标参考 +- **RSI > 70**:超买,可能回调 +- **RSI < 30**:超卖,可能反弹 +- **MACD > 0**:上涨趋势 +- **MACD < 0**:下跌趋势 + +## 📊 示例演示 + +### 示例:分析白酒板块 +```python +输入股票代码: +600519 # 贵州茅台 +000858 # 五粮液 +000568 # 泸州老窖 +002304 # 洋河股份 +603369 # 今世缘 + +选择:多线程并行 + +结果对比表格: +| 股票代码 | 股票名称 | 当前价格 | 涨跌幅 | 市盈率 | 投资评级 | 信心度 | +|---------|---------|---------|-------|-------|---------|-------| +| 600519 | 贵州茅台 | 1688.00 | 2.5% | 38.5 | 买入 | 8 | +| 000858 | 五粮液 | 168.50 | 1.8% | 28.3 | 买入 | 7 | +| 000568 | 泸州老窖 | 188.20 | -0.5% | 32.1 | 持有 | 6 | +| 002304 | 洋河股份 | 128.60 | 0.3% | 24.7 | 买入 | 7 | +| 603369 | 今世缘 | 38.50 | -1.2% | 21.3 | 持有 | 5 | + +筛选结果: +按"买入"评级筛选 → 3只股票(茅台、五粮液、洋河) +按信心度降序 → 茅台(8) > 五粮液(7) = 洋河(7) +``` + +## 🔧 技术细节 + +### 多线程实现 +- 使用Python `concurrent.futures.ThreadPoolExecutor` +- 最大并发数:3(避免API限流) +- 线程安全:使用锁保护进度更新 +- 异常处理:单个股票失败不影响其他 + +### 数据保存 +- 每只股票分析完成后立即保存到数据库 +- 保存内容包括: + - 股票基本信息 + - 技术指标 + - 各分析师报告 + - 团队讨论 + - 最终决策 + +### 性能优化 +- 使用Streamlit缓存机制 +- 股票数据缓存5分钟 +- 避免重复获取相同数据 + +## 📞 问题反馈 + +如遇到问题或有改进建议,请联系: +- 邮箱:ws3101001@126.com +- 或在项目中提交Issue + +--- + +**免责声明**:本系统提供的分析结果仅供参考,不构成投资建议。股市有风险,投资需谨慎。 +