diff --git a/app.py b/app.py
index 9d3b34f..9f07daf 100644
--- a/app.py
+++ b/app.py
@@ -16,6 +16,7 @@ from monitor_manager import display_monitor_manager, get_monitor_summary
from monitor_service import monitor_service
from notification_service import notification_service
from config_manager import config_manager
+from main_force_ui import display_main_force_selector
# 页面配置
st.set_page_config(
@@ -296,6 +297,17 @@ def main():
st.session_state.show_monitor = True
if 'show_history' in st.session_state:
del st.session_state.show_history
+ if 'show_main_force' in st.session_state:
+ del st.session_state.show_main_force
+
+ if st.button("🎯 主力选股", use_container_width=True, key="nav_main_force"):
+ st.session_state.show_main_force = True
+ if 'show_history' in st.session_state:
+ del st.session_state.show_history
+ if 'show_monitor' in st.session_state:
+ del st.session_state.show_monitor
+ if 'show_config' in st.session_state:
+ del st.session_state.show_config
if st.button("🏠 返回首页", use_container_width=True, key="nav_home"):
if 'show_history' in st.session_state:
@@ -304,6 +316,8 @@ def main():
del st.session_state.show_monitor
if 'show_config' in st.session_state:
del st.session_state.show_config
+ if 'show_main_force' in st.session_state:
+ del st.session_state.show_main_force
if st.button("⚙️ 环境配置", use_container_width=True, key="nav_config"):
st.session_state.show_config = True
@@ -373,6 +387,7 @@ def main():
**功能说明**
- **智能分析**:AI团队深度分析
+ - **主力选股**:主力资金精选标的
- **实时监测**:价格监控与提醒
- **历史记录**:查看分析历史
@@ -393,6 +408,11 @@ def main():
display_monitor_manager()
return
+ # 检查是否显示主力选股
+ if 'show_main_force' in st.session_state and st.session_state.show_main_force:
+ display_main_force_selector()
+ return
+
# 检查是否显示环境配置
if 'show_config' in st.session_state and st.session_state.show_config:
display_config_manager()
@@ -539,6 +559,8 @@ def main():
del st.session_state.discussion_result
if 'final_decision' in st.session_state:
del st.session_state.final_decision
+ if 'just_completed' in st.session_state:
+ del st.session_state.just_completed
run_stock_analysis(stock_input, period)
@@ -566,32 +588,36 @@ def main():
# 运行批量分析
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
- agents_results = st.session_state.agents_results
- discussion_result = st.session_state.discussion_result
- final_decision = st.session_state.final_decision
-
- # 重新获取股票数据用于显示图表
- stock_info_current, stock_data, indicators = 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)
+ # 如果是刚刚完成的分析,清除标志,避免重复显示
+ if st.session_state.get('just_completed', False):
+ st.session_state.just_completed = False
+ else:
+ # 重新显示之前的分析结果(页面刷新后)
+ stock_info = st.session_state.stock_info
+ agents_results = st.session_state.agents_results
+ discussion_result = st.session_state.discussion_result
+ final_decision = st.session_state.final_decision
+
+ # 重新获取股票数据用于显示图表
+ stock_info_current, stock_data, indicators = 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)
# 检查是否有已完成的批量分析结果
elif 'batch_analysis_results' in st.session_state and st.session_state.batch_analysis_results:
@@ -1064,12 +1090,16 @@ def run_stock_analysis(symbol, period):
final_decision = agents.make_final_decision(discussion_result, stock_info, indicators)
progress_bar.progress(100)
- # 保存分析结果到session_state
+ # 显示最终决策
+ display_final_decision(final_decision, stock_info, agents_results, discussion_result)
+
+ # 保存分析结果到session_state(用于页面刷新后显示)
st.session_state.analysis_completed = True
st.session_state.stock_info = stock_info
st.session_state.agents_results = agents_results
st.session_state.discussion_result = discussion_result
st.session_state.final_decision = final_decision
+ st.session_state.just_completed = True # 标记刚刚完成分析
# 保存到数据库
try:
@@ -1086,9 +1116,6 @@ def run_stock_analysis(symbol, period):
except Exception as e:
st.warning(f"⚠️ 保存到数据库时出现错误: {str(e)}")
- # 显示最终决策
- display_final_decision(final_decision, stock_info, agents_results, discussion_result)
-
status_text.text("✅ 分析完成!")
time.sleep(1)
status_text.empty()
diff --git a/main_force_analysis.py b/main_force_analysis.py
new file mode 100644
index 0000000..158f7a1
--- /dev/null
+++ b/main_force_analysis.py
@@ -0,0 +1,549 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+主力选股AI分析整合模块
+整体批量分析,从板块热点和资金流向角度筛选优质标的
+"""
+
+from typing import Dict, List, Tuple
+import pandas as pd
+from main_force_selector import main_force_selector
+from stock_data import StockDataFetcher
+from ai_agents import StockAnalysisAgents
+from deepseek_client import DeepSeekClient
+import time
+import json
+
+class MainForceAnalyzer:
+ """主力选股分析器 - 批量整体分析"""
+
+ def __init__(self, model='deepseek-chat'):
+ self.selector = main_force_selector
+ self.fetcher = StockDataFetcher()
+ self.model = model
+ self.agents = StockAnalysisAgents(model=model)
+ self.deepseek_client = self.agents.deepseek_client
+ self.raw_stocks = None
+ self.final_recommendations = []
+
+ def run_full_analysis(self, start_date: str = None, days_ago: int = 90,
+ final_n: int = 5) -> Dict:
+ """
+ 运行完整的主力选股分析流程 - 整体批量分析
+
+ Args:
+ start_date: 开始日期,格式如"2025年10月1日"
+ days_ago: 距今多少天,默认90天
+ final_n: 最终精选N只,默认5只
+
+ Returns:
+ 分析结果字典
+ """
+ result = {
+ 'success': False,
+ 'total_stocks': 0,
+ 'filtered_stocks': 0,
+ 'final_recommendations': [],
+ 'error': None
+ }
+
+ try:
+ print(f"\n{'='*80}")
+ print(f"🚀 主力选股智能分析系统 - 批量整体分析")
+ print(f"{'='*80}\n")
+
+ # 步骤1: 获取主力资金净流入前100名股票
+ success, raw_data, message = self.selector.get_main_force_stocks(
+ start_date=start_date,
+ days_ago=days_ago
+ )
+
+ if not success:
+ result['error'] = message
+ return result
+
+ result['total_stocks'] = len(raw_data)
+
+ # 步骤2: 智能筛选(涨幅、市值等)
+ filtered_data = self.selector.filter_stocks(
+ raw_data,
+ max_range_change=30.0,
+ min_market_cap=50.0,
+ max_market_cap=1300.0
+ )
+
+ result['filtered_stocks'] = len(filtered_data)
+
+ if filtered_data.empty:
+ result['error'] = "筛选后没有符合条件的股票"
+ return result
+
+ # 保存原始数据
+ self.raw_stocks = filtered_data
+
+ # 步骤3: 整体数据分析(不是逐个分析)
+ print(f"\n{'='*80}")
+ print(f"🤖 AI分析师团队开始整体分析...")
+ print(f"{'='*80}\n")
+
+ # 准备整体数据摘要
+ overall_summary = self._prepare_overall_summary(filtered_data)
+
+ # 三大分析师整体分析
+ fund_flow_analysis = self._fund_flow_overall_analysis(filtered_data, overall_summary)
+ industry_analysis = self._industry_overall_analysis(filtered_data, overall_summary)
+ fundamental_analysis = self._fundamental_overall_analysis(filtered_data, overall_summary)
+
+ # 保存分析报告到对象属性,供UI展示
+ self.fund_flow_analysis = fund_flow_analysis
+ self.industry_analysis = industry_analysis
+ self.fundamental_analysis = fundamental_analysis
+
+ # 步骤4: 综合决策,精选优质标的
+ print(f"\n{'='*80}")
+ print(f"👔 资深研究员综合评估并精选标的...")
+ print(f"{'='*80}\n")
+
+ final_recommendations = self._select_best_stocks(
+ filtered_data,
+ fund_flow_analysis,
+ industry_analysis,
+ fundamental_analysis,
+ final_n=final_n
+ )
+
+ result['final_recommendations'] = final_recommendations
+ result['success'] = True
+
+ # 显示最终结果
+ self._print_final_recommendations(final_recommendations)
+
+ return result
+
+ except Exception as e:
+ result['error'] = f"分析过程出错: {str(e)}"
+ import traceback
+ traceback.print_exc()
+ return result
+
+ def _prepare_overall_summary(self, df: pd.DataFrame) -> str:
+ """准备整体数据摘要"""
+
+ summary_lines = []
+ summary_lines.append(f"候选股票总数: {len(df)}只")
+
+ # 主力资金统计
+ main_fund_cols = [col for col in df.columns if '主力' in col and '净流入' in col]
+ if main_fund_cols:
+ col_name = main_fund_cols[0]
+ df[col_name] = pd.to_numeric(df[col_name], errors='coerce')
+ total_inflow = df[col_name].sum()
+ avg_inflow = df[col_name].mean()
+ summary_lines.append(f"主力资金总净流入: {total_inflow/100000000:.2f}亿")
+ summary_lines.append(f"平均主力资金净流入: {avg_inflow/100000000:.2f}亿")
+
+ # 涨跌幅统计
+ range_cols = [col for col in df.columns if '涨跌幅' in col]
+ if range_cols:
+ col_name = range_cols[0]
+ df[col_name] = pd.to_numeric(df[col_name], errors='coerce')
+ avg_change = df[col_name].mean()
+ max_change = df[col_name].max()
+ min_change = df[col_name].min()
+ summary_lines.append(f"平均涨跌幅: {avg_change:.2f}%")
+ summary_lines.append(f"涨跌幅范围: {min_change:.2f}% ~ {max_change:.2f}%")
+
+ # 行业分布
+ industry_cols = [col for col in df.columns if '行业' in col]
+ if industry_cols:
+ col_name = industry_cols[0]
+ top_industries = df[col_name].value_counts().head(10)
+ summary_lines.append("\n主要行业分布:")
+ for industry, count in top_industries.items():
+ summary_lines.append(f" - {industry}: {count}只")
+
+ return "\n".join(summary_lines)
+
+ def _fund_flow_overall_analysis(self, df: pd.DataFrame, summary: str) -> str:
+ """资金流向整体分析"""
+
+ print("💰 资金流向分析师整体分析中...")
+
+ # 准备数据表格
+ data_table = self._prepare_data_table(df, focus='fund_flow')
+
+ prompt = f"""
+你是一名资深的资金面分析师,现在需要你从整体角度分析这批主力资金净流入的股票。
+
+【整体数据摘要】
+{summary}
+
+【候选股票详细数据】(共{len(df)}只)
+{data_table}
+
+【分析任务】
+请从资金流向的整体角度进行分析,重点关注:
+
+1. **资金流向特征**
+ - 哪些板块/行业资金流入最集中?
+ - 主力资金的整体行为特征(大规模建仓/试探性进场/板块轮动)
+ - 资金流向与涨跌幅的配合情况
+
+2. **优质标的识别**
+ - 从资金面角度,哪些股票最值得关注?
+ - 主力资金流入大但涨幅不高的潜力股
+ - 资金持续流入且趋势明确的股票
+
+3. **板块热点判断**
+ - 当前资金最看好哪些板块?
+ - 是否有板块轮动迹象?
+ - 新兴热点 vs 传统强势板块
+
+4. **投资建议**
+ - 从资金面角度,建议重点关注哪3-5只股票?
+ - 理由和风险提示
+
+请给出专业、系统的资金面整体分析报告。
+"""
+
+ messages = [
+ {"role": "system", "content": "你是资金面分析专家,擅长从整体资金流向中发现投资机会。"},
+ {"role": "user", "content": prompt}
+ ]
+
+ analysis = self.deepseek_client.call_api(messages, max_tokens=4000)
+
+ print(" ✅ 资金流向整体分析完成")
+ time.sleep(1)
+
+ return analysis
+
+ def _industry_overall_analysis(self, df: pd.DataFrame, summary: str) -> str:
+ """行业板块整体分析"""
+
+ print("📊 行业板块分析师整体分析中...")
+
+ # 准备数据表格
+ data_table = self._prepare_data_table(df, focus='industry')
+
+ prompt = f"""
+你是一名资深的行业板块分析师,现在需要你从行业热点和板块轮动角度分析这批股票。
+
+【整体数据摘要】
+{summary}
+
+【候选股票详细数据】(共{len(df)}只)
+{data_table}
+
+【分析任务】
+请从行业板块的整体角度进行分析,重点关注:
+
+1. **热点板块识别**
+ - 哪些行业/板块最受资金青睐?
+ - 热点板块的持续性如何?
+ - 是否有新兴热点正在形成?
+
+2. **板块特征分析**
+ - 各板块的涨幅与资金流入匹配度
+ - 哪些板块处于启动阶段(资金流入但涨幅不大)
+ - 哪些板块可能过热(涨幅高但资金流入减弱)
+
+3. **行业前景评估**
+ - 主力资金集中的行业,基本面支撑如何?
+ - 政策面、产业面是否有催化因素?
+ - 行业竞争格局和龙头地位
+
+4. **优质标的推荐**
+ - 从行业板块角度,推荐3-5只最具潜力的股票
+ - 推荐理由(行业地位、成长空间、催化因素)
+
+请给出专业、深入的行业板块分析报告。
+"""
+
+ messages = [
+ {"role": "system", "content": "你是行业板块分析专家,擅长发现市场热点和板块机会。"},
+ {"role": "user", "content": prompt}
+ ]
+
+ analysis = self.deepseek_client.call_api(messages, max_tokens=4000)
+
+ print(" ✅ 行业板块整体分析完成")
+ time.sleep(1)
+
+ return analysis
+
+ def _fundamental_overall_analysis(self, df: pd.DataFrame, summary: str) -> str:
+ """财务基本面整体分析"""
+
+ print("📈 财务基本面分析师整体分析中...")
+
+ # 准备数据表格
+ data_table = self._prepare_data_table(df, focus='fundamental')
+
+ prompt = f"""
+你是一名资深的基本面分析师,现在需要你从财务质量和基本面角度分析这批股票。
+
+【整体数据摘要】
+{summary}
+
+【候选股票详细数据】(共{len(df)}只)
+{data_table}
+
+【分析任务】
+请从财务基本面的整体角度进行分析,重点关注:
+
+1. **财务质量评估**
+ - 整体财务指标健康度如何?
+ - 哪些股票盈利能力、成长性突出?
+ - 是否存在财务风险较大的股票?
+
+2. **估值水平分析**
+ - 市盈率、市净率的整体分布
+ - 哪些股票估值合理且有成长空间?
+ - 高估值是否有业绩支撑?
+
+3. **成长性评估**
+ - 营收、净利润增长情况
+ - 哪些股票成长性最好?
+ - 成长能力评分较高的股票
+
+4. **优质标的筛选**
+ - 从基本面角度,推荐3-5只最优质的股票
+ - 推荐理由(财务健康、估值合理、成长性好)
+
+请给出专业、详实的基本面分析报告。
+"""
+
+ messages = [
+ {"role": "system", "content": "你是基本面分析专家,擅长从财务角度评估投资价值。"},
+ {"role": "user", "content": prompt}
+ ]
+
+ analysis = self.deepseek_client.call_api(messages, max_tokens=4000)
+
+ print(" ✅ 财务基本面整体分析完成")
+ time.sleep(1)
+
+ return analysis
+
+ def _prepare_data_table(self, df: pd.DataFrame, focus: str = 'all') -> str:
+ """准备数据表格用于AI分析"""
+
+ # 选择关键列
+ key_columns = ['股票代码', '股票简称']
+
+ # 根据分析重点添加相关列
+ if focus == 'fund_flow' or focus == 'all':
+ fund_cols = [col for col in df.columns if '主力' in col or '资金' in col]
+ key_columns.extend(fund_cols[:3]) # 最多3列资金数据
+
+ if focus == 'industry' or focus == 'all':
+ industry_cols = [col for col in df.columns if '行业' in col]
+ key_columns.extend(industry_cols[:1])
+
+ # 智能匹配区间涨跌幅列
+ interval_pct_col = None
+ possible_names = [
+ '区间涨跌幅:前复权', '区间涨跌幅:前复权(%)', '区间涨跌幅(%)',
+ '区间涨跌幅', '涨跌幅:前复权', '涨跌幅:前复权(%)', '涨跌幅(%)', '涨跌幅'
+ ]
+ for name in possible_names:
+ for col in df.columns:
+ if name in col:
+ interval_pct_col = col
+ break
+ if interval_pct_col:
+ break
+ if interval_pct_col:
+ key_columns.append(interval_pct_col)
+
+ if focus == 'fundamental' or focus == 'all':
+ fundamental_cols = [col for col in df.columns if any(
+ keyword in col for keyword in ['市盈率', '市净率', '营收', '净利润', '评分']
+ )]
+ key_columns.extend(fundamental_cols[:5])
+
+ # 去重并保持顺序
+ seen = set()
+ unique_columns = []
+ for col in key_columns:
+ if col in df.columns and col not in seen:
+ seen.add(col)
+ unique_columns.append(col)
+
+ # 限制显示前50只股票的详细数据,避免超出token限制
+ display_df = df[unique_columns].head(50)
+
+ # 转换为表格字符串
+ table_str = display_df.to_string(index=False, max_rows=50)
+
+ if len(df) > 50:
+ table_str += f"\n... 还有 {len(df) - 50} 只股票未显示"
+
+ return table_str
+
+ def _select_best_stocks(self, df: pd.DataFrame,
+ fund_analysis: str,
+ industry_analysis: str,
+ fundamental_analysis: str,
+ final_n: int = 5) -> List[Dict]:
+ """综合三位分析师的意见,精选最优标的"""
+
+ # 准备完整数据表格
+ data_table = self._prepare_data_table(df, focus='all')
+
+ prompt = f"""
+你是一名资深股票研究员,具有20年以上的投资研究经验。现在需要你综合三位分析师的意见,
+从{len(df)}只候选股票中精选出{final_n}只最具投资价值的优质标的。
+
+【候选股票数据】
+{data_table}
+
+【资金流向分析师观点】
+{fund_analysis}
+
+【行业板块分析师观点】
+{industry_analysis}
+
+【财务基本面分析师观点】
+{fundamental_analysis}
+
+【筛选标准】
+1. **主力资金**: 主力资金净流入较多,显示机构看好
+2. **涨幅适中**: 区间涨跌幅不是很高(避免追高),还有上涨空间
+3. **行业热点**: 所属行业有发展前景,是市场热点
+4. **基本面良好**: 财务指标健康,盈利能力强
+5. **综合平衡**: 资金、行业、基本面三方面都不错
+
+【任务要求】
+综合三位分析师的观点,精选出{final_n}只最优标的。
+
+对于每只精选股票,请提供:
+1. **股票代码和名称**
+2. **核心推荐理由**(3-5条,综合资金、行业、基本面)
+3. **投资亮点**(最突出的优势)
+4. **风险提示**(需要注意的风险)
+5. **建议仓位**(如20-30%)
+6. **投资周期**(短期/中期/长期)
+
+请按以下JSON格式输出(只输出JSON,不要其他内容):
+```json
+{{
+ "recommendations": [
+ {{
+ "rank": 1,
+ "symbol": "股票代码",
+ "name": "股票名称",
+ "reasons": [
+ "理由1:资金面角度",
+ "理由2:行业板块角度",
+ "理由3:基本面角度"
+ ],
+ "highlights": "投资亮点描述",
+ "risks": "风险提示",
+ "position": "建议仓位",
+ "investment_period": "投资周期"
+ }}
+ ]
+}}
+```
+
+注意:
+- 必须严格按照JSON格式输出
+- 推荐数量为{final_n}只
+- 按投资价值从高到低排序
+- 理由要具体、有说服力,体现三位分析师的综合观点
+"""
+
+ try:
+ print(" 🔍 正在综合评估并精选标的...")
+
+ messages = [
+ {"role": "system", "content": "你是资深股票研究员,擅长综合多维度分析做出投资决策。"},
+ {"role": "user", "content": prompt}
+ ]
+
+ response = self.deepseek_client.call_api(messages, max_tokens=4000)
+
+ # 解析JSON响应
+ import re
+
+ # 提取JSON部分
+ json_match = re.search(r'```json\s*(\{.*?\})\s*```', response, re.DOTALL)
+ if json_match:
+ json_str = json_match.group(1)
+ else:
+ # 尝试直接解析
+ json_str = response
+
+ result = json.loads(json_str)
+ recommendations = result.get('recommendations', [])
+
+ # 补充详细数据
+ for rec in recommendations:
+ symbol = rec['symbol']
+ # 从原始数据中找到对应股票
+ stock_data = df[df['股票代码'] == symbol]
+ if not stock_data.empty:
+ rec['stock_data'] = stock_data.iloc[0].to_dict()
+
+ return recommendations
+
+ except Exception as e:
+ print(f" ❌ JSON解析失败,使用备选方案: {e}")
+
+ # 降级方案:按主力资金排序返回前N个
+ main_fund_cols = [col for col in df.columns if '主力' in col and '净流入' in col]
+ if main_fund_cols:
+ col_name = main_fund_cols[0]
+ df[col_name] = pd.to_numeric(df[col_name], errors='coerce')
+ sorted_df = df.nlargest(final_n, col_name)
+ else:
+ sorted_df = df.head(final_n)
+
+ recommendations = []
+ for i, (idx, row) in enumerate(sorted_df.iterrows(), 1):
+ recommendations.append({
+ 'rank': i,
+ 'symbol': row.get('股票代码', 'N/A'),
+ 'name': row.get('股票简称', 'N/A'),
+ 'reasons': [
+ f"主力资金净流入较多",
+ f"所属行业: {row.get('所属同花顺行业', 'N/A')}",
+ f"涨跌幅适中"
+ ],
+ 'highlights': '主力资金持续关注',
+ 'risks': '需关注后续走势',
+ 'position': '15-25%',
+ 'investment_period': '中短期',
+ 'stock_data': row.to_dict()
+ })
+
+ return recommendations
+
+ def _print_final_recommendations(self, recommendations: List[Dict]):
+ """打印最终推荐结果"""
+ if not recommendations:
+ print("❌ 未能生成推荐结果")
+ return
+
+ print(f"\n{'='*80}")
+ print(f"⭐ 最终精选推荐 ({len(recommendations)}只)")
+ print(f"{'='*80}\n")
+
+ for rec in recommendations:
+ print(f"【第{rec['rank']}名】{rec['symbol']} - {rec['name']}")
+ print(f"{'-'*60}")
+
+ print(f"📌 推荐理由:")
+ for reason in rec.get('reasons', []):
+ print(f" • {reason}")
+
+ print(f"\n💡 投资亮点: {rec.get('highlights', 'N/A')}")
+ print(f"⚠️ 风险提示: {rec.get('risks', 'N/A')}")
+ print(f"📊 建议仓位: {rec.get('position', 'N/A')}")
+ print(f"⏰ 投资周期: {rec.get('investment_period', 'N/A')}")
+ print(f"{'='*80}\n")
+
+# 全局实例
+main_force_analyzer = MainForceAnalyzer()
diff --git a/main_force_pdf_generator.py b/main_force_pdf_generator.py
new file mode 100644
index 0000000..f9814cd
--- /dev/null
+++ b/main_force_pdf_generator.py
@@ -0,0 +1,456 @@
+import os
+import base64
+import re
+from datetime import datetime
+import streamlit as st
+import pandas as pd
+
+def generate_main_force_markdown_report(analyzer, result):
+ """生成主力选股Markdown格式的分析报告"""
+
+ # 获取当前时间
+ current_time = datetime.now().strftime("%Y年%m月%d日 %H:%M:%S")
+
+ # 获取分析参数
+ params = result.get('params', {})
+ start_date = params.get('start_date', 'N/A')
+ min_cap = params.get('min_market_cap', 50)
+ max_cap = params.get('max_market_cap', 5000)
+ max_change = params.get('max_range_change', 50)
+
+ markdown_content = f"""
+# 主力选股AI分析报告
+
+**生成时间**: {current_time}
+
+---
+
+## 📊 选股参数
+
+| 项目 | 值 |
+|------|-----|
+| **起始日期** | {start_date} |
+| **市值范围** | {min_cap}亿 - {max_cap}亿 |
+| **最大涨跌幅** | {max_change}% |
+| **初始数据量** | {result.get('total_fetched', 0)}只 |
+| **筛选后数量** | {result.get('filtered_count', 0)}只 |
+| **最终推荐** | {len(result.get('final_recommendations', []))}只 |
+
+---
+
+## 🤖 AI分析师团队报告
+
+"""
+
+ # 添加资金流向分析
+ if hasattr(analyzer, 'fund_flow_analysis') and analyzer.fund_flow_analysis:
+ markdown_content += f"""
+### 💰 资金流向分析师
+
+{analyzer.fund_flow_analysis}
+
+---
+
+"""
+
+ # 添加行业板块分析
+ if hasattr(analyzer, 'industry_analysis') and analyzer.industry_analysis:
+ markdown_content += f"""
+### 📊 行业板块及市场热点分析师
+
+{analyzer.industry_analysis}
+
+---
+
+"""
+
+ # 添加财务基本面分析
+ if hasattr(analyzer, 'fundamental_analysis') and analyzer.fundamental_analysis:
+ markdown_content += f"""
+### 📈 财务基本面分析师
+
+{analyzer.fundamental_analysis}
+
+---
+
+"""
+
+ # 添加精选推荐
+ markdown_content += """
+## ⭐ 精选推荐股票
+
+"""
+
+ final_recommendations = result.get('final_recommendations', [])
+ if final_recommendations:
+ for rec in final_recommendations:
+ markdown_content += f"""
+### 【第{rec['rank']}名】{rec['symbol']} - {rec['name']}
+
+**推荐理由**:
+{rec.get('reason', '暂无')}
+
+**关键指标**:
+"""
+ if 'stock_data' in rec:
+ stock_data = rec['stock_data']
+ markdown_content += f"""
+- **所属行业**: {stock_data.get('industry', 'N/A')}
+- **市值**: {stock_data.get('market_cap', 'N/A')}
+- **主力资金流向**: {stock_data.get('main_fund_inflow', 'N/A')}
+- **区间涨跌幅**: {stock_data.get('range_change', 'N/A')}%
+- **市盈率**: {stock_data.get('pe_ratio', 'N/A')}
+- **市净率**: {stock_data.get('pb_ratio', 'N/A')}
+
+"""
+
+ if 'scores' in rec.get('stock_data', {}):
+ scores = rec['stock_data']['scores']
+ if scores:
+ markdown_content += "**能力评分**:\n"
+ for score_name, score_value in scores.items():
+ markdown_content += f"- {score_name}: {score_value}\n"
+ markdown_content += "\n"
+
+ markdown_content += "---\n\n"
+ else:
+ markdown_content += "暂无推荐股票\n\n---\n\n"
+
+ # 添加候选股票列表(前100名,按主力资金排序)
+ if analyzer and analyzer.raw_stocks is not None and not analyzer.raw_stocks.empty:
+ markdown_content += """
+## 📋 候选股票完整列表(按主力资金净流入排序)
+
+"""
+
+ # 获取主力资金列名
+ df = analyzer.raw_stocks
+ main_fund_col = None
+ main_fund_patterns = [
+ '区间主力资金流向', '区间主力资金净流入',
+ '主力资金流向', '主力资金净流入', '主力净流入'
+ ]
+ for pattern in main_fund_patterns:
+ matching = [col for col in df.columns if pattern in col]
+ if matching:
+ main_fund_col = matching[0]
+ break
+
+ # 按主力资金排序
+ if main_fund_col:
+ df_sorted = df.copy()
+ df_sorted[main_fund_col] = pd.to_numeric(df_sorted[main_fund_col], errors='coerce')
+ df_sorted = df_sorted.sort_values(by=main_fund_col, ascending=False).head(100)
+ else:
+ df_sorted = df.head(100)
+
+ # 选择要显示的列
+ display_cols = []
+ if '股票代码' in df_sorted.columns:
+ display_cols.append('股票代码')
+ if '股票简称' in df_sorted.columns:
+ display_cols.append('股票简称')
+
+ # 行业
+ industry_cols = [col for col in df_sorted.columns if '行业' in col]
+ if industry_cols:
+ display_cols.append(industry_cols[0])
+
+ # 主力资金
+ if main_fund_col:
+ display_cols.append(main_fund_col)
+
+ # 涨跌幅
+ change_cols = [col for col in df_sorted.columns if '涨跌幅' in col]
+ if change_cols:
+ display_cols.append(change_cols[0])
+
+ # 市值、市盈率、市净率
+ for col_name in ['总市值', '市盈率', '市净率']:
+ matching_cols = [col for col in df_sorted.columns if col_name in col]
+ if matching_cols:
+ display_cols.append(matching_cols[0])
+
+ # 生成表格
+ if display_cols:
+ final_display_cols = [col for col in display_cols if col in df_sorted.columns]
+ markdown_content += "| 序号 | " + " | ".join(final_display_cols) + " |\n"
+ markdown_content += "|------|" + "|".join(['-----' for _ in final_display_cols]) + "|\n"
+
+ for idx, (_, row) in enumerate(df_sorted[final_display_cols].iterrows(), 1):
+ row_data = [str(idx)]
+ for col in final_display_cols:
+ value = row[col]
+ if pd.isna(value):
+ row_data.append('N/A')
+ else:
+ row_data.append(str(value))
+ markdown_content += "| " + " | ".join(row_data) + " |\n"
+
+ markdown_content += "\n"
+
+ # 添加免责声明
+ markdown_content += f"""
+---
+
+## 📝 免责声明
+
+本报告由AI系统生成,仅供参考,不构成投资建议。投资有风险,入市需谨慎。请在做出投资决策前咨询专业的投资顾问。
+
+---
+
+*报告生成时间: {current_time}*
+*主力选股AI分析系统 v1.0*
+"""
+
+ return markdown_content
+
+
+def generate_html_content(markdown_content):
+ """将Markdown转换为HTML"""
+ html_content = f"""
+
+
+
+
+ 主力选股AI分析报告
+
+
+
+
+"""
+
+ # 简单的Markdown到HTML转换
+ html_body = markdown_content
+ html_body = html_body.replace('\n# ', '\n
').replace('\n## ', '\n').replace('\n### ', '\n')
+ html_body = html_body.replace('# ', '').replace('## ', '').replace('### ', '')
+ html_body = html_body.replace('\n---\n', '\n
\n')
+
+ # 处理粗体文本
+ html_body = re.sub(r'\*\*(.*?)\*\*', r'\1', html_body)
+
+ # 处理表格
+ lines = html_body.split('\n')
+ in_table = False
+ processed_lines = []
+
+ for line in lines:
+ if '|' in line and not in_table and line.strip().startswith('|'):
+ processed_lines.append('
')
+ in_table = True
+ cells = [cell.strip() for cell in line.split('|')[1:-1]]
+ processed_lines.append('')
+ for cell in cells:
+ processed_lines.append(f'| {cell} | ')
+ processed_lines.append('
')
+ elif '|' in line and in_table:
+ if '---' not in line:
+ cells = [cell.strip() for cell in line.split('|')[1:-1]]
+ processed_lines.append('')
+ for cell in cells:
+ processed_lines.append(f'| {cell} | ')
+ processed_lines.append('
')
+ elif in_table and '|' not in line:
+ processed_lines.append('
')
+ in_table = False
+ processed_lines.append(line)
+ else:
+ processed_lines.append(line)
+
+ if in_table:
+ processed_lines.append('')
+
+ html_body = '\n'.join(processed_lines)
+
+ # 处理列表
+ html_body = re.sub(r'\n- (.*)', r'\n
\1', html_body)
+ html_body = re.sub(r'(
.*)\n(?!
)', r'\n', html_body)
+ html_body = re.sub(r'(.*\n)+', lambda m: '
\n', html_body)
+
+ # 处理换行
+ html_body = html_body.replace('\n\n', '
')
+ html_body = '
' + html_body + '
'
+
+ html_content += html_body
+ html_content += """
+
+
+
+"""
+
+ return html_content
+
+
+def create_download_link(content, filename, link_text):
+ """创建下载链接"""
+ b64 = base64.b64encode(content.encode()).decode()
+ href = f'{link_text}'
+ return href
+
+
+def create_html_download_link(content, filename, link_text):
+ """创建HTML下载链接"""
+ b64 = base64.b64encode(content.encode('utf-8')).decode()
+ href = f'{link_text}'
+ return href
+
+
+def display_report_download_section(analyzer, result):
+ """显示报告下载区域"""
+
+ st.markdown("---")
+ st.markdown("### 📥 下载分析报告")
+
+ col1, col2 = st.columns(2)
+
+ with col1:
+ st.markdown("#### 📄 Markdown格式")
+ st.caption("适合编辑和进一步处理")
+
+ # 生成Markdown报告
+ markdown_content = generate_main_force_markdown_report(analyzer, result)
+
+ # 生成文件名
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
+ md_filename = f"主力选股分析报告_{timestamp}.md"
+
+ # 创建下载链接
+ md_link = create_download_link(markdown_content, md_filename, "📥 下载Markdown报告")
+ st.markdown(md_link, unsafe_allow_html=True)
+
+ # 显示预览
+ with st.expander("👀 预览Markdown内容"):
+ st.code(markdown_content[:2000] + "..." if len(markdown_content) > 2000 else markdown_content)
+
+ with col2:
+ st.markdown("#### 🌐 HTML格式")
+ st.caption("可在浏览器中打开查看")
+
+ # 生成HTML报告
+ html_content = generate_html_content(markdown_content)
+
+ # 生成文件名
+ html_filename = f"主力选股分析报告_{timestamp}.html"
+
+ # 创建下载链接
+ html_link = create_html_download_link(html_content, html_filename, "📥 下载HTML报告")
+ st.markdown(html_link, unsafe_allow_html=True)
+
+ # 显示说明
+ st.info("💡 HTML报告可以直接在浏览器中打开,格式美观易读")
+
+ # 添加CSV下载(候选股票列表)
+ if analyzer and analyzer.raw_stocks is not None and not analyzer.raw_stocks.empty:
+ st.markdown("---")
+ st.markdown("#### 📊 候选股票数据")
+
+ # 按主力资金排序
+ df = analyzer.raw_stocks.copy()
+ main_fund_col = None
+ main_fund_patterns = [
+ '区间主力资金流向', '区间主力资金净流入',
+ '主力资金流向', '主力资金净流入', '主力净流入'
+ ]
+ for pattern in main_fund_patterns:
+ matching = [col for col in df.columns if pattern in col]
+ if matching:
+ main_fund_col = matching[0]
+ break
+
+ if main_fund_col:
+ df[main_fund_col] = pd.to_numeric(df[main_fund_col], errors='coerce')
+ df = df.sort_values(by=main_fund_col, ascending=False)
+
+ # 导出为CSV
+ csv = df.to_csv(index=False, encoding='utf-8-sig')
+ csv_filename = f"主力选股候选列表_{timestamp}.csv"
+
+ st.download_button(
+ label="📥 下载候选股票CSV",
+ data=csv,
+ file_name=csv_filename,
+ mime="text/csv",
+ use_container_width=True
+ )
+
diff --git a/main_force_selector.py b/main_force_selector.py
new file mode 100644
index 0000000..3ba5306
--- /dev/null
+++ b/main_force_selector.py
@@ -0,0 +1,386 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+主力选股模块
+使用pywencai获取主力资金净流入前100名股票,并进行智能筛选
+"""
+
+import pandas as pd
+import pywencai
+from datetime import datetime, timedelta
+from typing import Dict, List, Tuple
+import time
+
+class MainForceStockSelector:
+ """主力选股类"""
+
+ def __init__(self):
+ self.raw_data = None
+ self.filtered_stocks = None
+
+ def get_main_force_stocks(self, start_date: str = None, days_ago: int = 90) -> Tuple[bool, pd.DataFrame, str]:
+ """
+ 获取主力资金净流入前100名股票
+
+ Args:
+ start_date: 开始日期,格式如"2025年10月1日",如果不提供则使用days_ago
+ days_ago: 距今多少天,默认90天(约3个月)
+
+ Returns:
+ (success, dataframe, message)
+ """
+ try:
+ # 如果没有提供开始日期,根据days_ago计算
+ if not start_date:
+ date_obj = datetime.now() - timedelta(days=days_ago)
+ start_date = f"{date_obj.year}年{date_obj.month}月{date_obj.day}日"
+
+ print(f"\n{'='*60}")
+ print(f"🔍 主力选股 - 数据获取中")
+ print(f"{'='*60}")
+ print(f"开始日期: {start_date}")
+ print(f"目标: 获取主力资金净流入排名前100名股票")
+
+ # 构建查询语句 - 使用多个备选方案,所有方案都要求计算区间涨跌幅
+ queries = [
+ # 方案1: 完整查询(最优)
+ f"{start_date}以来主力资金净流入排名,并计算区间涨跌幅,市值50-5000亿之间,非科创非st,"
+ f"所属同花顺行业,总市值,净利润,营收,市盈率,市净率,"
+ f"盈利能力评分,成长能力评分,营运能力评分,偿债能力评分,"
+ f"现金流评分,资产质量评分,流动性评分,资本充足性评分",
+
+ # 方案2: 简化查询
+ f"{start_date}以来主力资金净流入,并计算区间涨跌幅,市值50-5000亿,非科创非st,"
+ f"所属同花顺行业,总市值,净利润,营收,市盈率,市净率",
+
+ # 方案3: 基础查询
+ f"{start_date}以来主力资金净流入排名,并计算区间涨跌幅,市值50-5000亿,非科创非st,"
+ f"所属行业,总市值",
+
+ # 方案4: 最简查询
+ f"{start_date}以来主力资金净流入前100名,并计算区间涨跌幅,市值50-5000亿,非st非科创板,所属行业,总市值",
+ ]
+
+ # 尝试不同的查询方案
+ for i, query in enumerate(queries, 1):
+ print(f"\n尝试方案 {i}/{len(queries)}...")
+ print(f"查询语句: {query[:100]}...")
+
+ try:
+ result = pywencai.get(query=query, loop=True)
+
+ if result is None:
+ print(f" ⚠️ 方案{i}返回None,尝试下一个方案")
+ continue
+
+ # 转换为DataFrame
+ df_result = self._convert_to_dataframe(result)
+
+ if df_result is None or df_result.empty:
+ print(f" ⚠️ 方案{i}数据为空,尝试下一个方案")
+ continue
+
+ # 成功获取数据
+ print(f" ✅ 方案{i}成功!获取到 {len(df_result)} 只股票")
+ self.raw_data = df_result
+
+ # 显示获取到的列名
+ print(f"\n获取到的数据字段:")
+ for col in df_result.columns[:15]: # 只显示前15个字段
+ print(f" - {col}")
+ if len(df_result.columns) > 15:
+ print(f" ... 还有 {len(df_result.columns) - 15} 个字段")
+
+ return True, df_result, f"成功获取{len(df_result)}只股票数据"
+
+ except Exception as e:
+ print(f" ❌ 方案{i}失败: {str(e)}")
+ time.sleep(2) # 失败后等待2秒再试
+ continue
+
+ # 所有方案都失败
+ error_msg = "所有查询方案都失败了,请检查网络或稍后重试"
+ print(f"\n❌ {error_msg}")
+ return False, None, error_msg
+
+ except Exception as e:
+ error_msg = f"获取主力选股数据失败: {str(e)}"
+ print(f"\n❌ {error_msg}")
+ return False, None, error_msg
+
+ def _convert_to_dataframe(self, result) -> pd.DataFrame:
+ """转换问财返回结果为DataFrame"""
+ try:
+ if isinstance(result, pd.DataFrame):
+ return result
+ elif isinstance(result, dict):
+ # 检查是否有嵌套的tableV1结构
+ if 'tableV1' in result:
+ table_data = result['tableV1']
+ if isinstance(table_data, pd.DataFrame):
+ return table_data
+ elif isinstance(table_data, list):
+ return pd.DataFrame(table_data)
+ # 直接转换字典
+ return pd.DataFrame([result])
+ elif isinstance(result, list):
+ return pd.DataFrame(result)
+ else:
+ return None
+ except Exception as e:
+ print(f" 转换DataFrame失败: {e}")
+ return None
+
+ def filter_stocks(self, df: pd.DataFrame,
+ max_range_change: float = 30.0,
+ min_market_cap: float = 50.0,
+ max_market_cap: float = 1300.0) -> pd.DataFrame:
+ """
+ 智能筛选股票
+
+ Args:
+ df: 原始数据
+ max_range_change: 区间涨跌幅上限(%),默认30%
+ min_market_cap: 最小市值(亿),默认50亿
+ max_market_cap: 最大市值(亿),默认1300亿
+
+ Returns:
+ 筛选后的DataFrame
+ """
+ if df is None or df.empty:
+ return df
+
+ print(f"\n{'='*60}")
+ print(f"🔍 智能筛选中...")
+ print(f"{'='*60}")
+ print(f"筛选条件:")
+ print(f" - 区间涨跌幅 < {max_range_change}%")
+ print(f" - 市值范围: {min_market_cap}-{max_market_cap}亿")
+
+ original_count = len(df)
+ filtered_df = df.copy()
+
+ # 1. 筛选区间涨跌幅(智能匹配列名)
+ # 优先精确匹配,按优先级查找
+ interval_pct_col = None
+ possible_interval_pct_names = [
+ '区间涨跌幅:前复权',
+ '区间涨跌幅:前复权(%)',
+ '区间涨跌幅(%)',
+ '区间涨跌幅',
+ '涨跌幅:前复权',
+ '涨跌幅:前复权(%)',
+ '涨跌幅(%)',
+ '涨跌幅'
+ ]
+
+ # 优先精确匹配
+ for name in possible_interval_pct_names:
+ for col in df.columns:
+ if name in col:
+ interval_pct_col = col
+ break
+ if interval_pct_col:
+ break
+
+ if interval_pct_col:
+ print(f"\n使用字段: {interval_pct_col}")
+
+ # 转换为数值并筛选
+ filtered_df[interval_pct_col] = pd.to_numeric(filtered_df[interval_pct_col], errors='coerce')
+ before = len(filtered_df)
+ filtered_df = filtered_df[
+ (filtered_df[interval_pct_col].notna()) &
+ (filtered_df[interval_pct_col] < max_range_change)
+ ]
+ print(f" 区间涨跌幅筛选: {before} -> {len(filtered_df)} 只")
+ else:
+ print(f" ⚠️ 未找到区间涨跌幅字段,跳过涨跌幅筛选")
+ print(f" 可用字段: {list(df.columns[:10])}")
+
+ # 2. 筛选市值
+ market_cap_cols = [col for col in df.columns if '总市值' in col or '市值' in col]
+ if market_cap_cols:
+ col_name = market_cap_cols[0]
+ print(f"\n使用字段: {col_name}")
+
+ # 转换为数值(单位可能是亿或元)
+ filtered_df[col_name] = pd.to_numeric(filtered_df[col_name], errors='coerce')
+
+ # 判断单位(如果值很大,可能是元)
+ max_val = filtered_df[col_name].max()
+ if max_val > 100000: # 大于10万,认为是元
+ print(f" 检测到单位为元,转换为亿")
+ filtered_df[col_name] = filtered_df[col_name] / 100000000
+
+ before = len(filtered_df)
+ filtered_df = filtered_df[
+ (filtered_df[col_name].notna()) &
+ (filtered_df[col_name] >= min_market_cap) &
+ (filtered_df[col_name] <= max_market_cap)
+ ]
+ print(f" 市值筛选: {before} -> {len(filtered_df)} 只")
+
+ # 3. 去除ST股票(额外保险)
+ if '股票简称' in filtered_df.columns:
+ before = len(filtered_df)
+ filtered_df = filtered_df[~filtered_df['股票简称'].str.contains('ST', na=False)]
+ if before != len(filtered_df):
+ print(f" ST股票过滤: {before} -> {len(filtered_df)} 只")
+
+ print(f"\n筛选完成: {original_count} -> {len(filtered_df)} 只股票")
+
+ self.filtered_stocks = filtered_df
+ return filtered_df
+
+ def get_top_stocks(self, df: pd.DataFrame, top_n: int = 20) -> pd.DataFrame:
+ """
+ 获取主力资金净流入最多的前N只股票
+
+ Args:
+ df: 筛选后的数据
+ top_n: 取前N名,默认20
+
+ Returns:
+ 前N名股票DataFrame
+ """
+ if df is None or df.empty:
+ return df
+
+ # 查找主力资金相关列(智能匹配)
+ main_fund_col = None
+ main_fund_patterns = [
+ '区间主力资金流向', # 实际列名
+ '区间主力资金净流入',
+ '主力资金流向',
+ '主力资金净流入',
+ '主力净流入'
+ ]
+ for pattern in main_fund_patterns:
+ matching = [col for col in df.columns if pattern in col]
+ if matching:
+ main_fund_col = matching[0]
+ break
+
+ if main_fund_col:
+ print(f"\n使用字段排序: {main_fund_col}")
+
+ # 转换为数值并排序
+ df[main_fund_col] = pd.to_numeric(df[main_fund_col], errors='coerce')
+ top_df = df.nlargest(top_n, main_fund_col)
+
+ print(f"获取主力资金净流入前 {len(top_df)} 名")
+ return top_df
+ else:
+ # 如果没有主力资金列,直接返回前N条
+ print(f"未找到主力资金列,返回前{top_n}条数据")
+ return df.head(top_n)
+
+ def format_stock_list_for_analysis(self, df: pd.DataFrame) -> List[Dict]:
+ """
+ 格式化股票列表,准备提交给AI分析师
+
+ Args:
+ df: 股票数据DataFrame
+
+ Returns:
+ 格式化后的股票列表
+ """
+ if df is None or df.empty:
+ return []
+
+ stock_list = []
+
+ for idx, row in df.iterrows():
+ stock_data = {
+ 'symbol': row.get('股票代码', 'N/A'),
+ 'name': row.get('股票简称', 'N/A'),
+ 'industry': row.get('所属同花顺行业', row.get('所属行业', 'N/A')),
+ 'market_cap': row.get('总市值[20241209]', row.get('总市值', 'N/A')),
+ 'range_change': None,
+ 'main_fund_inflow': None,
+ 'pe_ratio': row.get('市盈率', 'N/A'),
+ 'pb_ratio': row.get('市净率', 'N/A'),
+ 'revenue': row.get('营业收入', row.get('营收', 'N/A')),
+ 'net_profit': row.get('净利润', 'N/A'),
+ 'scores': {},
+ 'raw_data': row.to_dict()
+ }
+
+ # 提取区间涨跌幅(使用智能匹配)
+ interval_pct_col = None
+ possible_names = [
+ '区间涨跌幅:前复权', '区间涨跌幅:前复权(%)', '区间涨跌幅(%)',
+ '区间涨跌幅', '涨跌幅:前复权', '涨跌幅:前复权(%)', '涨跌幅(%)', '涨跌幅'
+ ]
+ for name in possible_names:
+ for col in df.columns:
+ if name in col:
+ interval_pct_col = col
+ break
+ if interval_pct_col:
+ break
+ if interval_pct_col:
+ stock_data['range_change'] = row.get(interval_pct_col, 'N/A')
+
+ # 提取主力资金(智能匹配)
+ main_fund_col = None
+ main_fund_patterns = [
+ '区间主力资金流向', '区间主力资金净流入',
+ '主力资金流向', '主力资金净流入', '主力净流入'
+ ]
+ for pattern in main_fund_patterns:
+ matching = [col for col in df.columns if pattern in col]
+ if matching:
+ main_fund_col = matching[0]
+ break
+ if main_fund_col:
+ stock_data['main_fund_inflow'] = row.get(main_fund_col, 'N/A')
+
+ # 提取评分
+ score_keywords = ['评分', '能力']
+ for col in df.columns:
+ if any(keyword in col for keyword in score_keywords):
+ stock_data['scores'][col] = row.get(col, 'N/A')
+
+ stock_list.append(stock_data)
+
+ return stock_list
+
+ def print_stock_summary(self, stock_list: List[Dict]):
+ """打印股票摘要信息"""
+ print(f"\n{'='*80}")
+ print(f"📊 候选股票列表 ({len(stock_list)}只)")
+ print(f"{'='*80}")
+ print(f"{'序号':<4} {'代码':<8} {'名称':<12} {'行业':<15} {'主力资金':<12} {'涨跌幅':<8}")
+ print(f"{'-'*80}")
+
+ for i, stock in enumerate(stock_list, 1):
+ symbol = stock['symbol']
+ name = stock['name'][:10] if isinstance(stock['name'], str) else 'N/A'
+ industry = stock['industry'][:13] if isinstance(stock['industry'], str) else 'N/A'
+
+ # 格式化主力资金
+ main_fund = stock['main_fund_inflow']
+ if isinstance(main_fund, (int, float)):
+ if abs(main_fund) >= 100000000: # 大于1亿
+ main_fund_str = f"{main_fund/100000000:.2f}亿"
+ else:
+ main_fund_str = f"{main_fund/10000:.2f}万"
+ else:
+ main_fund_str = 'N/A'
+
+ # 格式化涨跌幅
+ change = stock['range_change']
+ if isinstance(change, (int, float)):
+ change_str = f"{change:.2f}%"
+ else:
+ change_str = 'N/A'
+
+ print(f"{i:<4} {symbol:<8} {name:<12} {industry:<15} {main_fund_str:<12} {change_str:<8}")
+
+ print(f"{'='*80}\n")
+
+# 全局实例
+main_force_selector = MainForceStockSelector()
+
diff --git a/main_force_ui.py b/main_force_ui.py
new file mode 100644
index 0000000..8f37ca3
--- /dev/null
+++ b/main_force_ui.py
@@ -0,0 +1,419 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+主力选股UI模块
+"""
+
+import streamlit as st
+from datetime import datetime, timedelta
+from main_force_analysis import MainForceAnalyzer
+from main_force_pdf_generator import display_report_download_section
+import pandas as pd
+
+def display_main_force_selector():
+ """显示主力选股界面"""
+
+ st.markdown("## 🎯 主力选股 - 智能筛选优质标的")
+ st.markdown("---")
+
+ st.markdown("""
+ ### 功能说明
+
+ 本功能通过以下步骤筛选优质股票:
+
+ 1. **数据获取**: 使用问财获取指定日期以来主力资金净流入前100名股票
+ 2. **智能筛选**: 过滤掉涨幅过高、市值不符的股票
+ 3. **AI分析**: 调用资金流向、行业板块、财务基本面三大分析师团队
+ 4. **综合决策**: 资深研究员综合评估,精选3-5只优质标的
+
+ **筛选标准**:
+ - ✅ 主力资金净流入较多
+ - ✅ 区间涨跌幅适中(避免追高)
+ - ✅ 财务基本面良好
+ - ✅ 行业前景明朗
+ - ✅ 综合素质优秀
+ """)
+
+ st.markdown("---")
+
+ # 参数设置
+ col1, col2, col3 = st.columns(3)
+
+ with col1:
+ date_option = st.selectbox(
+ "选择时间区间",
+ ["最近3个月", "最近6个月", "最近1年", "自定义日期"]
+ )
+
+ if date_option == "最近3个月":
+ days_ago = 90
+ start_date = None
+ elif date_option == "最近6个月":
+ days_ago = 180
+ start_date = None
+ elif date_option == "最近1年":
+ days_ago = 365
+ start_date = None
+ else:
+ custom_date = st.date_input(
+ "选择开始日期",
+ value=datetime.now() - timedelta(days=90)
+ )
+ start_date = f"{custom_date.year}年{custom_date.month}月{custom_date.day}日"
+ days_ago = None
+
+ with col2:
+ final_n = st.slider(
+ "最终精选数量",
+ min_value=3,
+ max_value=10,
+ value=5,
+ step=1,
+ help="最终推荐的股票数量"
+ )
+
+ with col3:
+ st.info("💡 系统将获取前100名股票,进行整体分析后精选优质标的")
+
+ # 高级选项
+ with st.expander("⚙️ 高级筛选参数"):
+ col1, col2, col3 = st.columns(3)
+
+ with col1:
+ max_change = st.number_input(
+ "最大涨跌幅(%)",
+ min_value=10.0,
+ max_value=100.0,
+ value=30.0,
+ step=5.0,
+ help="过滤掉涨幅过高的股票,避免追高"
+ )
+
+ with col2:
+ min_cap = st.number_input(
+ "最小市值(亿)",
+ min_value=10.0,
+ max_value=500.0,
+ value=50.0,
+ step=10.0
+ )
+
+ with col3:
+ max_cap = st.number_input(
+ "最大市值(亿)",
+ min_value=100.0,
+ max_value=50000.0,
+ value=5000.0,
+ step=100.0
+ )
+
+ # 模型选择
+ model = st.selectbox(
+ "选择AI模型",
+ ["deepseek-chat", "deepseek-reasoner"],
+ help="deepseek-chat速度快,deepseek-reasoner推理能力强"
+ )
+
+ st.markdown("---")
+
+ # 开始分析按钮
+ if st.button("🚀 开始主力选股", type="primary", use_container_width=True):
+
+ with st.spinner("正在获取数据并分析,这可能需要几分钟..."):
+
+ # 创建分析器
+ analyzer = MainForceAnalyzer(model=model)
+
+ # 运行分析
+ result = analyzer.run_full_analysis(
+ start_date=start_date,
+ days_ago=days_ago,
+ final_n=final_n
+ )
+
+ # 保存结果到session_state
+ st.session_state.main_force_result = result
+ st.session_state.main_force_analyzer = analyzer
+
+ # 显示结果
+ if result['success']:
+ st.success(f"✅ 分析完成!共筛选出 {len(result['final_recommendations'])} 只优质标的")
+ st.rerun()
+ else:
+ st.error(f"❌ 分析失败: {result.get('error', '未知错误')}")
+
+ # 显示分析结果
+ if 'main_force_result' in st.session_state:
+ result = st.session_state.main_force_result
+
+ if result['success']:
+ display_analysis_results(result, st.session_state.get('main_force_analyzer'))
+
+def display_analysis_results(result: dict, analyzer):
+ """显示分析结果"""
+
+ st.markdown("---")
+ st.markdown("## 📊 分析结果")
+
+ # 统计信息
+ col1, col2, col3 = st.columns(3)
+
+ with col1:
+ st.metric("获取股票数", result['total_stocks'])
+
+ with col2:
+ st.metric("筛选后", result['filtered_stocks'])
+
+ with col3:
+ st.metric("最终推荐", len(result['final_recommendations']))
+
+ st.markdown("---")
+
+ # 显示AI分析师完整报告
+ if analyzer and hasattr(analyzer, 'fund_flow_analysis'):
+ display_analyst_reports(analyzer)
+
+ st.markdown("---")
+
+ # 显示推荐股票
+ if result['final_recommendations']:
+ st.markdown("### ⭐ 精选推荐")
+
+ for rec in result['final_recommendations']:
+ with st.expander(
+ f"【第{rec['rank']}名】{rec['symbol']} - {rec['name']}",
+ expanded=(rec['rank'] <= 3)
+ ):
+ display_recommendation_detail(rec)
+
+ # 显示候选股票列表
+ if analyzer and analyzer.raw_stocks is not None and not analyzer.raw_stocks.empty:
+ st.markdown("---")
+ st.markdown("### 📋 候选股票列表(筛选后)")
+
+ # 选择关键列显示
+ display_cols = ['股票代码', '股票简称']
+
+ # 添加行业列
+ industry_cols = [col for col in analyzer.raw_stocks.columns if '行业' in col]
+ if industry_cols:
+ display_cols.append(industry_cols[0])
+
+ # 添加区间主力资金净流入(智能匹配)
+ main_fund_col = None
+ main_fund_patterns = [
+ '区间主力资金流向', # 实际列名
+ '区间主力资金净流入',
+ '主力资金流向',
+ '主力资金净流入',
+ '主力净流入',
+ '主力资金'
+ ]
+ for pattern in main_fund_patterns:
+ matching = [col for col in analyzer.raw_stocks.columns if pattern in col]
+ if matching:
+ main_fund_col = matching[0]
+ break
+ if main_fund_col:
+ display_cols.append(main_fund_col)
+
+ # 添加区间涨跌幅(前复权)(智能匹配)
+ interval_pct_col = None
+ interval_pct_patterns = [
+ '区间涨跌幅:前复权', '区间涨跌幅:前复权(%)', '区间涨跌幅(%)',
+ '区间涨跌幅', '涨跌幅:前复权', '涨跌幅:前复权(%)', '涨跌幅(%)', '涨跌幅'
+ ]
+ for pattern in interval_pct_patterns:
+ matching = [col for col in analyzer.raw_stocks.columns if pattern in col]
+ if matching:
+ interval_pct_col = matching[0]
+ break
+ if interval_pct_col:
+ display_cols.append(interval_pct_col)
+
+ # 添加市值、市盈率、市净率
+ for col_name in ['总市值', '市盈率', '市净率']:
+ matching_cols = [col for col in analyzer.raw_stocks.columns if col_name in col]
+ if matching_cols:
+ display_cols.append(matching_cols[0])
+
+ # 选择存在的列
+ final_cols = [col for col in display_cols if col in analyzer.raw_stocks.columns]
+
+ # 调试信息:显示找到的列名
+ with st.expander("🔍 调试信息 - 查看数据列", expanded=False):
+ st.caption("所有可用列:")
+ cols_list = list(analyzer.raw_stocks.columns)
+ st.write(cols_list)
+ st.caption(f"\n已选择显示的列: {final_cols}")
+ if main_fund_col:
+ st.success(f"✅ 找到主力资金列: {main_fund_col}")
+ else:
+ st.warning("⚠️ 未找到主力资金列")
+ if interval_pct_col:
+ st.success(f"✅ 找到涨跌幅列: {interval_pct_col}")
+ else:
+ st.warning("⚠️ 未找到涨跌幅列")
+
+ # 显示DataFrame
+ display_df = analyzer.raw_stocks[final_cols].copy()
+ st.dataframe(display_df, use_container_width=True, height=400)
+
+ # 显示统计
+ st.caption(f"共 {len(display_df)} 只候选股票,显示 {len(final_cols)} 个字段")
+
+ # 下载按钮
+ csv = display_df.to_csv(index=False, encoding='utf-8-sig')
+ st.download_button(
+ label="📥 下载候选列表CSV",
+ data=csv,
+ file_name=f"main_force_stocks_{datetime.now().strftime('%Y%m%d')}.csv",
+ mime="text/csv"
+ )
+
+ # 显示PDF报告下载区域
+ if analyzer and result:
+ display_report_download_section(analyzer, result)
+
+def display_recommendation_detail(rec: dict):
+ """显示单个推荐股票的详细信息"""
+
+ col1, col2 = st.columns([1, 1])
+
+ with col1:
+ st.markdown("#### 📌 推荐理由")
+ for reason in rec.get('reasons', []):
+ st.markdown(f"- {reason}")
+
+ st.markdown("#### 💡 投资亮点")
+ st.info(rec.get('highlights', 'N/A'))
+
+ with col2:
+ st.markdown("#### 📊 投资建议")
+ st.markdown(f"**建议仓位**: {rec.get('position', 'N/A')}")
+ st.markdown(f"**投资周期**: {rec.get('investment_period', 'N/A')}")
+
+ st.markdown("#### ⚠️ 风险提示")
+ st.warning(rec.get('risks', 'N/A'))
+
+ # 显示股票详细数据
+ if 'stock_data' in rec:
+ st.markdown("---")
+ st.markdown("#### 📊 股票详细数据")
+
+ stock_data = rec['stock_data']
+
+ # 创建数据展示
+ col1, col2, col3 = st.columns(3)
+
+ with col1:
+ st.metric("股票代码", stock_data.get('股票代码', 'N/A'))
+
+ # 显示行业
+ industry_keys = [k for k in stock_data.keys() if '行业' in k]
+ if industry_keys:
+ st.metric("所属行业", stock_data.get(industry_keys[0], 'N/A'))
+
+ with col2:
+ # 显示主力资金
+ fund_keys = [k for k in stock_data.keys() if '主力' in k and '净流入' in k]
+ if fund_keys:
+ fund_value = stock_data.get(fund_keys[0], 'N/A')
+ if isinstance(fund_value, (int, float)):
+ st.metric("主力资金净流入", f"{fund_value/100000000:.2f}亿")
+ else:
+ st.metric("主力资金净流入", str(fund_value))
+
+ with col3:
+ # 显示涨跌幅
+ change_keys = [k for k in stock_data.keys() if '涨跌幅' in k]
+ if change_keys:
+ change_value = stock_data.get(change_keys[0], 'N/A')
+ if isinstance(change_value, (int, float)):
+ st.metric("区间涨跌幅", f"{change_value:.2f}%")
+ else:
+ st.metric("区间涨跌幅", str(change_value))
+
+ # 显示其他关键指标
+ st.markdown("**其他关键指标:**")
+ metrics_col1, metrics_col2, metrics_col3 = st.columns(3)
+
+ with metrics_col1:
+ if '市盈率' in stock_data or any('市盈率' in k for k in stock_data.keys()):
+ pe_keys = [k for k in stock_data.keys() if '市盈率' in k]
+ if pe_keys:
+ st.caption(f"市盈率: {stock_data.get(pe_keys[0], 'N/A')}")
+
+ with metrics_col2:
+ if '市净率' in stock_data or any('市净率' in k for k in stock_data.keys()):
+ pb_keys = [k for k in stock_data.keys() if '市净率' in k]
+ if pb_keys:
+ st.caption(f"市净率: {stock_data.get(pb_keys[0], 'N/A')}")
+
+ with metrics_col3:
+ if '总市值' in stock_data or any('总市值' in k for k in stock_data.keys()):
+ cap_keys = [k for k in stock_data.keys() if '总市值' in k]
+ if cap_keys:
+ st.caption(f"总市值: {stock_data.get(cap_keys[0], 'N/A')}")
+
+def display_analyst_reports(analyzer):
+ """显示AI分析师完整报告"""
+
+ st.markdown("### 🤖 AI分析师团队完整报告")
+
+ # 创建三个标签页
+ tab1, tab2, tab3 = st.tabs(["💰 资金流向分析", "📊 行业板块分析", "📈 财务基本面分析"])
+
+ with tab1:
+ st.markdown("#### 💰 资金流向分析师报告")
+ st.markdown("---")
+ if hasattr(analyzer, 'fund_flow_analysis') and analyzer.fund_flow_analysis:
+ st.markdown(analyzer.fund_flow_analysis)
+ else:
+ st.info("暂无资金流向分析报告")
+
+ with tab2:
+ st.markdown("#### 📊 行业板块及市场热点分析师报告")
+ st.markdown("---")
+ if hasattr(analyzer, 'industry_analysis') and analyzer.industry_analysis:
+ st.markdown(analyzer.industry_analysis)
+ else:
+ st.info("暂无行业板块分析报告")
+
+ with tab3:
+ st.markdown("#### 📈 财务基本面分析师报告")
+ st.markdown("---")
+ if hasattr(analyzer, 'fundamental_analysis') and analyzer.fundamental_analysis:
+ st.markdown(analyzer.fundamental_analysis)
+ else:
+ st.info("暂无财务基本面分析报告")
+
+def format_number(value, unit='', suffix=''):
+ """格式化数字显示"""
+ if value is None or value == 'N/A':
+ return 'N/A'
+
+ try:
+ num = float(value)
+
+ # 如果单位是亿,需要转换
+ if unit == '亿':
+ if abs(num) >= 100000000: # 大于1亿(以元为单位)
+ num = num / 100000000
+ elif abs(num) < 100: # 小于100,可能已经是亿
+ pass
+ else: # 100-100000000之间,可能是万
+ num = num / 10000
+
+ # 格式化显示
+ if abs(num) >= 1000:
+ formatted = f"{num:,.2f}"
+ elif abs(num) >= 1:
+ formatted = f"{num:.2f}"
+ else:
+ formatted = f"{num:.4f}"
+
+ return f"{formatted}{suffix}"
+ except (ValueError, TypeError):
+ return str(value)
+
diff --git a/pdf_generator.py b/pdf_generator.py
index 0bb0ada..346d525 100644
--- a/pdf_generator.py
+++ b/pdf_generator.py
@@ -249,8 +249,9 @@ def display_pdf_export_section(stock_info, agents_results, discussion_result, fi
col1, col2, col3 = st.columns([1, 2, 1])
with col2:
- # 生成PDF报告按钮
- if st.button("📄 生成并下载PDF报告", type="primary", use_container_width=True):
+ # 生成PDF报告按钮(使用股票代码作为key的一部分,确保唯一性)
+ button_key = f"pdf_btn_{stock_info.get('symbol', 'unknown')}"
+ if st.button("📄 生成并下载PDF报告", type="primary", use_container_width=True, key=button_key):
with st.spinner("正在生成PDF报告..."):
try:
# 生成PDF内容
diff --git a/stock_data.py b/stock_data.py
index 3dd9700..c80e529 100644
--- a/stock_data.py
+++ b/stock_data.py
@@ -463,20 +463,14 @@ class StockDataFetcher:
except Exception as e:
print(f"获取财务指标失败: {e}")
- # 5. 获取季度业绩(尝试不同API)
+ # 5. 获取季度业绩(使用问财)
try:
- # 尝试获取业绩预告
- quarter_data = ak.stock_profit_forecast_em(symbol=symbol)
- if quarter_data is not None and not quarter_data.empty:
- financial_data["quarter_data"] = quarter_data.head(4).to_dict('records')
- except:
- try:
- # 备用方案:获取季度财报
- quarter_data = ak.stock_financial_report_sina(stock=symbol, symbol="季报")
- if quarter_data is not None and not quarter_data.empty:
- financial_data["quarter_data"] = quarter_data.head(4).to_dict('records')
- except Exception as e:
- print(f"获取季度数据失败: {e}")
+ # 使用pywencai获取季报数据
+ quarter_data = self._get_quarter_data_from_wencai(symbol)
+ if quarter_data:
+ financial_data["quarter_data"] = quarter_data
+ except Exception as e:
+ print(f"获取季度数据失败: {e}")
return financial_data
@@ -484,6 +478,75 @@ class StockDataFetcher:
print(f"获取中国股票财务数据失败: {e}")
return financial_data
+ def _get_quarter_data_from_wencai(self, symbol):
+ """使用问财获取季报数据 - 直接返回原始数据给AI分析"""
+ try:
+ # 构建查询语句 - 获取最近4个季度的数据
+ query = f"{symbol}最近4个季度的营业收入、净利润、每股收益、净资产收益率、营业收入同比增长率、净利润同比增长率"
+
+ # 使用pywencai查询
+ print(f"正在使用问财获取 {symbol} 的季报数据...")
+ result = pywencai.get(question=query, perpage=10)
+
+ # 检查返回类型
+ if result is None:
+ print(f"问财未返回 {symbol} 的季报数据")
+ return None
+
+ # 处理不同返回类型
+ quarter_data = None
+
+ # 如果返回的是DataFrame
+ if isinstance(result, pd.DataFrame):
+ if result.empty:
+ print(f"问财返回的DataFrame为空")
+ return None
+
+ print(f" 问财返回DataFrame,共 {len(result)} 行数据")
+ print(f" 列名: {list(result.columns)}")
+
+ # 将DataFrame转为字典列表,保留所有原始数据
+ quarter_data = {
+ 'data_type': 'dataframe',
+ 'columns': list(result.columns),
+ 'records': result.head(10).to_dict('records'), # 最多取10条
+ 'row_count': len(result),
+ 'summary': f"获取到{len(result)}条季报相关数据"
+ }
+
+ # 如果返回的是字典
+ elif isinstance(result, dict):
+ print(f" 问财返回字典数据")
+ print(f" 字典键: {list(result.keys())}")
+
+ quarter_data = {
+ 'data_type': 'dict',
+ 'raw_data': result,
+ 'summary': f"获取到季报字典数据,包含 {len(result)} 个字段"
+ }
+
+ # 其他类型
+ else:
+ print(f" 问财返回未知类型: {type(result)}")
+ quarter_data = {
+ 'data_type': 'unknown',
+ 'raw_data': str(result),
+ 'summary': f"获取到季报数据,类型: {type(result).__name__}"
+ }
+
+ if quarter_data:
+ print(f"✅ 成功获取季报数据,将原始数据交由AI分析师处理")
+ return quarter_data
+ else:
+ print(f"⚠️ 未能获取有效的季报数据")
+ return None
+
+ except Exception as e:
+ print(f"使用问财获取季报数据失败: {e}")
+ import traceback
+ traceback.print_exc()
+ return None
+
def _get_us_financial_data(self, symbol):
"""获取美股财务数据"""
financial_data = {
diff --git a/test_main_force.py b/test_main_force.py
new file mode 100644
index 0000000..6168c59
--- /dev/null
+++ b/test_main_force.py
@@ -0,0 +1,221 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+主力选股功能测试脚本
+快速验证功能是否正常工作
+"""
+
+import sys
+from datetime import datetime, timedelta
+
+def test_imports():
+ """测试模块导入"""
+ print("="*60)
+ print("测试1: 检查模块导入")
+ print("="*60)
+
+ try:
+ print("导入 pywencai...", end=" ")
+ import pywencai
+ print("✅")
+ except Exception as e:
+ print(f"❌ {e}")
+ return False
+
+ try:
+ print("导入 main_force_selector...", end=" ")
+ from main_force_selector import main_force_selector
+ print("✅")
+ except Exception as e:
+ print(f"❌ {e}")
+ return False
+
+ try:
+ print("导入 main_force_analysis...", end=" ")
+ from main_force_analysis import MainForceAnalyzer
+ print("✅")
+ except Exception as e:
+ print(f"❌ {e}")
+ return False
+
+ try:
+ print("导入 main_force_ui...", end=" ")
+ from main_force_ui import display_main_force_selector
+ print("✅")
+ except Exception as e:
+ print(f"❌ {e}")
+ return False
+
+ print("\n✅ 所有模块导入成功!\n")
+ return True
+
+def test_data_fetch():
+ """测试数据获取"""
+ print("="*60)
+ print("测试2: 测试数据获取功能")
+ print("="*60)
+
+ try:
+ from main_force_selector import main_force_selector
+
+ # 使用较短的时间范围进行测试
+ print("\n尝试获取最近30天的主力资金数据...")
+ success, data, message = main_force_selector.get_main_force_stocks(days_ago=30)
+
+ if success:
+ print(f"\n✅ 数据获取成功!")
+ print(f" 获取到 {len(data)} 只股票")
+ print(f"\n前5只股票:")
+ print(data.head(5) if len(data) > 0 else "无数据")
+ return True
+ else:
+ print(f"\n❌ 数据获取失败: {message}")
+ print("\n可能原因:")
+ print(" 1. 网络连接问题")
+ print(" 2. pywencai服务暂时不可用")
+ print(" 3. 需要安装Node.js >= 16.0")
+ print("\n请检查:")
+ print(" - 网络连接是否正常")
+ print(" - Node.js版本: node --version")
+ print(" - pywencai是否正确安装: pip list | findstr pywencai")
+ return False
+
+ except Exception as e:
+ print(f"\n❌ 测试过程出错: {e}")
+ import traceback
+ traceback.print_exc()
+ return False
+
+def test_filter():
+ """测试筛选功能"""
+ print("\n" + "="*60)
+ print("测试3: 测试筛选功能")
+ print("="*60)
+
+ try:
+ from main_force_selector import main_force_selector
+ import pandas as pd
+
+ # 创建测试数据
+ test_data = pd.DataFrame({
+ '股票代码': ['000001', '000002', '600519', '300750'],
+ '股票简称': ['平安银行', '万科A', '贵州茅台', '宁德时代'],
+ '区间涨跌幅': [15.5, 35.8, 12.3, 28.9],
+ '总市值': [3000, 2500, 25000, 9000],
+ '主力资金净流入': [50000000, 80000000, 120000000, 95000000]
+ })
+
+ print("\n原始测试数据:")
+ print(test_data)
+
+ print("\n应用筛选条件:")
+ print(" - 区间涨跌幅 < 30%")
+ print(" - 市值 50-1300亿")
+
+ filtered_data = main_force_selector.filter_stocks(
+ test_data,
+ max_range_change=30.0,
+ min_market_cap=50.0,
+ max_market_cap=1300.0
+ )
+
+ print("\n筛选后数据:")
+ print(filtered_data)
+
+ print("\n✅ 筛选功能正常!")
+ return True
+
+ except Exception as e:
+ print(f"\n❌ 筛选测试失败: {e}")
+ import traceback
+ traceback.print_exc()
+ return False
+
+def test_ai_analysis():
+ """测试AI分析(需要API配置)"""
+ print("\n" + "="*60)
+ print("测试4: 测试AI分析功能")
+ print("="*60)
+
+ try:
+ import os
+ from dotenv import load_dotenv
+
+ load_dotenv()
+
+ api_key = os.getenv('DEEPSEEK_API_KEY')
+ if not api_key:
+ print("\n⚠️ 未配置DEEPSEEK_API_KEY,跳过AI分析测试")
+ print(" 请在.env文件中配置API密钥后再测试AI功能")
+ return None
+
+ print("\n✅ API密钥已配置")
+ print(" 如需测试完整AI分析,请运行主程序")
+ return True
+
+ except Exception as e:
+ print(f"\n⚠️ {e}")
+ return None
+
+def main():
+ """主测试函数"""
+ print("\n" + "="*80)
+ print(" "*20 + "主力选股功能测试")
+ print("="*80 + "\n")
+
+ results = []
+
+ # 测试1: 模块导入
+ result1 = test_imports()
+ results.append(("模块导入", result1))
+
+ if not result1:
+ print("\n❌ 模块导入失败,请先安装依赖:")
+ print(" pip install pywencai pandas streamlit")
+ return
+
+ # 测试2: 数据获取
+ result2 = test_data_fetch()
+ results.append(("数据获取", result2))
+
+ # 测试3: 筛选功能
+ result3 = test_filter()
+ results.append(("数据筛选", result3))
+
+ # 测试4: AI分析
+ result4 = test_ai_analysis()
+ if result4 is not None:
+ results.append(("AI分析", result4))
+
+ # 总结
+ print("\n" + "="*80)
+ print(" "*30 + "测试总结")
+ print("="*80 + "\n")
+
+ for test_name, result in results:
+ status = "✅ 通过" if result else "❌ 失败"
+ print(f"{test_name:<15} {status}")
+
+ passed = sum(1 for _, r in results if r)
+ total = len(results)
+
+ print(f"\n总计: {passed}/{total} 项测试通过")
+
+ if passed == total:
+ print("\n🎉 恭喜!所有测试通过,主力选股功能可以正常使用!")
+ print("\n下一步:")
+ print(" 1. 运行主程序: streamlit run app.py")
+ print(" 2. 点击侧边栏的 '🎯 主力选股' 按钮")
+ print(" 3. 设置参数并开始分析")
+ else:
+ print("\n⚠️ 部分测试未通过,请根据上述错误信息进行排查")
+ print("\n常见问题:")
+ print(" 1. 数据获取失败 → 检查网络和Node.js版本")
+ print(" 2. 模块导入失败 → 检查依赖安装")
+ print(" 3. API测试失败 → 检查.env配置")
+
+ print("\n" + "="*80 + "\n")
+
+if __name__ == "__main__":
+ main()
+
diff --git a/主力选股使用指南.md b/主力选股使用指南.md
new file mode 100644
index 0000000..fd3c30b
--- /dev/null
+++ b/主力选股使用指南.md
@@ -0,0 +1,313 @@
+# 主力选股功能 - 快速使用指南
+
+## ✨ 核心特点
+
+**整体批量分析,不是逐个分析!**
+
+传统方式:获取100只股票 → 逐个分析(慢,费时)
+本系统:获取100只股票 → **整体分析** → 精选优质标的(快速高效)
+
+## 🔄 工作流程
+
+```
+1️⃣ 数据获取
+ └─ 使用pywencai获取主力资金净流入前100名
+ └─ 包含:资金、涨跌幅、行业、市值、财务等完整数据
+
+2️⃣ 智能筛选
+ └─ 涨幅过滤(<30%,避免追高)
+ └─ 市值筛选(50-1300亿)
+ └─ ST股过滤
+
+3️⃣ 整体分析(核心创新!)
+ ├─ 💰 资金流向分析师
+ │ └─ 分析整体资金流向特征
+ │ └─ 识别板块资金集中度
+ │ └─ 发现资金流入但涨幅不高的机会
+ │
+ ├─ 📊 行业板块分析师
+ │ └─ 识别当前热点板块
+ │ └─ 分析板块轮动态势
+ │ └─ 评估各行业发展前景
+ │
+ └─ 📈 财务基本面分析师
+ └─ 评估整体财务质量
+ └─ 筛选估值合理的标的
+ └─ 关注成长性突出的股票
+
+4️⃣ 综合决策
+ └─ 资深研究员AI综合三位分析师意见
+ └─ 从候选股票中精选3-5只最优标的
+ └─ 给出详细推荐理由和风险提示
+```
+
+## 🚀 快速开始
+
+### 步骤1:进入功能
+侧边栏点击 **🎯 主力选股**
+
+### 步骤2:设置参数
+- **时间区间**:最近3个月(默认)
+- **精选数量**:5只(默认)
+- **AI模型**:deepseek-chat(快速)
+
+### 步骤3:开始分析
+点击 **🚀 开始主力选股**
+
+### 预期时间
+- 数据获取:30-60秒
+- 整体分析:1-2分钟
+- 总耗时:约2-3分钟(比逐个分析快10倍!)
+
+## 📊 分析逻辑
+
+### 为什么是"整体分析"?
+
+**传统逐个分析的问题**:
+- ❌ 耗时长(100只股票 × 15秒 = 25分钟)
+- ❌ 成本高(大量API调用)
+- ❌ 缺乏全局视角
+
+**整体分析的优势**:
+- ✅ 快速高效(2-3分钟完成)
+- ✅ 成本低(3次AI调用)
+- ✅ 宏观视角(板块热点、资金流向)
+- ✅ 更符合实际选股逻辑
+
+### AI分析师如何工作?
+
+#### 💰 资金流向分析师
+```
+看什么:
+- 哪些板块资金流入最多?
+- 主力资金的整体行为特征
+- 资金流入但涨幅不高的潜力股
+
+输出:
+- 资金集中的热点板块
+- 值得关注的3-5只股票
+```
+
+#### 📊 行业板块分析师
+```
+看什么:
+- 当前市场热点板块
+- 板块轮动和趋势
+- 行业发展前景
+
+输出:
+- 热点行业识别
+- 板块机会评估
+- 推荐的优质板块标的
+```
+
+#### 📈 财务基本面分析师
+```
+看什么:
+- 整体财务质量分布
+- 估值水平合理性
+- 成长性突出的股票
+
+输出:
+- 基本面优秀的股票
+- 估值合理的标的
+- 财务风险提示
+```
+
+## 🎯 筛选标准
+
+### 数据层面
+- ✅ 主力资金净流入前100名
+- ✅ 区间涨跌幅 < 30%(避免追高)
+- ✅ 市值 50-1300亿(优质中盘)
+- ✅ 排除ST股
+
+### 分析层面
+- ✅ 资金面:主力看好,持续流入
+- ✅ 行业面:热点板块,前景明朗
+- ✅ 基本面:财务健康,成长性好
+- ✅ 综合性:三方面都不错的均衡标的
+
+## 📋 结果展示
+
+### 精选推荐(3-5只)
+每只股票包含:
+- **推荐理由**(3-5条,综合三位分析师观点)
+- **投资亮点**(突出优势)
+- **风险提示**(需要注意的风险)
+- **建议仓位**(如20-30%)
+- **投资周期**(短期/中期/长期)
+- **详细数据**(主力资金、涨跌幅、行业等)
+
+### 候选列表(筛选后所有股票)
+- 完整的数据表格
+- 可下载CSV格式
+- 方便进一步研究
+
+## 💡 使用建议
+
+### 最佳使用时机
+```
+周末(推荐)
+ └─ 为下周交易做准备
+ └─ 时间充裕,深入研究
+
+月初
+ └─ 调整持仓结构
+ └─ 发现新的机会
+
+热点出现时
+ └─ 快速捕捉市场机会
+ └─ 参数设置:最近1个月
+```
+
+### 参数配置建议
+```
+保守型:
+ 时间:6个月
+ 涨幅:<20%
+ 精选:3只
+
+平衡型(推荐):
+ 时间:3个月
+ 涨幅:<30%
+ 精选:5只
+
+激进型:
+ 时间:1个月
+ 涨幅:<40%
+ 精选:8只
+```
+
+### 后续操作
+```
+1. 查看精选推荐
+ ↓
+2. 阅读AI分析报告
+ ↓
+3. 查看候选列表完整数据
+ ↓
+4. 选择2-3只深入研究
+ ↓
+5. 查看K线和技术指标
+ ↓
+6. 周一择机建仓
+ ↓
+7. 加入实时监测
+```
+
+## ⚠️ 注意事项
+
+### 环境要求
+```bash
+Node.js >= 16.0 # 重要!pywencai依赖
+pywencai >= 0.7.0
+DEEPSEEK_API_KEY # 在.env中配置
+```
+
+### 数据时效
+- 数据有1-2天延迟
+- 适合中短期投资
+- 不适合日内交易
+
+### 市场环境
+- **牛市**:效果最佳,资金推动明显
+- **震荡**:关注短期机会
+- **熊市**:谨慎使用,关注防御性板块
+
+## 🆚 对比说明
+
+### 主力选股 vs 单个分析
+
+| 特性 | 主力选股 | 单个分析 |
+|-----|---------|---------|
+| 分析对象 | 100只股票整体 | 1只股票 |
+| 分析视角 | 宏观(板块、热点) | 微观(个股) |
+| 耗时 | 2-3分钟 | 10-15秒/只 |
+| 适用场景 | 选股、发现机会 | 研究特定股票 |
+| 输出 | 3-5只精选标的 | 1只详细分析 |
+
+### 何时使用?
+
+**使用主力选股**:
+- ✅ 不知道买什么股票
+- ✅ 想发现市场热点
+- ✅ 需要批量筛选
+- ✅ 寻找新的投资机会
+
+**使用单个分析**:
+- ✅ 已有明确目标股票
+- ✅ 需要深入研究
+- ✅ 验证投资想法
+- ✅ 查看详细技术面
+
+## ❓ 常见问题
+
+### Q: 为什么只精选3-5只?
+A: 精选的目的是找出最优标的,太多会降低质量。3-5只足够分散风险,又便于深入研究。
+
+### Q: 推荐结果每次一样吗?
+A: 不一样。数据每天更新,AI分析也会考虑最新市场环境。
+
+### Q: 如何判断推荐质量?
+A: 看推荐理由是否具体、有数据支撑、逻辑连贯。建议结合自己判断,不要盲目跟从。
+
+### Q: 可以每天用吗?
+A: 可以,但建议每周1-2次。数据有延迟,频繁使用意义不大。
+
+## 📈 实战示例
+
+### 场景:周末选股
+```
+1. 周日晚上运行主力选股
+ 参数:最近3个月,精选5只
+
+2. 查看结果
+ - 精选推荐:5只
+ - AI分析:资金、行业、基本面
+
+3. 深入研究
+ - 选择前3只
+ - 查看K线图
+ - 了解公司业务
+
+4. 制定策略
+ - 周一开盘价
+ - 买入价位
+ - 止损止盈
+
+5. 周一执行
+ - 分批建仓
+ - 加入监测
+```
+
+## 🎓 学习建议
+
+1. **先了解逻辑**
+ - 理解整体分析的优势
+ - 明白三位分析师的作用
+
+2. **实践使用**
+ - 先用默认参数试试
+ - 查看完整结果
+
+3. **调整优化**
+ - 根据市场环境调整参数
+ - 找到适合自己的配置
+
+4. **结合其他功能**
+ - 单个分析:深入研究精选标的
+ - 实时监测:跟踪股价变化
+ - 批量分析:对比分析
+
+---
+
+**开始使用**:
+```bash
+streamlit run app.py
+→ 点击 🎯 主力选股
+→ 开始分析
+```
+
+**祝您投资顺利!** 🚀📈💰
+
diff --git a/主力选股功能说明.md b/主力选股功能说明.md
new file mode 100644
index 0000000..221f409
--- /dev/null
+++ b/主力选股功能说明.md
@@ -0,0 +1,435 @@
+# 主力选股功能说明
+
+## 功能概述
+
+**主力选股**是一个智能选股系统,通过分析主力资金流向,结合AI多智能体分析,自动筛选出近期拉升概率高、主力资金净流入较多、综合素质优秀的优质投资标的。
+
+## 核心特点
+
+### 1. 数据来源权威
+- 使用 `pywencai` 获取同花顺数据
+- 主力资金流向真实可靠
+- 包含完整的财务评分和行业信息
+
+### 2. 智能筛选机制
+- **主力资金排名**:优先选择主力资金净流入前100名
+- **涨幅过滤**:排除涨幅过高的股票,避免追高
+- **市值筛选**:聚焦50-1300亿市值区间的优质标的
+- **基本面保护**:自动过滤ST股、科创板(可配置)
+
+### 3. AI多维度分析
+整合三大分析师团队:
+- **💰 资金流向分析师**:分析主力资金行为和市场情绪
+- **📊 行业板块分析师**:评估行业前景和市场热点
+- **📈 财务基本面分析师**:考察盈利能力和财务健康度
+
+### 4. 资深研究员决策
+- AI研究员综合所有分析结果
+- 从候选股票中精选最优标的
+- 提供详细的推荐理由和风险提示
+
+## 工作流程
+
+```
+┌─────────────────────────────────────────────────────────┐
+│ 1. 数据获取 │
+│ 使用pywencai获取主力资金净流入前100名股票 │
+│ 包含:资金流向、涨跌幅、市值、财务指标、行业等 │
+└──────────────────┬──────────────────────────────────────┘
+ │
+┌──────────────────▼──────────────────────────────────────┐
+│ 2. 智能筛选 │
+│ ✓ 区间涨跌幅 < 30%(避免追高) │
+│ ✓ 市值 50-1300亿(优质中盘股) │
+│ ✓ 排除ST股和科创板 │
+│ ✓ 按主力资金排序,取前20名 │
+└──────────────────┬──────────────────────────────────────┘
+ │
+┌──────────────────▼──────────────────────────────────────┐
+│ 3. AI深度分析 │
+│ 对每只候选股票进行三维度分析: │
+│ • 资金流向(主力行为、资金结构) │
+│ • 行业板块(行业地位、市场热度) │
+│ • 财务基本面(盈利能力、成长性) │
+└──────────────────┬──────────────────────────────────────┘
+ │
+┌──────────────────▼──────────────────────────────────────┐
+│ 4. 综合决策 │
+│ 资深研究员AI综合评估: │
+│ • 对比所有候选股票 │
+│ • 精选3-5只最优标的 │
+│ • 提供推荐理由、投资亮点、风险提示 │
+└─────────────────────────────────────────────────────────┘
+```
+
+## 使用方法
+
+### 步骤1:进入主力选股界面
+
+在系统侧边栏点击 **🎯 主力选股** 按钮
+
+### 步骤2:设置参数
+
+#### 基础参数
+1. **时间区间**:选择数据统计的时间范围
+ - 最近3个月(默认)
+ - 最近6个月
+ - 最近1年
+ - 自定义日期
+
+2. **初步筛选数量**:从符合条件的股票中选取前N名(默认20只)
+ - 范围:10-50只
+ - 建议:20-30只
+
+3. **最终精选数量**:最终推荐的股票数量(默认5只)
+ - 范围:3-10只
+ - 建议:3-5只
+
+#### 高级参数(可选)
+4. **最大涨跌幅**:过滤涨幅过高的股票(默认30%)
+ - 作用:避免追高
+ - 建议:20%-40%
+
+5. **市值范围**:
+ - 最小市值:默认50亿
+ - 最大市值:默认1300亿
+ - 作用:聚焦优质中盘股
+
+6. **AI模型**:选择分析使用的AI模型
+ - `deepseek-chat`:速度快,适合快速筛选
+ - `deepseek-reasoner`:推理能力强,分析更深入
+
+### 步骤3:开始分析
+
+点击 **🚀 开始主力选股** 按钮,系统将自动:
+1. 获取数据(约30-60秒)
+2. 智能筛选(约10秒)
+3. AI深度分析(约2-5分钟,取决于股票数量)
+4. 综合决策(约30秒)
+
+### 步骤4:查看结果
+
+#### 统计概览
+显示四个关键指标:
+- **获取股票数**:原始数据数量(通常100只)
+- **筛选后**:符合筛选条件的数量
+- **深度分析**:成功完成AI分析的数量
+- **最终推荐**:精选的优质标的数量
+
+#### 精选推荐
+对每只推荐股票,系统提供:
+
+1. **📌 推荐理由**(3-5条核心理由)
+ - 为什么推荐这只股票
+ - 关键优势是什么
+
+2. **💡 投资亮点**
+ - 突出的投资价值
+ - 主要看点
+
+3. **⚠️ 风险提示**
+ - 需要关注的风险
+ - 注意事项
+
+4. **📊 投资建议**
+ - 建议仓位:如20-30%
+ - 目标价位:预期目标价
+ - 投资周期:短期/中期/长期
+
+5. **🤖 AI分析师详细报告**
+ - 资金流向分析
+ - 行业板块分析
+ - 财务基本面分析
+
+#### 候选股票列表
+显示所有候选股票的详细数据:
+- 股票代码、名称
+- 所属行业
+- 市值
+- 主力资金净流入
+- 区间涨跌幅
+- 市盈率、市净率
+
+支持下载CSV格式数据
+
+## 筛选逻辑详解
+
+### 主力资金标准
+- **主力资金净流入排名**:选择资金流入最多的股票
+- **资金持续性**:优先考虑持续流入的股票
+- **资金占比**:主力资金占比高的更受关注
+
+### 涨幅控制
+```
+✅ 涨幅适中(<30%):
+ - 还有上涨空间
+ - 风险相对可控
+
+❌ 涨幅过高(>30%):
+ - 可能面临回调压力
+ - 追高风险较大
+ - 自动过滤
+```
+
+### 市值要求
+```
+市值范围:50-1300亿
+
+原因:
+✓ <50亿:流动性差,风险高
+✓ 50-1300亿:优质中盘股,兼顾成长性和稳定性
+✓ >1300亿:大盘股,成长空间有限
+```
+
+### 基本面保护
+- 自动排除ST股票(特别处理)
+- 可配置排除科创板(波动较大)
+- 要求财务数据完整
+
+## AI分析维度
+
+### 1. 资金流向分析
+**分析内容**:
+- 主力资金流向趋势
+- 大单、中单、小单分布
+- 机构与散户资金博弈
+- 资金推动与股价配合度
+- 换手率与成交活跃度
+
+**评判标准**:
+- ✅ 主力持续流入 + 股价上涨 = 强势信号
+- ⚠️ 主力流出 + 股价上涨 = 警惕接盘
+- 💡 主力流入 + 股价下跌 = 可能低吸
+- ❌ 主力流出 + 股价下跌 = 弱势信号
+
+### 2. 行业板块分析
+**分析内容**:
+- 所属行业发展前景
+- 行业在市场中的地位
+- 板块轮动和市场热度
+- 政策支持和行业催化
+- 行业竞争格局
+
+**关注重点**:
+- 行业是否处于上升周期
+- 是否有政策利好支持
+- 市场关注度和热度
+- 行业龙头还是跟随者
+
+### 3. 财务基本面分析
+**分析内容**:
+- 盈利能力(净利润、ROE等)
+- 成长能力(营收增长、利润增长)
+- 营运能力(周转率等)
+- 偿债能力(资产负债率)
+- 现金流状况
+- 资产质量
+
+**评分体系**:
+系统获取8大评分:
+1. 盈利能力评分
+2. 成长能力评分
+3. 营运能力评分
+4. 偿债能力评分
+5. 现金流评分
+6. 资产质量评分
+7. 流动性评分
+8. 资本充足性评分
+
+## 使用建议
+
+### 适用场景
+1. **中短期波段交易**
+ - 捕捉主力资金推动的行情
+ - 关注3个月内的主力行为
+
+2. **行业轮动投资**
+ - 发现市场热点板块
+ - 把握资金流向切换
+
+3. **价值成长投资**
+ - 寻找被主力关注的潜力股
+ - 结合基本面和资金面
+
+### 使用频率
+- **建议**:每周执行1-2次
+- **最佳时机**:周末或交易日收盘后
+- **避免**:开盘前或盘中(数据可能不完整)
+
+### 注意事项
+
+#### 1. 数据时效性
+- 数据有一定延迟(通常1-2天)
+- 适合中短期布局,不适合日内交易
+- 建议结合实时行情确认
+
+#### 2. 市场环境
+- **牛市**:主力选股效果更佳,资金推动明显
+- **震荡市**:关注短期波段机会
+- **熊市**:谨慎使用,关注防御性板块
+
+#### 3. 仓位管理
+- 不要孤注一掷,建议分散投资
+- 单只股票仓位不超过30%
+- 保留一定现金仓位
+
+#### 4. 风险控制
+- 设置止损位(建议-8%到-10%)
+- 设置止盈位(建议+15%到+20%)
+- 不追高,不贪婪
+
+## 常见问题
+
+### Q1: 为什么获取数据失败?
+**可能原因**:
+1. 网络连接问题
+2. pywencai服务暂时不可用
+3. 查询语句触发限制
+
+**解决方法**:
+- 检查网络连接
+- 等待片刻后重试
+- 调整时间区间参数
+
+### Q2: 为什么筛选后股票很少?
+**可能原因**:
+1. 筛选条件过于严格
+2. 当前市场符合条件的股票较少
+
+**解决方法**:
+- 适当放宽涨跌幅限制(如改为40%)
+- 扩大市值范围
+- 调整时间区间
+
+### Q3: AI分析需要多长时间?
+**时间估算**:
+- 单只股票:约10-15秒
+- 20只股票:约3-5分钟
+- 50只股票:约8-10分钟
+
+**影响因素**:
+- 股票数量
+- AI模型选择
+- 网络速度
+
+### Q4: 推荐结果每次都一样吗?
+**答案**:不完全一样
+
+**原因**:
+1. 数据每天更新(主力资金、价格等)
+2. AI分析会考虑最新市场环境
+3. 不同时间段的数据可能不同
+
+### Q5: 如何判断推荐质量?
+**评判标准**:
+1. **理由充分性**:推荐理由是否具体、有说服力
+2. **数据支撑**:是否有真实数据支撑
+3. **风险提示**:是否明确指出风险
+4. **逻辑连贯性**:资金、行业、基本面是否相互印证
+
+**建议**:
+- 认真阅读AI分析报告
+- 对比多个候选股票
+- 结合自己的投资逻辑
+- 不盲目跟从,独立思考
+
+## 技术要求
+
+### 环境依赖
+```bash
+# 必须安装
+pywencai>=0.7.0
+pandas
+streamlit
+
+# Node.js要求
+Node.js >= 16.0(pywencai依赖)
+```
+
+### 安装方法
+```bash
+# 安装Python依赖
+pip install pywencai pandas streamlit
+
+# 检查Node.js版本
+node --version
+
+# 如果版本低于16.0,需要升级Node.js
+```
+
+## 最佳实践
+
+### 1. 每周选股流程
+```
+周日晚上 → 运行主力选股
+ ↓
+ 查看推荐结果
+ ↓
+ 深入研究前3名
+ ↓
+ 周一开盘前制定策略
+ ↓
+ 择机建仓
+```
+
+### 2. 结合技术分析
+推荐股票后,建议再进行:
+- K线形态分析
+- 支撑压力位判断
+- 均线系统确认
+- 成交量变化观察
+
+### 3. 分批建仓
+```
+发现优质标的后:
+第1批:20% → 试探性建仓
+第2批:30% → 确认后加仓
+第3批:30% → 突破后追加
+保留:20% → 备用资金
+```
+
+### 4. 持仓管理
+```
+持有期间关注:
+✓ 主力资金是否继续流入
+✓ 股价是否沿着上升趋势
+✓ 行业是否仍有催化
+✓ 基本面是否发生变化
+
+出现以下情况考虑减仓:
+✗ 主力资金持续流出
+✗ 技术面破位
+✗ 基本面恶化
+✗ 达到目标价位
+```
+
+## 更新日志
+
+### v1.0.0 (2025-10-09)
+- ✅ 初始版本发布
+- ✅ 支持主力资金数据获取
+- ✅ 实现智能筛选算法
+- ✅ 集成AI三大分析师
+- ✅ 资深研究员综合决策
+- ✅ Web界面集成
+
+## 技术支持
+
+如遇问题,请提供:
+1. 错误截图或错误信息
+2. 使用的参数配置
+3. 操作步骤描述
+
+## 免责声明
+
+**重要提示**:
+1. 本功能仅供学习和研究使用
+2. 不构成任何投资建议
+3. 股市有风险,投资需谨慎
+4. 请根据自身情况做出投资决策
+5. 盈亏自负,谨慎操作
+
+---
+
+**祝您投资顺利!** 🚀📈
+
diff --git a/主力选股快速开始.md b/主力选股快速开始.md
new file mode 100644
index 0000000..25a92ac
--- /dev/null
+++ b/主力选股快速开始.md
@@ -0,0 +1,316 @@
+# 主力选股功能 - 快速开始指南
+
+## ✅ 已完成的功能模块
+
+### 核心模块
+1. **main_force_selector.py** - 数据获取和筛选
+ - 使用pywencai获取主力资金数据
+ - 智能筛选算法
+ - 数据格式化
+
+2. **main_force_analysis.py** - AI分析整合
+ - 整合三大分析师团队
+ - 资深研究员综合决策
+ - 完整的分析流程
+
+3. **main_force_ui.py** - Web界面
+ - 参数配置界面
+ - 结果展示
+ - 数据导出
+
+4. **app.py** - 主应用集成
+ - 添加导航按钮
+ - 路由配置
+
+### 文档
+5. **主力选股功能说明.md** - 详细使用手册
+6. **test_main_force.py** - 功能测试脚本
+
+## 🚀 快速开始
+
+### 步骤1: 测试功能
+```bash
+# 运行测试脚本
+python test_main_force.py
+```
+
+测试内容:
+- ✅ 模块导入检查
+- ✅ 数据获取功能
+- ✅ 筛选功能
+- ✅ API配置检查
+
+### 步骤2: 启动系统
+```bash
+streamlit run app.py
+```
+
+### 步骤3: 使用主力选股
+1. 在侧边栏点击 **🎯 主力选股**
+2. 设置参数(时间区间、筛选数量等)
+3. 点击 **🚀 开始主力选股**
+4. 等待3-5分钟完成分析
+5. 查看精选推荐结果
+
+## 📊 功能特点
+
+### 智能筛选
+```
+原始数据(100只)
+ ↓ 涨幅过滤(<30%)
+ ↓ 市值筛选(50-1300亿)
+ ↓ ST股过滤
+筛选结果(20-50只)
+ ↓ 主力资金排序
+候选股票(前20名)
+```
+
+### AI三维分析
+```
+💰 资金流向分析师
+ ├─ 主力资金行为
+ ├─ 资金流向趋势
+ └─ 市场情绪判断
+
+📊 行业板块分析师
+ ├─ 行业发展前景
+ ├─ 市场热点判断
+ └─ 板块轮动分析
+
+📈 财务基本面分析师
+ ├─ 盈利能力评估
+ ├─ 成长性分析
+ └─ 财务健康度
+```
+
+### 综合决策
+```
+资深研究员AI
+ ├─ 综合评估所有候选
+ ├─ 精选3-5只最优标的
+ ├─ 提供推荐理由
+ ├─ 投资建议和风险提示
+ └─ 目标价位和持仓建议
+```
+
+## 📋 参数说明
+
+### 基础参数
+| 参数 | 默认值 | 范围 | 说明 |
+|-----|--------|------|------|
+| 时间区间 | 最近3个月 | 自定义 | 主力资金统计周期 |
+| 初步筛选 | 20只 | 10-50 | 候选股票数量 |
+| 最终精选 | 5只 | 3-10 | 推荐标的数量 |
+
+### 高级参数
+| 参数 | 默认值 | 范围 | 说明 |
+|-----|--------|------|------|
+| 最大涨幅 | 30% | 10-100% | 过滤涨幅过高的股票 |
+| 最小市值 | 50亿 | 10-500亿 | 市值下限 |
+| 最大市值 | 1300亿 | 100-5000亿 | 市值上限 |
+| AI模型 | deepseek-chat | - | 分析使用的模型 |
+
+## 🎯 使用场景
+
+### 1. 周末选股
+**时机**: 每周日晚上
+**参数**: 最近3个月,精选5只
+**用途**: 为下周交易做准备
+
+### 2. 月度调仓
+**时机**: 每月初
+**参数**: 最近6个月,精选8-10只
+**用途**: 调整持仓结构
+
+### 3. 热点捕捉
+**时机**: 市场出现明显热点时
+**参数**: 最近1个月,精选3只
+**用途**: 抓住短期机会
+
+## ⚠️ 注意事项
+
+### 环境要求
+```bash
+# 必须安装
+Python >= 3.8
+Node.js >= 16.0(重要!pywencai依赖)
+
+# Python包
+pywencai >= 0.7.0
+pandas
+streamlit
+deepseek
+```
+
+### 检查Node.js版本
+```bash
+node --version
+
+# 如果低于16.0,需要升级
+# Windows: 从nodejs.org下载最新版本
+# Mac: brew install node
+# Linux: nvm install 18
+```
+
+### API配置
+确保.env文件中配置了:
+```env
+DEEPSEEK_API_KEY=your_api_key_here
+EMAIL_ENABLED=true # 如需邮件通知
+```
+
+## 🔍 常见问题
+
+### Q: 数据获取失败?
+**检查**:
+- [ ] 网络连接正常
+- [ ] Node.js版本 >= 16.0
+- [ ] pywencai已正确安装
+
+**解决**:
+```bash
+# 重新安装pywencai
+pip uninstall pywencai
+pip install pywencai --upgrade
+
+# 检查Node.js
+node --version
+```
+
+### Q: 分析时间太长?
+**原因**: 候选股票太多
+
+**优化**:
+- 减少初步筛选数量(改为15只)
+- 使用deepseek-chat模型(更快)
+- 调整筛选条件减少候选数量
+
+### Q: 推荐结果不理想?
+**建议**:
+- 调整时间区间(试试最近6个月)
+- 放宽涨幅限制(改为40%)
+- 多次运行对比结果
+- 结合自己的投资逻辑
+
+## 📈 最佳实践
+
+### 1. 参数配置建议
+```yaml
+保守型:
+ 时间区间: 6个月
+ 最大涨幅: 20%
+ 市值范围: 100-1300亿
+ 精选数量: 3只
+
+平衡型:
+ 时间区间: 3个月
+ 最大涨幅: 30%
+ 市值范围: 50-1300亿
+ 精选数量: 5只
+
+激进型:
+ 时间区间: 1个月
+ 最大涨幅: 40%
+ 市值范围: 50-800亿
+ 精选数量: 8只
+```
+
+### 2. 使用流程
+```
+1. 周末运行主力选股
+ ↓
+2. 查看推荐结果和AI分析
+ ↓
+3. 深入研究前3名标的
+ ↓
+4. 查看实时K线和技术指标
+ ↓
+5. 周一开盘后择机建仓
+ ↓
+6. 加入实时监测,设置止盈止损
+```
+
+### 3. 风险控制
+```
+✅ 分散投资: 不超过3-5只股票
+✅ 仓位控制: 单只不超过30%
+✅ 止损设置: -8%到-10%
+✅ 止盈目标: +15%到+20%
+✅ 动态调整: 根据走势调整策略
+```
+
+## 📊 输出结果
+
+### 统计数据
+- 获取股票总数
+- 筛选后数量
+- 深度分析数量
+- 最终推荐数量
+
+### 推荐详情
+每只股票包含:
+- ✅ 推荐理由(3-5条)
+- ✅ 投资亮点
+- ✅ 风险提示
+- ✅ 建议仓位
+- ✅ 目标价位
+- ✅ 投资周期
+- ✅ AI详细分析报告
+
+### 数据导出
+- CSV格式候选列表
+- 包含所有关键指标
+- 可用于进一步分析
+
+## 🎓 学习资源
+
+### 相关文档
+- 主力选股功能说明.md - 完整使用手册
+- 资金流向功能说明.md - 资金分析原理
+- 实时监测优化说明.md - 监测功能
+- README.md - 系统总体说明
+
+### 代码学习
+- main_force_selector.py - 数据处理逻辑
+- main_force_analysis.py - AI分析流程
+- main_force_ui.py - 界面实现
+
+## 🆘 获取帮助
+
+### 运行测试
+```bash
+python test_main_force.py
+```
+
+### 查看日志
+运行时终端会显示详细日志:
+- 数据获取过程
+- 筛选统计
+- AI分析进度
+- 错误信息
+
+### 问题反馈
+提供以下信息:
+1. 错误截图
+2. 终端日志
+3. 使用的参数
+4. 系统环境(Python版本、Node.js版本)
+
+## ✨ 开始使用
+
+```bash
+# 1. 测试功能
+python test_main_force.py
+
+# 2. 启动系统
+streamlit run app.py
+
+# 3. 点击 🎯 主力选股
+
+# 4. 开始分析
+```
+
+---
+
+**祝您选股顺利,投资成功!** 🚀📈💰
+