#!/usr/bin/env python3 """ 按本周总涨幅排名东财概念板块 """ import sys from pathlib import Path import pandas as pd from datetime import datetime, timedelta # 添加项目根目录到路径 current_dir = Path(__file__).parent sys.path.insert(0, str(current_dir)) from src.data.tushare_fetcher import TushareFetcher from loguru import logger def get_this_week_dates(): """获取本周的交易日期(周一到周五)""" today = datetime.now() # 获取本周一 monday = today - timedelta(days=today.weekday()) # 获取本周五(或今天如果还没到周五) friday = monday + timedelta(days=4) if friday > today: friday = today # 生成本周所有交易日 dates = [] current = monday while current <= friday: if current.weekday() < 5: # 周一到周五 dates.append(current.strftime('%Y%m%d')) current += timedelta(days=1) return dates def calculate_weekly_concept_performance(fetcher: TushareFetcher): """计算概念板块本周总涨幅""" try: if not fetcher.pro: logger.error("需要Tushare Pro权限") return None logger.info("🚀 计算概念板块本周总涨幅排名...") # 获取本周交易日 week_dates = get_this_week_dates() logger.info(f"本周交易日: {week_dates}") if len(week_dates) < 2: logger.warning("本周交易日不足,无法计算周涨幅") return None start_date = week_dates[0] # 周一 end_date = week_dates[-1] # 最新交易日 logger.info(f"分析周期: {start_date} 到 {end_date}") # 获取周一的概念板块数据(基准) logger.info(f"获取 {start_date} 的概念数据作为基准...") start_concepts = fetcher.pro.dc_index(trade_date=start_date) # 获取最新交易日的概念板块数据 logger.info(f"获取 {end_date} 的概念数据...") end_concepts = fetcher.pro.dc_index(trade_date=end_date) if start_concepts.empty or end_concepts.empty: logger.error("无法获取概念板块数据") return None logger.info(f"周一概念数据: {len(start_concepts)} 个") logger.info(f"最新概念数据: {len(end_concepts)} 个") # 计算本周涨幅 weekly_performance = [] # 以最新数据为准,匹配周一数据 for _, end_concept in end_concepts.iterrows(): ts_code = end_concept['ts_code'] name = end_concept['name'] end_mv = end_concept['total_mv'] # 查找对应的周一数据 start_data = start_concepts[start_concepts['ts_code'] == ts_code] if not start_data.empty: start_mv = start_data.iloc[0]['total_mv'] # 计算本周总涨幅 if start_mv > 0: weekly_change = (end_mv - start_mv) / start_mv * 100 weekly_performance.append({ 'ts_code': ts_code, 'name': name, 'weekly_change': weekly_change, 'start_mv': start_mv, 'end_mv': end_mv, 'latest_daily_change': end_concept['pct_change'], 'up_num': end_concept.get('up_num', 0), 'down_num': end_concept.get('down_num', 0) }) if not weekly_performance: logger.error("无法计算概念板块周涨幅") return None # 转换为DataFrame并按周涨幅排序 df_weekly = pd.DataFrame(weekly_performance) df_weekly = df_weekly.sort_values('weekly_change', ascending=False) logger.info(f"成功计算 {len(df_weekly)} 个概念板块的本周涨幅") return df_weekly except Exception as e: logger.error(f"计算概念板块周涨幅失败: {e}") return None def display_weekly_ranking(df_weekly: pd.DataFrame): """显示本周涨幅排名""" if df_weekly is None or df_weekly.empty: logger.error("无数据可显示") return print("\n" + "="*100) print("📈 东财概念板块本周涨幅排行榜") print("="*100) print(f"{'排名':<4} {'概念名称':<25} {'本周涨幅':<12} {'今日涨幅':<12} {'上涨股数':<8} {'下跌股数':<8} {'概念代码':<15}") print("-" * 100) for i, (_, concept) in enumerate(df_weekly.head(30).iterrows()): rank = i + 1 name = concept['name'][:23] + '..' if len(concept['name']) > 23 else concept['name'] weekly_chg = f"{concept['weekly_change']:+.2f}%" daily_chg = f"{concept['latest_daily_change']:+.2f}%" up_num = f"{concept['up_num']:.0f}" down_num = f"{concept['down_num']:.0f}" ts_code = concept['ts_code'] print(f"{rank:<4} {name:<25} {weekly_chg:<12} {daily_chg:<12} {up_num:<8} {down_num:<8} {ts_code:<15}") # 强势概念TOP15 print(f"\n🚀 本周强势概念板块TOP15:") for i, (_, concept) in enumerate(df_weekly.head(15).iterrows()): print(f" {i+1:2d}. {concept['name']}: {concept['weekly_change']:+.2f}% (今日{concept['latest_daily_change']:+.2f}%)") # 弱势概念TOP10 print(f"\n📉 本周弱势概念板块TOP10:") weak_concepts = df_weekly.tail(10).iloc[::-1] # 反转顺序 for i, (_, concept) in enumerate(weak_concepts.iterrows()): print(f" {i+1:2d}. {concept['name']}: {concept['weekly_change']:+.2f}% (今日{concept['latest_daily_change']:+.2f}%)") # 统计分析 print(f"\n📊 本周概念板块统计:") total_concepts = len(df_weekly) positive_concepts = len(df_weekly[df_weekly['weekly_change'] > 0]) negative_concepts = len(df_weekly[df_weekly['weekly_change'] < 0]) print(f" 总概念数量: {total_concepts}") print(f" 上涨概念: {positive_concepts} ({positive_concepts/total_concepts*100:.1f}%)") print(f" 下跌概念: {negative_concepts} ({negative_concepts/total_concepts*100:.1f}%)") print(f" 平均涨幅: {df_weekly['weekly_change'].mean():+.2f}%") print(f" 涨幅中位数: {df_weekly['weekly_change'].median():+.2f}%") return df_weekly def analyze_top_concepts_detail(fetcher: TushareFetcher, df_weekly: pd.DataFrame, top_n=5): """分析TOP概念的详细趋势""" if df_weekly is None or df_weekly.empty: return logger.info(f"详细分析TOP{top_n}强势概念...") print(f"\n" + "="*80) print(f"📊 TOP{top_n}强势概念详细分析") print("="*80) week_dates = get_this_week_dates() for i, (_, concept) in enumerate(df_weekly.head(top_n).iterrows()): concept_code = concept['ts_code'] concept_name = concept['name'] print(f"\n📈 {i+1}. {concept_name} ({concept_code})") print(f" 本周总涨幅: {concept['weekly_change']:+.2f}%") # 获取每日详细数据 daily_data = [] for date in week_dates: try: daily_concept = fetcher.pro.dc_index( trade_date=date, ts_code=concept_code ) if not daily_concept.empty: daily_data.append({ 'date': date, 'pct_change': daily_concept.iloc[0]['pct_change'], 'total_mv': daily_concept.iloc[0]['total_mv'], 'up_num': daily_concept.iloc[0].get('up_num', 0), 'down_num': daily_concept.iloc[0].get('down_num', 0) }) except Exception as e: logger.debug(f"获取 {concept_code} 在 {date} 的数据失败: {e}") continue # 显示每日走势 if daily_data: print(f" 每日走势:") for data in daily_data: print(f" {data['date']}: {data['pct_change']:+6.2f}% (上涨{data['up_num']:.0f}只/下跌{data['down_num']:.0f}只)") def main(): """主函数""" logger.info("🚀 开始计算东财概念板块本周涨幅排名...") # 初始化Tushare数据获取器 token = "0ed6419a00d8923dc19c0b58fc92d94c9a0696949ab91a13aa58a0cc" fetcher = TushareFetcher(token=token) # 1. 计算本周涨幅 df_weekly = calculate_weekly_concept_performance(fetcher) # 2. 显示排名 if df_weekly is not None: display_weekly_ranking(df_weekly) # 3. 详细分析TOP5概念 analyze_top_concepts_detail(fetcher, df_weekly, top_n=5) logger.info("✅ 分析完成!") if __name__ == "__main__": main()