增加批量分析功能

This commit is contained in:
oficcejo
2025-10-08 13:03:15 +08:00
parent a03f2c0071
commit ea2c470483
3 changed files with 947 additions and 34 deletions
+89 -8
View File
@@ -11,6 +11,16 @@
## ✨ 更新说明 ## ✨ 更新说明
### 最新更新:增加批量分析功能 ⭐️
- **批量输入**:支持同时输入多个股票代码(每行一个或逗号分隔)
- **双模式分析**
- 顺序分析:按次序逐个分析,稳定可靠
- 多线程并行:同时分析多只股票,速度更快
- **对比视图**:横向对比多只股票的关键指标和投资评级
- **详细卡片**:逐个查看每只股票的完整分析报告
- **智能筛选**:按评级、涨跌幅、信心度等条件快速筛选
- **自动保存**:所有分析结果自动保存到历史记录
### 1006增加跟踪主力资金mcp,增加环境配置功能 ### 1006增加跟踪主力资金mcp,增加环境配置功能
### 1005增加股票监测功能,增加历史记录中个股导入到监测板块 ### 1005增加股票监测功能,增加历史记录中个股导入到监测板块
@@ -38,6 +48,19 @@
5. 📋 最终投资决策 5. 📋 最终投资决策
6. 🎯 操作建议和风险提示 6. 🎯 操作建议和风险提示
7. 📄 PDF报告导出 7. 📄 PDF报告导出
### 🔥 批量分析功能(新增)⭐️
- **批量输入支持**:一次性输入多只股票代码
- **两种分析模式**
- 顺序分析:稳定可靠,适合少量股票
- 多线程并行:快速高效,适合大量股票(最多3个并发)
- **实时进度显示**:清晰展示每只股票的分析进度和状态
- **对比分析表格**
- 横向对比多只股票的关键指标
- 投资评级可视化(买入/持有/卖出)
- 智能筛选和排序功能
- **详细卡片视图**:逐个查看完整的分析报告
- **批量保存**:所有分析结果自动保存到数据库
<img width="1910" height="923" alt="image" src="https://github.com/user-attachments/assets/836d758f-df6d-44a1-b64a-fd6ad442cb0f" /> <img width="1910" height="923" alt="image" src="https://github.com/user-attachments/assets/836d758f-df6d-44a1-b64a-fd6ad442cb0f" />
<img width="1910" height="923" alt="image" src="https://github.com/user-attachments/assets/903a64c7-7018-44dd-aa87-af4f7904048c" /> <img width="1910" height="923" alt="image" src="https://github.com/user-attachments/assets/903a64c7-7018-44dd-aa87-af4f7904048c" />
<img width="1910" height="923" alt="image" src="https://github.com/user-attachments/assets/d9878153-d743-4d65-9575-62ac37f8cbfb" /> <img width="1910" height="923" alt="image" src="https://github.com/user-attachments/assets/d9878153-d743-4d65-9575-62ac37f8cbfb" />
@@ -111,7 +134,7 @@ Copy-Item .env.example .env
cp .env.example .env cp .env.example .env
``` ```
2. 编辑 `.env` 文件,设置您的配置: 2. 编辑 `.env` 文件,设置您的配置(也可在前端web界面-环境配置中设置)
```env ```env
# DeepSeek API配置(必需) # DeepSeek API配置(必需)
DEEPSEEK_API_KEY=your_actual_deepseek_api_key_here DEEPSEEK_API_KEY=your_actual_deepseek_api_key_here
@@ -157,13 +180,71 @@ streamlit run app.py
- **美股**AAPL, MSFT, GOOGL, TSLA, NVDA - **美股**AAPL, MSFT, GOOGL, TSLA, NVDA
- **A股**000001, 600036, 000002, 600519 - **A股**000001, 600036, 000002, 600519
### 分析流程 ### 单个股票分析流程
1. 在输入框中输入股票代码 1. 选择"单个分析"模式
2. 点击"开始分析"按钮 2. 在输入框中输入股票代码
3. 等待AI分析师团队完成分析 3. 选择要参与分析的分析师团队
4. 查看各维度分析报告 4. 点击"开始分析"按钮
5. 阅读团队讨论结果 5. 等待AI分析师团队完成分析
6. 获取最终投资决策 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限流
- 批量分析时间较长,请耐心等待
- 可在历史记录中查看所有分析结果
### 结果解读 ### 结果解读
- **投资评级**:买入/持有/卖出 - **投资评级**:买入/持有/卖出
+568 -26
View File
@@ -399,22 +399,68 @@ def main():
return return
# 主界面 # 主界面
col1, col2, col3 = st.columns([2, 1, 1]) # 添加单个/批量分析切换
col_mode1, col_mode2 = st.columns([1, 3])
with col1: with col_mode1:
stock_input = st.text_input( analysis_mode = st.radio(
"🔍 请输入股票代码或名称", "分析模式",
placeholder="例如: AAPL, 000001, 600036", ["单个分析", "批量分析"],
help="支持美股代码(如AAPL)和A股代码(如000001)" horizontal=True,
help="单个分析:分析单只股票;批量分析:同时分析多只股票"
) )
with col2: with col_mode2:
analyze_button = st.button("🚀 开始分析", type="primary", use_container_width=True) if analysis_mode == "批量分析":
batch_mode = st.radio(
"批量模式",
["顺序分析", "多线程并行"],
horizontal=True,
help="顺序分析:按次序分析,稳定但较慢;多线程并行:同时分析多只,快速但消耗资源"
)
st.session_state.batch_mode = batch_mode
with col3: st.markdown("---")
if st.button("🔄 清除缓存", use_container_width=True):
st.cache_data.clear() if analysis_mode == "单个分析":
st.success("缓存已清除") # 单个股票分析界面
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("---") st.markdown("---")
@@ -480,21 +526,47 @@ def main():
st.error("❌ 请至少选择一位分析师参与分析") st.error("❌ 请至少选择一位分析师参与分析")
return return
# 清除之前的分析结果 if analysis_mode == "单个分析":
if 'analysis_completed' in st.session_state: # 单个股票分析
del st.session_state.analysis_completed # 清除之前的分析结果
if 'stock_info' in st.session_state: if 'analysis_completed' in st.session_state:
del st.session_state.stock_info del st.session_state.analysis_completed
if 'agents_results' in st.session_state: if 'stock_info' in st.session_state:
del st.session_state.agents_results del st.session_state.stock_info
if 'discussion_result' in st.session_state: if 'agents_results' in st.session_state:
del st.session_state.discussion_result del st.session_state.agents_results
if 'final_decision' in st.session_state: if 'discussion_result' in st.session_state:
del st.session_state.final_decision 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) run_stock_analysis(stock_input, period)
# 检查是否有已完成的分析结果 else:
# 批量股票分析
# 解析股票代码列表
stock_list = parse_stock_list(stock_input)
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: if 'analysis_completed' in st.session_state and st.session_state.analysis_completed:
# 重新显示分析结果 # 重新显示分析结果
stock_info = st.session_state.stock_info stock_info = st.session_state.stock_info
@@ -521,6 +593,10 @@ def main():
# 显示最终决策 # 显示最终决策
display_final_decision(final_decision, stock_info, agents_results, discussion_result) 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: elif not stock_input:
show_example_interface() show_example_interface()
@@ -548,6 +624,287 @@ def get_stock_data(symbol, period):
return stock_info, stock_data_with_indicators, indicators 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): 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')}" MINIQMT_PORT="{current_config.get('MINIQMT_PORT', '58610')}"
""", language="bash") """, 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__": if __name__ == "__main__":
main() main()
+290
View File
@@ -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
---
**免责声明**:本系统提供的分析结果仅供参考,不构成投资建议。股市有风险,投资需谨慎。