增加主力资金选股
This commit is contained in:
@@ -16,6 +16,7 @@ from monitor_manager import display_monitor_manager, get_monitor_summary
|
|||||||
from monitor_service import monitor_service
|
from monitor_service import monitor_service
|
||||||
from notification_service import notification_service
|
from notification_service import notification_service
|
||||||
from config_manager import config_manager
|
from config_manager import config_manager
|
||||||
|
from main_force_ui import display_main_force_selector
|
||||||
|
|
||||||
# 页面配置
|
# 页面配置
|
||||||
st.set_page_config(
|
st.set_page_config(
|
||||||
@@ -296,6 +297,17 @@ def main():
|
|||||||
st.session_state.show_monitor = True
|
st.session_state.show_monitor = True
|
||||||
if 'show_history' in st.session_state:
|
if 'show_history' in st.session_state:
|
||||||
del st.session_state.show_history
|
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 st.button("🏠 返回首页", use_container_width=True, key="nav_home"):
|
||||||
if 'show_history' in st.session_state:
|
if 'show_history' in st.session_state:
|
||||||
@@ -304,6 +316,8 @@ def main():
|
|||||||
del st.session_state.show_monitor
|
del st.session_state.show_monitor
|
||||||
if 'show_config' in st.session_state:
|
if 'show_config' in st.session_state:
|
||||||
del st.session_state.show_config
|
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"):
|
if st.button("⚙️ 环境配置", use_container_width=True, key="nav_config"):
|
||||||
st.session_state.show_config = True
|
st.session_state.show_config = True
|
||||||
@@ -373,6 +387,7 @@ def main():
|
|||||||
|
|
||||||
**功能说明**
|
**功能说明**
|
||||||
- **智能分析**:AI团队深度分析
|
- **智能分析**:AI团队深度分析
|
||||||
|
- **主力选股**:主力资金精选标的
|
||||||
- **实时监测**:价格监控与提醒
|
- **实时监测**:价格监控与提醒
|
||||||
- **历史记录**:查看分析历史
|
- **历史记录**:查看分析历史
|
||||||
|
|
||||||
@@ -393,6 +408,11 @@ def main():
|
|||||||
display_monitor_manager()
|
display_monitor_manager()
|
||||||
return
|
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:
|
if 'show_config' in st.session_state and st.session_state.show_config:
|
||||||
display_config_manager()
|
display_config_manager()
|
||||||
@@ -539,6 +559,8 @@ def main():
|
|||||||
del st.session_state.discussion_result
|
del st.session_state.discussion_result
|
||||||
if 'final_decision' in st.session_state:
|
if 'final_decision' in st.session_state:
|
||||||
del st.session_state.final_decision
|
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)
|
run_stock_analysis(stock_input, period)
|
||||||
|
|
||||||
@@ -566,32 +588,36 @@ def main():
|
|||||||
# 运行批量分析
|
# 运行批量分析
|
||||||
run_batch_analysis(stock_list, period, batch_mode)
|
run_batch_analysis(stock_list, period, batch_mode)
|
||||||
|
|
||||||
# 检查是否有已完成的单个分析结果
|
# 检查是否有已完成的单个分析结果(但不是刚刚完成的,避免重复显示)
|
||||||
if 'analysis_completed' in st.session_state and st.session_state.analysis_completed:
|
if 'analysis_completed' in st.session_state and st.session_state.analysis_completed:
|
||||||
# 重新显示分析结果
|
# 如果是刚刚完成的分析,清除标志,避免重复显示
|
||||||
stock_info = st.session_state.stock_info
|
if st.session_state.get('just_completed', False):
|
||||||
agents_results = st.session_state.agents_results
|
st.session_state.just_completed = False
|
||||||
discussion_result = st.session_state.discussion_result
|
else:
|
||||||
final_decision = st.session_state.final_decision
|
# 重新显示之前的分析结果(页面刷新后)
|
||||||
|
stock_info = st.session_state.stock_info
|
||||||
# 重新获取股票数据用于显示图表
|
agents_results = st.session_state.agents_results
|
||||||
stock_info_current, stock_data, indicators = get_stock_data(stock_info['symbol'], period)
|
discussion_result = st.session_state.discussion_result
|
||||||
|
final_decision = st.session_state.final_decision
|
||||||
# 显示股票基本信息
|
|
||||||
display_stock_info(stock_info, indicators)
|
# 重新获取股票数据用于显示图表
|
||||||
|
stock_info_current, stock_data, indicators = get_stock_data(stock_info['symbol'], period)
|
||||||
# 显示股票图表
|
|
||||||
if stock_data is not None:
|
# 显示股票基本信息
|
||||||
display_stock_chart(stock_data, stock_info)
|
display_stock_info(stock_info, indicators)
|
||||||
|
|
||||||
# 显示各分析师报告
|
# 显示股票图表
|
||||||
display_agents_analysis(agents_results)
|
if stock_data is not None:
|
||||||
|
display_stock_chart(stock_data, stock_info)
|
||||||
# 显示团队讨论
|
|
||||||
display_team_discussion(discussion_result)
|
# 显示各分析师报告
|
||||||
|
display_agents_analysis(agents_results)
|
||||||
# 显示最终决策
|
|
||||||
display_final_decision(final_decision, stock_info, agents_results, discussion_result)
|
# 显示团队讨论
|
||||||
|
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:
|
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)
|
final_decision = agents.make_final_decision(discussion_result, stock_info, indicators)
|
||||||
progress_bar.progress(100)
|
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.analysis_completed = True
|
||||||
st.session_state.stock_info = stock_info
|
st.session_state.stock_info = stock_info
|
||||||
st.session_state.agents_results = agents_results
|
st.session_state.agents_results = agents_results
|
||||||
st.session_state.discussion_result = discussion_result
|
st.session_state.discussion_result = discussion_result
|
||||||
st.session_state.final_decision = final_decision
|
st.session_state.final_decision = final_decision
|
||||||
|
st.session_state.just_completed = True # 标记刚刚完成分析
|
||||||
|
|
||||||
# 保存到数据库
|
# 保存到数据库
|
||||||
try:
|
try:
|
||||||
@@ -1086,9 +1116,6 @@ def run_stock_analysis(symbol, period):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
st.warning(f"⚠️ 保存到数据库时出现错误: {str(e)}")
|
st.warning(f"⚠️ 保存到数据库时出现错误: {str(e)}")
|
||||||
|
|
||||||
# 显示最终决策
|
|
||||||
display_final_decision(final_decision, stock_info, agents_results, discussion_result)
|
|
||||||
|
|
||||||
status_text.text("✅ 分析完成!")
|
status_text.text("✅ 分析完成!")
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
status_text.empty()
|
status_text.empty()
|
||||||
|
|||||||
@@ -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()
|
||||||
@@ -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
|
||||||
|
)
|
||||||
|
|
||||||
@@ -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()
|
||||||
|
|
||||||
@@ -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
@@ -249,8 +249,9 @@ def display_pdf_export_section(stock_info, agents_results, discussion_result, fi
|
|||||||
col1, col2, col3 = st.columns([1, 2, 1])
|
col1, col2, col3 = st.columns([1, 2, 1])
|
||||||
|
|
||||||
with col2:
|
with col2:
|
||||||
# 生成PDF报告按钮
|
# 生成PDF报告按钮(使用股票代码作为key的一部分,确保唯一性)
|
||||||
if st.button("📄 生成并下载PDF报告", type="primary", use_container_width=True):
|
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报告..."):
|
with st.spinner("正在生成PDF报告..."):
|
||||||
try:
|
try:
|
||||||
# 生成PDF内容
|
# 生成PDF内容
|
||||||
|
|||||||
+76
-13
@@ -463,20 +463,14 @@ class StockDataFetcher:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"获取财务指标失败: {e}")
|
print(f"获取财务指标失败: {e}")
|
||||||
|
|
||||||
# 5. 获取季度业绩(尝试不同API)
|
# 5. 获取季度业绩(使用问财)
|
||||||
try:
|
try:
|
||||||
# 尝试获取业绩预告
|
# 使用pywencai获取季报数据
|
||||||
quarter_data = ak.stock_profit_forecast_em(symbol=symbol)
|
quarter_data = self._get_quarter_data_from_wencai(symbol)
|
||||||
if quarter_data is not None and not quarter_data.empty:
|
if quarter_data:
|
||||||
financial_data["quarter_data"] = quarter_data.head(4).to_dict('records')
|
financial_data["quarter_data"] = quarter_data
|
||||||
except:
|
except Exception as e:
|
||||||
try:
|
print(f"获取季度数据失败: {e}")
|
||||||
# 备用方案:获取季度财报
|
|
||||||
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}")
|
|
||||||
|
|
||||||
return financial_data
|
return financial_data
|
||||||
|
|
||||||
@@ -484,6 +478,75 @@ class StockDataFetcher:
|
|||||||
print(f"获取中国股票财务数据失败: {e}")
|
print(f"获取中国股票财务数据失败: {e}")
|
||||||
return financial_data
|
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):
|
def _get_us_financial_data(self, symbol):
|
||||||
"""获取美股财务数据"""
|
"""获取美股财务数据"""
|
||||||
financial_data = {
|
financial_data = {
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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. 盈亏自负,谨慎操作
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**祝您投资顺利!** 🚀📈
|
||||||
|
|
||||||
+316
@@ -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. 开始分析
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**祝您选股顺利,投资成功!** 🚀📈💰
|
||||||
|
|
||||||
Reference in New Issue
Block a user