增加主力资金选股

This commit is contained in:
oficcejo
2025-10-09 20:51:10 +08:00
parent ad21672786
commit 88c7db1ab9
11 changed files with 3230 additions and 44 deletions
+50 -23
View File
@@ -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
# 如果是刚刚完成的分析,清除标志,避免重复显示
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)
# 重新获取股票数据用于显示图表
stock_info_current, stock_data, indicators = get_stock_data(stock_info['symbol'], period)
# 显示股票基本信息
display_stock_info(stock_info, indicators)
# 显示股票基本信息
display_stock_info(stock_info, indicators)
# 显示股票图表
if stock_data is not None:
display_stock_chart(stock_data, stock_info)
# 显示股票图表
if stock_data is not None:
display_stock_chart(stock_data, stock_info)
# 显示各分析师报告
display_agents_analysis(agents_results)
# 显示各分析师报告
display_agents_analysis(agents_results)
# 显示团队讨论
display_team_discussion(discussion_result)
# 显示团队讨论
display_team_discussion(discussion_result)
# 显示最终决策
display_final_decision(final_decision, stock_info, agents_results, discussion_result)
# 显示最终决策
display_final_decision(final_decision, stock_info, agents_results, discussion_result)
# 检查是否有已完成的批量分析结果
elif 'batch_analysis_results' in st.session_state and st.session_state.batch_analysis_results:
@@ -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()
+549
View File
@@ -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()
+456
View File
@@ -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"""
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>主力选股AI分析报告</title>
<style>
body {{
font-family: 'Microsoft YaHei', Arial, sans-serif;
line-height: 1.6;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}}
.container {{
background-color: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}}
h1 {{
color: #2c3e50;
border-bottom: 3px solid #3498db;
padding-bottom: 10px;
}}
h2 {{
color: #34495e;
border-left: 4px solid #3498db;
padding-left: 15px;
margin-top: 30px;
}}
h3 {{
color: #2980b9;
margin-top: 25px;
}}
table {{
width: 100%;
border-collapse: collapse;
margin: 20px 0;
font-size: 14px;
}}
th, td {{
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}}
th {{
background-color: #3498db;
color: white;
font-weight: bold;
}}
tr:nth-child(even) {{
background-color: #f9f9f9;
}}
tr:hover {{
background-color: #f0f0f0;
}}
.disclaimer {{
background-color: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: 5px;
padding: 15px;
margin-top: 30px;
}}
.footer {{
text-align: center;
margin-top: 30px;
color: #7f8c8d;
font-style: italic;
}}
hr {{
border: none;
height: 2px;
background-color: #ecf0f1;
margin: 20px 0;
}}
strong {{
color: #2c3e50;
}}
ul, ol {{
margin: 10px 0;
padding-left: 30px;
}}
li {{
margin: 5px 0;
}}
</style>
</head>
<body>
<div class="container">
"""
# 简单的Markdown到HTML转换
html_body = markdown_content
html_body = html_body.replace('\n# ', '\n<h1>').replace('\n## ', '\n<h2>').replace('\n### ', '\n<h3>')
html_body = html_body.replace('# ', '<h1>').replace('## ', '<h2>').replace('### ', '<h3>')
html_body = html_body.replace('\n---\n', '\n<hr>\n')
# 处理粗体文本
html_body = re.sub(r'\*\*(.*?)\*\*', r'<strong>\1</strong>', 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('<table>')
in_table = True
cells = [cell.strip() for cell in line.split('|')[1:-1]]
processed_lines.append('<tr>')
for cell in cells:
processed_lines.append(f'<th>{cell}</th>')
processed_lines.append('</tr>')
elif '|' in line and in_table:
if '---' not in line:
cells = [cell.strip() for cell in line.split('|')[1:-1]]
processed_lines.append('<tr>')
for cell in cells:
processed_lines.append(f'<td>{cell}</td>')
processed_lines.append('</tr>')
elif in_table and '|' not in line:
processed_lines.append('</table>')
in_table = False
processed_lines.append(line)
else:
processed_lines.append(line)
if in_table:
processed_lines.append('</table>')
html_body = '\n'.join(processed_lines)
# 处理列表
html_body = re.sub(r'\n- (.*)', r'\n<li>\1</li>', html_body)
html_body = re.sub(r'(<li>.*</li>)\n(?!<li>)', r'<ul>\1</ul>\n', html_body)
html_body = re.sub(r'(<li>.*</li>\n)+', lambda m: '<ul>\n' + m.group(0) + '</ul>\n', html_body)
# 处理换行
html_body = html_body.replace('\n\n', '</p><p>')
html_body = '<p>' + html_body + '</p>'
html_content += html_body
html_content += """
</div>
</body>
</html>
"""
return html_content
def create_download_link(content, filename, link_text):
"""创建下载链接"""
b64 = base64.b64encode(content.encode()).decode()
href = f'<a href="data:text/markdown;base64,{b64}" download="{filename}" style="display: inline-block; padding: 10px 20px; background-color: #4CAF50; color: white; text-decoration: none; border-radius: 5px; margin: 5px;">{link_text}</a>'
return href
def create_html_download_link(content, filename, link_text):
"""创建HTML下载链接"""
b64 = base64.b64encode(content.encode('utf-8')).decode()
href = f'<a href="data:text/html;base64,{b64}" download="{filename}" style="display: inline-block; padding: 10px 20px; background-color: #2196F3; color: white; text-decoration: none; border-radius: 5px; margin: 5px;">{link_text}</a>'
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
)
+386
View File
@@ -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()
+419
View File
@@ -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)
+3 -2
View File
@@ -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内容
+76 -13
View File
@@ -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 = {
+221
View File
@@ -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()
+313
View File
@@ -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
→ 点击 🎯 主力选股
→ 开始分析
```
**祝您投资顺利!** 🚀📈💰
+435
View File
@@ -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.0pywencai依赖)
```
### 安装方法
```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. 盈亏自负,谨慎操作
---
**祝您投资顺利!** 🚀📈
+316
View File
@@ -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. 开始分析
```
---
**祝您选股顺利,投资成功!** 🚀📈💰