增加批量分析功能
This commit is contained in:
@@ -11,6 +11,16 @@
|
||||
|
||||
|
||||
## ✨ 更新说明
|
||||
### 最新更新:增加批量分析功能 ⭐️
|
||||
- **批量输入**:支持同时输入多个股票代码(每行一个或逗号分隔)
|
||||
- **双模式分析**:
|
||||
- 顺序分析:按次序逐个分析,稳定可靠
|
||||
- 多线程并行:同时分析多只股票,速度更快
|
||||
- **对比视图**:横向对比多只股票的关键指标和投资评级
|
||||
- **详细卡片**:逐个查看每只股票的完整分析报告
|
||||
- **智能筛选**:按评级、涨跌幅、信心度等条件快速筛选
|
||||
- **自动保存**:所有分析结果自动保存到历史记录
|
||||
|
||||
### 1006增加跟踪主力资金mcp,增加环境配置功能
|
||||
### 1005增加股票监测功能,增加历史记录中个股导入到监测板块
|
||||
|
||||
@@ -38,6 +48,19 @@
|
||||
5. 📋 最终投资决策
|
||||
6. 🎯 操作建议和风险提示
|
||||
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/903a64c7-7018-44dd-aa87-af4f7904048c" />
|
||||
<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
|
||||
```
|
||||
|
||||
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限流
|
||||
- 批量分析时间较长,请耐心等待
|
||||
- 可在历史记录中查看所有分析结果
|
||||
|
||||
### 结果解读
|
||||
- **投资评级**:买入/持有/卖出
|
||||
|
||||
@@ -399,6 +399,30 @@ def main():
|
||||
return
|
||||
|
||||
# 主界面
|
||||
# 添加单个/批量分析切换
|
||||
col_mode1, col_mode2 = st.columns([1, 3])
|
||||
with col_mode1:
|
||||
analysis_mode = st.radio(
|
||||
"分析模式",
|
||||
["单个分析", "批量分析"],
|
||||
horizontal=True,
|
||||
help="单个分析:分析单只股票;批量分析:同时分析多只股票"
|
||||
)
|
||||
|
||||
with col_mode2:
|
||||
if analysis_mode == "批量分析":
|
||||
batch_mode = st.radio(
|
||||
"批量模式",
|
||||
["顺序分析", "多线程并行"],
|
||||
horizontal=True,
|
||||
help="顺序分析:按次序分析,稳定但较慢;多线程并行:同时分析多只,快速但消耗资源"
|
||||
)
|
||||
st.session_state.batch_mode = batch_mode
|
||||
|
||||
st.markdown("---")
|
||||
|
||||
if analysis_mode == "单个分析":
|
||||
# 单个股票分析界面
|
||||
col1, col2, col3 = st.columns([2, 1, 1])
|
||||
|
||||
with col1:
|
||||
@@ -416,6 +440,28 @@ def main():
|
||||
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.subheader("👥 选择分析师团队")
|
||||
@@ -480,6 +526,8 @@ def main():
|
||||
st.error("❌ 请至少选择一位分析师参与分析")
|
||||
return
|
||||
|
||||
if analysis_mode == "单个分析":
|
||||
# 单个股票分析
|
||||
# 清除之前的分析结果
|
||||
if 'analysis_completed' in st.session_state:
|
||||
del st.session_state.analysis_completed
|
||||
@@ -494,7 +542,31 @@ def main():
|
||||
|
||||
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:
|
||||
# 重新显示分析结果
|
||||
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()
|
||||
+290
@@ -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
|
||||
|
||||
---
|
||||
|
||||
**免责声明**:本系统提供的分析结果仅供参考,不构成投资建议。股市有风险,投资需谨慎。
|
||||
|
||||
Reference in New Issue
Block a user