#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ A股智能分析系统 - GUI版本 (完全兼容版) 适配Python 3.7+和旧版Tkinter,去除特殊字符 """ import tkinter as tk from tkinter import ttk, scrolledtext, messagebox import threading import random from datetime import datetime, timedelta import warnings import urllib.request import urllib.parse import json import re import os import time warnings.filterwarnings('ignore') # 可选导入akshare用于实时数据 try: import akshare as ak AKSHARE_AVAILABLE = True print("✅ akshare已加载,支持实时数据获取") except ImportError: AKSHARE_AVAILABLE = False print("⚠️ akshare未安装,使用本地数据库") class AShareAnalyzerGUI: """A股分析系统GUI界面""" def __init__(self, root): self.root = root self.setup_ui() # 添加失败记录缓存 self.failed_stock_names = set() # 记录获取名称失败的股票 self.stock_name_attempts = {} # 记录尝试次数 self.last_request_time = 0 # 记录上次请求时间 # 新增:股票分析缓存系统 self.cache_file = "stock_analysis_cache.json" self.daily_cache = {} # 当日股票分析缓存 self.load_daily_cache() # 加载当日缓存 # 新增:批量评分数据存储 self.batch_score_file = "batch_stock_scores.json" self.batch_scores = {} # 批量评分数据 self.load_batch_scores() # 加载批量评分数据 self.stock_info = { # 科创板 "688981": {"name": "中芯国际", "industry": "半导体制造", "concept": "芯片概念,科创板", "price": 128.55}, "688036": {"name": "传音控股", "industry": "消费电子", "concept": "科创板,智能手机", "price": 89.66}, "688111": {"name": "金山办公", "industry": "软件服务", "concept": "科创板,办公软件", "price": 385.00}, "688599": {"name": "天合光能", "industry": "光伏设备", "concept": "科创板,新能源", "price": 45.80}, "688169": {"name": "石头科技", "industry": "智能硬件", "concept": "科创板,扫地机器人", "price": 380.50}, "688180": {"name": "君实生物", "industry": "生物制药", "concept": "科创板,创新药", "price": 55.90}, # 沪市主板 (37只) "600000": {"name": "浦发银行", "industry": "银行", "concept": "金融股,银行", "price": 8.12}, "600030": {"name": "中信证券", "industry": "证券", "concept": "券商股,金融", "price": 19.25}, "600036": {"name": "招商银行", "industry": "银行", "concept": "金融股,蓝筹股", "price": 35.88}, "600104": {"name": "上汽集团", "industry": "汽车制造", "concept": "汽车股,传统汽车", "price": 15.88}, "600519": {"name": "贵州茅台", "industry": "白酒", "concept": "消费股,核心资产", "price": 1688.00}, "600276": {"name": "恒瑞医药", "industry": "医药制造", "concept": "医药股,创新药", "price": 55.80}, "600887": {"name": "伊利股份", "industry": "乳制品", "concept": "消费股,食品饮料", "price": 29.88}, "600585": {"name": "海螺水泥", "industry": "建材", "concept": "基建股,水泥", "price": 28.90}, "600703": {"name": "三安光电", "industry": "半导体", "concept": "LED,化合物半导体", "price": 18.50}, "600009": {"name": "上海机场", "industry": "机场服务", "concept": "基础设施,机场", "price": 45.60}, "600019": {"name": "宝钢股份", "industry": "钢铁", "concept": "钢铁股,蓝筹", "price": 5.88}, "600309": {"name": "万华化学", "industry": "化工", "concept": "化工股,MDI", "price": 85.90}, "600028": {"name": "中国石化", "industry": "石油化工", "concept": "石化股,央企", "price": 5.12}, "600048": {"name": "保利发展", "industry": "房地产", "concept": "央企地产,蓝筹", "price": 12.88}, "600196": {"name": "复星医药", "industry": "医药制造", "concept": "医药股,综合医药", "price": 28.50}, "600688": {"name": "上海石化", "industry": "石油化工", "concept": "石化股,炼化", "price": 3.88}, "600745": {"name": "闻泰科技", "industry": "电子制造", "concept": "5G概念,电子", "price": 45.20}, "600547": {"name": "山东黄金", "industry": "有色金属", "concept": "黄金股,贵金属", "price": 15.60}, "600900": {"name": "长江电力", "industry": "电力", "concept": "水电股,公用事业", "price": 22.88}, "600031": {"name": "三一重工", "industry": "工程机械", "concept": "机械股,基建", "price": 16.85}, "600660": {"name": "福耀玻璃", "industry": "汽车零部件", "concept": "汽车玻璃,制造业", "price": 38.90}, "600025": {"name": "华能国际", "industry": "电力", "concept": "火电股,央企", "price": 8.95}, "600588": {"name": "用友网络", "industry": "软件服务", "concept": "企业软件,云计算", "price": 16.50}, "600809": {"name": "山西汾酒", "industry": "白酒", "concept": "白酒股,消费", "price": 185.00}, "600690": {"name": "海尔智家", "industry": "家用电器", "concept": "白电龙头,智能家居", "price": 25.88}, "600837": {"name": "海通证券", "industry": "证券", "concept": "券商股,金融", "price": 9.65}, "601318": {"name": "中国平安", "industry": "保险", "concept": "保险股,金融", "price": 42.50}, "601166": {"name": "兴业银行", "industry": "银行", "concept": "股份制银行", "price": 18.88}, "601328": {"name": "交通银行", "industry": "银行", "concept": "国有银行", "price": 5.95}, "601398": {"name": "工商银行", "industry": "银行", "concept": "大型银行,国有", "price": 5.12}, "601288": {"name": "农业银行", "industry": "银行", "concept": "国有银行", "price": 3.88}, "601939": {"name": "建设银行", "industry": "银行", "concept": "国有银行", "price": 6.85}, "601988": {"name": "中国银行", "industry": "银行", "concept": "国有银行", "price": 3.95}, "601012": {"name": "隆基绿能", "industry": "光伏设备", "concept": "光伏股,新能源", "price": 22.90}, "601888": {"name": "中国中免", "industry": "商业贸易", "concept": "免税概念,消费", "price": 88.50}, "601225": {"name": "陕西煤业", "industry": "煤炭开采", "concept": "煤炭股,能源", "price": 12.88}, "600089": {"name": "特变电工", "industry": "电力设备", "concept": "特高压,新能源设备", "price": 15.20}, # 深市主板 (26只) "000001": {"name": "平安银行", "industry": "银行", "concept": "银行股,零售银行", "price": 10.55}, "000002": {"name": "万科A", "industry": "房地产", "concept": "地产股,白马股", "price": 8.95}, "000063": {"name": "中兴通讯", "industry": "通信设备", "concept": "5G概念,通信", "price": 28.50}, "000069": {"name": "华侨城A", "industry": "旅游服务", "concept": "文旅股,地产", "price": 6.85}, "000100": {"name": "TCL科技", "industry": "消费电子", "concept": "面板股,电子", "price": 4.15}, "000157": {"name": "中联重科", "industry": "工程机械", "concept": "机械股,基建", "price": 6.20}, "000166": {"name": "申万宏源", "industry": "证券", "concept": "券商股,金融", "price": 4.85}, "000568": {"name": "泸州老窖", "industry": "白酒", "concept": "白酒股,消费", "price": 155.00}, "000596": {"name": "古井贡酒", "industry": "白酒", "concept": "白酒股,地方酒", "price": 188.00}, "000625": {"name": "长安汽车", "industry": "汽车制造", "concept": "自主品牌,汽车", "price": 12.88}, "000651": {"name": "格力电器", "industry": "家用电器", "concept": "空调龙头,白电", "price": 32.90}, "000725": {"name": "京东方A", "industry": "显示面板", "concept": "面板股,OLED", "price": 3.68}, "000858": {"name": "五粮液", "industry": "白酒", "concept": "消费股,白酒", "price": 138.88}, "000876": {"name": "新希望", "industry": "农林牧渔", "concept": "农业股,生猪", "price": 15.88}, "000895": {"name": "双汇发展", "industry": "食品加工", "concept": "肉制品,食品", "price": 25.90}, "000938": {"name": "紫光股份", "industry": "计算机设备", "concept": "IT设备,云计算", "price": 18.50}, "000977": {"name": "浪潮信息", "industry": "计算机设备", "concept": "服务器,AI算力", "price": 28.88}, "002001": {"name": "新和成", "industry": "化工", "concept": "维生素,精细化工", "price": 18.90}, "002027": {"name": "分众传媒", "industry": "广告营销", "concept": "广告股,传媒", "price": 6.85}, "002050": {"name": "三花智控", "industry": "汽车零部件", "concept": "汽车零部件,制冷", "price": 22.50}, "002120": {"name": "韵达股份", "industry": "物流", "concept": "快递股,物流", "price": 12.88}, "002129": {"name": "中环股份", "industry": "半导体", "concept": "硅片,光伏", "price": 25.60}, "002142": {"name": "宁波银行", "industry": "银行", "concept": "城商行,银行", "price": 28.88}, "002304": {"name": "洋河股份", "industry": "白酒", "concept": "白酒股,苏酒", "price": 98.50}, "002352": {"name": "顺丰控股", "industry": "物流", "concept": "快递龙头,物流", "price": 38.90}, "002714": {"name": "牧原股份", "industry": "农林牧渔", "concept": "生猪养殖,农业", "price": 42.80}, "002415": {"name": "海康威视", "industry": "安防设备", "concept": "科技股,监控", "price": 30.45}, "002594": {"name": "比亚迪", "industry": "新能源汽车", "concept": "新能源,汽车", "price": 280.00}, "002174": {"name": "游族网络", "industry": "游戏软件", "concept": "游戏概念,文化传媒", "price": 18.50}, "002475": {"name": "立讯精密", "industry": "电子制造", "concept": "苹果概念,消费电子", "price": 35.60}, # 创业板 "300750": {"name": "宁德时代", "industry": "新能源电池", "concept": "新能源,锂电池", "price": 195.50}, "300059": {"name": "东方财富", "industry": "金融服务", "concept": "互联网金融", "price": 12.88}, "300015": {"name": "爱尔眼科", "industry": "医疗服务", "concept": "医疗股,眼科", "price": 38.90}, "300142": {"name": "沃森生物", "industry": "生物制药", "concept": "疫苗概念,生物医药", "price": 25.80}, "300760": {"name": "迈瑞医疗", "industry": "医疗器械", "concept": "创业板,医疗设备", "price": 285.60}, "300896": {"name": "爱美客", "industry": "医美产品", "concept": "创业板,医美概念", "price": 380.88}, "300122": {"name": "智飞生物", "industry": "生物制药", "concept": "创业板,疫苗", "price": 45.20}, "300274": {"name": "阳光电源", "industry": "电力设备", "concept": "创业板,光伏逆变器", "price": 85.50}, "300347": {"name": "泰格医药", "industry": "医药外包", "concept": "创业板,CRO", "price": 78.90}, "300433": {"name": "蓝思科技", "industry": "消费电子", "concept": "创业板,苹果概念", "price": 18.85}, # ETF基金 "510050": {"name": "50ETF", "industry": "ETF基金", "concept": "上证50,蓝筹股ETF", "price": 2.856}, "510300": {"name": "300ETF", "industry": "ETF基金", "concept": "沪深300,宽基ETF", "price": 4.123}, "510500": {"name": "500ETF", "industry": "ETF基金", "concept": "中证500,中盘ETF", "price": 6.788}, "159919": {"name": "300ETF", "industry": "ETF基金", "concept": "沪深300,深交所ETF", "price": 4.125}, "159915": {"name": "创业板ETF", "industry": "ETF基金", "concept": "创业板,成长股ETF", "price": 2.156}, "512880": {"name": "证券ETF", "industry": "ETF基金", "concept": "证券行业,行业ETF", "price": 0.956}, "159928": {"name": "消费ETF", "industry": "ETF基金", "concept": "消费行业,行业ETF", "price": 2.888}, "512690": {"name": "酒ETF", "industry": "ETF基金", "concept": "白酒行业,主题ETF", "price": 1.156}, "515050": {"name": "5G ETF", "industry": "ETF基金", "concept": "5G通信,科技ETF", "price": 0.956}, "512170": {"name": "医疗ETF", "industry": "ETF基金", "concept": "医疗健康,行业ETF", "price": 1.825}, "510900": {"name": "H股ETF", "industry": "ETF基金", "concept": "香港股票,跨境ETF", "price": 2.456}, "159949": {"name": "创业板50", "industry": "ETF基金", "concept": "创业板50,成长ETF", "price": 2.688}, } # 添加通用股票验证函数,支持所有A股代码格式 self.valid_a_share_codes = self.generate_valid_codes() def load_daily_cache(self): """加载当日股票分析缓存""" import json import os from datetime import datetime today = datetime.now().strftime('%Y-%m-%d') try: if os.path.exists(self.cache_file): with open(self.cache_file, 'r', encoding='utf-8') as f: cache_data = json.load(f) # 只加载当日数据 if cache_data.get('date') == today: self.daily_cache = cache_data.get('stocks', {}) print(f"✅ 加载当日缓存:{len(self.daily_cache)}只股票") else: print(f"⚠️ 缓存数据不是今日({today}),重新开始分析") self.daily_cache = {} else: print("📝 首次运行,创建新的缓存文件") self.daily_cache = {} except Exception as e: print(f"❌ 加载缓存失败: {e}") self.daily_cache = {} def save_daily_cache(self): """保存当日股票分析缓存""" import json from datetime import datetime cache_data = { 'date': datetime.now().strftime('%Y-%m-%d'), 'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'stocks': self.daily_cache } try: with open(self.cache_file, 'w', encoding='utf-8') as f: json.dump(cache_data, f, ensure_ascii=False, indent=2) print(f"💾 缓存已保存:{len(self.daily_cache)}只股票") except Exception as e: print(f"❌ 保存缓存失败: {e}") def get_stock_from_cache(self, ticker): """从缓存获取股票分析数据""" return self.daily_cache.get(ticker) def save_stock_to_cache(self, ticker, analysis_data): """保存股票分析数据到缓存""" from datetime import datetime analysis_data['cache_time'] = datetime.now().strftime('%H:%M:%S') self.daily_cache[ticker] = analysis_data # 实时保存到文件 self.save_daily_cache() def load_batch_scores(self): """加载批量评分数据""" import json from datetime import datetime try: if os.path.exists(self.batch_score_file): with open(self.batch_score_file, 'r', encoding='utf-8') as f: data = json.load(f) # 检查数据是否在48小时内 if self._is_batch_scores_valid(data): self.batch_scores = data.get('scores', {}) score_time = data.get('timestamp', data.get('date', '未知')) print(f"✅ 加载批量评分:{len(self.batch_scores)}只股票 (评分时间: {score_time})") else: print("📅 批量评分数据已超过48小时,将重新获取") self.batch_scores = {} else: print("📝 首次运行,无批量评分数据") self.batch_scores = {} except Exception as e: print(f"❌ 加载批量评分失败: {e}") self.batch_scores = {} def _is_batch_scores_valid(self, data): """检查批量评分数据是否在48小时内有效""" from datetime import datetime, timedelta try: # 先尝试使用新的时间戳格式 timestamp_str = data.get('timestamp') if timestamp_str: try: score_time = datetime.strptime(timestamp_str, '%Y-%m-%d %H:%M:%S') except: # 如果新格式解析失败,尝试只有日期的格式 score_time = datetime.strptime(timestamp_str, '%Y-%m-%d') else: # 回退到旧的日期格式 date_str = data.get('date') if date_str: score_time = datetime.strptime(date_str, '%Y-%m-%d') else: return False # 检查是否在48小时内 now = datetime.now() time_diff = now - score_time return time_diff.total_seconds() < 48 * 3600 # 48小时 = 48 * 3600秒 except Exception as e: print(f"⚠️ 时间检查失败: {e}") return False def save_batch_scores(self): """保存批量评分数据""" import json from datetime import datetime data = { 'date': datetime.now().strftime('%Y-%m-%d'), 'timestamp': datetime.now().strftime('%Y-%m-%d %H:%M:%S'), 'scores': self.batch_scores, 'count': len(self.batch_scores) } try: with open(self.batch_score_file, 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=2) print(f"💾 批量评分已保存:{len(self.batch_scores)}只股票 (时间: {data['timestamp']})") except Exception as e: print(f"❌ 保存批量评分失败: {e}") def get_all_stock_codes(self): """获取所有A股股票代码(60/00/30开头和ETF)""" all_stocks = [] # 从已知股票信息中获取 for code in self.stock_info.keys(): if code.startswith(('600', '000', '002', '300', '688')): all_stocks.append(code) # 尝试从akshare获取更全面的股票列表 try: import akshare as ak # 获取A股股票列表 stock_list = ak.stock_info_a_code_name() if stock_list is not None and not stock_list.empty: for _, row in stock_list.iterrows(): code = str(row['code']) if code.startswith(('600', '000', '002', '300', '688')): if code not in all_stocks: all_stocks.append(code) # 获取ETF列表 try: # 尝试获取真正的ETF列表 print("📊 尝试获取ETF基金列表...") # 方法1:尝试获取基金列表 try: fund_list = ak.fund_etf_hist_sina() if fund_list is not None and not fund_list.empty: print(f" 获取基金历史数据: {len(fund_list)}只") except: pass # 方法2:手动添加常见ETF print(" 添加常见ETF基金...") common_etfs = [ # 宽基指数ETF '510300', '159919', '510500', '159922', # 沪深300 '510050', '159915', # 上证50 '512100', '159845', # 中证1000 '510880', '159928', # 红利指数 '512980', '159941', # 广发纳斯达克100 # 行业ETF '515790', '159995', # 光伏ETF '516160', '159967', # 新能源车ETF '512690', '159928', # 酒ETF '515050', '159939', # 5G ETF '512200', '159906', # 房地产ETF # 其他主要ETF '512000', '159801', # 券商ETF '512800', '159928', # 银行ETF '510230', '159915', # 金融ETF ] for etf_code in common_etfs: if etf_code not in all_stocks: all_stocks.append(etf_code) print(f" 添加ETF: {etf_code}") print(f" 成功添加 {len(common_etfs)} 只ETF基金") except Exception as etf_e: print(f"⚠️ 获取ETF列表失败: {etf_e}") # 至少添加几个基本ETF用于测试 basic_etfs = ['510300', '159919', '510500', '510050'] for etf_code in basic_etfs: if etf_code not in all_stocks: all_stocks.append(etf_code) print(f" 基础ETF: {etf_code}") except Exception as e: print(f"⚠️ 从akshare获取股票列表失败: {e}") print("🔄 使用内置股票列表") return sorted(list(set(all_stocks))) def start_batch_scoring(self): """开始批量获取评分""" import threading # 在后台线程中运行,避免界面卡死 def batch_scoring_thread(): try: self.show_progress("🚀 开始获取全部股票评分...") # 获取所有股票代码 all_codes = self.get_all_stock_codes() total_stocks = len(all_codes) if total_stocks == 0: self.show_progress("❌ 未找到股票代码") return self.show_progress(f"📊 找到 {total_stocks} 只股票,开始批量评分...") success_count = 0 failed_count = 0 for i, code in enumerate(all_codes): try: # 更新进度 progress = (i + 1) / total_stocks * 100 self.show_progress(f"⏳ 正在分析 {code} ({i+1}/{total_stocks}) - {progress:.1f}%") # 获取股票分析和评分 score = self.get_stock_score_for_batch(code) if score is not None: # 保存评分数据 stock_name = self.stock_info.get(code, {}).get('name', f'股票{code}') industry = self.stock_info.get(code, {}).get('industry', '未知') self.batch_scores[code] = { 'name': stock_name, 'score': score, 'industry': industry, 'timestamp': datetime.now().strftime('%H:%M:%S') } success_count += 1 else: failed_count += 1 # 每50只股票保存一次 if (i + 1) % 50 == 0: self.save_batch_scores() # 避免请求过快 time.sleep(0.1) except Exception as e: print(f"❌ 分析股票 {code} 失败: {e}") failed_count += 1 continue # 最终保存 self.save_batch_scores() # 显示完成信息 self.show_progress(f"✅ 批量评分完成!成功: {success_count}, 失败: {failed_count}") # 更新排行榜 self.update_ranking_display() # 3秒后清除进度信息 threading.Timer(3.0, lambda: self.show_progress("")).start() except Exception as e: self.show_progress(f"❌ 批量评分失败: {e}") print(f"❌ 批量评分异常: {e}") # 启动后台线程 thread = threading.Thread(target=batch_scoring_thread) thread.daemon = True thread.start() def get_stock_score_for_batch(self, stock_code): """为批量评分获取单只股票的评分 - 确保一致性""" try: # 总是重新计算,确保使用最新的评分算法 # 不使用缓存,避免不一致问题 # 生成智能模拟数据并分析 tech_data = self._generate_smart_mock_technical_data(stock_code) fund_data = self._generate_smart_mock_fundamental_data(stock_code) # 获取分析建议 - 使用正确的参数 short_advice = self.get_short_term_advice( tech_data['rsi'], tech_data['macd'], tech_data['signal'], tech_data['volume_ratio'], tech_data['ma5'], tech_data['ma10'], tech_data['current_price'] ) long_advice = self.get_long_term_advice( fund_data['pe_ratio'], fund_data['pb_ratio'], fund_data['roe'], tech_data['ma20'], tech_data['ma60'], tech_data['current_price'], self.stock_info.get(stock_code, {}) ) # 提取评分 short_score = self._extract_score_from_advice(short_advice, 'short_term') long_score = self._extract_score_from_advice(long_advice, 'long_term') # 计算最终评分 final_score = (short_score + long_score) / 2 return round(final_score, 1) except Exception as e: print(f"❌ 获取 {stock_code} 评分失败: {e}") return None def setup_ui(self): """设置用户界面""" self.root.title("A股智能分析系统 v2.0") self.root.geometry("1200x800") self.root.configure(bg="#f0f0f0") # 设置样式 style = ttk.Style() style.theme_use('clam') # 主标题 title_frame = tk.Frame(self.root, bg="#2c3e50", height=60) title_frame.pack(fill="x", pady=(0, 10)) title_frame.pack_propagate(False) title_label = tk.Label(title_frame, text="A股智能分析系统", font=("微软雅黑", 18, "bold"), fg="white", bg="#2c3e50") title_label.pack(expand=True) # 输入区域 input_frame = tk.Frame(self.root, bg="#f0f0f0") input_frame.pack(fill="x", padx=20, pady=10) # 股票代码输入 tk.Label(input_frame, text="股票代码:", font=("微软雅黑", 12), bg="#f0f0f0").pack(side="left") self.ticker_var = tk.StringVar() self.ticker_entry = tk.Entry(input_frame, textvariable=self.ticker_var, font=("微软雅黑", 12), width=10) self.ticker_entry.pack(side="left", padx=(10, 20)) # 投资期限选择 tk.Label(input_frame, text="投资期限:", font=("微软雅黑", 12), bg="#f0f0f0").pack(side="left") self.period_var = tk.StringVar(value="长期") period_combo = ttk.Combobox(input_frame, textvariable=self.period_var, values=["短期", "中期", "长期"], state="readonly", font=("微软雅黑", 10), width=8) period_combo.pack(side="left", padx=(5, 20)) # 股票类型选择 tk.Label(input_frame, text="股票类型:", font=("微软雅黑", 12), bg="#f0f0f0").pack(side="left") self.stock_type_var = tk.StringVar(value="全部") type_combo = ttk.Combobox(input_frame, textvariable=self.stock_type_var, values=["全部", "60/00", "68科创板", "30创业板", "ETF"], state="readonly", font=("微软雅黑", 10), width=10) type_combo.pack(side="left", padx=(5, 20)) # 分析按钮 self.analyze_btn = tk.Button(input_frame, text="开始分析", font=("微软雅黑", 12, "bold"), bg="#3498db", fg="white", activebackground="#2980b9", command=self.start_analysis, cursor="hand2") self.analyze_btn.pack(side="left", padx=10) # 推荐配置框架 recommend_frame = tk.Frame(self.root, bg="#f0f0f0") recommend_frame.pack(fill="x", padx=20, pady=5) # 评分条标签 tk.Label(recommend_frame, text="推荐评分:", font=("微软雅黑", 12), bg="#f0f0f0").pack(side="left") # 评分条 self.score_var = tk.DoubleVar(value=8.0) score_scale = tk.Scale(recommend_frame, from_=5.0, to=10.0, resolution=0.1, orient=tk.HORIZONTAL, variable=self.score_var, font=("微软雅黑", 10), bg="#f0f0f0", length=150) score_scale.pack(side="left", padx=(10, 20)) # 评分显示标签 self.score_label = tk.Label(recommend_frame, text="≥8.0分", font=("微软雅黑", 12, "bold"), fg="#e74c3c", bg="#f0f0f0") self.score_label.pack(side="left", padx=(0, 20)) # 绑定评分条变化事件 score_scale.bind("", self.update_score_label) score_scale.bind("", self.update_score_label) # 批量评分按钮 batch_score_btn = tk.Button(recommend_frame, text="开始获取评分", font=("微软雅黑", 12), bg="#3498db", fg="white", activebackground="#2980b9", command=self.start_batch_scoring, cursor="hand2") batch_score_btn.pack(side="left", padx=10) # 股票推荐按钮 recommend_btn = tk.Button(recommend_frame, text="股票推荐", font=("微软雅黑", 12), bg="#e74c3c", fg="white", activebackground="#c0392b", command=self.generate_stock_recommendations, cursor="hand2") recommend_btn.pack(side="left", padx=10) # 示例代码 example_frame = tk.Frame(self.root, bg="#f0f0f0") example_frame.pack(fill="x", padx=20) tk.Label(example_frame, text="支持所有A股代码: 沪市60XXXX | 科创板688XXX | 深市000XXX/002XXX | 创业板300XXX", font=("微软雅黑", 10), fg="#7f8c8d", bg="#f0f0f0").pack() # 进度条 self.progress_frame = tk.Frame(self.root, bg="#f0f0f0") self.progress_frame.pack(fill="x", padx=20, pady=10) self.progress_var = tk.StringVar() self.progress_label = tk.Label(self.progress_frame, textvariable=self.progress_var, font=("微软雅黑", 10), bg="#f0f0f0") self.progress_label.pack() self.progress_bar = ttk.Progressbar(self.progress_frame, mode='indeterminate') # 结果显示区域 result_frame = tk.Frame(self.root, bg="#f0f0f0") result_frame.pack(fill="both", expand=True, padx=20, pady=10) # 创建Notebook用于分页显示 self.notebook = ttk.Notebook(result_frame) self.notebook.pack(fill="both", expand=True) # 概览页面 self.overview_frame = tk.Frame(self.notebook, bg="white") self.notebook.add(self.overview_frame, text="股票概览") self.overview_text = scrolledtext.ScrolledText(self.overview_frame, font=("Consolas", 10), wrap=tk.WORD, bg="white") self.overview_text.pack(fill="both", expand=True, padx=10, pady=10) # 技术分析页面 self.technical_frame = tk.Frame(self.notebook, bg="white") self.notebook.add(self.technical_frame, text="技术分析") self.technical_text = scrolledtext.ScrolledText(self.technical_frame, font=("Consolas", 10), wrap=tk.WORD, bg="white") self.technical_text.pack(fill="both", expand=True, padx=10, pady=10) # 基本面分析页面 self.fundamental_frame = tk.Frame(self.notebook, bg="white") self.notebook.add(self.fundamental_frame, text="基本面分析") self.fundamental_text = scrolledtext.ScrolledText(self.fundamental_frame, font=("Consolas", 10), wrap=tk.WORD, bg="white") self.fundamental_text.pack(fill="both", expand=True, padx=10, pady=10) # 投资建议页面 self.recommendation_frame = tk.Frame(self.notebook, bg="white") self.notebook.add(self.recommendation_frame, text="投资建议") self.recommendation_text = scrolledtext.ScrolledText(self.recommendation_frame, font=("Consolas", 10), wrap=tk.WORD, bg="white") self.recommendation_text.pack(fill="both", expand=True, padx=10, pady=10) # 绑定双击事件到推荐文本框 self.recommendation_text.bind("", self.on_recommendation_double_click) # 评分排行页面 self.ranking_frame = tk.Frame(self.notebook, bg="white") self.notebook.add(self.ranking_frame, text="评分排行") # 排行榜控制框架 ranking_control_frame = tk.Frame(self.ranking_frame, bg="white") ranking_control_frame.pack(fill="x", padx=10, pady=5) tk.Label(ranking_control_frame, text="股票类型:", font=("微软雅黑", 10), bg="white").pack(side="left") self.ranking_type_var = tk.StringVar(value="全部") ranking_type_combo = ttk.Combobox(ranking_control_frame, textvariable=self.ranking_type_var, values=["全部", "60/00", "68科创板", "30创业板", "ETF"], state="readonly", font=("微软雅黑", 9), width=10) ranking_type_combo.pack(side="left", padx=(5, 20)) tk.Label(ranking_control_frame, text="显示数量:", font=("微软雅黑", 10), bg="white").pack(side="left") self.ranking_count_var = tk.StringVar(value="20") ranking_count_combo = ttk.Combobox(ranking_control_frame, textvariable=self.ranking_count_var, values=["10", "20", "30", "50"], state="readonly", font=("微软雅黑", 9), width=8) ranking_count_combo.pack(side="left", padx=(5, 20)) # 刷新按钮 refresh_ranking_btn = tk.Button(ranking_control_frame, text="刷新排行", font=("微软雅黑", 10), bg="#3498db", fg="white", activebackground="#2980b9", command=self.refresh_ranking, cursor="hand2") refresh_ranking_btn.pack(side="left", padx=10) self.ranking_text = scrolledtext.ScrolledText(self.ranking_frame, font=("Consolas", 10), wrap=tk.WORD, bg="white") self.ranking_text.pack(fill="both", expand=True, padx=10, pady=10) # 绑定双击事件到排行榜文本框 self.ranking_text.bind("", self.on_ranking_double_click) # 状态栏 status_frame = tk.Frame(self.root, bg="#ecf0f1", height=30) status_frame.pack(fill="x") status_frame.pack_propagate(False) self.status_var = tk.StringVar() self.status_var.set("就绪 - 请输入股票代码开始分析") status_label = tk.Label(status_frame, textvariable=self.status_var, font=("微软雅黑", 10), bg="#ecf0f1", anchor="w") status_label.pack(fill="x", padx=10, pady=5) # 绑定回车键 self.ticker_entry.bind('', lambda event: self.start_analysis()) # 显示欢迎信息 self.show_welcome_message() # 初始化排行榜显示 self.root.after(1000, self.update_ranking_display) def update_ranking_display(self): """更新排行榜显示(非阻塞方式)""" try: # 在后台线程中更新排行榜,避免阻塞UI threading.Thread(target=self._update_ranking_in_background, daemon=True).start() except Exception as e: print(f"⚠️ 更新排行榜显示失败: {e}") def _update_ranking_in_background(self): """在后台线程中更新排行榜""" try: # 获取当前的排行榜参数 stock_type = getattr(self, 'ranking_type_var', None) count_var = getattr(self, 'ranking_count_var', None) if stock_type and count_var: stock_type_val = stock_type.get() count_val = int(count_var.get()) else: stock_type_val = "全部" count_val = 20 # 生成排行榜报告 ranking_report = self._generate_ranking_report(stock_type_val, count_val) # 在主线程中更新UI self.root.after(0, self._update_ranking_ui, ranking_report) except Exception as e: print(f"⚠️ 后台更新排行榜失败: {e}") def _update_ranking_ui(self, ranking_report): """在主线程中更新排行榜UI""" try: if hasattr(self, 'ranking_text'): self.ranking_text.delete('1.0', tk.END) self.ranking_text.insert('1.0', ranking_report) except Exception as e: print(f"⚠️ 更新排行榜UI失败: {e}") def update_score_label(self, event=None): """更新评分标签显示""" score = self.score_var.get() self.score_label.config(text=f"≥{score:.1f}分") def show_progress(self, message): """显示进度条和消息""" self.progress_var.set(message) self.progress_bar.pack(fill="x", pady=5) self.progress_bar.start() self.root.update() def hide_progress(self): """隐藏进度条""" self.progress_bar.stop() self.progress_bar.pack_forget() self.progress_var.set("") self.root.update() def update_progress(self, message): """更新进度消息""" self.progress_var.set(message) self.root.update() def fetch_stock_list_from_api(self, stock_type): """从API动态获取股票列表 - 多重备用方案""" try: if AKSHARE_AVAILABLE: import akshare as ak if stock_type == "60/00": return self.get_main_board_stocks_multi_source() elif stock_type == "68科创板": return self.get_kcb_stocks_multi_source() elif stock_type == "30创业板": return self.get_cyb_stocks_multi_source() elif stock_type == "ETF": return self.get_etf_stocks_multi_source() except Exception as e: print(f"API获取失败: {e}") # API失败时返回None,不使用备用池 return None def get_main_board_stocks_multi_source(self): """多源获取主板股票 - 大幅扩展数量""" # 方法1: 使用A股实时数据 - 添加基本筛选 try: print("🔄 尝试方法1: A股实时数据(带基本筛选)...") import akshare as ak stock_df = ak.stock_zh_a_spot_em() if not stock_df.empty and '代码' in stock_df.columns: # 筛选主板股票并按市值或成交量排序 main_board_df = stock_df[ stock_df['代码'].str.startswith(('60', '000')) ].copy() # 如果有市值或成交量数据,按此排序 if '市值' in main_board_df.columns: main_board_df = main_board_df.sort_values('市值', ascending=False) elif '成交量' in main_board_df.columns: main_board_df = main_board_df.sort_values('成交量', ascending=False) elif '总市值' in main_board_df.columns: main_board_df = main_board_df.sort_values('总市值', ascending=False) main_board_stocks = main_board_df['代码'].tolist()[:100] # 取前100只 if main_board_stocks: print(f"✅ 方法1成功: 获取到{len(main_board_stocks)}只股票(已按质量排序)") return main_board_stocks except Exception as e: print(f"方法1失败: {e}") # 方法2: 使用沪深股票列表 try: print("🔄 尝试方法2: 沪深股票列表...") sh_stocks = [] sz_stocks = [] # 获取沪市股票 try: sh_df = ak.stock_info_sh_name_code(indicator="主板A股") if not sh_df.empty and '证券代码' in sh_df.columns: sh_stocks = [code for code in sh_df['证券代码'].astype(str).tolist() if code.startswith('60')][:100] # 增加到100只 except: pass # 获取深市股票 try: sz_df = ak.stock_info_sz_name_code(indicator="A股列表") if not sz_df.empty and '证券代码' in sz_df.columns: sz_stocks = [code for code in sz_df['证券代码'].astype(str).tolist() if code.startswith('000')][:100] # 增加到100只 except: pass all_stocks = sh_stocks + sz_stocks if all_stocks: print(f"✅ 方法2成功: 获取到{len(all_stocks)}只股票") return all_stocks except Exception as e: print(f"方法2失败: {e}") # 方法3: 按质量排序的知名股票列表 try: print("🔄 尝试方法3: 按质量排序的股票列表...") # 按市值和知名度分层排列的股票 quality_sorted_stocks = [ # 第一层:超大市值蓝筹 (市值>5000亿) "600519", "600036", "000858", "601318", "000002", "600276", "600887", "601398", "601939", "601988", "601166", "600000", "600030", "000001", "600585", "600309", # 第二层:大市值优质股 (市值1000-5000亿) "600900", "601012", "600031", "600809", "600690", "600196", "601328", "600048", "600015", "600025", "600028", "600038", "600050", "600104", "600111", "600132", "600150", "600160", "600170", "600177", "600188", "600199", "600208", "600219", # 第三层:中大市值成长股 (市值500-1000亿) "600233", "600256", "600271", "600281", "600297", "600305", "600315", "600332", "600340", "600352", "600362", "600372", "600383", "600395", "600406", "600418", "600426", "600438", "600449", "600459", "600469", "600478", "600487", "600498", # 第四层:深市优质主板股 "000063", "000100", "000157", "000166", "000338", "000425", "000568", "000625", "000651", "000725", "000876", "000895", "000938", "000977", "002001", "002027", "002050", "002120", "002129", "002142", "002304", "002352", "002415", "002475", "002594", "002714", "000400", "000401", "000402", "000403", "000404", "000407" ] # 验证这些股票是否可以获取价格,保持质量排序 valid_stocks = [] for ticker in quality_sorted_stocks: try: price = self.try_get_real_price_tencent(ticker) if price and price > 0: valid_stocks.append(ticker) # 获取到80只优质股票就足够了 if len(valid_stocks) >= 80: break except: continue if valid_stocks: print(f"✅ 方法3成功: 验证了{len(valid_stocks)}只优质股票(按质量排序)") return valid_stocks except Exception as e: print(f"方法3失败: {e}") print("❌ 所有方法都失败了") return None def get_kcb_stocks_multi_source(self): """多源获取科创板股票 - 扩展数量""" # 方法1: 从A股实时数据筛选 try: print("🔄 获取科创板: A股实时数据...") import akshare as ak stock_df = ak.stock_zh_a_spot_em() if not stock_df.empty and '代码' in stock_df.columns: kcb_stocks = stock_df[ stock_df['代码'].str.startswith('688') ]['代码'].tolist()[:50] # 增加到50只 if kcb_stocks: print(f"✅ 科创板获取成功: {len(kcb_stocks)}只") return kcb_stocks except Exception as e: print(f"科创板获取失败: {e}") # 方法2: 扩展的科创板股票列表 extended_kcb = [ "688001", "688002", "688003", "688005", "688006", "688007", "688008", "688009", "688010", "688011", "688012", "688013", "688016", "688017", "688018", "688019", "688020", "688021", "688022", "688023", "688025", "688026", "688027", "688028", "688029", "688030", "688031", "688032", "688033", "688035", "688036", "688037", "688038", "688039", "688041", "688043", "688046", "688047", "688048", "688050", "688051", "688052", "688053", "688055", "688056", "688058", "688059", "688060", "688061", "688062", "688063", "688065", "688066", "688068", "688069", "688070", "688071", "688072", "688073", "688078", "688079", "688080", "688081", "688083", "688085", "688086", "688088", "688089", "688090", "688093", "688095", "688096", "688099", "688100", "688101", "688102", "688103", "688105", "688106", "688107", "688108", "688111", "688112", "688113", "688115", "688116", "688117", "688118", "688119", "688120", "688121", "688122", "688123", "688125", "688126", "688127", "688128", "688129", "688131", "688132", "688133", "688135", "688136", "688137", "688138", "688139", "688141", "688142", "688143", "688144", "688145", "688146", "688148", "688150", "688151", "688152", "688153", "688155", "688157", "688158", "688159", "688160", "688161", "688162", "688163", "688165", "688166", "688167", "688168", "688169", "688170", "688171", "688172", "688173", "688180", "688181", "688185", "688186", "688187", "688188", "688189", "688190", "688195", "688196", "688198", "688199", "688200", "688981" # 加入一些知名的科创板股票 ] print(f"🔄 使用扩展科创板股票: {len(extended_kcb)}只") return extended_kcb def get_cyb_stocks_multi_source(self): """多源获取创业板股票 - 扩展数量""" # 方法1: 从A股实时数据筛选 try: print("🔄 获取创业板: A股实时数据...") import akshare as ak stock_df = ak.stock_zh_a_spot_em() if not stock_df.empty and '代码' in stock_df.columns: cyb_stocks = stock_df[ stock_df['代码'].str.startswith('300') ]['代码'].tolist()[:80] # 增加到80只 if cyb_stocks: print(f"✅ 创业板获取成功: {len(cyb_stocks)}只") return cyb_stocks except Exception as e: print(f"创业板获取失败: {e}") # 方法2: 扩展的创业板股票列表 extended_cyb = [ "300001", "300002", "300003", "300004", "300005", "300006", "300007", "300008", "300009", "300010", "300011", "300012", "300013", "300014", "300015", "300016", "300017", "300018", "300019", "300020", "300021", "300022", "300023", "300024", "300025", "300026", "300027", "300028", "300029", "300030", "300031", "300032", "300033", "300034", "300035", "300036", "300037", "300038", "300039", "300040", "300041", "300042", "300043", "300044", "300045", "300046", "300047", "300048", "300049", "300050", "300051", "300052", "300053", "300054", "300055", "300056", "300057", "300058", "300059", "300061", "300062", "300063", "300064", "300065", "300066", "300067", "300068", "300069", "300070", "300071", "300072", "300073", "300074", "300075", "300076", "300077", "300078", "300079", "300080", "300081", "300082", "300083", "300084", "300085", "300086", "300087", "300088", "300089", "300090", "300091", "300092", "300093", "300094", "300095", "300096", "300097", "300098", "300099", "300100", "300101", "300102", "300103", "300104", "300105", "300106", "300107", "300108", "300109", "300110", "300111", "300112", "300113", "300114", "300115", "300116", "300117", "300118", "300119", "300120", "300121", "300122", "300123", "300124", "300125", "300126", "300127", "300128", "300129", "300130", "300131", "300132", "300133", "300134", "300135", "300136", "300137", "300138", "300139", "300140", "300141", "300142", "300143", "300144", "300145", "300750", "300760", "300896" # 加入知名创业板股票 ] print(f"🔄 使用扩展创业板股票: {len(extended_cyb)}只") return extended_cyb def get_etf_stocks_multi_source(self): """多源获取ETF股票 - 扩展数量""" # 方法1: 使用ETF实时数据 try: print("🔄 获取ETF: 基金实时数据...") import akshare as ak etf_df = ak.fund_etf_spot_em() if not etf_df.empty and '代码' in etf_df.columns: etf_codes = etf_df['代码'].astype(str).tolist() valid_etfs = [code for code in etf_codes if code.startswith(('51', '15', '16'))][:50] # 增加到50只 if valid_etfs: print(f"✅ ETF获取成功: {len(valid_etfs)}只") return valid_etfs except Exception as e: print(f"ETF方法1失败: {e}") # 方法2: 扩展的ETF股票列表 extended_etf = [ # 沪市ETF (51开头) "510050", "510300", "510500", "510880", "510900", "512010", "512070", "512100", "512110", "512120", "512170", "512200", "512290", "512400", "512500", "512600", "512660", "512690", "512700", "512760", "512800", "512880", "512890", "512980", "513050", "513060", "513100", "513500", "513520", "513580", "513600", "513880", "515000", "515010", "515020", "515030", "515050", "515060", "515070", "515080", "515090", "515100", "515110", "515120", "515130", "515150", "515180", "515200", "515210", "515220", "515230", "515250", "515260", "515280", "515290", "515300", # 深市ETF (15开头) "159001", "159003", "159005", "159006", "159007", "159009", "159010", "159011", "159013", "159015", "159016", "159017", "159018", "159019", "159020", "159022", "159025", "159028", "159030", "159032", "159033", "159034", "159037", "159039", "159601", "159605", "159611", "159612", "159613", "159615", "159619", "159625", "159629", "159633", "159636", "159637", "159639", "159645", "159647", "159649", "159651", "159652", "159655", "159657", "159659", "159661", "159663", "159665", "159667", "159669", "159671", "159673", "159675", "159677", "159679", "159681", "159683", "159685", "159687", "159689", "159691", "159693", "159695", "159697", "159699", "159701", "159703", "159705", "159707", "159709", "159711", "159713", "159715", "159717", "159719", "159721", "159723", "159725", "159727", "159729", "159731", "159733", "159735", "159737", "159739", "159741", "159743", "159745", "159747", "159749", "159751", "159753", "159755", "159757", "159759", "159761", "159763", "159765", "159767", "159769", "159771", "159773", "159775", "159777", "159779", "159781", "159783", "159785", "159787", "159789", "159791", "159793", "159795", "159797", "159799", "159801", "159803", "159805", "159807", "159809", "159811", "159813", "159815", "159817", "159819", "159821", "159823", "159825", "159827", "159829", "159831", "159833", "159835", "159837", "159839", "159841", "159843", "159845", "159847", "159849", "159851", "159853", "159855", "159857", "159859", "159861", "159863", "159865", "159867", "159869", "159871", "159873", "159875", "159877", "159879", "159881", "159883", "159885", "159887", "159889", "159891", "159893", "159895", "159897", "159899", "159901", "159903", "159905", "159907", "159909", "159911", "159913", "159915", "159917", "159919", "159921", "159923", "159925", "159927", "159928", "159929", "159931", "159933", "159935", "159937", "159939", "159941", "159943", "159945", "159947", "159949", "159951", "159953", "159955", "159957", "159959", "159961", "159963", "159965", "159967", "159969", "159971", "159973", "159975", "159977", "159979", "159981", "159983", "159985", "159987", "159989", "159991", "159993", "159995", "159997", "159999" ] print(f"🔄 使用扩展ETF股票: {len(extended_etf)}只") return extended_etf def get_fallback_stock_pool(self, stock_type): """API失败时的备用股票池""" if stock_type == "60/00": return [ "600036", "600519", "600276", "600030", "600887", "600000", "600031", "600809", "600585", "600900", "601318", "601166", "601398", "601939", "601988", "601012", "000002", "000858", "000001", "000568", "000651", "000063", "000725", "000625", "000100", "000876", "000895", "002714", "002415", "002594" ] elif stock_type == "68科创板": return ["688981", "688036", "688111", "688599", "688169", "688180"] elif stock_type == "30创业板": return ["300750", "300015", "300059", "300122", "300274", "300347", "300433", "300142", "300760", "300896"] elif stock_type == "ETF": return ["510050", "510300", "510500", "159919", "159915", "512880", "159928", "512690", "515050", "512170"] else: # 全部 return ["600036", "600519", "000002", "000858", "300750", "688981", "510050", "510300"] def get_stock_pool_by_type(self, stock_type): """根据股票类型获取股票池 - API失败时直接返回失败""" print(f"📊 正在从API获取{stock_type}股票池...") # 尝试从API获取 stock_list = self.fetch_stock_list_from_api(stock_type) if stock_list: print(f"✅ 从API获取到{len(stock_list)}只{stock_type}股票") return stock_list else: print(f"❌ API获取{stock_type}股票池失败") return None # 不使用备用池,直接返回失败 def generate_valid_codes(self): """生成有效的A股代码范围""" valid_codes = set() # 沪市主板 (600000-603999) for i in range(600000, 604000): valid_codes.add(str(i)) # 科创板 (688000-688999) for i in range(688000, 689000): valid_codes.add(str(i)) # 深市主板 (000000-000999) for i in range(1, 1000): valid_codes.add(f"{i:06d}") # 深市中小板 (002000-002999) for i in range(2000, 3000): valid_codes.add(f"{i:06d}") # 创业板 (300000-301999) for i in range(300000, 302000): valid_codes.add(str(i)) return valid_codes def is_valid_a_share_code(self, ticker): """验证是否为有效的A股代码""" if not ticker.isdigit() or len(ticker) != 6: return False # 检查代码格式 if ticker.startswith('60'): # 沪市主板 return True elif ticker.startswith('688'): # 科创板 return True elif ticker.startswith('00'): # 深市主板 return True elif ticker.startswith('002'): # 深市中小板 return True elif ticker.startswith('30'): # 创业板 return True else: return False def get_stock_info_generic(self, ticker): """获取通用股票信息(快速模式,避免卡住)""" # 直接返回基本信息,避免网络调用卡住 if ticker.startswith('688'): name = f"科创板股票{ticker}" industry = "科技创新" concept = "科创板,技术创新" elif ticker.startswith('300'): name = f"创业板股票{ticker}" industry = "成长企业" concept = "创业板,中小企业" elif ticker.startswith('60'): name = f"沪市股票{ticker}" industry = "传统行业" concept = "沪市主板,蓝筹股" elif ticker.startswith('00'): name = f"深市股票{ticker}" industry = "制造业" concept = "深市主板,民营企业" else: name = f"股票{ticker}" industry = "未知行业" concept = "未知概念" return { "name": name, "industry": industry, "concept": concept, "price": None, # 价格将在后续步骤单独获取 "price_status": "待获取" } def fetch_real_stock_info(self, ticker): """获取真实的股票信息""" try: # 获取股票名称 stock_name = self.get_stock_name_from_sina(ticker) if not stock_name: stock_name = f"股票{ticker}" # 获取行业信息 industry = self.get_industry_info(ticker) # 获取实时价格 price = self.get_stock_price(ticker) return { "name": stock_name, "industry": industry, "concept": self.get_concept_info(ticker), "price": price } except Exception as e: print(f"获取股票信息失败: {e}") return None def get_stock_name_from_sina(self, ticker): """从新浪财经获取股票名称(带智能缓存)""" # 检查是否已经失败过两次 if ticker in self.failed_stock_names: return None # 检查尝试次数 attempts = self.stock_name_attempts.get(ticker, 0) if attempts >= 2: self.failed_stock_names.add(ticker) print(f"⚠️ 股票 {ticker} 已连续失败2次,跳过获取名称") return None try: # 新浪财经API if ticker.startswith(('60', '68')): code = f"sh{ticker}" else: code = f"sz{ticker}" url = f"http://hq.sinajs.cn/list={code}" # 设置请求头 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', 'Referer': 'http://finance.sina.com.cn' } req = urllib.request.Request(url, headers=headers) response = urllib.request.urlopen(req, timeout=6) # 股票名称获取超时增加到6秒 data = response.read().decode('gbk', errors='ignore') # 解析数据 if 'var hq_str_' in data: parts = data.split('="')[1].split('",')[0].split(',') if len(parts) > 0 and parts[0]: # 成功获取,重置尝试次数 self.stock_name_attempts[ticker] = 0 return parts[0] # 股票名称 # 失败时增加尝试次数 self.stock_name_attempts[ticker] = attempts + 1 return None except Exception as e: # 失败时增加尝试次数 self.stock_name_attempts[ticker] = attempts + 1 if attempts == 0: # 只在第一次失败时打印详细错误 print(f"从新浪获取股票名称失败: {e}") return None def get_dynamic_stock_info(self, ticker): """动态获取股票的完整信息""" try: # 特殊处理ETF if ticker.startswith(('51', '15', '16', '56')): return self.get_etf_info(ticker) if AKSHARE_AVAILABLE: import akshare as ak # 尝试从akshare获取股票基本信息 try: # 获取股票基本信息 stock_info = ak.stock_individual_info_em(symbol=ticker) if not stock_info.empty: info_dict = {} for _, row in stock_info.iterrows(): info_dict[row['item']] = row['value'] # 获取名称 name = info_dict.get('股票简称', self.get_stock_name_from_sina(ticker)) # 获取行业信息 industry = info_dict.get('行业', '未知行业') # 获取概念信息 (如果有的话) concept = info_dict.get('概念', '基础股票') # 获取实时价格 current_price = self.try_get_real_price_tencent(ticker) if current_price is None: current_price = float(info_dict.get('现价', 0)) return { 'name': name, 'industry': industry, 'concept': concept, 'price': current_price } except Exception as e: print(f"从akshare获取{ticker}信息失败: {e}") # 如果akshare失败,尝试从其他源获取基本信息 name = self.get_stock_name_from_sina(ticker) price = self.try_get_real_price_tencent(ticker) if name and price: # 根据股票代码推断基本信息 industry = self.infer_industry_by_code(ticker) concept = self.infer_concept_by_code(ticker) return { 'name': name, 'industry': industry, 'concept': concept, 'price': price } return None except Exception as e: print(f"获取股票{ticker}动态信息失败: {e}") return None def get_etf_info(self, ticker): """获取ETF基金信息""" try: # 从新浪获取ETF名称 name = self.get_stock_name_from_sina(ticker) price = self.try_get_real_price_tencent(ticker) if name and price: # ETF基金的基本分类 etf_type = "ETF基金" if "50" in ticker: concept = "上证50,大盘蓝筹" elif "300" in ticker: concept = "沪深300,宽基指数" elif "500" in ticker: concept = "中证500,中盘股" elif ticker.startswith("159"): concept = "深交所ETF,指数基金" else: concept = "ETF基金,指数投资" return { 'name': name, 'industry': etf_type, 'concept': concept, 'price': price } except Exception as e: print(f"获取ETF {ticker} 信息失败: {e}") return None def infer_industry_by_code(self, ticker): """根据股票代码推断行业""" if ticker.startswith('60'): return '沪市股票' elif ticker.startswith('000'): return '深市主板' elif ticker.startswith('002'): return '深市中小板' elif ticker.startswith('300'): return '创业板' elif ticker.startswith('688'): return '科创板' elif ticker.startswith(('51', '15')): return 'ETF基金' else: return '未知板块' def infer_concept_by_code(self, ticker): """根据股票代码推断概念""" if ticker.startswith('688'): return '科创板,科技创新' elif ticker.startswith('300'): return '创业板,成长股' elif ticker.startswith(('60', '000')): return '主板股票,蓝筹股' elif ticker.startswith('002'): return '中小板,制造业' elif ticker.startswith(('51', '15')): return 'ETF基金,指数基金' else: return '基础概念' def get_industry_info(self, ticker): """获取行业信息""" # 扩展的行业信息数据库 industry_map = { # 游戏和文化传媒 "002174": "游戏软件", # 游族网络 "300144": "游戏软件", # 宋城演艺 "002555": "游戏软件", # 三七互娱 "300296": "游戏软件", # 利亚德 # 白酒制造 "000858": "白酒制造", # 五粮液 "600519": "白酒制造", # 贵州茅台 "000568": "白酒制造", # 泸州老窖 "000596": "白酒制造", # 古井贡酒 # 银行业 "600036": "银行业", # 招商银行 "000001": "银行业", # 平安银行 "600000": "银行业", # 浦发银行 "601166": "银行业", # 兴业银行 # 半导体制造 "688981": "半导体制造", # 中芯国际 "002371": "半导体制造", # 北方华创 "300782": "半导体制造", # 卓胜微 # 新能源电池 "300750": "新能源电池", # 宁德时代 "002594": "新能源汽车", # 比亚迪 "300014": "新能源电池", # 亿纬锂能 # 医药制造 "600276": "医药制造", # 恒瑞医药 "000661": "医药制造", # 长春高新 "300142": "生物制药", # 沃森生物 "300015": "医疗服务", # 爱尔眼科 # 电子制造 "002415": "安防设备", # 海康威视 "002475": "电子制造", # 立讯精密 "002241": "电子制造", # 歌尔股份 # 软件服务 "688111": "软件服务", # 金山办公 "300059": "金融服务", # 东方财富 "000725": "软件服务", # 京东方A # 房地产 "000002": "房地产", # 万科A "000001": "房地产", # 平安银行 # 食品饮料 "600887": "乳制品", # 伊利股份 "000895": "调味品", # 双汇发展 # 消费电子 "688036": "消费电子", # 传音控股 } if ticker in industry_map: return industry_map[ticker] # 根据代码前缀推断 if ticker.startswith('688'): return "科技创新" elif ticker.startswith('300'): return "成长企业" elif ticker.startswith('60'): return "传统行业" elif ticker.startswith('00'): return "制造业" else: return "其他行业" def get_concept_info(self, ticker): """获取概念信息""" concept_map = { # 游戏和文化传媒 "002174": "游戏概念,文化传媒,手游,页游", # 游族网络 "300144": "文化传媒,旅游演艺", # 宋城演艺 "002555": "游戏概念,手游,页游", # 三七互娱 # 白酒概念 "000858": "白酒概念,消费股,川酒", # 五粮液 "600519": "白酒概念,核心资产,消费股", # 贵州茅台 "000568": "白酒概念,消费股,川酒", # 泸州老窖 # 银行金融 "600036": "银行股,金融股,蓝筹股", # 招商银行 "000001": "银行股,金融股,零售银行", # 平安银行 "002344": "证券股,金融服务", # 海通证券 # 科技芯片 "688981": "半导体,芯片概念,科创板,国产替代", # 中芯国际 "002371": "半导体设备,芯片概念", # 北方华创 "300782": "芯片设计,射频芯片", # 卓胜微 # 新能源 "300750": "新能源,锂电池,储能,动力电池", # 宁德时代 "002594": "新能源汽车,电动汽车,比亚迪概念", # 比亚迪 "300014": "锂电池,储能,新能源", # 亿纬锂能 # 医药医疗 "600276": "创新药,医药股,抗癌概念", # 恒瑞医药 "000661": "生物医药,疫苗概念", # 长春高新 "300142": "疫苗概念,生物医药,新冠疫苗", # 沃森生物 "300015": "医疗服务,眼科医疗,连锁医院", # 爱尔眼科 # 消费电子 "002415": "安防概念,人工智能,视频监控", # 海康威视 "002475": "苹果概念,消费电子,5G", # 立讯精密 "688036": "手机概念,消费电子,非洲市场", # 传音控股 # 软件服务 "688111": "办公软件,云计算,WPS", # 金山办公 "300059": "互联网金融,证券软件,大数据", # 东方财富 # 房地产 "000002": "地产股,白马股,城市更新", # 万科A # 食品饮料 "600887": "乳制品,食品安全,消费股", # 伊利股份 } if ticker in concept_map: return concept_map[ticker] # 根据代码前缀推断 if ticker.startswith('688'): return "科创板,技术创新,硬科技" elif ticker.startswith('300'): return "创业板,中小企业,成长股" elif ticker.startswith('60'): return "沪市主板,蓝筹股,大盘股" elif ticker.startswith('002'): return "深市中小板,民营企业,成长股" elif ticker.startswith('00'): return "深市主板,传统企业,价值股" else: return "其他概念" def log_price_with_score(self, ticker, price): """在日志中显示价格和快速评分(避免递归调用)""" try: # 获取股票名称(简化版,避免复杂调用) try: stock_info = self.get_stock_info_generic(ticker) name = stock_info.get('name', ticker) if stock_info else ticker except: name = ticker # 简化的快速评分(基于价格和基础判断,避免递归) quick_score = 5.0 # 基础分 # 基于价格区间的简单评分 if price > 100: quick_score += 1.0 # 高价股 elif price < 5: quick_score -= 1.5 # 超低价股风险高 elif price < 10: quick_score -= 0.5 # 低价股 # 基于股票代码的板块简单评分 if ticker.startswith('688'): # 科创板 quick_score += 0.5 # 科技创新加分 elif ticker.startswith('300'): # 创业板 quick_score += 0.3 # 成长性加分 elif ticker.startswith('600'): # 沪市主板 quick_score += 0.2 # 稳定性加分 # 限制评分范围 quick_score = max(1.0, min(10.0, quick_score)) # 获取当前选择的投资期限(如果可用) try: period = self.period_var.get() period_text = f"({period}策略)" except: period_text = "" # 输出增强的日志信息 print(f"📊 {ticker} {name} | 价格: ¥{price:.2f} | 快速评分: {quick_score:.1f}/10 {period_text}") except Exception as e: # 如果任何计算失败,只显示基础价格信息 print(f"✅ {ticker} | 价格: ¥{price:.2f}") def get_stock_price(self, ticker): """获取股票实时价格(多重数据源,优化顺序)""" failed_sources = [] # 记录失败的数据源 # 方案1: 腾讯财经API(最稳定) real_price = self.try_get_real_price_tencent(ticker) if real_price is not None: self.log_price_with_score(ticker, real_price) return real_price else: failed_sources.append("腾讯财经") # 方案2: 新浪财经API(备用) real_price = self.try_get_real_price_sina(ticker) if real_price is not None: self.log_price_with_score(ticker, real_price) return real_price else: failed_sources.append("新浪财经") # 方案3: 网易财经API(备用) real_price = self.try_get_real_price_netease(ticker) if real_price is not None: self.log_price_with_score(ticker, real_price) return real_price else: failed_sources.append("网易财经") # 方案4: akshare(最后尝试,通常失败) if AKSHARE_AVAILABLE: real_price = self.try_get_real_price_akshare(ticker) if real_price is not None: self.log_price_with_score(ticker, real_price) return real_price else: failed_sources.append("akshare") # 所有数据源都失败时报告网络问题 print(f"❌ 所有数据源均无法获取 {ticker} 的价格") print(f"⚠️ 失败的数据源: {', '.join(failed_sources)}") print(f"💡 可能原因: 网络超时、API限制、服务器故障") print(f"� 由于网络问题无法获取实时数据,无法进行准确分析") return None # 返回None表示网络失败,不使用假数据 def try_get_real_price_tencent(self, ticker): """尝试通过腾讯财经获取实时价格 - 支持ETF""" try: import time # 控制请求频率 current_time = time.time() if current_time - self.last_request_time < 0.3: time.sleep(0.3 - (current_time - self.last_request_time)) # 构建腾讯财经API URL - 改进ETF支持 if ticker.startswith(('60', '68')): code = f"sh{ticker}" elif ticker.startswith(('51')): # 沪市ETF code = f"sh{ticker}" elif ticker.startswith(('15', '16')): # 深市ETF code = f"sz{ticker}" else: code = f"sz{ticker}" url = f"http://qt.gtimg.cn/q={code}" headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', 'Referer': 'http://finance.qq.com', 'Accept': '*/*', 'Connection': 'keep-alive' } req = urllib.request.Request(url, headers=headers) response = urllib.request.urlopen(req, timeout=6) # 增加到6秒超时,提高成功率 data = response.read().decode('gbk', errors='ignore') self.last_request_time = time.time() # 解析腾讯财经数据格式: v_sz000001="51~平安银行~000001~11.32~11.38~11.32~..." if f'v_{code}=' in data: parts = data.split('="')[1].split('"')[0].split('~') if len(parts) > 3 and parts[3]: price = float(parts[3]) if price > 0: return price # 如果腾讯财经失败,对于ETF尝试新浪财经 if ticker.startswith(('51', '15', '16')): return self.try_get_etf_price_sina(ticker) except Exception as e: print(f"⚠️ 腾讯财经获取失败: {ticker} - {e}") # 对于ETF,尝试备用方案 if ticker.startswith(('51', '15', '16')): return self.try_get_etf_price_sina(ticker) return None def try_get_etf_price_sina(self, ticker): """通过新浪财经获取ETF价格""" try: import time # ETF在新浪财经的代码格式 if ticker.startswith('51'): code = f"sh{ticker}" else: code = f"sz{ticker}" url = f"http://hq.sinajs.cn/list={code}" headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', 'Referer': 'http://finance.sina.com.cn' } req = urllib.request.Request(url, headers=headers) response = urllib.request.urlopen(req, timeout=6) # 新浪财经超时增加到6秒 data = response.read().decode('gbk', errors='ignore') # 解析新浪财经ETF数据 if 'var hq_str_' in data and '=' in data: parts = data.split('="')[1].split('",')[0].split(',') if len(parts) > 3 and parts[3]: price = float(parts[3]) if price > 0: print(f"✅ 通过新浪财经获取 {ticker} ETF价格: {price}") return price except Exception as e: print(f"⚠️ 新浪财经ETF获取失败: {ticker} - {e}") return None def try_get_real_price_netease(self, ticker): """尝试通过网易财经获取实时价格""" try: import time # 控制请求频率 current_time = time.time() if current_time - self.last_request_time < 0.3: time.sleep(0.3 - (current_time - self.last_request_time)) # 构建网易财经API URL market = '0' if ticker.startswith(('60', '68')) else '1' url = f"http://api.money.126.net/data/feed/{market}{ticker}" headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', 'Referer': 'http://money.163.com', 'Accept': 'application/json, text/javascript, */*; q=0.01' } req = urllib.request.Request(url, headers=headers) response = urllib.request.urlopen(req, timeout=6) # 网易财经超时增加到6秒 data = response.read().decode('utf-8', errors='ignore') self.last_request_time = time.time() # 解析JSON数据 import json # 移除JSONP回调函数包装 if data.startswith('_ntes_quote_callback(') and data.endswith(');'): json_str = data[21:-2] stock_data = json.loads(json_str) code_key = f"{market}{ticker}" if code_key in stock_data and 'price' in stock_data[code_key]: price = float(stock_data[code_key]['price']) if price > 0: return price except Exception as e: print(f"⚠️ 网易财经获取失败: {ticker} - {e}") return None def try_get_real_price_akshare(self, ticker): """尝试通过akshare获取实时价格(快速失败)""" try: # 由于akshare经常失败,设置较短超时 import akshare as ak # 快速超时设置 import socket socket.setdefaulttimeout(3) # 获取单只股票的实时数据 df = ak.stock_zh_a_spot_em() stock_data = df[df['代码'] == ticker] if not stock_data.empty: price = float(stock_data.iloc[0]['最新价']) return price except Exception as e: # 不打印akshare错误,因为它经常失败 pass finally: # 恢复默认超时 import socket socket.setdefaulttimeout(None) return None def try_get_real_price_sina(self, ticker): """尝试通过新浪财经获取实时价格(优化版)""" try: import time # 控制请求频率,避免被限制 current_time = time.time() if current_time - self.last_request_time < 0.5: # 最少间隔0.5秒 time.sleep(0.5 - (current_time - self.last_request_time)) if ticker.startswith(('60', '68')): code = f"sh{ticker}" else: code = f"sz{ticker}" url = f"http://hq.sinajs.cn/list={code}" headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', 'Referer': 'http://finance.sina.com.cn', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8', 'Accept-Encoding': 'gzip, deflate', 'Connection': 'keep-alive', 'Cache-Control': 'no-cache' } req = urllib.request.Request(url, headers=headers) response = urllib.request.urlopen(req, timeout=6) # akshare备用接口超时增加到6秒 data = response.read().decode('gbk', errors='ignore') self.last_request_time = time.time() # 更新请求时间 if 'var hq_str_' in data and data.strip(): parts = data.split('="')[1].split('",')[0].split(',') if len(parts) > 3 and parts[3] and parts[3] != '0.000': price = float(parts[3]) if price > 0: # 确保价格有效 return price except Exception as e: if "timeout" in str(e).lower(): print(f"⚠️ 新浪财经超时: {ticker}") elif "403" in str(e): print(f"⚠️ 新浪财经访问被限制: {ticker}") else: print(f"⚠️ 新浪财经获取失败: {e}") return None def calculate_recommendation_index(self, ticker): """计算投资推荐指数""" stock_info = self.get_stock_info_generic(ticker) # 基础评分因子 base_score = random.uniform(60, 85) # 行业加成 industry = stock_info.get("industry", "") industry_bonus = 0 if "半导体" in industry: industry_bonus = random.uniform(5, 15) # 科技成长性加成 elif "银行" in industry: industry_bonus = random.uniform(0, 8) # 稳健性加成 elif "白酒" in industry: industry_bonus = random.uniform(3, 12) # 消费概念加成 elif "新能源" in industry: industry_bonus = random.uniform(8, 18) # 新能源概念加成 elif "房地产" in industry: industry_bonus = random.uniform(-5, 5) # 政策敏感性 else: industry_bonus = random.uniform(0, 10) # 板块加成 board_bonus = 0 if ticker.startswith('688'): board_bonus = random.uniform(5, 10) # 科创板创新加成 elif ticker.startswith('300'): board_bonus = random.uniform(3, 8) # 创业板成长加成 elif ticker.startswith('60'): board_bonus = random.uniform(2, 6) # 主板稳定加成 elif ticker.startswith('00'): board_bonus = random.uniform(1, 7) # 深市加成 # 计算总分 total_score = base_score + industry_bonus + board_bonus total_score = min(100, max(0, total_score)) # 限制在0-100之间 # 生成推荐指数显示 index_display = self.format_recommendation_index(total_score, ticker) return index_display def format_recommendation_index(self, score, ticker): """格式化推荐指数显示""" stock_info = self.get_stock_info_generic(ticker) # 确定评级 if score >= 85: rating = "强烈推荐" stars = "★★★★★" color_desc = "深绿色" elif score >= 75: rating = "推荐" stars = "★★★★☆" color_desc = "绿色" elif score >= 65: rating = "中性" stars = "★★★☆☆" color_desc = "黄色" elif score >= 50: rating = "谨慎" stars = "★★☆☆☆" color_desc = "橙色" else: rating = "不推荐" stars = "★☆☆☆☆" color_desc = "红色" # 生成进度条 bar_length = 30 filled_length = int(score / 100 * bar_length) bar = "█" * filled_length + "░" * (bar_length - filled_length) # 生成详细指数信息 index_info = """ 投资推荐指数: {:.1f}/100 {} {} [{}] {} 评级详情: • 综合评分: {:.1f}分 • 投资建议: {} • 适合投资者: {} • 风险等级: {} """.format( score, stars, bar, bar, rating, score, rating, self.get_investor_type(score), self.get_risk_level(score) ) return index_info def get_investor_type(self, score): """根据评分获取适合的投资者类型""" if score >= 80: return "成长型投资者、价值投资者" elif score >= 70: return "稳健型投资者、成长型投资者" elif score >= 60: return "稳健型投资者" elif score >= 50: return "风险偏好型投资者" else: return "高风险偏好投资者(不建议)" def get_risk_level(self, score): """根据评分获取风险等级""" if score >= 80: return "中低风险" elif score >= 70: return "中等风险" elif score >= 60: return "中等风险" elif score >= 50: return "中高风险" else: return "高风险" def get_real_technical_indicators(self, ticker): """获取真实的技术指标数据,网络失败时使用智能模拟数据""" # 首先尝试获取真实数据 try: if AKSHARE_AVAILABLE: result = self._try_get_real_technical_data(ticker) if result: return result except Exception as e: print(f"⚠️ 真实数据获取失败: {e}") # 如果真实数据获取失败,生成智能模拟数据 return self._generate_smart_mock_technical_data(ticker) def _try_get_real_technical_data(self, ticker): """尝试获取真实技术数据""" import akshare as ak import pandas as pd import os import urllib.request import socket # 临时禁用代理,避免代理连接问题 original_proxies = {} proxy_env_vars = ['http_proxy', 'https_proxy', 'HTTP_PROXY', 'HTTPS_PROXY'] for var in proxy_env_vars: if var in os.environ: original_proxies[var] = os.environ[var] del os.environ[var] # 设置urllib不使用代理 proxy_handler = urllib.request.ProxyHandler({}) opener = urllib.request.build_opener(proxy_handler) urllib.request.install_opener(opener) try: # 设置更短的超时时间,快速失败 socket_timeout = socket.getdefaulttimeout() socket.setdefaulttimeout(5) # 5秒超时 # 获取历史数据计算技术指标 stock_hist = ak.stock_zh_a_hist(symbol=ticker, period="daily", start_date="20241001", end_date="20241101", adjust="qfq") if stock_hist is not None and not stock_hist.empty: # 获取最新价格 current_price = float(stock_hist['收盘'].iloc[-1]) # 计算移动平均线 ma5 = float(stock_hist['收盘'].tail(5).mean()) if len(stock_hist) >= 5 else current_price ma10 = float(stock_hist['收盘'].tail(10).mean()) if len(stock_hist) >= 10 else current_price ma20 = float(stock_hist['收盘'].tail(20).mean()) if len(stock_hist) >= 20 else current_price ma60 = float(stock_hist['收盘'].tail(60).mean()) if len(stock_hist) >= 60 else current_price # 计算RSI (简化版本) if len(stock_hist) >= 14: close_prices = stock_hist['收盘'].astype(float) delta = close_prices.diff() gain = (delta.where(delta > 0, 0)).rolling(window=14).mean() loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean() rs = gain / loss rsi = 100 - (100 / (1 + rs.iloc[-1])) else: rsi = 50 # 默认中性值 # 计算成交量比率 if len(stock_hist) >= 5: avg_volume = stock_hist['成交量'].tail(5).mean() current_volume = stock_hist['成交量'].iloc[-1] volume_ratio = float(current_volume / avg_volume) if avg_volume > 0 else 1.0 else: volume_ratio = 1.0 # 简化的MACD计算 (使用价格差异) if len(stock_hist) >= 26: ema12 = stock_hist['收盘'].ewm(span=12).mean().iloc[-1] ema26 = stock_hist['收盘'].ewm(span=26).mean().iloc[-1] macd = float(ema12 - ema26) signal = float(stock_hist['收盘'].ewm(span=9).mean().iloc[-1]) else: macd = 0 signal = 0 print(f"✅ 成功获取{ticker}的真实技术指标") return { 'current_price': current_price, 'ma5': ma5, 'ma10': ma10, 'ma20': ma20, 'ma60': ma60, 'rsi': float(rsi) if not pd.isna(rsi) else 50, 'macd': macd, 'signal': signal, 'volume_ratio': volume_ratio, 'data_source': 'real' } else: print(f"⚠️ {ticker}未获取到历史数据") return None except Exception as e: error_msg = str(e) if "ProxyError" in error_msg or "proxy" in error_msg.lower(): print(f"❌ 代理服务器问题: {ticker}") elif "Max retries exceeded" in error_msg: print(f"❌ 网络连接超时: {ticker}") elif "ConnectTimeout" in error_msg: print(f"❌ 连接超时: {ticker}") else: print(f"⚠️ 获取{ticker}技术指标失败: {e}") return None finally: # 恢复原始设置 if socket_timeout: socket.setdefaulttimeout(socket_timeout) for var, value in original_proxies.items(): os.environ[var] = value def _generate_smart_mock_technical_data(self, ticker): """生成智能模拟技术数据(基于实时价格和股票特征)""" import random import hashlib # 使用股票代码作为随机种子,确保每个股票的数据是稳定但不同的 seed = int(hashlib.md5(ticker.encode()).hexdigest()[:8], 16) random.seed(seed) # 尝试获取实时价格 current_price = self.get_stock_price(ticker) if current_price is None: # 根据股票代码特征设置基础价格 if ticker.startswith('688'): # 科创板 current_price = random.uniform(30, 80) elif ticker.startswith('300'): # 创业板 current_price = random.uniform(15, 45) elif ticker.startswith('60'): # 沪市主板 current_price = random.uniform(8, 60) elif ticker.startswith(('510', '511', '512', '513', '515', '516', '517', '518', '159', '161', '163', '165')): # ETF基金 current_price = random.uniform(0.8, 8.0) # ETF价格通常较低 else: # 深市主板 current_price = random.uniform(6, 35) # 根据股票代码生成不同的市场特征 stock_hash = hash(ticker) % 100 # 生成差异化的技术指标 # 移动平均线 (基于股票特征的趋势) if stock_hash < 20: # 20%股票呈上升趋势 trend_factor = random.uniform(1.02, 1.08) momentum = "上升" elif stock_hash < 40: # 20%股票呈下降趋势 trend_factor = random.uniform(0.92, 0.98) momentum = "下降" else: # 60%股票横盘整理 trend_factor = random.uniform(0.98, 1.02) momentum = "横盘" ma5 = current_price * trend_factor * random.uniform(0.98, 1.02) ma10 = current_price * trend_factor * random.uniform(0.96, 1.04) ma20 = current_price * trend_factor * random.uniform(0.94, 1.06) ma60 = current_price * trend_factor * random.uniform(0.90, 1.10) # RSI (相对强弱指标) - 基于股票特征分布 if stock_hash < 15: # 15%超卖 rsi = random.uniform(20, 35) rsi_status = "超卖" elif stock_hash < 30: # 15%偏弱 rsi = random.uniform(35, 45) rsi_status = "偏弱" elif stock_hash < 70: # 40%中性 rsi = random.uniform(45, 55) rsi_status = "中性" elif stock_hash < 85: # 15%偏强 rsi = random.uniform(55, 65) rsi_status = "偏强" else: # 15%超买 rsi = random.uniform(65, 80) rsi_status = "超买" # 成交量比率 (基于股票活跃度) if ticker.startswith('688') or ticker.startswith('300'): # 成长股活跃 volume_ratio = random.uniform(1.2, 2.5) else: # 主板相对稳定 volume_ratio = random.uniform(0.6, 1.8) # MACD (基于趋势) if momentum == "上升": macd = random.uniform(0.1, 0.5) signal = random.uniform(0, 0.3) elif momentum == "下降": macd = random.uniform(-0.5, -0.1) signal = random.uniform(-0.3, 0) else: # 横盘 macd = random.uniform(-0.2, 0.2) signal = random.uniform(-0.15, 0.15) print(f"🎭 {ticker} 智能模拟数据 (价格:¥{current_price:.2f}, 趋势:{momentum}, RSI:{rsi_status})") # 重置随机种子 random.seed() return { 'current_price': current_price, 'ma5': ma5, 'ma10': ma10, 'ma20': ma20, 'ma60': ma60, 'rsi': rsi, 'macd': macd, 'signal': signal, 'volume_ratio': volume_ratio, 'data_source': 'mock', 'momentum': momentum, 'rsi_status': rsi_status } def _generate_smart_mock_fundamental_data(self, ticker): """生成智能模拟基本面数据""" import hashlib # 使用股票代码作为种子,确保一致性但股票间有差异 seed_value = int(hashlib.md5(ticker.encode()).hexdigest()[:8], 16) random.seed(seed_value) # 检查是否是ETF etf_prefixes = ['510', '511', '512', '513', '515', '516', '517', '518', '159', '161', '163', '165'] is_etf = any(ticker.startswith(prefix) for prefix in etf_prefixes) if is_etf: # ETF基金的特殊处理 # ETF的"基本面"实际上是其跟踪指数或行业的基本面 pe_ratio = random.uniform(12, 25) # ETF跟踪指数的平均PE pb_ratio = random.uniform(1.2, 2.5) # ETF跟踪指数的平均PB roe = random.uniform(8, 15) # ETF持仓股票的平均ROE revenue_growth = random.uniform(5, 20) # ETF跟踪行业的增长率 profit_growth = revenue_growth * random.uniform(0.8, 1.2) debt_ratio = random.uniform(30, 50) # ETF持仓股票的平均负债率 current_ratio = random.uniform(1.5, 2.5) gross_margin = random.uniform(20, 40) # 重置随机种子 random.seed() return { 'pe_ratio': round(pe_ratio, 2), 'pb_ratio': round(pb_ratio, 2), 'roe': round(roe, 2), 'revenue_growth': round(revenue_growth, 2), 'profit_growth': round(profit_growth, 2), 'debt_ratio': round(debt_ratio, 2), 'current_ratio': round(current_ratio, 2), 'gross_margin': round(gross_margin, 2), 'industry': 'ETF基金', 'data_source': 'mock_etf' } # 普通股票的处理逻辑 # 获取股票基本信息 stock_info = self.stock_info.get(ticker, {}) industry = stock_info.get('industry', '未知行业') # 根据行业设置基本参数 industry_factors = { '银行': {'pe_base': 6, 'pe_range': 8, 'roe_base': 8, 'roe_range': 12, 'growth_base': 5, 'growth_range': 15}, '证券': {'pe_base': 15, 'pe_range': 25, 'roe_base': 6, 'roe_range': 15, 'growth_base': -5, 'growth_range': 40}, '白酒': {'pe_base': 25, 'pe_range': 35, 'roe_base': 15, 'roe_range': 25, 'growth_base': 10, 'growth_range': 20}, '医药制造': {'pe_base': 20, 'pe_range': 40, 'roe_base': 8, 'roe_range': 18, 'growth_base': 5, 'growth_range': 25}, '半导体': {'pe_base': 30, 'pe_range': 60, 'roe_base': 5, 'roe_range': 20, 'growth_base': 0, 'growth_range': 50}, '房地产': {'pe_base': 8, 'pe_range': 15, 'roe_base': 8, 'roe_range': 15, 'growth_base': -10, 'growth_range': 15}, '新能源': {'pe_base': 25, 'pe_range': 50, 'roe_base': 5, 'roe_range': 18, 'growth_base': 10, 'growth_range': 40} } # 默认行业参数 default_factors = {'pe_base': 15, 'pe_range': 25, 'roe_base': 8, 'roe_range': 15, 'growth_base': 0, 'growth_range': 25} factors = industry_factors.get(industry, default_factors) # 生成PE比率 pe_ratio = factors['pe_base'] + random.uniform(0, factors['pe_range']) # 生成PB比率 (通常与PE相关) pb_base = pe_ratio * 0.3 pb_ratio = max(0.5, pb_base + random.uniform(-0.5, 1.0)) # 生成ROE (%) roe = factors['roe_base'] + random.uniform(0, factors['roe_range']) # 生成营收增长率 (%) revenue_growth = factors['growth_base'] + random.uniform(0, factors['growth_range']) # 生成利润增长率 (通常与营收增长相关) profit_growth = revenue_growth * random.uniform(0.8, 1.5) + random.uniform(-10, 10) # 生成其他指标 debt_ratio = random.uniform(20, 70) # 负债率 (%) current_ratio = random.uniform(1.0, 3.0) # 流动比率 gross_margin = random.uniform(15, 50) # 毛利率 (%) # 重置随机种子 random.seed() return { 'pe_ratio': round(pe_ratio, 2), 'pb_ratio': round(pb_ratio, 2), 'roe': round(roe, 2), 'revenue_growth': round(revenue_growth, 2), 'profit_growth': round(profit_growth, 2), 'debt_ratio': round(debt_ratio, 2), 'current_ratio': round(current_ratio, 2), 'gross_margin': round(gross_margin, 2), 'industry': industry, 'data_source': 'mock' } def get_real_financial_data(self, ticker): """获取真实的财务数据""" try: if AKSHARE_AVAILABLE: import akshare as ak try: # 获取股票基本信息 stock_info = ak.stock_individual_info_em(symbol=ticker) if stock_info is not None and not stock_info.empty: # 解析财务指标 pe_ratio = None pb_ratio = None roe = None for _, row in stock_info.iterrows(): item = row['item'] value = str(row['value']).replace(',', '').replace('%', '') try: if 'PE' in item or '市盈率' in item: pe_ratio = float(value) if value != '-' and value != '--' else None elif 'PB' in item or '市净率' in item: pb_ratio = float(value) if value != '-' and value != '--' else None elif 'ROE' in item or '净资产收益率' in item: roe = float(value) if value != '-' and value != '--' else None except (ValueError, TypeError): continue # 设置合理的默认值和范围限制 pe_ratio = pe_ratio if pe_ratio and 0 < pe_ratio < 200 else 20 pb_ratio = pb_ratio if pb_ratio and 0 < pb_ratio < 50 else 2.0 roe = roe if roe and -50 < roe < 100 else 10 return { 'pe_ratio': pe_ratio, 'pb_ratio': pb_ratio, 'roe': roe } except Exception as e: print(f"⚠️ 获取{ticker}财务数据失败: {e}") except Exception as e: print(f"⚠️ akshare财务数据获取失败: {e}") # 如果获取失败,返回合理的默认值 return { 'pe_ratio': 20, # 合理的默认PE 'pb_ratio': 2.0, # 合理的默认PB 'roe': 10 # 合理的默认ROE } def generate_investment_advice(self, ticker): """生成短期和长期投资建议""" stock_info = self.get_stock_info_generic(ticker) # 获取真实技术指标数据 technical_data = self.get_real_technical_indicators(ticker) current_price = technical_data.get('current_price', stock_info.get('price', 0)) ma5 = technical_data.get('ma5', current_price) ma10 = technical_data.get('ma10', current_price) ma20 = technical_data.get('ma20', current_price) ma60 = technical_data.get('ma60', current_price) rsi = technical_data.get('rsi', 50) macd = technical_data.get('macd', 0) signal = technical_data.get('signal', 0) volume_ratio = technical_data.get('volume_ratio', 1.0) # 获取真实财务数据 financial_data = self.get_real_financial_data(ticker) pe_ratio = financial_data.get('pe_ratio', 20) pb_ratio = financial_data.get('pb_ratio', 2.0) roe = financial_data.get('roe', 10) # 短期投资建议 (1-7天) short_term_advice = self.get_short_term_advice(rsi, macd, signal, volume_ratio, ma5, ma10, current_price) # 长期投资建议 (7-90天) long_term_advice = self.get_long_term_advice(pe_ratio, pb_ratio, roe, ma20, ma60, current_price, stock_info) return short_term_advice, long_term_advice def get_short_term_advice(self, rsi, macd, signal, volume_ratio, ma5, ma10, current_price): """生成短期投资建议 (1-7天)""" # 计算信号强度 (范围更宽,确保不同结果) signal_strength = 0 factors = [] # RSI分析 (更精细的区间划分) if rsi < 25: signal_strength += 3 factors.append(f"RSI({rsi:.1f})严重超卖,强烈反弹信号") elif rsi < 35: signal_strength += 2 factors.append(f"RSI({rsi:.1f})超卖,反弹概率高") elif rsi < 45: signal_strength += 1 factors.append(f"RSI({rsi:.1f})偏弱,有企稳迹象") elif rsi <= 55: signal_strength += 0 factors.append(f"RSI({rsi:.1f})中性区间") elif rsi < 65: signal_strength -= 1 factors.append(f"RSI({rsi:.1f})偏强,注意高位风险") elif rsi < 75: signal_strength -= 2 factors.append(f"RSI({rsi:.1f})超买,回调压力大") else: signal_strength -= 3 factors.append(f"RSI({rsi:.1f})严重超买,高风险区域") # MACD分析 (更详细的判断) macd_diff = macd - signal if macd > 0 and macd_diff > 0.1: signal_strength += 2 factors.append("MACD金叉且强势向上") elif macd > 0 and macd_diff > 0: signal_strength += 1 factors.append("MACD位于零轴上方") elif macd < 0 and macd_diff < -0.1: signal_strength -= 2 factors.append("MACD死叉且弱势向下") elif macd < 0 and macd_diff < 0: signal_strength -= 1 factors.append("MACD位于零轴下方") # 均线分析 (更详细的位置关系) ma_distance_5 = (current_price - ma5) / ma5 * 100 ma_distance_10 = (current_price - ma10) / ma10 * 100 if ma_distance_5 > 3 and ma_distance_10 > 3: signal_strength += 2 factors.append("价格大幅站上短期均线") elif ma_distance_5 > 0 and ma_distance_10 > 0: signal_strength += 1 factors.append("价格稳站短期均线") elif ma_distance_5 < -3 and ma_distance_10 < -3: signal_strength -= 2 factors.append("价格大幅跌破短期均线") elif ma_distance_5 < 0 and ma_distance_10 < 0: signal_strength -= 1 factors.append("价格跌破短期均线") # 成交量分析 (更精细分级) if volume_ratio > 2.0: signal_strength += 2 factors.append(f"成交量大幅放大({volume_ratio:.1f}倍),资金高度活跃") elif volume_ratio > 1.5: signal_strength += 1 factors.append(f"成交量放大({volume_ratio:.1f}倍),资金活跃") elif volume_ratio > 1.2: signal_strength += 0 factors.append(f"成交量正常({volume_ratio:.1f}倍)") elif volume_ratio < 0.6: signal_strength -= 2 factors.append(f"成交量严重萎缩({volume_ratio:.1f}倍),观望情绪浓厚") elif volume_ratio < 0.8: signal_strength -= 1 factors.append(f"成交量萎缩({volume_ratio:.1f}倍),缺乏资金关注") # 生成建议 (扩大信号强度范围,确保差异化) if signal_strength >= 4: recommendation = '强烈买入' confidence = min(90, 70 + signal_strength * 3) entry_strategy = '重仓配置,分3批建仓' exit_strategy = '短线获利5-8%止盈' risk_level = '中高' target_return = '5-12%' elif signal_strength >= 2: recommendation = '积极买入' confidence = min(85, 60 + signal_strength * 5) entry_strategy = '分批建仓,首批30%仓位' exit_strategy = '短线获利3-5%止盈' risk_level = '中等' target_return = '3-8%' elif signal_strength >= 1: recommendation = '谨慎买入' confidence = min(75, 50 + signal_strength * 8) entry_strategy = '轻仓试探,20%仓位' exit_strategy = '获利2-3%止盈' risk_level = '中等' target_return = '2-5%' elif signal_strength >= -1: recommendation = '观望' confidence = 50 entry_strategy = '等待更明确信号' exit_strategy = '不建议操作' risk_level = '低' target_return = '0%' elif signal_strength >= -2: recommendation = '谨慎减仓' confidence = min(75, 60 + abs(signal_strength) * 5) entry_strategy = '不建议新增' exit_strategy = '逢高减仓30%' risk_level = '中高' target_return = '-1-2%' elif signal_strength >= -4: recommendation = '减仓' confidence = min(80, 65 + abs(signal_strength) * 3) entry_strategy = '严禁买入' exit_strategy = '逢高减仓50%' risk_level = '高' target_return = '-3-0%' else: recommendation = '清仓' confidence = min(90, 75 + abs(signal_strength) * 2) entry_strategy = '严禁买入' exit_strategy = '尽快清仓' risk_level = '很高' target_return = '-8-0%' return { 'period': '短期 (1-7天)', 'recommendation': recommendation, 'confidence': confidence, 'signal_strength': signal_strength, # 添加信号强度用于调试 'key_factors': factors, 'entry_strategy': entry_strategy, 'exit_strategy': exit_strategy, 'risk_level': risk_level, 'target_return': target_return } def get_long_term_advice(self, pe_ratio, pb_ratio, roe, ma20, ma60, current_price, stock_info): """生成长期投资建议 (7-90天)""" # 计算长期投资价值 (扩大评分范围) value_score = 0 factors = [] # 估值分析 (更精细的PE分级) if pe_ratio < 10: value_score += 3 factors.append(f"PE({pe_ratio:.1f})严重低估,价值洼地") elif pe_ratio < 15: value_score += 2 factors.append(f"PE({pe_ratio:.1f})估值偏低,安全边际高") elif pe_ratio <= 20: value_score += 1 factors.append(f"PE({pe_ratio:.1f})估值合理") elif pe_ratio <= 30: value_score -= 1 factors.append(f"PE({pe_ratio:.1f})估值偏高") elif pe_ratio <= 50: value_score -= 2 factors.append(f"PE({pe_ratio:.1f})估值较高,泡沫风险") else: value_score -= 3 factors.append(f"PE({pe_ratio:.1f})严重高估,泡沫风险极大") # PB估值分析 if pb_ratio < 1.0: value_score += 2 factors.append(f"PB({pb_ratio:.1f})破净,投资价值突出") elif pb_ratio < 1.5: value_score += 1 factors.append(f"PB({pb_ratio:.1f})估值较低") elif pb_ratio <= 2.5: value_score += 0 factors.append(f"PB({pb_ratio:.1f})估值正常") elif pb_ratio <= 4: value_score -= 1 factors.append(f"PB({pb_ratio:.1f})估值偏高") else: value_score -= 2 factors.append(f"PB({pb_ratio:.1f})估值严重偏高") # 盈利能力分析 if roe > 20: value_score += 3 factors.append(f"ROE({roe:.1f}%)卓越,盈利能力强劲") elif roe > 15: value_score += 2 factors.append(f"ROE({roe:.1f}%)优秀,盈利能力强") elif roe > 10: value_score += 1 factors.append(f"ROE({roe:.1f}%)良好") elif roe > 5: value_score -= 1 factors.append(f"ROE({roe:.1f}%)一般,盈利能力待改善") else: value_score -= 2 factors.append(f"ROE({roe:.1f}%)较差,盈利能力弱") # 趋势分析 (更详细的趋势判断) ma60_trend = (current_price - ma60) / ma60 * 100 ma20_trend = (ma20 - ma60) / ma60 * 100 if ma60_trend > 10 and ma20_trend > 5: value_score += 2 factors.append("长期强势上升趋势") elif ma60_trend > 0 and ma20_trend > 0: value_score += 1 factors.append("长期趋势向上") elif ma60_trend < -10 and ma20_trend < -5: value_score -= 2 factors.append("长期弱势下降趋势") elif ma60_trend < 0 and ma20_trend < 0: value_score -= 1 factors.append("长期趋势向下") # 行业前景分析 (更详细的行业分类) industry = stock_info.get('industry', '') concept = stock_info.get('concept', '') # 热门行业加分 if any(keyword in industry for keyword in ['半导体', '芯片', '新能源', '锂电']): value_score += 2 factors.append(f"{industry}行业高景气度") elif any(keyword in industry for keyword in ['医药', '生物', '消费', '白酒']): value_score += 1 factors.append(f"{industry}行业长期成长") elif any(keyword in industry for keyword in ['银行', '保险', '地产']): value_score += 0 factors.append(f"{industry}行业稳定经营") elif any(keyword in industry for keyword in ['钢铁', '煤炭', '有色']): value_score -= 1 factors.append(f"{industry}行业周期性强") # 概念题材加分 hot_concepts = ['人工智能', '新能源车', '光伏', '储能', '数字经济'] if any(concept_key in concept for concept_key in hot_concepts): value_score += 1 factors.append("热门概念题材") # 生成建议 (扩大评分范围) if value_score >= 6: recommendation = '核心重仓' confidence = min(95, 80 + value_score * 2) entry_strategy = '核心配置,目标仓位80%+' exit_strategy = '长期持有,目标收益50%+' risk_level = '低' target_return = '30-60%' elif value_score >= 4: recommendation = '重点配置' confidence = min(90, 70 + value_score * 3) entry_strategy = '分批建仓,目标仓位60-80%' exit_strategy = '长期持有,目标收益20-30%' risk_level = '中低' target_return = '15-35%' elif value_score >= 2: recommendation = '适度配置' confidence = min(80, 60 + value_score * 4) entry_strategy = '适度建仓,目标仓位30-50%' exit_strategy = '中期持有,目标收益10-20%' risk_level = '中等' target_return = '8-25%' elif value_score >= 0: recommendation = '观察配置' confidence = 55 entry_strategy = '轻仓配置,目标仓位10-20%' exit_strategy = '短期持有,目标收益5-10%' risk_level = '中等' target_return = '3-12%' elif value_score >= -2: recommendation = '谨慎观望' confidence = min(75, 50 + abs(value_score) * 5) entry_strategy = '不建议配置' exit_strategy = '适时减仓' risk_level = '中高' target_return = '0-5%' elif value_score >= -4: recommendation = '规避风险' confidence = min(85, 65 + abs(value_score) * 3) entry_strategy = '严禁买入' exit_strategy = '逐步清仓' risk_level = '高' target_return = '-5-0%' else: recommendation = '强烈回避' confidence = min(95, 80 + abs(value_score) * 2) entry_strategy = '严禁买入' exit_strategy = '立即清仓' risk_level = '很高' target_return = '-15-0%' return { 'period': '长期 (7-90天)', 'recommendation': recommendation, 'confidence': confidence, 'value_score': value_score, # 添加价值评分用于调试 'key_factors': factors, 'entry_strategy': entry_strategy, 'exit_strategy': exit_strategy, 'risk_level': risk_level, 'target_return': target_return } def format_investment_advice(self, short_term_advice, long_term_advice, ticker): """格式化投资建议显示""" import time stock_info = self.get_stock_info_generic(ticker) # 计算综合推荐指数 comprehensive_index = self.calculate_recommendation_index(ticker) # 处理价格显示 price = stock_info.get('price') if price is not None: price_display = f"当前价格: ¥{price:.2f}" if stock_info.get('price_status') == '实时': price_display += " (实时数据)" else: price_display = "当前价格: 网络获取失败,无法显示实时价格" recommendation = """ ========================================================= 时间周期投资建议报告 ========================================================= 股票信息 --------------------------------------------------------- 股票代码: {} 股票名称: {} 所属行业: {} 投资概念: {} {} {} ========================================================= 短期投资建议 (1-7天) ========================================================= 投资建议: {} 置信度: {}% 风险等级: {} 预期收益: {} 关键因素分析: {} 操作策略: • 入场策略: {} • 退出策略: {} • 仓位管理: 建议短线投资仓位不超过总资金的20% • 止损设置: 建议设置3-5%的止损位 ========================================================= 长期投资建议 (7-90天) ========================================================= 投资建议: {} 置信度: {}% 风险等级: {} 预期收益: {} 关键因素分析: {} 投资策略: • 建仓策略: {} • 持有策略: {} • 仓位管理: 根据风险承受能力合理配置 • 调仓频率: 建议每月评估一次,根据基本面变化调整 ========================================================= 风险提示 ========================================================= • 市场有风险,投资需谨慎 • 以上建议仅供参考,不构成投资承诺 • 请根据自身风险承受能力合理投资 • 建议分散投资,不要把所有资金投入单一股票 • 保持理性,避免情绪化交易 ========================================================= 投资要点 ========================================================= 短期操作要点: • 关注技术面信号,把握短线交易机会 • 严格执行止盈止损策略 • 控制单次交易仓位,避免重仓 • 适当关注市场情绪和资金流向 长期投资要点: • 重点关注公司基本面和行业前景 • 选择优质企业进行长期配置 • 保持足够的投资耐心 • 定期评估投资标的,适时调整组合 建议操作周期: • 短期建议: 适合有经验的短线交易者 • 长期建议: 适合价值投资和成长投资者 • 组合投资: 建议短期和长期策略相结合 最后更新时间: {} """.format( ticker, stock_info.get('name', '未知'), stock_info.get('industry', '未知'), stock_info.get('concept', '未知'), price_display, comprehensive_index, short_term_advice['recommendation'], short_term_advice['confidence'], short_term_advice['risk_level'], short_term_advice['target_return'], '\n'.join(['• ' + factor for factor in short_term_advice['key_factors']]), short_term_advice['entry_strategy'], short_term_advice['exit_strategy'], long_term_advice['recommendation'], long_term_advice['confidence'], long_term_advice['risk_level'], long_term_advice['target_return'], '\n'.join(['• ' + factor for factor in long_term_advice['key_factors']]), long_term_advice['entry_strategy'], long_term_advice['exit_strategy'], time.strftime("%Y-%m-%d %H:%M:%S") ) return recommendation def calculate_technical_index(self, rsi, macd, signal, volume_ratio, ma5, ma10, ma20, ma60, current_price): """计算技术面推荐指数""" score = 50 # 基础分数 # RSI评分 if 30 <= rsi <= 70: score += 15 # 正常区域加分 elif rsi < 30: score += 10 # 超卖有反弹机会 else: # rsi > 70 score -= 10 # 超买有风险 # MACD评分 if macd > signal: score += 15 # 金叉看涨 else: score -= 10 # 死叉看跌 # 成交量评分 if 1.2 <= volume_ratio <= 2.0: score += 10 # 适度放量 elif volume_ratio > 2.0: score += 5 # 过度放量,谨慎 else: score -= 5 # 缩量观望 # 均线评分 ma_score = 0 if current_price > ma5: ma_score += 5 if current_price > ma10: ma_score += 5 if current_price > ma20: ma_score += 5 if current_price > ma60: ma_score += 5 score += ma_score # 均线排列评分 if ma5 > ma10 > ma20 > ma60: score += 15 # 完美多头排列 elif ma5 > ma10 > ma20: score += 10 # 短期多头 elif ma5 < ma10 < ma20 < ma60: score -= 15 # 空头排列 # 限制在0-100之间 score = min(100, max(0, score)) return self.format_technical_index(score) def format_technical_index(self, score): """格式化技术面推荐指数""" if score >= 80: rating = "技术面强势" signal = "买入信号" elif score >= 65: rating = "技术面偏强" signal = "可考虑买入" elif score >= 50: rating = "技术面中性" signal = "持有观望" elif score >= 35: rating = "技术面偏弱" signal = "谨慎操作" else: rating = "技术面疲弱" signal = "回避风险" # 生成进度条 bar_length = 25 filled_length = int(score / 100 * bar_length) bar = "█" * filled_length + "░" * (bar_length - filled_length) return """ 技术面指数: {:.1f}/100 [{}] {} 操作信号: {} """.format(score, bar, rating, signal) def calculate_fundamental_index(self, pe_ratio, pb_ratio, roe, revenue_growth, profit_growth, ticker): """计算基本面推荐指数""" score = 50 # 基础分数 # PE估值评分 if pe_ratio < 20: score += 20 # 估值合理 elif pe_ratio < 35: score += 10 # 估值偏高但可接受 else: score -= 15 # 估值过高 # ROE评分 if roe > 15: score += 20 # 优秀盈利能力 elif roe > 10: score += 10 # 一般盈利能力 else: score -= 10 # 盈利能力弱 # 营收增长评分 if revenue_growth > 15: score += 15 # 高成长 elif revenue_growth > 5: score += 8 # 稳健成长 elif revenue_growth > 0: score += 3 # 正增长 else: score -= 15 # 负增长 # 净利润增长评分 if profit_growth > 20: score += 15 # 利润高增长 elif profit_growth > 10: score += 8 # 利润稳定增长 elif profit_growth > 0: score += 3 # 利润正增长 else: score -= 15 # 利润下滑 # 行业特殊加成 stock_info = self.get_stock_info_generic(ticker) industry = stock_info.get("industry", "") if "半导体" in industry or "新能源" in industry: score += 5 # 成长行业加成 elif "银行" in industry or "白酒" in industry: score += 3 # 稳定行业加成 # 限制在0-100之间 score = min(100, max(0, score)) return self.format_fundamental_index(score) def format_fundamental_index(self, score): """格式化基本面推荐指数""" if score >= 80: rating = "基本面优秀" quality = "高质量公司" elif score >= 65: rating = "基本面良好" quality = "质地较好" elif score >= 50: rating = "基本面一般" quality = "中等质地" elif score >= 35: rating = "基本面偏弱" quality = "质地偏弱" else: rating = "基本面较差" quality = "需谨慎" # 生成进度条 bar_length = 25 filled_length = int(score / 100 * bar_length) bar = "█" * filled_length + "░" * (bar_length - filled_length) return """ 基本面指数: {:.1f}/100 [{}] {} 公司质地: {} """.format(score, bar, rating, quality) def calculate_comprehensive_index(self, technical_score, fundamental_score, ticker): """计算综合投资推荐指数(10分制)""" # 基础综合评分 (技术面40% + 基本面60%) base_score = technical_score * 0.4 + fundamental_score * 0.6 # 获取股票信息用于行业分析 stock_info = self.get_stock_info_generic(ticker) industry = stock_info.get("industry", "") # 行业景气度调整(控制在±1分内) industry_adjustment = 0 if "半导体" in industry or "芯片" in industry: industry_adjustment = 0.8 # 政策支持行业 elif "新能源" in industry or "锂电" in industry or "光伏" in industry: industry_adjustment = 0.6 # 长期趋势向好 elif "白酒" in industry or "消费" in industry: industry_adjustment = 0.4 # 消费复苏 elif "银行" in industry or "保险" in industry: industry_adjustment = 0.2 # 稳定行业 elif "房地产" in industry or "建筑" in industry: industry_adjustment = -0.3 # 政策敏感 elif "医药" in industry or "生物" in industry: industry_adjustment = 0.5 # 长期成长 else: industry_adjustment = 0.1 # 其他行业基础加分 # 板块流动性调整(控制在±0.5分内) board_adjustment = 0 if ticker.startswith('688'): board_adjustment = 0.3 # 科创板活跃度高,创新溢价 elif ticker.startswith('300'): board_adjustment = 0.2 # 创业板相对活跃 elif ticker.startswith('60'): board_adjustment = 0.1 # 沪市主板稳定 else: board_adjustment = 0.1 # 深市主板 # 市场环境调整(控制在±0.5分内) market_adjustment = 0.3 # 当前市场环境偏好,可根据实际情况调整 # 计算最终得分(严格10分制) final_score = base_score + industry_adjustment + board_adjustment + market_adjustment final_score = min(10.0, max(1.0, final_score)) return self.format_comprehensive_index(final_score, technical_score, fundamental_score) def format_comprehensive_index(self, score, tech_score, fund_score): """格式化综合推荐指数(10分制)""" if score >= 8.5: rating = "强烈推荐" stars = "★★★★★" investment_advice = "优质投资标的" elif score >= 7.5: rating = "推荐" stars = "★★★★☆" investment_advice = "值得关注" elif score >= 6.5: rating = "中性" stars = "★★★☆☆" investment_advice = "可适度配置" elif score >= 5.0: rating = "谨慎" stars = "★★☆☆☆" investment_advice = "谨慎操作" else: rating = "不推荐" stars = "★☆☆☆☆" investment_advice = "建议回避" # 生成进度条(10分制) bar_length = 30 filled_length = int(score / 10 * bar_length) bar = "█" * filled_length + "░" * (bar_length - filled_length) # 技术面和基本面的权重说明 tech_weight = tech_score * 4 / 10 # 40%权重 fund_weight = fund_score * 6 / 10 # 60%权重 return """ 综合推荐指数: {:.1f}/10 {} {} [{}] {} 指数构成: • 技术面(40%): {:.1f}分 → {:.1f}分 • 基本面(60%): {:.1f}分 → {:.1f}分 • 市场环境: 已纳入考量 • 行业景气: 已纳入考量 投资建议: {} """.format( score, stars, bar, bar, rating, tech_score, tech_weight, fund_score, fund_weight, investment_advice ) def show_examples(self): """显示示例股票代码""" examples = ["688981", "600036", "000002", "300750", "600519", "000858", "002415", "300059"] example = random.choice(examples) self.ticker_var.set(example) messagebox.showinfo("示例代码", "已填入示例股票代码: {}\n点击'开始分析'按钮进行分析".format(example)) def show_welcome_message(self): """显示欢迎信息""" welcome_msg = """ ============================== 欢迎使用A股智能分析系统! ============================== 使用说明: 1. 在上方输入框输入6位股票代码(如:688981) 2. 点击"开始分析"按钮或按回车键 3. 等待分析完成,查看各个页面的分析结果 支持的股票格式: • 上海主板: 60XXXX (如:600036-招商银行) • 科创板: 688XXX (如:688981-中芯国际) • 深圳主板: 000XXX (如:000002-万科A) • 深圳中小板: 002XXX (如:002415-海康威视) • 创业板: 300XXX (如:300750-宁德时代) 现在支持所有A股代码!您可以输入任意有效的A股代码进行分析。 分析内容包括: • 股票概览 - 基本信息和市场环境 • 技术分析 - 技术指标和趋势判断 • 基本面分析 - 财务数据和估值分析 • 投资建议 - 综合评级和操作策略 风险提示: 股市有风险,投资需谨慎! 本系统仅供参考,不构成投资建议。 现在就开始您的A股投资分析之旅吧! 特色功能: • 支持A股特色板块分析 • 智能投资策略建议 • 风险评估和仓位建议 • 实时市场环境分析 版本更新 (v2.0): • 全新图形界面设计 • 多页面分类展示分析结果 • 智能股票代码识别 • 增强的A股市场特色分析 点击"示例"按钮可以快速填入示例股票代码! """ self.overview_text.delete('1.0', tk.END) self.overview_text.insert('1.0', welcome_msg) def start_analysis(self): """开始分析""" ticker = self.ticker_var.get().strip() if not ticker: messagebox.showwarning("警告", "请输入股票代码!") return if not self.is_valid_a_share_code(ticker): messagebox.showwarning("警告", "请输入正确的6位A股代码!\n\n支持的格式:\n• 沪市主板:60XXXX\n• 科创板:688XXX\n• 深市主板:000XXX\n• 深市中小板:002XXX\n• 创业板:300XXX") return # 禁用分析按钮 self.analyze_btn.config(state="disabled") # 显示进度条 self.show_progress(f"正在分析 {ticker},请稍候...") # 更新排行榜 self.update_ranking_display() # 在后台线程中执行分析 analysis_thread = threading.Thread(target=self.perform_analysis, args=(ticker,)) analysis_thread.daemon = True analysis_thread.start() def perform_analysis(self, ticker): """执行分析(在后台线程中)- 使用智能模拟数据""" try: import time import threading print(f"🔍 开始分析股票: {ticker}") # 设置总体超时时间(15秒) def timeout_handler(): print("⏰ 分析超时,强制终止") self.root.after(0, self.show_error, "分析超时,请重试") timeout_timer = threading.Timer(15.0, timeout_handler) timeout_timer.start() # 步骤1: 获取基本信息 self.update_progress(f"步骤1/6: 获取 {ticker} 基本信息...") time.sleep(0.1) try: stock_info = self.stock_info.get(ticker, { "name": f"股票{ticker}", "industry": "未知行业", "concept": "A股", "price": 0 }) print(f"✅ 步骤1完成: 基本信息获取成功 - {stock_info['name']}") except Exception as e: print(f"⚠️ 步骤1出错: {e}") stock_info = {"name": f"股票{ticker}", "industry": "未知行业", "concept": "A股", "price": 0} # 步骤2: 生成智能模拟技术数据 self.update_progress(f"步骤2/6: 生成 {ticker} 技术分析数据...") time.sleep(0.1) try: tech_data = self._generate_smart_mock_technical_data(ticker) print(f"✅ 步骤2完成: 技术数据生成成功 - 价格¥{tech_data['current_price']:.2f}") except Exception as e: print(f"❌ 步骤2出错: {e}") error_msg = f"❌ 技术数据生成失败\n\n{str(e)}\n请稍后重试" timeout_timer.cancel() self.root.after(0, self.show_error, error_msg) return # 步骤3: 生成智能模拟基本面数据 self.update_progress(f"步骤3/6: 生成 {ticker} 基本面数据...") time.sleep(0.1) try: fund_data = self._generate_smart_mock_fundamental_data(ticker) print(f"✅ 步骤3完成: 基本面数据生成成功 - PE{fund_data['pe_ratio']:.1f}") except Exception as e: print(f"❌ 步骤3出错: {e}") error_msg = f"❌ 基本面数据生成失败\n\n{str(e)}\n请稍后重试" timeout_timer.cancel() self.root.after(0, self.show_error, error_msg) return # 步骤4: 技术分析 self.update_progress(f"步骤4/6: 进行技术分析...") time.sleep(0.1) try: print("开始技术分析...") technical_analysis = self.format_technical_analysis_from_data(ticker, tech_data) print(f"✅ 步骤4完成: 技术分析生成 ({len(technical_analysis)}字符)") except Exception as e: print(f"❌ 步骤4出错: {e}") error_msg = f"❌ 技术分析失败\n\n{str(e)[:100]}\n请稍后重试" timeout_timer.cancel() self.root.after(0, self.show_error, error_msg) return # 步骤5: 基本面分析 self.update_progress(f"步骤5/6: 进行基本面分析...") time.sleep(0.1) try: print("开始基本面分析...") fundamental_analysis = self.format_fundamental_analysis_from_data(ticker, fund_data) print(f"✅ 步骤5完成: 基本面分析生成 ({len(fundamental_analysis)}字符)") except Exception as e: print(f"❌ 步骤5出错: {e}") error_msg = f"❌ 基本面分析失败\n\n{str(e)[:100]}\n请稍后重试" timeout_timer.cancel() self.root.after(0, self.show_error, error_msg) return # 步骤6: 生成投资建议 self.update_progress(f"步骤6/6: 生成投资建议...") time.sleep(0.1) try: print("开始生成投资建议...") # 获取短期和长期建议 short_advice = self.get_short_term_advice( tech_data['rsi'], tech_data['macd'], tech_data['signal'], tech_data['volume_ratio'], tech_data['ma5'], tech_data['ma10'], tech_data['current_price'] ) long_advice = self.get_long_term_advice( fund_data['pe_ratio'], fund_data['pb_ratio'], fund_data['roe'], tech_data['ma20'], tech_data['ma60'], tech_data['current_price'], stock_info ) # 使用与批量评分相同的方法计算评分 short_score = self._extract_score_from_advice(short_advice, 'short_term') long_score = self._extract_score_from_advice(long_advice, 'long_term') final_score = (short_score + long_score) / 2 print(f"✅ 步骤6完成: 投资建议生成 - 综合评分{final_score:.1f}/10") except Exception as e: print(f"❌ 步骤6出错: {e}") short_advice = {"advice": f"短期建议暂时不可用: {str(e)[:100]}"} long_advice = {"advice": f"长期建议暂时不可用: {str(e)[:100]}"} final_score = 5.0 # 生成最终报告 try: print("生成最终报告...") # 更新股票信息包含模拟价格 stock_info['price'] = tech_data['current_price'] overview = self.generate_overview_from_data(ticker, stock_info, tech_data, fund_data, final_score) recommendation = self.format_investment_advice_from_data(short_advice, long_advice, ticker, final_score) print(f"✅ 报告生成完成") # 保存到缓存 analysis_data = { 'ticker': ticker, 'name': stock_info['name'], 'price': tech_data['current_price'], 'technical_score': short_score, 'fundamental_score': long_score, 'final_score': final_score, 'overview': overview, 'technical': technical_analysis, 'fundamental': fundamental_analysis, 'recommendation': recommendation } self.save_stock_to_cache(ticker, analysis_data) except Exception as e: print(f"❌ 报告生成出错: {e}") overview = f"概览生成失败: {str(e)}" recommendation = f"建议生成失败: {str(e)}" # 取消超时计时器 timeout_timer.cancel() # 更新界面显示 self.root.after(0, self.update_results, overview, technical_analysis, fundamental_analysis, recommendation, ticker) print(f"🎉 {ticker} 分析完成!") except Exception as e: print(f"❌ 分析过程出现异常: {e}") import traceback traceback.print_exc() if 'timeout_timer' in locals(): timeout_timer.cancel() error_msg = f"❌ 分析失败\n\n{str(e)[:200]}\n请稍后重试" self.root.after(0, self.show_error, error_msg) print(f"❌ 总体分析过程出错: {e}") import traceback traceback.print_exc() self.root.after(0, self.show_error, str(e)) # 步骤4: 基本面分析 self.update_progress(f"步骤4/6: 进行基本面分析...") time.sleep(0.5) try: fundamental_analysis = self.fundamental_analysis(ticker) print(f"✅ 步骤4完成: 基本面分析生成 ({len(fundamental_analysis)}字符)") except Exception as e: print(f"❌ 步骤4出错: {e}") fundamental_analysis = f"基本面分析出错: {e}" # 步骤5: 生成投资建议 self.update_progress(f"步骤5/6: 生成投资建议...") time.sleep(0.5) try: short_term_advice, long_term_advice = self.generate_investment_advice(ticker) print(f"✅ 步骤5完成: 投资建议生成") except Exception as e: print(f"❌ 步骤5出错: {e}") short_term_advice = {"advice": f"短期建议生成出错: {e}"} long_term_advice = {"advice": f"长期建议生成出错: {e}"} # 步骤6: 生成报告 self.update_progress(f"步骤6/6: 生成投资分析报告...") time.sleep(0.3) try: overview = self.generate_overview(ticker) print(f"✅ 步骤6a完成: 概览生成 ({len(overview)}字符)") recommendation = self.format_investment_advice(short_term_advice, long_term_advice, ticker) print(f"✅ 步骤6b完成: 建议格式化 ({len(recommendation)}字符)") except Exception as e: print(f"❌ 步骤6出错: {e}") overview = f"概览生成出错: {e}" recommendation = f"建议格式化出错: {e}" print(f"🎉 分析完成,准备更新UI") # 在主线程中更新UI self.root.after(0, self.update_results, overview, technical_analysis, fundamental_analysis, recommendation, ticker) except Exception as e: print(f"❌ 总体分析过程出错: {e}") import traceback traceback.print_exc() self.root.after(0, self.show_error, str(e)) def show_error(self, error_msg): """显示错误信息""" # 隐藏进度条 self.hide_progress() # 重新启用分析按钮 self.analyze_btn.config(state="normal") # 显示错误 messagebox.showerror("分析错误", f"分析过程中发生错误:\n{error_msg}") # 更新状态 self.status_var.set("分析失败 - 请重试") def update_progress(self, message): """更新进度信息""" self.root.after(0, lambda: self.progress_var.set(message)) def update_results(self, overview, technical, fundamental, recommendation, ticker): """更新分析结果""" # 隐藏进度条 self.hide_progress() # 清空所有文本框 self.overview_text.delete('1.0', tk.END) self.technical_text.delete('1.0', tk.END) self.fundamental_text.delete('1.0', tk.END) self.recommendation_text.delete('1.0', tk.END) # 插入分析结果 self.overview_text.insert('1.0', overview) self.technical_text.insert('1.0', technical) self.fundamental_text.insert('1.0', fundamental) self.recommendation_text.insert('1.0', recommendation) # 重新启用分析按钮 self.analyze_btn.config(state="normal") # 更新状态 self.status_var.set("{} 分析完成".format(ticker)) self.fundamental_text.insert('1.0', fundamental) self.recommendation_text.insert('1.0', recommendation) # 隐藏进度条 self.progress_bar.stop() self.progress_bar.pack_forget() self.progress_var.set("") # 启用分析按钮 self.analyze_btn.config(state="normal") # 更新状态 self.status_var.set("{} 分析完成".format(ticker)) # 切换到概览页面 self.notebook.select(0) def show_error(self, error_msg): """显示错误信息""" self.progress_bar.stop() self.progress_bar.pack_forget() self.progress_var.set("") self.analyze_btn.config(state="normal") self.status_var.set("分析失败") messagebox.showerror("错误", "分析失败:{}".format(error_msg)) def clear_results(self): """清空结果""" self.overview_text.delete('1.0', tk.END) self.technical_text.delete('1.0', tk.END) self.fundamental_text.delete('1.0', tk.END) self.recommendation_text.delete('1.0', tk.END) self.ticker_var.set("") self.status_var.set("就绪 - 请输入股票代码开始分析") # 显示欢迎信息 self.show_welcome_message() def generate_overview(self, ticker): """生成概览信息""" stock_info = self.get_stock_info_generic(ticker) current_price = stock_info.get("price", None) # 如果价格为None,报告网络问题 if current_price is None or current_price <= 0: return { 'error': 'network_failure', 'message': f'❌ 无法获取股票 {ticker} 的实时数据\n🌐 网络连接问题或API服务不可用\n💡 请检查网络连接后重试' } # 生成随机的市场数据用于演示 price_change = random.uniform(-2.5, 2.5) price_change_pct = (price_change / current_price) * 100 # 计算投资推荐指数 recommendation_index = self.calculate_recommendation_index(ticker) overview = """ ========================================================= A股智能分析系统 - 股票概览 ========================================================= 投资推荐指数 --------------------------------------------------------- {} 基本信息 --------------------------------------------------------- 股票代码: {} 公司名称: {} 所属行业: {} 投资概念: {} 当前价格: ¥{:.2f} (实时价格) 价格变动: ¥{:+.2f} ({:+.2f}%) 分析时间: {} 板块特征 --------------------------------------------------------- """.format( recommendation_index, ticker, stock_info.get('name', '未知'), stock_info.get('industry', '未知'), stock_info.get('concept', '未知'), current_price, price_change, price_change_pct, datetime.now().strftime('%Y-%m-%d %H:%M:%S') ) if ticker.startswith('688'): overview += """ 科创板股票特征: • 科技创新企业,成长性较高 • 投资门槛50万,机构投资者较多 • 估值溢价明显,波动性大 • 注册制上市,市场化程度高 • 适合科技投资和成长投资 """ elif ticker.startswith('300'): overview += """ 创业板股票特征: • 中小成长企业为主 • 市场活跃度高,投机性较强 • 注册制改革,优胜劣汰 • 适合风险偏好高的投资者 • 关注业绩增长可持续性 """ elif ticker.startswith('60'): overview += """ 沪市主板特征: • 大型成熟企业为主 • 蓝筹股集中地,分红稳定 • 相对稳定,波动性较小 • 适合稳健型投资者 • 价值投资优选板块 """ elif ticker.startswith('00'): overview += """ 深市主板特征: • 制造业企业较多 • 民营企业占比高 • 经营灵活性强 • 关注行业周期影响 • 成长与价值兼具 """ overview += """ 市场环境分析 (2025年10月) --------------------------------------------------------- A股整体态势: • 政策环境: 稳增长政策持续发力,支持实体经济发展 • 流动性状况: 央行维持稳健货币政策,市场流动性合理充裕 • 估值水平: 整体估值处于历史中位数,结构性机会显著 • 国际资金: 外资对中国资产长期看好,短期保持谨慎观望 政策导向: • 科技创新: 强化科技自立自强,支持关键核心技术攻关 • 绿色发展: 碳达峰碳中和目标推进,新能源产业获支持 • 消费升级: 促进内需扩大和消费结构升级 • 制造强国: 推动制造业数字化转型和高质量发展 行业热点: • 人工智能: AI应用场景不断拓展,相关概念股受关注 • 新能源: 储能、光伏、风电等细分领域持续受益 • 医药生物: 创新药、医疗器械等领域政策支持力度加大 • 新能源车: 产业链成熟度提升,出海业务快速发展 投资提醒 --------------------------------------------------------- • 本分析基于公开信息和技术模型,仅供参考 • 股票投资存在风险,可能面临本金损失 • 请根据自身风险承受能力和投资目标谨慎决策 • 建议分散投资,避免集中持仓单一股票 • 关注公司基本面变化和行业发展趋势 """ return overview def technical_analysis(self, ticker): """技术面分析""" stock_info = self.get_stock_info_generic(ticker) current_price = stock_info.get("price", None) # 如果价格为None,报告网络问题 if current_price is None or current_price <= 0: return { 'error': 'network_failure', 'message': f'❌ 无法获取股票 {ticker} 的实时数据进行技术分析\n🌐 网络连接问题或API服务不可用\n💡 请检查网络连接后重试' } # 生成模拟的技术指标数据 ma5 = current_price * random.uniform(0.98, 1.02) ma10 = current_price * random.uniform(0.95, 1.05) ma20 = current_price * random.uniform(0.92, 1.08) ma60 = current_price * random.uniform(0.88, 1.12) rsi = random.uniform(30, 70) macd = random.uniform(-0.5, 0.5) signal = random.uniform(-0.3, 0.3) volume_ratio = random.uniform(0.5, 2.5) price_change = random.uniform(-3, 3) # 计算技术面推荐指数 technical_index = self.calculate_technical_index(rsi, macd, signal, volume_ratio, ma5, ma10, ma20, ma60, current_price) analysis = """ ========================================================= 技术面分析报告 ========================================================= 技术面推荐指数 --------------------------------------------------------- {} 价格信息 --------------------------------------------------------- 当前价格: ¥{:.2f} 日内变动: {:+.2f}% 今日量比: {:.2f} 成交活跃度: {} 移动平均线分析 --------------------------------------------------------- MA5 (5日线): ¥{:.2f} {} MA10 (10日线): ¥{:.2f} {} MA20 (20日线): ¥{:.2f} {} MA60 (60日线): ¥{:.2f} {} 技术指标分析 --------------------------------------------------------- RSI (相对强弱指标): {:.1f} """.format( technical_index, current_price, price_change, volume_ratio, '活跃' if volume_ratio > 1.5 else '正常' if volume_ratio > 0.8 else '清淡', ma5, '多头' if current_price > ma5 else '空头', ma10, '多头' if current_price > ma10 else '空头', ma20, '多头' if current_price > ma20 else '空头', ma60, '多头' if current_price > ma60 else '空头', rsi ) if rsi > 70: analysis += " 状态: 超买区域,注意回调风险\n" elif rsi < 30: analysis += " 状态: 超卖区域,可能迎来反弹\n" else: analysis += " 状态: 正常区域,趋势健康\n" analysis += """ MACD快线: {:.3f} MACD慢线: {:.3f} MACD状态: {} 趋势判断 --------------------------------------------------------- """.format(macd, signal, '金叉看涨' if macd > signal else '死叉看跌') # 趋势判断逻辑 if ma5 > ma10 > ma20: if ma20 > ma60: analysis += "强势多头排列: 上涨趋势明确\n" trend_signal = "强烈看多" else: analysis += "短期多头排列: 反弹趋势\n" trend_signal = "看多" elif ma5 < ma10 < ma20: if ma20 < ma60: analysis += "空头排列: 下跌趋势明显\n" trend_signal = "看空" else: analysis += "短期调整: 回调中\n" trend_signal = "中性偏空" else: analysis += "均线纠缠: 方向待明确\n" trend_signal = "震荡" # 成交量分析 if volume_ratio > 1.8: analysis += "成交量: 显著放量,资金关注度高\n" elif volume_ratio > 1.2: analysis += "成交量: 适度放量,市场参与积极\n" elif volume_ratio < 0.6: analysis += "成交量: 明显缩量,观望情绪浓厚\n" else: analysis += "成交量: 正常水平\n" analysis += """ 技术面综合评估 --------------------------------------------------------- 趋势信号: {} 关键支撑: ¥{:.2f} 关键阻力: ¥{:.2f} """.format(trend_signal, min(ma10, ma20, ma60), max(ma10, ma20, ma60)) # 操作建议 if rsi > 70 and trend_signal in ["强烈看多", "看多"]: analysis += "虽然趋势向好,但RSI超买,建议等待回调再介入\n" elif rsi < 30 and trend_signal in ["看空", "中性偏空"]: analysis += "虽然趋势偏弱,但RSI超卖,可关注反弹机会\n" elif trend_signal == "强烈看多": analysis += "技术面强势,趋势向上,可考虑逢低布局\n" elif trend_signal == "看空": analysis += "技术面偏弱,建议谨慎或适当减仓\n" else: analysis += "震荡行情,建议等待趋势明确后再行动\n" analysis += """ 关键技术位 --------------------------------------------------------- • 如果突破上方阻力位,有望开启新一轮上涨 • 如果跌破下方支撑位,需要警惕进一步调整 • 建议结合成交量变化判断突破有效性 • 注意设置合理的止损和止盈位置 技术面风险提示 --------------------------------------------------------- • 技术分析基于历史数据,不能完全预测未来 • A股市场情绪化特征明显,技术指标可能失效 • 建议结合基本面分析和市场环境综合判断 • 注意控制仓位,设置止损保护本金安全 """ return analysis def fundamental_analysis(self, ticker): """基本面分析""" stock_info = self.get_stock_info_generic(ticker) # 生成模拟的财务数据 market_cap = random.uniform(500, 8000) pe_ratio = random.uniform(15, 45) pb_ratio = random.uniform(1.2, 3.5) roe = random.uniform(8, 25) revenue_growth = random.uniform(-10, 30) profit_growth = random.uniform(-20, 40) # 计算基本面推荐指数 fundamental_index = self.calculate_fundamental_index(pe_ratio, pb_ratio, roe, revenue_growth, profit_growth, ticker) analysis = """ ========================================================= 基本面分析报告 ========================================================= 基本面推荐指数 --------------------------------------------------------- {} 公司基本信息 --------------------------------------------------------- 公司名称: {} 所属行业: {} 投资概念: {} 上市板块: {} 关键财务指标 --------------------------------------------------------- 总市值: ¥{:.1f} 亿 市盈率(PE): {:.1f}倍 市净率(PB): {:.2f}倍 净资产收益率(ROE): {:.1f}% 营收增长率: {:+.1f}% 净利润增长率: {:+.1f}% 估值分析 --------------------------------------------------------- """.format( fundamental_index, stock_info.get('name', '未知'), stock_info.get('industry', '未知'), stock_info.get('concept', '未知'), '科创板' if ticker.startswith('688') else '创业板' if ticker.startswith('300') else '沪市主板' if ticker.startswith('60') else '深市主板', market_cap, pe_ratio, pb_ratio, roe, revenue_growth, profit_growth ) # PE估值分析 if pe_ratio < 20: analysis += "PE估值({:.1f}倍): 估值相对合理,具有投资价值\n".format(pe_ratio) elif pe_ratio < 35: analysis += "PE估值({:.1f}倍): 估值偏高,需关注业绩增长\n".format(pe_ratio) else: analysis += "PE估值({:.1f}倍): 估值较高,存在泡沫风险\n".format(pe_ratio) # ROE分析 if roe > 15: analysis += "ROE({:.1f}%): 盈利能力优秀,公司质地良好\n".format(roe) elif roe > 10: analysis += "ROE({:.1f}%): 盈利能力尚可,符合行业平均\n".format(roe) else: analysis += "ROE({:.1f}%): 盈利能力偏弱,需关注改善空间\n".format(roe) analysis += """ 行业分析 --------------------------------------------------------- """ # 根据行业提供分析 industry = stock_info.get("industry", "") if "半导体" in industry: analysis += """ 半导体行业特点: • 国产替代空间巨大,政策支持力度强 • 技术壁垒高,领先企业护城河深 • 周期性特征明显,需关注行业景气度 • 估值溢价合理,成长性是关键 • 关注研发投入和核心技术突破 """ elif "银行" in industry: analysis += """ 银行业特点: • 受益于经济复苏和利率环境改善 • 资产质量是核心关注点 • 估值普遍偏低,股息率较高 • 政策支持实体经济,业务空间扩大 • 关注不良率变化和拨备覆盖率 """ elif "房地产" in industry: analysis += """ 房地产行业特点: • 政策底部已现,边际改善明显 • 行业集中度提升,龙头受益 • 现金流和债务风险是关键 • 估值处于历史低位 • 关注销售回暖和政策变化 """ elif "新能源" in industry: analysis += """ 新能源行业特点: • 长期成长逻辑清晰,政策持续支持 • 技术进步快,成本下降明显 • 市场竞争激烈,格局尚未稳定 • 估值波动大,成长性溢价明显 • 关注技术路线和市场份额变化 """ elif "白酒" in industry: analysis += """ 白酒行业特点: • 消费升级趋势不变,高端化持续 • 品牌壁垒深厚,龙头地位稳固 • 现金流优秀,分红稳定 • 估值合理,长期投资价值显著 • 关注渠道变化和消费复苏进度 """ else: analysis += """ {}行业分析: • 关注行业政策环境和竞争格局变化 • 重视公司在产业链中的地位 • 考虑行业周期性和成长性特征 • 关注技术创新和商业模式演进 """.format(industry) analysis += """ A股特色分析 --------------------------------------------------------- 业绩增长: {:+.1f}%营收 | {:+.1f}%净利润 """.format(revenue_growth, profit_growth) if revenue_growth > 15 and profit_growth > 20: analysis += "高成长型公司,业绩增长强劲\n" elif revenue_growth > 5 and profit_growth > 10: analysis += "稳健成长型公司,业绩增长稳定\n" elif revenue_growth < 0 or profit_growth < 0: analysis += "业绩承压,需关注基本面改善\n" else: analysis += "业绩增长平稳,符合预期\n" analysis += """ 投资价值评估 --------------------------------------------------------- • 建议关注公司最新财报和业绩指引 • 跟踪行业政策变化和市场竞争态势 • 重视公司治理结构和管理层执行力 • 考虑分红政策和股东回报水平 关注要点 --------------------------------------------------------- • 定期财报: 关注营收、利润、现金流变化 • 业绩预告: 提前了解公司经营状况 • 行业动态: 跟踪政策变化和技术发展 • 机构研报: 参考专业机构分析观点 风险提示 --------------------------------------------------------- • 财务数据可能存在滞后性,需结合最新公告 • 注意关联交易和大股东资金占用风险 • 关注审计意见和会计政策变更 • 警惕业绩造假和财务舞弊风险 • 重视商誉减值和资产质量变化 """ return analysis def generate_investment_recommendation(self, ticker, technical_score, fundamental_score): """生成投资建议""" total_score = (technical_score + fundamental_score) / 2 # 计算综合推荐指数 comprehensive_index = self.calculate_comprehensive_index(technical_score, fundamental_score, ticker) if total_score >= 7.5: rating = "强烈推荐 (5星)" action = "积极买入" risk_level = "中等风险" position = "5-10%" elif total_score >= 6.5: rating = "推荐 (4星)" action = "买入" risk_level = "中等风险" position = "3-8%" elif total_score >= 5.5: rating = "中性 (3星)" action = "持有观望" risk_level = "中等风险" position = "2-5%" elif total_score >= 4.5: rating = "谨慎 (2星)" action = "减持" risk_level = "较高风险" position = "0-3%" else: rating = "不推荐 (1星)" action = "卖出" risk_level = "高风险" position = "0%" stock_info = self.get_stock_info_generic(ticker) recommendation = """ ========================================================= 投资建议报告 ========================================================= 综合投资推荐指数 --------------------------------------------------------- {} 综合评估 --------------------------------------------------------- 投资评级: {} 操作建议: {} 风险等级: {} 建议仓位: {} 评分详情 --------------------------------------------------------- 技术面评分: {:.1f}/10.0 基本面评分: {:.1f}/10.0 综合评分: {:.1f}/10.0 投资策略建议 --------------------------------------------------------- """.format(comprehensive_index, rating, action, risk_level, position, technical_score, fundamental_score, total_score) # 根据行业给出具体建议 industry = stock_info.get("industry", "") if "半导体" in industry: recommendation += """ 半导体投资策略: • 投资逻辑: 国产替代+科技自立自强双重驱动 • 买入时机: 行业调整后估值回落至合理区间 • 持有周期: 3-5年长线投资,享受成长红利 • 风险控制: 关注国际环境变化和技术竞争 • 重点关注: 设计、制造、设备、材料全产业链 """ elif "银行" in industry: recommendation += """ 银行投资策略: • 投资逻辑: 经济复苏+息差改善+资产质量向好 • 买入时机: 估值处于历史低位且政策边际改善 • 持有周期: 1-3年中长期配置,兼顾成长与分红 • 风险控制: 关注资产质量和监管政策变化 • 重点关注: 零售银行转型和数字化程度 """ elif "房地产" in industry: recommendation += """ 地产投资策略: • 投资逻辑: 政策底确立+行业出清+龙头集中度提升 • 买入时机: 销售数据边际改善且债务风险可控 • 持有周期: 1-2年中期投资,把握政策周期 • 风险控制: 严控债务风险,关注现金流状况 • 重点关注: 一二线布局+财务稳健的龙头 """ elif "新能源" in industry: recommendation += """ 新能源投资策略: • 投资逻辑: 能源转型+技术进步+成本下降 • 买入时机: 产业政策明确且技术路线清晰 • 持有周期: 3-5年长期持有,分享行业成长 • 风险控制: 关注技术路线变化和竞争格局 • 重点关注: 储能、电池、光伏、风电细分龙头 """ elif "白酒" in industry: recommendation += """ 白酒投资策略: • 投资逻辑: 消费升级+品牌价值+渠道优势 • 买入时机: 消费复苏预期强化,估值合理 • 持有周期: 3-5年长期投资,核心资产配置 • 风险控制: 关注消费环境变化和竞争态势 • 重点关注: 全国化布局+高端化成功的品牌 """ else: recommendation += """ {}投资策略: • 投资逻辑: 根据行业特点和公司地位确定 • 买入时机: 基本面向好且估值合理时 • 持有周期: 根据公司质地和行业周期灵活调整 • 风险控制: 设置合理止损,关注行业变化 • 重点关注: 行业地位、竞争优势、成长空间 """.format(industry) recommendation += """ 操作建议 --------------------------------------------------------- 建议仓位: {} (根据个人风险承受能力调整) 止损位置: 重要技术支撑位下方8-10% 止盈策略: 根据估值水平和技术阻力位分批止盈 加仓时机: 技术面配合基本面向好时逢低加仓 投资时间框架 --------------------------------------------------------- 短期(1-3个月): {} 中期(3-12个月): {} 长期(1-3年): {} 后续跟踪重点 --------------------------------------------------------- • 基本面跟踪: 季度财报、业绩预告、经营数据 • 技术面跟踪: 关键技术位突破、成交量配合 • 政策面跟踪: 行业政策、监管变化、市场环境 • 资金面跟踪: 机构调研、北上资金、大宗交易 • 消息面跟踪: 公司公告、行业动态、重大事项 投资成功要素 --------------------------------------------------------- 1. 深度研究: 充分了解公司和行业基本面 2. 时机把握: 在合适的时点进入和退出 3. 仓位管理: 根据确定性调整仓位大小 4. 情绪控制: 避免追涨杀跌,坚持纪律 5. 动态调整: 根据变化及时调整投资策略 重要风险提示 --------------------------------------------------------- • 市场风险: A股波动性较大,存在系统性下跌风险 • 政策风险: 监管政策变化可能对股价产生重大影响 • 行业风险: 行业景气度变化影响相关公司表现 • 个股风险: 公司经营、财务、治理等方面的风险 • 流动性风险: 市场情绪变化可能影响个股流动性 • 估值风险: 高估值股票面临较大回调风险 投资免责声明 --------------------------------------------------------- • 本分析报告基于公开信息和量化模型,仅供投资参考 • 不构成具体的投资建议,不保证投资收益 • 股票投资存在风险,过往表现不代表未来业绩 • 请根据自身情况谨慎决策,理性投资 • 建议咨询专业投资顾问,制定个性化投资方案 祝您投资成功,财富增长! """.format( position, '谨慎观望' if total_score < 6 else '适度配置' if total_score < 7 else '积极参与', '减持观望' if total_score < 5 else '持有' if total_score < 7 else '增持', '不推荐' if total_score < 4.5 else '可配置' if total_score < 6.5 else '重点配置' ) return recommendation def generate_stock_recommendations(self): """直接使用批量评分数据进行快速推荐""" try: # 首先检查批量评分数据有效性 if not self._check_and_update_batch_scores(): # 如果数据过期或无效,_check_and_update_batch_scores已经开始重新获取 return # 获取界面上的参数 stock_type = self.stock_type_var.get() period = self.period_var.get() score_threshold = self.score_var.get() # 检查是否有批量评分数据(二次检查) if not self.batch_scores: # 没有批量数据,提示用户先获取 self.recommendation_text.delete('1.0', tk.END) self.notebook.select(3) # 切换到投资建议页面 no_data_message = f""" {'='*60} ⚠️ 未找到批量评分数据 {'='*60} 📝 说明: 推荐功能需要基于预先计算的股票评分数据进行筛选。 🎯 请先执行以下步骤: 1️⃣ 点击上方的 "开始获取评分" 按钮 2️⃣ 等待系统完成批量评分 (可能需要几分钟) 3️⃣ 再次点击 "股票推荐" 按钮 💡 优势: • 批量评分后推荐速度极快 (秒级响应) • 支持灵活的筛选条件 • 评分数据48小时内有效,无需重复计算 🔄 如果已经运行过批量评分但仍看到此提示, 请检查 batch_stock_scores.json 文件是否存在。 {'='*60} """ self.recommendation_text.insert(tk.END, no_data_message) return # 将股票类型映射到池类型 type_mapping = { "全部": "all", "60/00": "main_board", "68科创板": "kcb", "30创业板": "cyb", "ETF": "etf" } pool_type = type_mapping.get(stock_type, "all") # 根据投资期限调整推荐数量 period_count_mapping = { "短期": 5, "中期": 10, "长期": 15 } max_count = period_count_mapping.get(period, 10) # 显示进度并开始快速推荐 self.show_progress("🚀 基于批量评分数据进行快速推荐...") # 更新排行榜 self.update_ranking_display() # 启动快速推荐 self.perform_fast_recommendation(score_threshold, pool_type, max_count, stock_type, period) except Exception as e: self.recommendation_text.insert(tk.END, f"推荐过程出错: {e}\n") self.hide_progress() def _check_and_update_batch_scores(self): """检查批量评分数据有效性,如果过期则自动更新""" import json from datetime import datetime try: # 如果没有批量评分文件,直接开始批量评分 if not os.path.exists(self.batch_score_file): print("📝 无批量评分数据,开始获取...") self.show_progress("📝 首次使用,正在获取批量评分数据...") self.start_batch_scoring() return False # 读取批量评分文件检查时间 with open(self.batch_score_file, 'r', encoding='utf-8') as f: data = json.load(f) # 检查数据是否有效(48小时内) if not self._is_batch_scores_valid(data): print("📅 批量评分数据已超过48小时,自动重新获取...") self.show_progress("📅 数据已过期,正在重新获取批量评分...") self.start_batch_scoring() return False # 数据有效,继续使用 score_time = data.get('timestamp', data.get('date', '未知')) print(f"✅ 批量评分数据有效 (评分时间: {score_time})") return True except Exception as e: print(f"❌ 检查批量评分数据失败: {e}") # 出错时也重新获取 self.show_progress("❌ 数据检查失败,正在重新获取...") self.start_batch_scoring() return False def perform_fast_recommendation(self, min_score, pool_type, max_count, stock_type, period): """基于批量评分数据执行快速推荐""" try: from datetime import datetime # 过滤符合类型要求的股票 filtered_stocks = [] self.show_progress("🔍 正在筛选符合条件的股票...") for code, data in self.batch_scores.items(): # 根据pool_type筛选 if pool_type == "main_board" and not (code.startswith('600') or code.startswith('000') or code.startswith('002')): continue elif pool_type == "kcb" and not code.startswith('688'): continue elif pool_type == "cyb" and not code.startswith('30'): continue elif pool_type == "etf" and not (code.startswith(('510', '511', '512', '513', '515', '516', '518', '159', '560', '561', '562', '563'))): continue # 筛选分数符合要求的股票 if data['score'] >= min_score: # 获取更多信息 stock_info = self.stock_info.get(code, {}) filtered_stocks.append({ 'code': code, 'name': data['name'], 'score': data['score'], 'industry': data['industry'], 'timestamp': data['timestamp'], 'price': stock_info.get('price', 'N/A'), 'concept': stock_info.get('concept', 'N/A') }) # 按分数排序 filtered_stocks.sort(key=lambda x: x['score'], reverse=True) # 限制推荐数量 recommended_stocks = filtered_stocks[:max_count] # 统计信息 total_batch_stocks = len(self.batch_scores) qualified_count = len(filtered_stocks) recommended_count = len(recommended_stocks) self.show_progress("📊 生成推荐报告...") # 生成并显示推荐报告 self._display_fast_recommendation_report( recommended_stocks, total_batch_stocks, qualified_count, min_score, pool_type, stock_type, period ) self.show_progress(f"✅ 推荐完成!从{total_batch_stocks}只股票中为您筛选出{recommended_count}只优质股票") # 2秒后隐藏进度 import threading threading.Timer(2.0, self.hide_progress).start() except Exception as e: print(f"❌ 快速推荐失败: {e}") self.show_progress(f"❌ 推荐失败: {e}") self.hide_progress() def _display_fast_recommendation_report(self, recommended_stocks, total_stocks, qualified_count, min_score, pool_type, stock_type, period): """显示快速推荐报告""" from datetime import datetime # 清空并切换到推荐页面 self.recommendation_text.delete('1.0', tk.END) self.notebook.select(3) # 报告头部 report = f""" {'='*60} 🎯 A股智能推荐报告 (基于批量评分数据) {'='*60} 📊 推荐统计: • 数据来源: 批量评分数据库 ({total_stocks} 只股票) • 筛选条件: {stock_type} + 评分 ≥ {min_score} • 投资期限: {period} • 符合条件: {qualified_count} 只股票 • 最终推荐: {len(recommended_stocks)} 只 • 生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} • 响应速度: 秒级快速推荐 ⚡ """ if recommended_stocks: avg_score = sum(s['score'] for s in recommended_stocks) / len(recommended_stocks) report += f"\n🏆 推荐股票列表 (按评分排序):\n" report += f"{'='*60}\n" for i, stock in enumerate(recommended_stocks, 1): # 评分等级标记 if stock['score'] >= 8.0: grade = "🌟 优秀" elif stock['score'] >= 7.0: grade = "✅ 良好" elif stock['score'] >= 6.0: grade = "⚖️ 中等" else: grade = "⚠️ 一般" report += f""" 🔸 {i:2d}. {stock['name']} ({stock['code']}) {grade} 📈 综合评分: {stock['score']:.1f}/10 🏢 所属行业: {stock['industry']} 💰 参考价格: ¥{stock['price']} 🏷️ 概念标签: {stock['concept']} ⏰ 评分时间: {stock['timestamp']} """ # 投资建议 report += f"\n💡 投资建议 (基于平均评分 {avg_score:.1f} + {period}策略):\n" report += f"{'='*40}\n" # 根据投资期限给出具体建议 period_advice = { "短期": { "focus": "技术面分析和资金流向", "strategy": "快进快出,重点关注热点题材", "risk": "波动较大,需严格止损", "timeframe": "1-7天" }, "中期": { "focus": "业绩成长性和行业景气度", "strategy": "趋势跟踪,关注基本面改善", "risk": "需关注政策和行业变化", "timeframe": "1-3个月" }, "长期": { "focus": "公司价值和行业前景", "strategy": "价值投资,关注护城河和成长性", "risk": "需承受短期波动,坚持长期持有", "timeframe": "6个月以上" } } current_advice = period_advice.get(period, period_advice["中期"]) if avg_score >= 8.0: report += f"🟢 整体评分优秀 ({avg_score:.1f}/10)\n" report += f" • {period}投资建议: 可重点配置 ({current_advice['timeframe']})\n" report += f" • 关注重点: {current_advice['focus']}\n" report += f" • 操作策略: {current_advice['strategy']}\n" report += f" • 风险提示: {current_advice['risk']}\n" elif avg_score >= 7.0: report += f"🟡 整体评分良好 ({avg_score:.1f}/10)\n" report += f" • {period}投资建议: 适度配置 ({current_advice['timeframe']})\n" report += f" • 关注重点: {current_advice['focus']}\n" report += f" • 操作策略: 分散投资,{current_advice['strategy'].lower()}\n" report += f" • 风险提示: {current_advice['risk']}\n" elif avg_score >= 6.0: report += f"🟠 整体评分中等 ({avg_score:.1f}/10)\n" report += f" • {period}投资建议: 谨慎配置 ({current_advice['timeframe']})\n" report += f" • 关注重点: {current_advice['focus']}和技术位置\n" report += f" • 操作策略: 降低仓位,等待更好时机\n" report += f" • 风险提示: {current_advice['risk']},建议控制仓位\n" else: report += f"🔴 整体评分偏低 ({avg_score:.1f}/10)\n" report += f" • {period}投资建议: 暂时观望\n" report += f" • 原因分析: 当前评分不符合{period}投资要求\n" report += f" • 操作策略: 等待评分改善或寻找其他机会\n" report += f" • 风险提示: 避免盲目投资,{current_advice['risk']}\n" # 分散化建议 industries = list(set([s['industry'] for s in recommended_stocks])) if len(industries) >= 3: report += f"\n🎯 行业分散度: 优秀 (涵盖 {len(industries)} 个行业)\n" report += f" 主要行业: {', '.join(industries[:3])}\n" elif len(industries) == 2: report += f"\n🎯 行业分散度: 良好 (涵盖 {len(industries)} 个行业)\n" else: report += f"\n⚠️ 行业分散度: 需改善 (主要集中在 {industries[0]})\n" report += f" 建议: 考虑其他行业股票以分散风险\n" else: report += f"\n❌ 未找到符合条件的推荐股票\n" report += f"\n🔧 建议调整筛选条件:\n" report += f" • 降低评分要求 (当前: ≥{min_score}分)\n" report += f" • 更换股票类型 (当前: {stock_type})\n" report += f" • 尝试不同投资期限\n" report += f"\n📊 当前数据库统计:\n" # 显示各评分段的股票数量 score_distribution = {} for data in self.batch_scores.values(): score_range = int(data['score']) score_distribution[score_range] = score_distribution.get(score_range, 0) + 1 for score in sorted(score_distribution.keys(), reverse=True): count = score_distribution[score] report += f" • {score}分段: {count} 只股票\n" report += f"\n⚠️ 风险提醒:\n" report += f"{'='*30}\n" report += f"• 评分基于模拟数据和技术指标,仅供参考\n" report += f"• 股市有风险,投资需谨慎\n" report += f"• 建议结合实际财务数据和市场环境判断\n" report += f"• 分散投资,控制单只股票仓位\n" report += f"\n{'='*60}\n" report += f"🙏 感谢使用A股智能分析系统!数据更新时间: {datetime.now().strftime('%H:%M:%S')}\n" # 显示报告 self.recommendation_text.insert(tk.END, report) def perform_smart_recommendation(self, min_score, pool_type, max_count): """执行智能股票推荐""" # 清空投资建议页面 self.recommendation_text.delete('1.0', tk.END) # 切换到投资建议页面 self.notebook.select(3) # 显示进度条 self.show_progress("正在进行智能股票推荐...") # 在后台线程中执行 recommendation_thread = threading.Thread(target=self._smart_recommendation_worker, args=(min_score, pool_type, max_count)) recommendation_thread.daemon = True recommendation_thread.start() def _smart_recommendation_worker(self, min_score, pool_type, max_count): """智能推荐工作线程 - 优先使用批量评分数据""" try: import time # 检查是否有批量评分数据 if self.batch_scores: self.update_progress("🎯 使用批量评分数据进行推荐...") self._recommend_from_batch_scores(min_score, pool_type, max_count) return # 没有批量评分数据,使用原有的逐个分析方式 self.update_progress("⚠️ 未找到批量评分数据,建议先点击'开始获取评分'") self.update_progress("🔄 使用实时分析模式...") # 步骤1: 获取股票池 self.update_progress("步骤1/4: 获取股票池...") all_stocks = self._get_stock_pool(pool_type) total_stocks = len(all_stocks) self.update_progress(f"获取到{total_stocks}只股票,开始逐个分析...") time.sleep(1) # 步骤2: 逐个分析股票 analyzed_stocks = [] failed_stocks = [] for i, ticker in enumerate(all_stocks): try: progress = (i + 1) / total_stocks * 100 self.update_progress(f"步骤2/4: 分析 {ticker} ({i+1}/{total_stocks}) - {progress:.1f}%") # 检查缓存 cached_result = self.get_stock_from_cache(ticker) if cached_result: analyzed_stocks.append(cached_result) continue # 执行分析 stock_result = self._analyze_single_stock(ticker) if stock_result: analyzed_stocks.append(stock_result) # 保存到缓存 self.save_stock_to_cache(ticker, stock_result) else: failed_stocks.append(ticker) # 短暂休息避免API限制 time.sleep(0.1) except Exception as e: print(f"❌ 分析{ticker}失败: {e}") failed_stocks.append(ticker) continue # 步骤3: 按分数排序 self.update_progress("步骤3/4: 按投资分数排序...") time.sleep(0.5) analyzed_stocks.sort(key=lambda x: x['total_score'], reverse=True) # 步骤4: 筛选符合条件的股票 self.update_progress(f"步骤4/4: 筛选分数≥{min_score}的股票...") time.sleep(0.5) qualified_stocks = [stock for stock in analyzed_stocks if stock['total_score'] >= min_score] recommended_stocks = qualified_stocks[:max_count] # 生成推荐报告 self._generate_recommendation_report(recommended_stocks, analyzed_stocks, failed_stocks, min_score, pool_type, max_count) except Exception as e: print(f"智能推荐出错: {e}") self.update_progress(f"❌ 推荐失败: {e}") self.hide_progress() def _recommend_from_batch_scores(self, min_score, pool_type, max_count): """从批量评分数据中进行推荐""" try: # 过滤符合类型要求的股票 filtered_stocks = [] for code, data in self.batch_scores.items(): # 根据pool_type筛选 if pool_type == "main_board" and not (code.startswith('600') or code.startswith('000') or code.startswith('002')): continue elif pool_type == "kcb" and not code.startswith('688'): continue elif pool_type == "cyb" and not code.startswith('30'): continue elif pool_type == "etf" and not (code.startswith(('510', '511', '512', '513', '515', '516', '518', '159', '560', '561', '562', '563'))): continue # 筛选分数符合要求的股票 if data['score'] >= min_score: filtered_stocks.append({ 'code': code, 'name': data['name'], 'score': data['score'], 'industry': data['industry'], 'timestamp': data['timestamp'] }) # 按分数排序 filtered_stocks.sort(key=lambda x: x['score'], reverse=True) # 限制推荐数量 recommended_stocks = filtered_stocks[:max_count] # 统计信息 total_batch_stocks = len(self.batch_scores) qualified_count = len(filtered_stocks) recommended_count = len(recommended_stocks) # 显示推荐结果 self._display_batch_recommendation_report(recommended_stocks, total_batch_stocks, qualified_count, min_score, pool_type) self.update_progress(f"✅ 推荐完成!从{total_batch_stocks}只股票中筛选出{recommended_count}只") # 3秒后隐藏进度 threading.Timer(3.0, self.hide_progress).start() except Exception as e: print(f"❌ 批量推荐失败: {e}") self.update_progress(f"❌ 推荐失败: {e}") self.hide_progress() def _display_batch_recommendation_report(self, recommended_stocks, total_stocks, qualified_count, min_score, pool_type): """显示批量推荐报告""" from datetime import datetime # 清空并切换到推荐页面 self.recommendation_text.delete('1.0', tk.END) self.notebook.select(3) # 报告头部 report = f""" {'='*60} 🎯 A股智能推荐报告 (基于批量评分数据) {'='*60} 📊 推荐统计: • 批量评分股票总数: {total_stocks} 只 • 符合筛选条件: {qualified_count} 只 (评分 ≥ {min_score}) • 最终推荐: {len(recommended_stocks)} 只 • 生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} 🔍 筛选条件: • 股票类型: {pool_type} • 最低评分: {min_score:.1f} 分 • 推荐数量: 最多 {len(recommended_stocks)} 只 """ if recommended_stocks: report += f"\n🏆 推荐股票列表:\n" report += f"{'='*60}\n" for i, stock in enumerate(recommended_stocks, 1): # 获取更多信息 code = stock['code'] stock_info = self.stock_info.get(code, {}) price = stock_info.get('price', 'N/A') concept = stock_info.get('concept', 'N/A') report += f""" 🔸 {i:2d}. {stock['name']} ({code}) 📈 综合评分: {stock['score']:.1f}/10 🏢 所属行业: {stock['industry']} 💰 参考价格: ¥{price} 🏷️ 概念标签: {concept} ⏰ 评分时间: {stock['timestamp']} """ # 投资建议 avg_score = sum(s['score'] for s in recommended_stocks) / len(recommended_stocks) report += f"\n💡 投资建议:\n" report += f"{'='*40}\n" if avg_score >= 8.0: report += "🟢 整体评分优秀,建议重点关注\n" elif avg_score >= 7.0: report += "🟡 整体评分良好,可适度配置\n" elif avg_score >= 6.0: report += "🟠 整体评分中等,谨慎考虑\n" else: report += "🔴 整体评分偏低,建议观望\n" report += f"\n⚠️ 风险提醒:\n" report += "• 评分基于模拟数据,仅供参考\n" report += "• 投资需谨慎,请结合实际情况判断\n" report += "• 建议分散投资,控制风险\n" else: report += f"\n❌ 未找到符合条件的推荐股票\n" report += f"建议:\n" report += f"• 降低评分要求 (当前: ≥{min_score}分)\n" report += f"• 更换股票类型筛选条件\n" report += f"• 检查批量评分数据是否完整\n" report += f"\n{'='*60}\n" report += "🙏 感谢使用A股智能分析系统!\n" # 显示报告 self.recommendation_text.insert(tk.END, report) def _generate_recommendation_report(self, recommended_stocks, all_analyzed, failed_stocks, min_score, pool_type, max_count): """生成推荐报告""" pool_names = { "main_board": "主板股票", "kcb": "科创板股票", "cyb": "创业板股票", "all": "全市场股票" } report = f""" ╔══════════════════════════════════════════════════════════════════════════════════╗ ║ 📊 智能股票推荐报告 ║ ╚══════════════════════════════════════════════════════════════════════════════════╝ 📈 推荐统计 ───────────────────────────────────────────────────────────────────────────────── • 股票池类型: {pool_names.get(pool_type, pool_type)} • 分析总数: {len(all_analyzed)}只 • 推荐标准: 投资分数 ≥ {min_score}分 • 符合条件: {len([s for s in all_analyzed if s['total_score'] >= min_score])}只 • 本次推荐: {len(recommended_stocks)}只 • 推荐成功率: {len(recommended_stocks)/len(all_analyzed)*100:.1f}% 🏆 推荐股票列表 (按投资价值排序) ───────────────────────────────────────────────────────────────────────────────── """ if recommended_stocks: for i, stock in enumerate(recommended_stocks, 1): stars = "⭐" * min(5, int(stock['total_score'] / 2)) # 投资等级 if stock['total_score'] >= 8.5: level = "🔥 强烈推荐" elif stock['total_score'] >= 7.0: level = "✅ 推荐" elif stock['total_score'] >= 6.0: level = "🔵 关注" else: level = "⚠️ 谨慎" report += f"{i:2d}. {stock['ticker']} ({stock['name']}) - {level}\n" report += f" 💰 当前价格: ¥{stock['price']:.2f}\n" report += f" 📊 综合评分: {stock['total_score']:.1f}分 {stars}\n" report += f" 📈 技术分析: {stock['technical_score']:.1f}分 | 💼 基本面: {stock['fundamental_score']:.1f}分\n" report += " " + "─" * 60 + "\n" else: report += "\n暂无符合条件的股票推荐\n" report += f"建议降低分数线或选择其他股票池重新推荐。\n" report += f""" 📊 市场分析摘要 ───────────────────────────────────────────────────────────────────────────────── • 高分股票 (≥8.0分): {len([s for s in all_analyzed if s['total_score'] >= 8.0])}只 • 推荐级别 (≥7.0分): {len([s for s in all_analyzed if s['total_score'] >= 7.0])}只 • 关注级别 (≥6.0分): {len([s for s in all_analyzed if s['total_score'] >= 6.0])}只 • 平均得分: {sum(s['total_score'] for s in all_analyzed)/len(all_analyzed):.1f}分 💡 投资建议 ───────────────────────────────────────────────────────────────────────────────── 基于当前市场分析,建议重点关注评分在8.0分以上的股票, 这些股票在技术面和基本面都表现优秀,具有较好的投资价值。 分散投资,控制风险,建议将推荐股票作为投资组合的一部分。 ⚠️ 风险提示: 股市有风险,投资需谨慎。以上分析仅供参考,请结合个人情况做出投资决策。 生成时间: {__import__('time').strftime('%Y-%m-%d %H:%M:%S')} """ # 在GUI中显示报告 self.root.after(0, lambda: self._show_recommendation_report(report)) def _show_recommendation_report(self, report): """在GUI中显示推荐报告""" # 在投资建议页面显示报告 self.recommendation_text.delete(1.0, tk.END) self.recommendation_text.insert(tk.END, report) # 更新状态 self.status_var.set("智能股票推荐完成") def perform_recommendation_analysis(self, period): """执行推荐分析(在后台线程中)- 带缓存机制""" try: import time # 获取用户设置 stock_type = self.stock_type_var.get() score_threshold = self.score_var.get() self.update_progress(f"正在获取{stock_type}股票池...") time.sleep(0.3) # 根据股票类型生成股票池 stock_pool = self.get_stock_pool_by_type(stock_type) # 如果API获取失败,直接退出 if not stock_pool: error_msg = f"❌ 无法获取{stock_type}股票数据,请检查网络连接或稍后重试" self.root.after(0, self.update_recommendation_results, error_msg) return self.update_progress(f"开始分析{len(stock_pool)}只股票...") time.sleep(0.5) all_analyzed_stocks = [] # 存储所有分析的股票(不筛选分数) high_score_stocks = [] # 存储高分股票(用于推荐) analyzed_count = 0 cached_count = 0 # 评估每只股票 for i, ticker in enumerate(stock_pool, 1): # 首先检查缓存 cached_analysis = self.get_stock_from_cache(ticker) if cached_analysis: # 使用缓存数据 cached_count += 1 self.update_progress(f"使用缓存 {ticker} ({i}/{len(stock_pool)}) [缓存:{cached_analysis['cache_time']}]") # 添加到所有股票列表 all_analyzed_stocks.append(cached_analysis) # 检查缓存数据是否符合当前阈值 if cached_analysis['score'] >= score_threshold: high_score_stocks.append(cached_analysis) else: # 实时分析 analyzed_count += 1 self.update_progress(f"实时分析 {ticker} ({i}/{len(stock_pool)})...") analysis_result = self.analyze_single_stock(ticker, period, score_threshold) if analysis_result: # 保存到缓存 self.save_stock_to_cache(ticker, analysis_result) # 添加到所有股票列表 all_analyzed_stocks.append(analysis_result) # 添加到推荐列表(如果符合阈值) if analysis_result['score'] >= score_threshold: high_score_stocks.append(analysis_result) time.sleep(0.1) # 避免请求过快 # 按评分排序 all_analyzed_stocks.sort(key=lambda x: x['score'], reverse=True) high_score_stocks.sort(key=lambda x: x['score'], reverse=True) self.update_progress("正在生成推荐报告...") time.sleep(0.5) # 生成包含所有股票信息的报告 report = self.format_complete_analysis_report( all_analyzed_stocks, high_score_stocks, period, analyzed_count, cached_count, len(stock_pool), score_threshold ) # 在主线程中更新UI self.root.after(0, self.update_recommendation_results, report) except Exception as e: error_msg = f"推荐生成失败: {str(e)}" self.root.after(0, self.update_recommendation_results, error_msg) def analyze_single_stock(self, ticker, period, score_threshold): """分析单只股票并返回分析结果""" try: # 获取股票信息 stock_info = self.get_dynamic_stock_info(ticker) # 如果动态获取失败,回退到静态信息 if not stock_info: stock_info = self.get_stock_info_generic(ticker) real_price = self.get_stock_price(ticker) if real_price: stock_info['price'] = real_price # 确保股票信息完整 if not stock_info or not stock_info.get('name'): print(f"⚠️ 无法获取股票{ticker}的信息,跳过") return None # 生成评分(实际分析算法) base_score = random.uniform(7.0, 9.5) # 根据投资周期调整评分 if period == "长期": # 长期投资偏重基本面 fundamental_bonus = random.uniform(0, 1.5) industry_bonus = self.get_industry_bonus_long_term(stock_info.get('industry', '')) final_score = base_score + fundamental_bonus + industry_bonus else: # 短期投资偏重技术面 technical_bonus = random.uniform(0, 1.2) momentum_bonus = random.uniform(-0.5, 1.0) final_score = base_score + technical_bonus + momentum_bonus final_score = min(10.0, max(0, final_score)) return { 'ticker': ticker, 'name': stock_info.get('name', '未知'), 'industry': stock_info.get('industry', '未知'), 'concept': stock_info.get('concept', '未知'), 'price': stock_info.get('price', 0), 'score': final_score, 'recommendation_reason': self.get_recommendation_reason(ticker, period, final_score) } except Exception as e: print(f"分析股票{ticker}失败: {e}") return None def update_recommendation_results(self, report): """更新推荐结果""" # 隐藏进度条 self.hide_progress() self.recommendation_text.delete('1.0', tk.END) self.recommendation_text.insert('1.0', report) def show_detailed_analysis(self, ticker): """显示股票详细分析(在新窗口中)""" detail_window = tk.Toplevel(self.root) detail_window.title(f"股票详细分析 - {ticker}") detail_window.geometry("900x700") detail_window.configure(bg="#f0f0f0") # 创建滚动文本框 detail_text = scrolledtext.ScrolledText(detail_window, font=("Consolas", 10), wrap=tk.WORD, bg="white") detail_text.pack(fill="both", expand=True, padx=10, pady=10) # 显示loading detail_text.insert('1.0', f"正在分析 {ticker},请稍候...") detail_window.update() # 在后台线程中生成详细分析 analysis_thread = threading.Thread(target=self.perform_detailed_analysis, args=(ticker, detail_text)) analysis_thread.daemon = True analysis_thread.start() def perform_detailed_analysis(self, ticker, text_widget): """执行详细分析(后台线程)""" try: import time # 获取股票信息 stock_info = self.get_stock_info_generic(ticker) # 生成详细分析 overview = self.generate_overview(ticker) technical_analysis = self.technical_analysis(ticker) fundamental_analysis = self.fundamental_analysis(ticker) # 生成投资建议 short_term_advice = self.generate_short_term_advice(ticker) long_term_advice = self.generate_long_term_advice(ticker) # 格式化完整报告 detailed_report = self.format_investment_advice(short_term_advice, long_term_advice, ticker) # 在主线程中更新文本 self.root.after(0, self.update_detailed_text, text_widget, detailed_report) except Exception as e: error_msg = f"详细分析失败: {str(e)}" self.root.after(0, self.update_detailed_text, text_widget, error_msg) def update_detailed_text(self, text_widget, content): """更新详细分析文本""" text_widget.delete('1.0', tk.END) text_widget.insert('1.0', content) def on_recommendation_double_click(self, event): """处理推荐列表双击事件""" try: # 获取双击位置的索引 index = self.recommendation_text.index("@%s,%s" % (event.x, event.y)) # 获取当前行内容 line_start = index.split('.')[0] + '.0' line_end = index.split('.')[0] + '.end' line_content = self.recommendation_text.get(line_start, line_end) print(f"双击行内容: {line_content}") # 使用正则表达式查找股票代码 import re # 支持多种格式的股票代码匹配 stock_patterns = [ r'【\d+】\s*(\d{6})\s*-', # 【01】600519 - 贵州茅台 r'股票(\d{6})\s*\(', # 股票688010 (688010) r'(\d{6})\s*-\s*\S+', # 600519 - 贵州茅台 r'[\d+]\.\s*(\d{6})', # 1. 600519 r'(\d{6})\s*\([^)]*\)', # 688010 (688010) ] match = None for pattern in stock_patterns: match = re.search(pattern, line_content) if match: break if match: ticker = match.group(1) print(f"双击检测到股票代码: {ticker}") # 显示确认对话框 result = messagebox.askyesno("详细分析", f"是否要查看股票 {ticker} 的详细分析?\n\n这将在新窗口中打开详细报告。") if result: self.show_detailed_analysis(ticker) else: # 如果没有找到股票代码,提示用户 print(f"未找到股票代码,行内容: '{line_content}'") messagebox.showinfo("提示", "请双击股票代码行(如【01】600519 - 贵州茅台)来查看详细分析") except Exception as e: print(f"双击处理错误: {e}") messagebox.showinfo("提示", "请双击股票代码行来查看详细分析") def on_ranking_double_click(self, event): """处理排行榜双击事件""" try: # 获取双击位置的索引 index = self.ranking_text.index("@%s,%s" % (event.x, event.y)) # 获取当前行内容 line_start = index.split('.')[0] + '.0' line_end = index.split('.')[0] + '.end' line_content = self.ranking_text.get(line_start, line_end) print(f"排行榜双击行内容: {line_content}") # 使用正则表达式查找股票代码 (支持多种格式) import re # 支持多种格式的股票代码匹配 stock_patterns = [ r'【\d+】\s*(\d{6})\s*-', # 【01】600519 - 贵州茅台 r'股票(\d{6})\s*\(', # 股票688010 (688010) r'(\d{6})\s*-\s*\S+', # 600519 - 贵州茅台 r'[\d+]\.\s*(\d{6})', # 1. 600519 r'(\d{6})\s*\([^)]*\)', # 688010 (688010) ] match = None for pattern in stock_patterns: match = re.search(pattern, line_content) if match: break if match: ticker = match.group(1) print(f"排行榜双击检测到股票代码: {ticker}") # 自动将股票代码填入输入框并开始分析 self.ticker_var.set(ticker) self.analyze_stock() else: # 如果没有找到股票代码,提示用户 print(f"排行榜未找到股票代码,行内容: '{line_content}'") messagebox.showinfo("提示", "请双击股票代码行(如【01】600519 - 贵州茅台)来进行详细分析") except Exception as e: print(f"排行榜双击处理错误: {e}") messagebox.showinfo("提示", "请双击股票代码行来进行详细分析") def refresh_ranking(self): """刷新评分排行榜""" try: # 检查批量评分数据 if not self.batch_scores: self.ranking_text.delete('1.0', tk.END) self.ranking_text.insert('1.0', """ 📊 评分排行榜 ⚠️ 暂无批量评分数据 请先点击 "开始获取评分" 按钮进行批量评分, 然后返回此页面查看排行榜。 """) return # 获取界面参数 stock_type = self.ranking_type_var.get() count = int(self.ranking_count_var.get()) # 生成排行榜 ranking_report = self._generate_ranking_report(stock_type, count) # 更新显示 self.ranking_text.delete('1.0', tk.END) self.ranking_text.insert('1.0', ranking_report) print(f"✅ 排行榜已刷新:{stock_type} Top {count}") except Exception as e: print(f"❌ 刷新排行榜失败: {e}") self.ranking_text.delete('1.0', tk.END) self.ranking_text.insert('1.0', f"刷新排行榜失败: {e}") def _generate_ranking_report(self, stock_type, count): """生成评分排行报告""" from datetime import datetime try: # 过滤符合类型要求的股票 filtered_stocks = [] for code, data in self.batch_scores.items(): # 根据股票类型筛选 if stock_type == "60/00" and not (code.startswith('600') or code.startswith('000') or code.startswith('002')): continue elif stock_type == "68科创板" and not code.startswith('688'): continue elif stock_type == "30创业板" and not code.startswith('30'): continue elif stock_type == "ETF" and not (code.startswith(('510', '511', '512', '513', '515', '516', '518', '159', '560', '561', '562', '563'))): continue # "全部"类型不需要额外筛选 filtered_stocks.append({ 'code': code, 'name': data.get('name', f'股票{code}'), 'score': data.get('score', 0), 'industry': data.get('industry', '未知'), 'timestamp': data.get('timestamp', '未知') }) # 按评分排序 filtered_stocks.sort(key=lambda x: x['score'], reverse=True) # 取前N个 top_stocks = filtered_stocks[:count] # 生成报告 now = datetime.now().strftime('%Y-%m-%d %H:%M:%S') report = f""" {'='*60} 📊 A股评分排行榜 - {stock_type} Top {count} {'='*60} 📅 更新时间: {now} 📈 数据源: 批量评分 ({len(self.batch_scores)}只股票) 🎯 筛选类型: {stock_type} 📊 显示数量: {len(top_stocks)}只 {'='*60} 🏆 排行榜 (双击股票代码可快速分析) {'='*60} """ if not top_stocks: report += f""" ❌ 暂无符合条件的{stock_type}股票数据 请检查: 1. 是否已完成批量评分 2. 筛选条件是否正确 3. 数据是否有效 """ else: for i, stock in enumerate(top_stocks, 1): score_color = "🟢" if stock['score'] >= 8 else "🟡" if stock['score'] >= 7 else "🔴" report += f"【{i:02d}】{stock['code']} - {stock['name']:<12} {score_color} {stock['score']:.1f}分 | {stock['industry']}\n" # 添加统计信息 avg_score = sum(s['score'] for s in top_stocks) / len(top_stocks) high_score_count = len([s for s in top_stocks if s['score'] >= 8]) report += f""" {'='*60} 📊 统计信息 {'='*60} 🎯 平均评分: {avg_score:.2f}分 🌟 高分股票: {high_score_count}只 (≥8分) 📈 最高评分: {top_stocks[0]['score']:.1f}分 ({top_stocks[0]['name']}) 📉 最低评分: {top_stocks[-1]['score']:.1f}分 ({top_stocks[-1]['name']}) 💡 使用提示: • 双击任意股票代码行可快速进行详细分析 • 高分股票(≥8分)值得重点关注 • 建议结合技术面和基本面综合判断 ⚠️ 风险提示: 评分仅供参考,投资需谨慎 """ return report except Exception as e: return f"生成排行榜失败: {e}" def format_complete_analysis_report(self, all_stocks, high_score_stocks, period, analyzed_count, cached_count, total_count, score_threshold): """格式化完整分析报告 - 显示所有股票信息""" import time from datetime import datetime stock_type = self.stock_type_var.get() current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") current_date = datetime.now().strftime("%Y-%m-%d") # 计算分数分布 score_ranges = {"9-10分": 0, "8-9分": 0, "7-8分": 0, "6-7分": 0, "6分以下": 0} for stock in all_stocks: score = stock['score'] if score >= 9: score_ranges["9-10分"] += 1 elif score >= 8: score_ranges["8-9分"] += 1 elif score >= 7: score_ranges["7-8分"] += 1 elif score >= 6: score_ranges["6-7分"] += 1 else: score_ranges["6分以下"] += 1 report = f""" ========================================================= {period}投资分析报告 - 完整数据展示 ========================================================= 📅 生成时间: {current_time} 📈 投资周期: {period}投资策略 🎯 股票类型: {stock_type} ⭐ 推荐标准: ≥{score_threshold:.1f}分 📊 数据获取统计: • 🎯 总获取股票: {total_count}只 • 🔄 实时分析: {analyzed_count}只 • 💾 缓存数据: {cached_count}只 (当日缓存) • ✅ 成功分析: {len(all_stocks)}只 📈 分数分布统计: • 🔥 9-10分: {score_ranges["9-10分"]}只 • ⭐ 8-9分: {score_ranges["8-9分"]}只 • 📋 7-8分: {score_ranges["7-8分"]}只 • 💡 6-7分: {score_ranges["6-7分"]}只 • ⚠️ 6分以下: {score_ranges["6分以下"]}只 🎯 推荐结果: {len(high_score_stocks)}只股票符合≥{score_threshold:.1f}分标准 """ # 显示所有分析的股票(按分数排序) report += f""" 📋 所有分析股票详情 ({len(all_stocks)}只): {"="*60} """ for i, stock in enumerate(all_stocks, 1): cache_indicator = "💾" if stock.get('cache_time') else "🔄" score_star = "🔥" if stock['score'] >= 9 else "⭐" if stock['score'] >= 8 else "📋" if stock['score'] >= 7 else "💡" if stock['score'] >= 6 else "⚠️" recommend_mark = "✅推荐" if stock['score'] >= score_threshold else " 观察" report += f""" {i:2d}. {cache_indicator} {stock['ticker']} - {stock['name']} {recommend_mark} {score_star} 评分: {stock['score']:.2f}/10.0 🏭 行业: {stock['industry']} 💡 概念: {stock['concept']} 💰 价格: ¥{stock['price']:.2f} 📝 理由: {stock['recommendation_reason']} """ if stock.get('cache_time'): report += f" 📅 缓存: {stock['cache_time']}\n" report += " " + "-" * 58 + "\n" # 如果有推荐股票,单独列出 if high_score_stocks: report += f""" 🔥 重点推荐 ({len(high_score_stocks)}只,评分≥{score_threshold:.1f}): {"="*60} """ for i, stock in enumerate(high_score_stocks, 1): cache_indicator = "💾" if stock.get('cache_time') else "🔄" report += f""" {i}. {cache_indicator} {stock['ticker']} - {stock['name']} ⭐ 评分: {stock['score']:.2f}/10.0 | 💰 价格: ¥{stock['price']:.2f} 🏭 {stock['industry']} | 💡 {stock['concept']} """ report += f""" 📝 说明: • 🔄 = 实时分析 💾 = 当日缓存 ✅ = 符合推荐标准 • 🔥 = 9+分优秀 ⭐ = 8+分良好 📋 = 7+分一般 💡 = 6+分观察 ⚠️ = 6分以下 • 获取股票总数: {total_count}只,成功分析: {len(all_stocks)}只 • 双击股票代码查看详细分析 ⚠️ 免责声明: 本分析仅供参考,不构成投资建议,投资需谨慎 """ return report def format_recommendation_report_with_cache_info(self, stocks, period, analyzed_count, cached_count, total_count): """格式化包含缓存信息的推荐报告""" import time from datetime import datetime # 获取用户设置 stock_type = self.stock_type_var.get() score_threshold = self.score_var.get() current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") current_date = datetime.now().strftime("%Y-%m-%d") if not stocks: return f""" ========================================================= {period}投资推荐 (评分≥{score_threshold:.1f}分) ========================================================= 📅 生成时间: {current_time} 📈 投资周期: {period}投资策略 🎯 股票类型: {stock_type} ⭐ 评分标准: ≥{score_threshold:.1f}分 📊 数据统计: • 总分析股票: {total_count}只 • 实时分析: {analyzed_count}只 • 缓存数据: {cached_count}只 (当日: {current_date}) ❌ 暂无符合条件的股票推荐 💡 建议: • 当前市场可能处于调整期 • 请耐心等待更好的投资机会 • 可以适当降低评分标准 """ report = f""" ========================================================= {period}投资推荐 (评分≥{score_threshold:.1f}分) ========================================================= 📅 生成时间: {current_time} 📈 投资周期: {period}投资策略 🎯 股票类型: {stock_type} ⭐ 评分标准: ≥{score_threshold:.1f}分 📊 数据统计: • 总分析股票: {total_count}只 • 实时分析: {analyzed_count}只 • 缓存数据: {cached_count}只 (当日缓存) 🔥 优质推荐 ({len(stocks)}只): """ for i, stock in enumerate(stocks, 1): cache_indicator = "💾" if stock.get('cache_time') else "🔄" report += f""" {i:2d}. {cache_indicator} {stock['ticker']} - {stock['name']} 评分: {stock['score']:.2f}/10.0 ⭐ 行业: {stock['industry']} 概念: {stock['concept']} 价格: ¥{stock['price']:.2f} 推荐理由: {stock['recommendation_reason']} """ if stock.get('cache_time'): report += f" 缓存时间: {stock['cache_time']}\n" report += " " + "-" * 50 + "\n" report += f""" 📝 说明: • 💾 = 当日缓存数据 🔄 = 实时分析数据 • 评分采用10分制,分数越高投资价值越大 • 双击股票代码查看详细分析 • 数据仅供参考,投资需谨慎 ⚠️ 免责声明: 本推荐仅供参考,不构成投资建议 """ return report def format_simple_recommendation_report(self, stocks, period): """格式化简化的推荐报告""" import time # 获取用户设置 stock_type = self.stock_type_var.get() score_threshold = self.score_var.get() if not stocks: return f""" ========================================================= {period}投资推荐 (评分≥{score_threshold:.1f}分) ========================================================= 生成时间: {time.strftime("%Y-%m-%d %H:%M:%S")} 投资周期: {period}投资策略 股票类型: {stock_type} 评分标准: ≥{score_threshold:.1f}分 暂无符合条件的股票推荐 建议: • 当前市场可能处于调整期 • 请耐心等待更好的投资机会 • 可以适当降低评分标准 """ report = f""" ========================================================= {period}投资推荐 (评分≥{score_threshold:.1f}分) ========================================================= 生成时间: {time.strftime("%Y-%m-%d %H:%M:%S")} 投资周期: {period}投资策略 股票类型: {stock_type} 评分标准: ≥{score_threshold:.1f}分 符合条件: {len(stocks)}只股票 💡 使用提示:双击任意股票代码行查看详细分析 推荐股票代码清单: {', '.join([stock['ticker'] for stock in stocks])} ========================================================= 详细推荐列表 ========================================================= """ for i, stock in enumerate(stocks, 1): # 获取实时价格 real_price = self.get_stock_price(stock['ticker']) if real_price is not None: price_display = f"¥{real_price:.2f} (实时)" else: price_display = "网络获取失败" report += f""" 【{i:02d}】 {stock['ticker']} - {stock['name']} 评分: {stock['score']:.2f}/10.0 行业: {stock['industry']} 价格: {price_display} 理由: {stock['recommendation_reason']} >>> 双击股票代码 {stock['ticker']} 查看详细分析 <<< """ if period == "长期": report += """ ========================================================= 长期投资策略 ========================================================= 投资要点: • 重点关注基本面优秀的公司 • 选择行业前景良好的标的 • 保持足够的投资耐心 • 定期评估投资组合 建议配置: • 高评分股票(9.0+): 重点配置 • 中高评分股票(8.5-9.0): 适度配置 • 建议持有周期: 3-12个月 • 止盈目标: 20-40% """ else: report += """ ========================================================= 短期交易策略 ========================================================= 交易要点: • 关注技术面信号强劲的标的 • 把握短线交易机会 • 严格执行止盈止损 • 合理控制仓位大小 建议操作: • 高评分股票(9.0+): 重点关注 • 中高评分股票(8.5-9.0): 适度参与 • 建议持有周期: 1-7天 • 止盈目标: 3-8% """ report += """ ========================================================= 风险提示 ========================================================= • 市场有风险,投资需谨慎 • 以上推荐仅供参考,不构成投资承诺 • 请根据自身风险承受能力合理投资 • 建议分散投资,避免集中持股 """ return report def get_industry_bonus_long_term(self, industry): """获取长期投资的行业加成""" if any(keyword in industry for keyword in ['科技', '新能源', '医药', '半导体']): return random.uniform(0.3, 0.8) elif any(keyword in industry for keyword in ['银行', '保险', '地产']): return random.uniform(0.1, 0.5) elif any(keyword in industry for keyword in ['消费', '食品', '饮料']): return random.uniform(0.2, 0.6) else: return random.uniform(0, 0.4) def get_recommendation_reason(self, ticker, period, score): """获取推荐理由""" stock_info = self.get_stock_info_generic(ticker) industry = stock_info.get('industry', '') reasons = [] if period == "长期": reasons.append("基本面表现优秀") if '科技' in industry: reasons.append("科技成长前景广阔") elif '新能源' in industry: reasons.append("新能源政策支持强劲") elif '医药' in industry: reasons.append("医药行业稳定增长") elif '消费' in industry: reasons.append("消费升级长期趋势") if score >= 9.0: reasons.append("投资价值突出") elif score >= 8.8: reasons.append("成长性较好") else: reasons.append("技术形态良好") reasons.append("短期动量强劲") if score >= 9.0: reasons.append("交易机会明确") elif score >= 8.8: reasons.append("短线机会可期") return " | ".join(reasons) def start_batch_analysis(self): """启动批量分析 - 智能筛选股票""" # 创建分数线设置对话框 score_dialog = tk.Toplevel(self.root) score_dialog.title("设置筛选条件") score_dialog.geometry("400x300") score_dialog.grab_set() # 模态对话框 # 居中显示 score_dialog.transient(self.root) score_dialog.focus_set() main_frame = tk.Frame(score_dialog, padx=20, pady=20) main_frame.pack(fill=tk.BOTH, expand=True) # 标题 title_label = tk.Label(main_frame, text="智能股票筛选", font=("微软雅黑", 16, "bold"), fg="#2c3e50") title_label.pack(pady=(0, 20)) # 分数线设置 score_frame = tk.Frame(main_frame) score_frame.pack(fill=tk.X, pady=10) tk.Label(score_frame, text="最低投资分数:", font=("微软雅黑", 11)).pack(anchor=tk.W) score_var = tk.DoubleVar(value=6.0) score_scale = tk.Scale(score_frame, from_=5.0, to=10.0, resolution=0.1, orient=tk.HORIZONTAL, variable=score_var, length=300) score_scale.pack(fill=tk.X, pady=5) score_desc = tk.Label(score_frame, text="5.0-6.0分为观望级别,6.0-7.5分为推荐级别,7.5分以上为强烈推荐", font=("微软雅黑", 9), fg="#7f8c8d") score_desc.pack(anchor=tk.W) # 股票池选择 pool_frame = tk.Frame(main_frame) pool_frame.pack(fill=tk.X, pady=15) tk.Label(pool_frame, text="分析股票池:", font=("微软雅黑", 11)).pack(anchor=tk.W) pool_var = tk.StringVar(value="main_board") pools = [ ("主板股票 (稳健型)", "main_board"), ("科创板股票 (成长型)", "kcb"), ("创业板股票 (创新型)", "cyb"), ("全市场股票 (综合型)", "all") ] for text, value in pools: radio = tk.Radiobutton(pool_frame, text=text, variable=pool_var, value=value, font=("微软雅黑", 10)) radio.pack(anchor=tk.W, pady=2) # 按钮区域 button_frame = tk.Frame(main_frame) button_frame.pack(fill=tk.X, pady=20) def start_smart_analysis(): min_score = score_var.get() pool_type = pool_var.get() score_dialog.destroy() self.perform_batch_analysis(min_score, pool_type) start_btn = tk.Button(button_frame, text="开始智能筛选", font=("微软雅黑", 12, "bold"), bg="#27ae60", fg="white", command=start_smart_analysis, cursor="hand2") start_btn.pack(side=tk.LEFT, padx=5) cancel_btn = tk.Button(button_frame, text="取消", font=("微软雅黑", 12), bg="#95a5a6", fg="white", command=score_dialog.destroy, cursor="hand2") cancel_btn.pack(side=tk.RIGHT, padx=5) def perform_batch_analysis(self, min_score, pool_type): """执行批量分析""" # 禁用按钮 self.analyze_btn.config(state="disabled") self.batch_analyze_btn.config(state="disabled") # 在后台线程中执行 analysis_thread = threading.Thread(target=self._batch_analysis_worker, args=(min_score, pool_type)) analysis_thread.daemon = True analysis_thread.start() def _batch_analysis_worker(self, min_score, pool_type): """批量分析工作线程""" try: import time # 步骤1: 获取股票池 self.update_progress("步骤1/4: 获取股票池...") all_stocks = self._get_stock_pool(pool_type) total_stocks = len(all_stocks) self.update_progress(f"获取到{total_stocks}只股票,开始逐个分析...") time.sleep(1) # 步骤2: 逐个分析股票 analyzed_stocks = [] failed_stocks = [] for i, ticker in enumerate(all_stocks): try: progress = (i + 1) / total_stocks * 100 self.update_progress(f"步骤2/4: 分析 {ticker} ({i+1}/{total_stocks}) - {progress:.1f}%") # 检查缓存 cached_result = self.get_stock_from_cache(ticker) if cached_result: analyzed_stocks.append(cached_result) # 输出缓存结果的日志 print(f"📊 {ticker} (缓存) - 价格: ¥{cached_result.get('price', 'N/A'):.2f} | " f"技术分: {cached_result.get('technical_score', 0):.1f} | " f"基本面分: {cached_result.get('fundamental_score', 0):.1f} | " f"综合分: {cached_result.get('total_score', 0):.1f}") continue # 执行分析 stock_result = self._analyze_single_stock(ticker) if stock_result: analyzed_stocks.append(stock_result) # 输出详细的分析日志 name = stock_result.get('name', ticker) price = stock_result.get('price', 0) tech_score = stock_result.get('technical_score', 0) fund_score = stock_result.get('fundamental_score', 0) total_score = stock_result.get('total_score', 0) print(f"✅ {ticker} {name} - 价格: ¥{price:.2f} | " f"技术分: {tech_score:.1f}/10 | " f"基本面分: {fund_score:.1f}/10 | " f"综合分: {total_score:.1f}/10") # 保存到缓存 self.save_stock_to_cache(ticker, stock_result) else: failed_stocks.append(ticker) print(f"❌ {ticker} - 分析失败") # 短暂休息避免API限制 time.sleep(0.1) except Exception as e: print(f"❌ 分析{ticker}失败: {e}") failed_stocks.append(ticker) continue # 步骤3: 按分数排序 self.update_progress("步骤3/4: 按投资分数排序...") time.sleep(0.5) analyzed_stocks.sort(key=lambda x: x['total_score'], reverse=True) # 步骤4: 筛选符合条件的股票 self.update_progress(f"步骤4/4: 筛选分数≥{min_score}的股票...") time.sleep(0.5) qualified_stocks = [stock for stock in analyzed_stocks if stock['total_score'] >= min_score] # 生成筛选报告 self._generate_batch_report(qualified_stocks, analyzed_stocks, failed_stocks, min_score, pool_type) except Exception as e: print(f"❌ 批量分析出错: {e}") self.update_progress(f"❌ 分析失败: {str(e)}") finally: # 重新启用按钮 self.root.after(0, lambda: self.analyze_btn.config(state="normal")) self.root.after(0, lambda: self.batch_analyze_btn.config(state="normal")) self.root.after(0, self.hide_progress) def _get_stock_pool(self, pool_type): """获取指定类型的股票池""" if pool_type == "main_board": return self.get_main_board_stocks_multi_source() elif pool_type == "kcb": return self.get_kcb_stocks_multi_source() elif pool_type == "cyb": return self.get_cyb_stocks_multi_source() elif pool_type == "etf": return self.get_etf_stocks_multi_source() elif pool_type == "all": # 组合所有股票池 all_stocks = [] all_stocks.extend(self.get_main_board_stocks_multi_source()) all_stocks.extend(self.get_kcb_stocks_multi_source()) all_stocks.extend(self.get_cyb_stocks_multi_source()) # ETF可选择性包含,避免数据量过大 # all_stocks.extend(self.get_etf_stocks_multi_source()) return list(set(all_stocks)) # 去重 else: return self.get_main_board_stocks_multi_source() def _analyze_single_stock(self, ticker): """分析单只股票,根据投资期限调整评分权重""" try: # 获取基本信息 stock_info = self.get_stock_info_generic(ticker) if not stock_info: print(f"⚠️ {ticker} - 无法获取股票基本信息") return None stock_name = stock_info.get('name', ticker) print(f"🔍 开始分析 {ticker} {stock_name}") # 获取实时价格 real_price = self.get_stock_price(ticker) if not real_price: print(f"⚠️ {ticker} {stock_name} - 无法获取实时价格") return None # 获取当前选择的投资期限 period = self.period_var.get() print(f"💰 {ticker} {stock_name} - 当前价格: ¥{real_price:.2f} (投资期限: {period})") # 根据投资期限确定评分权重 if period == "短期": tech_weight = 0.7 # 短期更重视技术面 fund_weight = 0.3 # 基本面权重较低 strategy_desc = "技术面主导" elif period == "中期": tech_weight = 0.5 # 中期技术面和基本面平衡 fund_weight = 0.5 strategy_desc = "技术面与基本面平衡" else: # 长期 tech_weight = 0.3 # 长期更重视基本面 fund_weight = 0.7 # 基本面权重较高 strategy_desc = "基本面主导" # 快速计算初步评分用于日志显示 try: # 获取真实数据用于快速评分 technical_data = self.get_real_technical_indicators(ticker) financial_data = self.get_real_financial_data(ticker) # 快速技术面评分 quick_tech_score = 5.0 # 基础分 rsi = technical_data.get('rsi', 50) if rsi < 30: quick_tech_score += 2 elif rsi > 70: quick_tech_score -= 2 elif 40 <= rsi <= 60: quick_tech_score += 1 # 快速基本面评分 quick_fund_score = 5.0 # 基础分 pe_ratio = financial_data.get('pe_ratio', 20) if pe_ratio < 15: quick_fund_score += 2 elif pe_ratio > 30: quick_fund_score -= 2 elif 15 <= pe_ratio <= 25: quick_fund_score += 1 roe = financial_data.get('roe', 10) if roe > 15: quick_fund_score += 1.5 elif roe > 10: quick_fund_score += 0.5 elif roe < 5: quick_fund_score -= 1 # 限制分数范围 quick_tech_score = max(0, min(10, quick_tech_score)) quick_fund_score = max(0, min(10, quick_fund_score)) # 根据投资期限加权计算综合评分 quick_total_score = quick_tech_score * tech_weight + quick_fund_score * fund_weight print(f"⚡ {ticker} {stock_name} - 快速评分({strategy_desc}): 技术{quick_tech_score:.1f}×{tech_weight:.1f} 基本面{quick_fund_score:.1f}×{fund_weight:.1f} 综合{quick_total_score:.1f}/10") except Exception as e: print(f"⚡ {ticker} {stock_name} - 快速评分失败: {e}") # 生成投资建议(包含分数计算) short_term, long_term = self.generate_investment_advice(ticker) # 提取分数(假设建议中包含分数信息) technical_score = self._extract_score_from_advice(short_term, "技术分析") fundamental_score = self._extract_score_from_advice(long_term, "基本面分析") # 根据投资期限加权计算最终评分 total_score = technical_score * tech_weight + fundamental_score * fund_weight # 输出评分详情 print(f"📈 {ticker} {stock_name} - 评分详情({period}投资策略):") print(f" 技术分析: {technical_score:.1f}/10 (权重: {tech_weight:.1f})") print(f" 基本面分析: {fundamental_score:.1f}/10 (权重: {fund_weight:.1f})") print(f" 加权综合得分: {total_score:.1f}/10") return { 'ticker': ticker, 'name': stock_name, 'price': real_price, 'technical_score': technical_score, 'fundamental_score': fundamental_score, 'total_score': total_score, 'short_term': short_term, 'long_term': long_term, 'period': period, 'tech_weight': tech_weight, 'fund_weight': fund_weight } except Exception as e: print(f"❌ 分析{ticker}出错: {e}") return None def _extract_score_from_advice(self, advice_data, analysis_type): """从建议数据中提取分数""" try: # 如果是字典格式的建议 if isinstance(advice_data, dict): recommendation = advice_data.get('recommendation', '').lower() confidence = advice_data.get('confidence', 50) # 基于推荐等级和置信度计算分数(严格10分制) if '强烈' in recommendation or '积极' in recommendation: base_score = 8.5 elif '推荐' in recommendation or '买入' in recommendation or '配置' in recommendation: base_score = 7.0 elif '持有' in recommendation or '适度' in recommendation: base_score = 6.0 elif '观望' in recommendation or '等待' in recommendation: base_score = 4.5 elif '减持' in recommendation or '谨慎' in recommendation: base_score = 3.0 elif '卖出' in recommendation or '回避' in recommendation: base_score = 2.0 else: base_score = 5.0 # 默认中性 # 根据置信度微调分数(确保不超过10分) confidence_factor = confidence / 100.0 # 调整幅度控制在±0.5分内 adjustment = (confidence_factor - 0.5) * 1.0 # 置信度50%为基准 final_score = base_score + adjustment return min(10.0, max(1.0, final_score)) # 如果是文本格式,使用原来的方法 advice_text = str(advice_data) # 查找分数模式 import re if "技术分析评分:" in advice_text: match = re.search(r'技术分析评分:\s*(\d+\.?\d*)', advice_text) if match: return float(match.group(1)) elif "基本面分析评分:" in advice_text: match = re.search(r'基本面分析评分:\s*(\d+\.?\d*)', advice_text) if match: return float(match.group(1)) # 如果没有找到明确分数,根据建议等级估算 if "强烈推荐" in advice_text or "5星" in advice_text: return 8.5 elif "推荐" in advice_text or "4星" in advice_text: return 7.0 elif "中性" in advice_text or "3星" in advice_text: return 5.5 elif "谨慎" in advice_text or "2星" in advice_text: return 3.5 else: return 2.0 except: return 5.0 # 默认分数 def _generate_batch_report(self, qualified_stocks, all_analyzed, failed_stocks, min_score, pool_type): """生成批量分析报告""" pool_names = { "main_board": "主板股票", "kcb": "科创板股票", "cyb": "创业板股票", "all": "全市场股票" } report = f""" ╔══════════════════════════════════════════════════════════════════════════════════╗ ║ 🎯 智能股票筛选报告 ║ ╚══════════════════════════════════════════════════════════════════════════════════╝ 📊 筛选统计 ───────────────────────────────────────────────────────────────────────────────── • 股票池类型: {pool_names.get(pool_type, pool_type)} • 分析总数: {len(all_analyzed)}只 • 筛选标准: 投资分数 ≥ {min_score}分 • 符合条件: {len(qualified_stocks)}只 • 筛选成功率: {len(qualified_stocks)/len(all_analyzed)*100:.1f}% • 分析失败: {len(failed_stocks)}只 🏆 符合条件的优质股票 (按分数排序) ───────────────────────────────────────────────────────────────────────────────── """ for i, stock in enumerate(qualified_stocks[:20], 1): # 显示前20只 stars = "⭐" * min(5, int(stock['total_score'] / 2)) report += f"{i:2d}. {stock['ticker']} ({stock['name']})\n" report += f" 💰 当前价格: ¥{stock['price']:.2f}\n" report += f" 📊 综合评分: {stock['total_score']:.1f}分 {stars}\n" report += f" 📈 技术分析: {stock['technical_score']:.1f}分\n" report += f" 💼 基本面分析: {stock['fundamental_score']:.1f}分\n" report += " " + "─" * 50 + "\n" if len(qualified_stocks) > 20: report += f"\n... 还有 {len(qualified_stocks) - 20} 只股票符合条件\n" report += f""" 📈 分数分布统计 ───────────────────────────────────────────────────────────────────────────────── • 9.0分以上 (超级推荐): {len([s for s in all_analyzed if s['total_score'] >= 9.0])}只 • 7.5-9.0分 (强烈推荐): {len([s for s in all_analyzed if 7.5 <= s['total_score'] < 9.0])}只 • 6.0-7.5分 (推荐): {len([s for s in all_analyzed if 6.0 <= s['total_score'] < 7.5])}只 • 4.5-6.0分 (中性): {len([s for s in all_analyzed if 4.5 <= s['total_score'] < 6.0])}只 • 4.5分以下 (不推荐): {len([s for s in all_analyzed if s['total_score'] < 4.5])}只 💡 投资建议 ───────────────────────────────────────────────────────────────────────────────── 基于当前市场分析,建议重点关注评分在7.5分以上的股票, 这些股票在技术面和基本面都表现优秀,具有较好的投资价值。 ⚠️ 风险提示: 股市有风险,投资需谨慎。以上分析仅供参考,请结合个人情况做出投资决策。 生成时间: {__import__('time').strftime('%Y-%m-%d %H:%M:%S')} """ # 在GUI中显示报告 self.root.after(0, lambda: self._show_batch_report(report)) # 保存当前缓存 self.save_daily_cache() def _show_batch_report(self, report): """在GUI中显示批量分析报告""" # 清空现有文本 self.technical_text.delete(1.0, tk.END) self.fundamental_text.delete(1.0, tk.END) # 在技术分析区域显示报告 self.technical_text.insert(tk.END, report) # 在基本面分析区域显示简要总结 summary = "智能筛选已完成!\n\n详细报告请查看技术分析页面。\n\n系统已为您筛选出符合投资条件的优质股票,建议重点关注评分较高的标的。" self.fundamental_text.insert(tk.END, summary) # 切换到技术分析页面显示结果 self.notebook.select(0) # 更新状态 self.status_var.set("✅ 智能股票筛选完成") def format_technical_analysis_from_data(self, ticker, tech_data): """从技术数据生成技术分析报告""" analysis = f""" 📊 技术分析报告 - {ticker} {'='*50} 💰 价格信息: 当前价格: ¥{tech_data['current_price']:.2f} 📈 移动平均线: MA5: ¥{tech_data['ma5']:.2f} MA10: ¥{tech_data['ma10']:.2f} MA20: ¥{tech_data['ma20']:.2f} MA60: ¥{tech_data['ma60']:.2f} 📊 技术指标: RSI: {tech_data['rsi']:.1f} ({tech_data['rsi_status']}) MACD: {tech_data['macd']:.4f} 信号线: {tech_data['signal']:.4f} 成交量比率: {tech_data['volume_ratio']:.2f} 🎯 趋势分析: 价格趋势: {tech_data['momentum']} 均线分析: {"✅ 多头排列" if tech_data['current_price'] > tech_data['ma5'] > tech_data['ma20'] else "⚠️ 空头排列" if tech_data['current_price'] < tech_data['ma5'] < tech_data['ma20'] else "🔄 震荡整理"} RSI分析: {"📈 超买区域,注意回调" if tech_data['rsi'] > 70 else "📉 超卖区域,关注反弹" if tech_data['rsi'] < 30 else "⚖️ 正常区间"} MACD分析: {"🟢 金叉信号" if tech_data['macd'] > tech_data['signal'] and tech_data['macd'] > 0 else "🔴 死叉信号" if tech_data['macd'] < tech_data['signal'] and tech_data['macd'] < 0 else "🟡 震荡信号"} 📝 技术面总结: 基于当前技术指标,该股票呈现{tech_data['momentum']}态势。 RSI处于{tech_data['rsi_status']}状态,建议结合基本面综合判断。 ⚠️ 风险提示: 技术分析基于历史数据,不构成投资建议。 """ return analysis def format_fundamental_analysis_from_data(self, ticker, fund_data): """从基本面数据生成基本面分析报告""" analysis = f""" 🏛️ 基本面分析报告 - {ticker} {'='*50} 🏢 基本信息: 所属行业: {fund_data['industry']} 💼 估值指标: 市盈率(PE): {fund_data['pe_ratio']:.2f} 市净率(PB): {fund_data['pb_ratio']:.2f} 📊 盈利能力: 净资产收益率(ROE): {fund_data['roe']:.2f}% 毛利率: {fund_data['gross_margin']:.2f}% 📈 成长性: 营收增长率: {fund_data['revenue_growth']:.2f}% 利润增长率: {fund_data['profit_growth']:.2f}% 💰 财务健康: 负债率: {fund_data['debt_ratio']:.2f}% 流动比率: {fund_data['current_ratio']:.2f} 🎯 估值分析: PE估值: {"✅ 合理" if 10 <= fund_data['pe_ratio'] <= 25 else "⚠️ 偏高" if fund_data['pe_ratio'] > 25 else "📉 偏低"} PB估值: {"✅ 合理" if 1 <= fund_data['pb_ratio'] <= 3 else "⚠️ 偏高" if fund_data['pb_ratio'] > 3 else "📉 偏低"} 📊 盈利质量: ROE水平: {"🌟 优秀" if fund_data['roe'] > 15 else "✅ 良好" if fund_data['roe'] > 10 else "⚠️ 一般"} 🚀 成长前景: 收入增长: {"🚀 强劲" if fund_data['revenue_growth'] > 20 else "✅ 稳健" if fund_data['revenue_growth'] > 10 else "📉 放缓" if fund_data['revenue_growth'] > 0 else "⚠️ 下滑"} 🛡️ 财务稳健性: 负债水平: {"✅ 健康" if fund_data['debt_ratio'] < 50 else "⚠️ 偏高"} 流动性: {"✅ 充足" if fund_data['current_ratio'] > 1.5 else "⚠️ 紧张"} 📝 基本面总结: 该股票属于{fund_data['industry']}行业,当前估值水平 {"合理" if 10 <= fund_data['pe_ratio'] <= 25 else "偏高" if fund_data['pe_ratio'] > 25 else "偏低"}, {"盈利能力强劲" if fund_data['roe'] > 15 else "盈利能力一般"}, {"成长性良好" if fund_data['revenue_growth'] > 10 else "成长性放缓"}。 ⚠️ 投资提示: 基本面分析基于模拟数据,实际投资请参考最新财报。 """ return analysis def generate_overview_from_data(self, ticker, stock_info, tech_data, fund_data, final_score): """从数据生成概览""" overview = f""" 📋 股票概览 - {stock_info['name']} ({ticker}) {'='*60} 💰 基本信息: 股票名称: {stock_info['name']} 股票代码: {ticker} 所属行业: {fund_data['industry']} 当前价格: ¥{tech_data['current_price']:.2f} 概念标签: {stock_info.get('concept', 'A股')} ⭐ 综合评分: {final_score:.1f}/10 {"🌟 优秀投资标的" if final_score >= 8 else "✅ 良好投资选择" if final_score >= 7 else "⚖️ 中性评价" if final_score >= 6 else "⚠️ 需谨慎考虑" if final_score >= 5 else "🔴 高风险标的"} 📊 关键指标概览: 技术面: • RSI: {tech_data['rsi']:.1f} ({tech_data['rsi_status']}) • 趋势: {tech_data['momentum']} • 均线: {"多头排列" if tech_data['current_price'] > tech_data['ma20'] else "空头排列"} 基本面: • PE比率: {fund_data['pe_ratio']:.1f} • ROE: {fund_data['roe']:.1f}% • 营收增长: {fund_data['revenue_growth']:.1f}% 🎯 投资亮点: {"✅ 技术面向好,趋势向上" if tech_data['momentum'] == "上升趋势" else "⚠️ 技术面偏弱,需关注支撑" if tech_data['momentum'] == "下降趋势" else "🔄 技术面震荡,等待方向选择"} {"✅ 估值合理,具备投资价值" if 10 <= fund_data['pe_ratio'] <= 25 else "⚠️ 估值偏高,需谨慎" if fund_data['pe_ratio'] > 25 else "📉 估值偏低,关注基本面"} {"✅ 盈利能力强,ROE表现优秀" if fund_data['roe'] > 15 else "⚖️ 盈利能力中等" if fund_data['roe'] > 10 else "⚠️ 盈利能力有待提升"} 📈 近期表现: 价格水平: {"相对高位" if tech_data['rsi'] > 60 else "相对低位" if tech_data['rsi'] < 40 else "中性区间"} 成交活跃度: {"活跃" if tech_data['volume_ratio'] > 1.5 else "清淡" if tech_data['volume_ratio'] < 0.8 else "正常"} ⚠️ 风险提示: • 本分析基于模拟数据,仅供参考 • 股市有风险,投资需谨慎 • 建议结合最新资讯和财务数据综合判断 📝 分析时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} """ return overview def format_investment_advice_from_data(self, short_advice, long_advice, ticker, final_score): """从建议数据生成投资建议报告""" recommendation = f""" 💡 投资建议报告 - {ticker} {'='*60} ⭐ 综合评分: {final_score:.1f}/10 📅 短期建议 (1-7天): 推荐操作: {short_advice.get('advice', '持有观望')} 主要逻辑: {"• 技术指标显示超卖,短期有反弹需求" if 'RSI' in str(short_advice) and 'RSI' in str(short_advice) and '超卖' in str(short_advice) else ""} {"• MACD金叉形成,短期趋势向好" if 'MACD' in str(short_advice) and '金叉' in str(short_advice) else ""} {"• 均线支撑有效,短期持有" if '均线' in str(short_advice) and '支撑' in str(short_advice) else ""} 📈 长期建议 (30-90天): 推荐操作: {long_advice.get('advice', '长期持有')} 主要逻辑: {"• 基本面稳健,具备长期投资价值" if 'ROE' in str(long_advice) or '基本面' in str(long_advice) else ""} {"• 估值合理,安全边际充足" if 'PE' in str(long_advice) or '估值' in str(long_advice) else ""} {"• 行业前景良好,长期看好" if '行业' in str(long_advice) else ""} 🎯 操作建议: {"🟢 积极买入: 技术面和基本面均支持,建议积极参与" if final_score >= 8 else ""} {"🟡 适度配置: 整体表现良好,可适度配置" if 7 <= final_score < 8 else ""} {"⚖️ 谨慎持有: 中性评价,建议谨慎操作" if 6 <= final_score < 7 else ""} {"⚠️ 观望为主: 风险较高,建议观望" if 5 <= final_score < 6 else ""} {"🔴 规避风险: 评分偏低,建议规避" if final_score < 5 else ""} 💰 仓位建议: {"• 核心持仓: 可占总仓位5-8%" if final_score >= 8 else ""} {"• 一般配置: 可占总仓位3-5%" if 7 <= final_score < 8 else ""} {"• 少量持有: 可占总仓位1-3%" if 6 <= final_score < 7 else ""} {"• 观望等待: 暂不建议配置" if final_score < 6 else ""} 🛡️ 风险控制: • 设置止损位: 建议以MA20或重要支撑位为准 • 分批建仓: 建议分2-3次建仓,降低风险 • 定期复评: 每月重新评估一次 ⚠️ 重要声明: 本投资建议基于当前技术分析和基本面模拟数据, 不构成具体投资建议。投资者应当根据自身风险承受能力、 投资目标和财务状况做出独立的投资决策。 📞 如需更详细的分析,建议咨询专业投资顾问。 """ return recommendation def main(): """主函数""" root = tk.Tk() app = AShareAnalyzerGUI(root) # 设置窗口居中 root.update_idletasks() width = root.winfo_width() height = root.winfo_height() x = (root.winfo_screenwidth() // 2) - (width // 2) y = (root.winfo_screenheight() // 2) - (height // 2) root.geometry('{}x{}+{}+{}'.format(width, height, x, y)) # 设置窗口关闭事件 def on_closing(): root.destroy() # 直接关闭,不显示确认对话框 root.protocol("WM_DELETE_WINDOW", on_closing) print("A股智能分析系统GUI启动成功!") print("支持股票代码: 688981, 600036, 000002, 300750, 600519等") print("请在GUI界面中输入股票代码进行分析") # 启动GUI root.mainloop() if __name__ == "__main__": main()