258 lines
9.9 KiB
Python
258 lines
9.9 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
测试使用Tushare找出本周强势板块
|
||
"""
|
||
|
||
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
|
||
|
||
return monday.strftime('%Y%m%d'), friday.strftime('%Y%m%d')
|
||
|
||
|
||
def analyze_sector_performance(fetcher: TushareFetcher):
|
||
"""分析行业板块表现"""
|
||
try:
|
||
logger.info("开始分析本周强势板块...")
|
||
|
||
# 获取本周日期范围
|
||
start_date, end_date = get_this_week_dates()
|
||
logger.info(f"分析时间范围: {start_date} 到 {end_date}")
|
||
|
||
if not fetcher.pro:
|
||
logger.error("需要Tushare Pro权限才能获取行业数据")
|
||
return
|
||
|
||
# 直接使用概念和行业数据分析
|
||
logger.info("通过个股数据分析板块表现...")
|
||
|
||
# 获取股票基本信息(包含行业分类)
|
||
try:
|
||
stock_basic = fetcher.pro.stock_basic(
|
||
exchange='',
|
||
list_status='L',
|
||
fields='ts_code,symbol,name,area,industry,market'
|
||
)
|
||
logger.info(f"获取到 {len(stock_basic)} 只股票基本信息")
|
||
|
||
except Exception as e:
|
||
logger.error(f"获取股票基本信息失败: {e}")
|
||
return
|
||
|
||
# 按行业分组分析
|
||
industry_performance = {}
|
||
|
||
# 取样分析(避免API请求过多)
|
||
sample_stocks = stock_basic.sample(min(200, len(stock_basic))) # 随机取200只股票
|
||
logger.info(f"随机抽样 {len(sample_stocks)} 只股票进行分析...")
|
||
|
||
for _, stock in sample_stocks.iterrows():
|
||
ts_code = stock['ts_code']
|
||
industry = stock['industry']
|
||
stock_name = stock['name']
|
||
|
||
if pd.isna(industry) or industry == '':
|
||
continue
|
||
|
||
try:
|
||
# 获取个股本周数据
|
||
stock_data = fetcher.pro.daily(
|
||
ts_code=ts_code,
|
||
start_date=start_date,
|
||
end_date=end_date
|
||
)
|
||
|
||
if not stock_data.empty and len(stock_data) >= 1:
|
||
# 获取最新价格和前一个交易日价格
|
||
if len(stock_data) >= 2:
|
||
latest_close = stock_data.iloc[0]['close']
|
||
prev_close = stock_data.iloc[1]['close']
|
||
else:
|
||
# 如果只有一天数据,获取开盘价作为对比
|
||
latest_close = stock_data.iloc[0]['close']
|
||
prev_close = stock_data.iloc[0]['open']
|
||
|
||
if prev_close > 0:
|
||
change_pct = (latest_close - prev_close) / prev_close * 100
|
||
|
||
# 按行业归类
|
||
if industry not in industry_performance:
|
||
industry_performance[industry] = {
|
||
'stock_changes': [],
|
||
'stock_count': 0,
|
||
'stock_names': []
|
||
}
|
||
|
||
industry_performance[industry]['stock_changes'].append(change_pct)
|
||
industry_performance[industry]['stock_count'] += 1
|
||
industry_performance[industry]['stock_names'].append(f"{stock_name}({change_pct:+.2f}%)")
|
||
|
||
logger.debug(f"{stock_name} ({industry}): {change_pct:+.2f}%")
|
||
|
||
except Exception as e:
|
||
logger.debug(f"分析个股 {ts_code} 失败: {e}")
|
||
continue
|
||
|
||
# 计算各行业平均表现
|
||
industry_results = []
|
||
for industry, data in industry_performance.items():
|
||
if data['stock_count'] >= 3: # 至少要有3只股票才参与排名
|
||
avg_change = sum(data['stock_changes']) / len(data['stock_changes'])
|
||
industry_results.append({
|
||
'industry_name': industry,
|
||
'avg_change_pct': avg_change,
|
||
'stock_count': data['stock_count'],
|
||
'best_stocks': sorted(data['stock_names'], key=lambda x: float(x.split('(')[1].split('%')[0]), reverse=True)[:3]
|
||
})
|
||
|
||
# 3. 分析结果
|
||
if industry_results:
|
||
df_performance = pd.DataFrame(industry_results)
|
||
|
||
# 按平均涨跌幅排序
|
||
df_performance = df_performance.sort_values('avg_change_pct', ascending=False)
|
||
|
||
logger.info("\n" + "="*80)
|
||
logger.info("📈 本周强势板块排行榜(基于抽样股票分析)")
|
||
logger.info("="*80)
|
||
|
||
print(f"{'排名':<4} {'行业名称':<20} {'平均涨跌幅':<12} {'样本数量':<8} {'代表个股':<30}")
|
||
print("-" * 80)
|
||
|
||
for i, (_, row) in enumerate(df_performance.head(15).iterrows()):
|
||
rank = i + 1
|
||
industry_name = row['industry_name'][:18] + '..' if len(row['industry_name']) > 18 else row['industry_name']
|
||
change_pct = f"{row['avg_change_pct']:+.2f}%"
|
||
stock_count = f"{row['stock_count']}只"
|
||
best_stock = row['best_stocks'][0] if row['best_stocks'] else "无数据"
|
||
|
||
print(f"{rank:<4} {industry_name:<20} {change_pct:<12} {stock_count:<8} {best_stock:<30}")
|
||
|
||
# 输出强势板块(涨幅前5)
|
||
top_sectors = df_performance.head(5)
|
||
logger.info(f"\n🚀 本周TOP5强势板块:")
|
||
for i, (_, sector) in enumerate(top_sectors.iterrows()):
|
||
logger.info(f" {i+1}. {sector['industry_name']}: {sector['avg_change_pct']:+.2f}% (样本{sector['stock_count']}只)")
|
||
for j, stock in enumerate(sector['best_stocks'][:3]):
|
||
logger.info(f" └─ {stock}")
|
||
|
||
# 输出弱势板块(跌幅前5)
|
||
weak_sectors = df_performance.tail(5)
|
||
logger.info(f"\n📉 本周TOP5弱势板块:")
|
||
for i, (_, sector) in enumerate(weak_sectors.iterrows()):
|
||
logger.info(f" {i+1}. {sector['industry_name']}: {sector['avg_change_pct']:+.2f}% (样本{sector['stock_count']}只)")
|
||
|
||
else:
|
||
logger.warning("未获取到有效的行业表现数据")
|
||
|
||
except Exception as e:
|
||
logger.error(f"分析行业表现失败: {e}")
|
||
|
||
|
||
def get_sector_top_stocks(fetcher: TushareFetcher, industry_code: str, industry_name: str, limit: int = 5):
|
||
"""获取指定板块的强势个股"""
|
||
try:
|
||
logger.info(f"获取 {industry_name} 板块的强势个股...")
|
||
|
||
if not fetcher.pro:
|
||
return
|
||
|
||
# 获取该行业的成分股
|
||
try:
|
||
constituents = fetcher.pro.index_member(index_code=industry_code)
|
||
if constituents.empty:
|
||
logger.warning(f"{industry_name} 行业无成分股数据")
|
||
return
|
||
|
||
stock_codes = constituents['con_code'].tolist()[:20] # 取前20只股票测试
|
||
logger.info(f"{industry_name} 行业共 {len(constituents)} 只成分股,分析前 {len(stock_codes)} 只")
|
||
|
||
except Exception as e:
|
||
logger.error(f"获取 {industry_name} 成分股失败: {e}")
|
||
return
|
||
|
||
# 获取本周日期
|
||
start_date, end_date = get_this_week_dates()
|
||
|
||
# 分析各股票本周表现
|
||
stock_performance = []
|
||
|
||
for stock_code in stock_codes[:10]: # 限制分析数量
|
||
try:
|
||
# 获取个股本周数据
|
||
stock_data = fetcher.pro.daily(
|
||
ts_code=stock_code,
|
||
start_date=start_date,
|
||
end_date=end_date
|
||
)
|
||
|
||
if not stock_data.empty and len(stock_data) >= 2:
|
||
latest_close = stock_data.iloc[0]['close']
|
||
week_start_close = stock_data.iloc[-1]['close']
|
||
week_change = (latest_close - week_start_close) / week_start_close * 100
|
||
|
||
# 获取股票名称
|
||
stock_name = fetcher.get_stock_name(stock_code.split('.')[0])
|
||
|
||
stock_performance.append({
|
||
'stock_code': stock_code,
|
||
'stock_name': stock_name,
|
||
'week_change_pct': week_change,
|
||
'latest_close': latest_close
|
||
})
|
||
|
||
except Exception as e:
|
||
logger.debug(f"分析个股 {stock_code} 失败: {e}")
|
||
continue
|
||
|
||
# 输出该板块强势个股
|
||
if stock_performance:
|
||
df_stocks = pd.DataFrame(stock_performance)
|
||
df_stocks = df_stocks.sort_values('week_change_pct', ascending=False)
|
||
|
||
logger.info(f"\n📊 {industry_name} 板块强势个股 TOP{limit}:")
|
||
for _, stock in df_stocks.head(limit).iterrows():
|
||
logger.info(f" {stock['stock_name']} ({stock['stock_code']}): {stock['week_change_pct']:+.2f}%")
|
||
|
||
except Exception as e:
|
||
logger.error(f"获取 {industry_name} 板块个股失败: {e}")
|
||
|
||
|
||
def main():
|
||
"""主函数"""
|
||
logger.info("开始测试Tushare获取本周强势板块...")
|
||
|
||
# 初始化Tushare数据获取器
|
||
token = "0ed6419a00d8923dc19c0b58fc92d94c9a0696949ab91a13aa58a0cc"
|
||
fetcher = TushareFetcher(token=token)
|
||
|
||
# 分析板块表现
|
||
analyze_sector_performance(fetcher)
|
||
|
||
# 分析某个强势板块的个股(示例)
|
||
# get_sector_top_stocks(fetcher, "801010.SI", "农林牧渔")
|
||
|
||
logger.info("测试完成!")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main() |