trading.ai/test_weekly_concept_ranking.py
2025-09-23 16:12:18 +08:00

244 lines
8.5 KiB
Python

#!/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()