318 lines
12 KiB
Python
318 lines
12 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 explore_ths_interfaces(fetcher: TushareFetcher):
|
||
"""探索同花顺相关接口"""
|
||
try:
|
||
if not fetcher.pro:
|
||
logger.error("需要Tushare Pro权限")
|
||
return
|
||
|
||
logger.info("🔍 探索同花顺概念板块相关接口...")
|
||
|
||
# 1. 获取同花顺概念指数列表
|
||
try:
|
||
logger.info("1. 获取同花顺概念指数列表...")
|
||
ths_index = fetcher.pro.ths_index(exchange='A', type='N')
|
||
logger.info(f"获取到 {len(ths_index)} 个同花顺概念指数")
|
||
|
||
if not ths_index.empty:
|
||
print("\n📋 同花顺概念指数(前20个):")
|
||
print(f"{'代码':<15} {'名称':<30} {'发布日期':<12}")
|
||
print("-" * 60)
|
||
for _, index in ths_index.head(20).iterrows():
|
||
code = index['ts_code']
|
||
name = index['name'][:28] + '..' if len(index['name']) > 28 else index['name']
|
||
pub_date = index.get('list_date', 'N/A')
|
||
print(f"{code:<15} {name:<30} {pub_date:<12}")
|
||
|
||
return ths_index
|
||
|
||
except Exception as e:
|
||
logger.error(f"获取同花顺概念指数失败: {e}")
|
||
|
||
# 2. 尝试获取同花顺概念成分股
|
||
try:
|
||
logger.info("\n2. 测试获取同花顺概念成分股...")
|
||
# 尝试获取一个概念的成分股
|
||
sample_concept = "885311.TI" # 智能电网
|
||
ths_member = fetcher.pro.ths_member(ts_code=sample_concept)
|
||
logger.info(f"获取智能电网概念成分股: {len(ths_member)} 只")
|
||
|
||
if not ths_member.empty:
|
||
print(f"\n📊 智能电网概念成分股(前10只):")
|
||
for _, stock in ths_member.head(10).iterrows():
|
||
print(f" {stock['code']}: {stock.get('name', 'N/A')}")
|
||
|
||
except Exception as e:
|
||
logger.error(f"获取同花顺概念成分股失败: {e}")
|
||
|
||
# 3. 尝试获取同花顺概念日行情
|
||
try:
|
||
logger.info("\n3. 测试获取同花顺概念日行情...")
|
||
today = datetime.now().strftime('%Y%m%d')
|
||
yesterday = (datetime.now() - timedelta(days=1)).strftime('%Y%m%d')
|
||
|
||
ths_daily = fetcher.pro.ths_daily(
|
||
ts_code="885311.TI", # 智能电网
|
||
start_date=yesterday,
|
||
end_date=today
|
||
)
|
||
logger.info(f"获取智能电网概念日行情: {len(ths_daily)} 条记录")
|
||
|
||
if not ths_daily.empty:
|
||
print(f"\n📈 智能电网概念近期行情:")
|
||
print(ths_daily[['trade_date', 'close', 'pct_chg', 'vol']].head())
|
||
|
||
except Exception as e:
|
||
logger.error(f"获取同花顺概念日行情失败: {e}")
|
||
|
||
# 4. 探索其他可能的同花顺接口
|
||
try:
|
||
logger.info("\n4. 探索同花顺行业分类...")
|
||
ths_industry = fetcher.pro.ths_index(exchange='A', type='I')
|
||
logger.info(f"获取到 {len(ths_industry)} 个同花顺行业指数")
|
||
|
||
if not ths_industry.empty:
|
||
print(f"\n📊 同花顺行业指数(前10个):")
|
||
for _, index in ths_industry.head(10).iterrows():
|
||
print(f" {index['ts_code']}: {index['name']}")
|
||
|
||
except Exception as e:
|
||
logger.error(f"获取同花顺行业分类失败: {e}")
|
||
|
||
except Exception as e:
|
||
logger.error(f"探索同花顺接口失败: {e}")
|
||
|
||
|
||
def get_ths_concept_7day_ranking(fetcher: TushareFetcher):
|
||
"""获取同花顺概念板块过去7个交易日排名"""
|
||
try:
|
||
if not fetcher.pro:
|
||
logger.error("需要Tushare Pro权限")
|
||
return
|
||
|
||
logger.info("📊 计算同花顺概念板块过去7个交易日涨幅...")
|
||
|
||
# 获取同花顺概念指数列表
|
||
ths_concepts = fetcher.pro.ths_index(exchange='A', type='N')
|
||
if ths_concepts.empty:
|
||
logger.error("未获取到同花顺概念指数")
|
||
return
|
||
|
||
# 过滤掉指数型板块,只保留真正的概念板块
|
||
logger.info("过滤指数型板块...")
|
||
index_keywords = [
|
||
'成份股', '样本股', '成分股', '50', '100', '300', '500', '1000',
|
||
'上证', '深证', '中证', '创业板', '科创板', '北证',
|
||
'ETF', '指数', 'Index', '基准'
|
||
]
|
||
|
||
def is_concept_plate(name: str) -> bool:
|
||
"""判断是否为真正的概念板块"""
|
||
name_lower = name.lower()
|
||
for keyword in index_keywords:
|
||
if keyword.lower() in name_lower:
|
||
return False
|
||
return True
|
||
|
||
# 过滤数据
|
||
original_count = len(ths_concepts)
|
||
ths_concepts = ths_concepts[ths_concepts['name'].apply(is_concept_plate)]
|
||
filtered_count = len(ths_concepts)
|
||
|
||
logger.info(f"过滤结果: {original_count} -> {filtered_count} 个概念板块(剔除{original_count - filtered_count}个指数型板块)")
|
||
|
||
# 获取过去7个交易日
|
||
trading_dates = []
|
||
current = datetime.now()
|
||
|
||
while len(trading_dates) < 7:
|
||
if current.weekday() < 5: # 周一到周五
|
||
trading_dates.append(current.strftime('%Y%m%d'))
|
||
current -= timedelta(days=1)
|
||
|
||
trading_dates.reverse() # 升序排列,最早的日期在前
|
||
|
||
if len(trading_dates) < 2:
|
||
logger.warning("交易日不足")
|
||
return
|
||
|
||
start_date = trading_dates[0] # 7个交易日前
|
||
end_date = trading_dates[-1] # 最新交易日
|
||
|
||
logger.info(f"分析周期: {start_date} 到 {end_date} (过去7个交易日)")
|
||
|
||
# 计算各概念的7日涨幅
|
||
concept_performance = []
|
||
|
||
# 限制分析数量,避免API调用过多
|
||
sample_concepts = ths_concepts.head(50) # 分析前50个概念(过滤后数量减少)
|
||
logger.info(f"分析前 {len(sample_concepts)} 个同花顺概念...")
|
||
|
||
for _, concept in sample_concepts.iterrows():
|
||
ts_code = concept['ts_code']
|
||
name = concept['name']
|
||
|
||
try:
|
||
# 获取过去7个交易日行情数据
|
||
daily_data = fetcher.pro.ths_daily(
|
||
ts_code=ts_code,
|
||
start_date=start_date,
|
||
end_date=end_date
|
||
)
|
||
|
||
if not daily_data.empty:
|
||
# 检查数据结构
|
||
logger.debug(f"{name} 数据字段: {list(daily_data.columns)}")
|
||
|
||
if len(daily_data) >= 2:
|
||
# 按日期排序
|
||
daily_data = daily_data.sort_values('trade_date')
|
||
|
||
start_close = daily_data.iloc[0]['close']
|
||
end_close = daily_data.iloc[-1]['close']
|
||
|
||
if start_close > 0:
|
||
period_change = (end_close - start_close) / start_close * 100
|
||
|
||
# 检查涨跌幅字段名
|
||
pct_change_col = None
|
||
for col in ['pct_chg', 'pct_change', 'change']:
|
||
if col in daily_data.columns:
|
||
pct_change_col = col
|
||
break
|
||
|
||
latest_daily_change = daily_data.iloc[-1][pct_change_col] if pct_change_col else 0
|
||
|
||
concept_performance.append({
|
||
'ts_code': ts_code,
|
||
'name': name,
|
||
'period_change': period_change,
|
||
'start_close': start_close,
|
||
'end_close': end_close,
|
||
'latest_daily_change': latest_daily_change,
|
||
'trading_days': len(daily_data)
|
||
})
|
||
|
||
logger.debug(f"{name}: 过去7日{period_change:+.2f}%")
|
||
|
||
except Exception as e:
|
||
logger.debug(f"获取 {name} 数据失败: {e}")
|
||
continue
|
||
|
||
# 显示结果
|
||
if concept_performance:
|
||
df_ths = pd.DataFrame(concept_performance)
|
||
df_ths = df_ths.sort_values('period_change', ascending=False)
|
||
|
||
print(f"\n" + "="*80)
|
||
print("📈 同花顺概念板块过去7个交易日涨幅排行榜")
|
||
print("="*80)
|
||
print(f"{'排名':<4} {'概念名称':<30} {'7日涨幅':<12} {'今日涨幅':<12} {'指数代码':<15}")
|
||
print("-" * 80)
|
||
|
||
for i, (_, concept) in enumerate(df_ths.iterrows()):
|
||
rank = i + 1
|
||
name = concept['name'][:28] + '..' if len(concept['name']) > 28 else concept['name']
|
||
period_chg = f"{concept['period_change']:+.2f}%"
|
||
daily_chg = f"{concept['latest_daily_change']:+.2f}%"
|
||
ts_code = concept['ts_code']
|
||
|
||
print(f"{rank:<4} {name:<30} {period_chg:<12} {daily_chg:<12} {ts_code:<15}")
|
||
|
||
# 强势概念TOP15
|
||
print(f"\n🚀 同花顺强势概念TOP15:")
|
||
for i, (_, concept) in enumerate(df_ths.head(15).iterrows()):
|
||
print(f" {i+1:2d}. {concept['name']}: {concept['period_change']:+.2f}%")
|
||
|
||
# 弱势概念TOP10
|
||
print(f"\n📉 同花顺弱势概念TOP10:")
|
||
weak_concepts = df_ths.tail(10).iloc[::-1] # 反转顺序
|
||
for i, (_, concept) in enumerate(weak_concepts.iterrows()):
|
||
print(f" {i+1:2d}. {concept['name']}: {concept['period_change']:+.2f}%")
|
||
|
||
return df_ths
|
||
|
||
else:
|
||
logger.warning("未能计算同花顺概念涨幅")
|
||
|
||
except Exception as e:
|
||
logger.error(f"获取同花顺概念排名失败: {e}")
|
||
|
||
|
||
def compare_concept_sources(fetcher: TushareFetcher):
|
||
"""对比东财和同花顺概念数据"""
|
||
try:
|
||
logger.info("📊 对比东财 vs 同花顺概念数据...")
|
||
|
||
# 获取东财概念数量
|
||
today = datetime.now().strftime('%Y%m%d')
|
||
dc_concepts = fetcher.pro.dc_index(trade_date=today)
|
||
dc_count = len(dc_concepts) if not dc_concepts.empty else 0
|
||
|
||
# 获取同花顺概念数量
|
||
ths_concepts = fetcher.pro.ths_index(exchange='A', type='N')
|
||
ths_count = len(ths_concepts) if not ths_concepts.empty else 0
|
||
|
||
print(f"\n📊 概念板块数据源对比:")
|
||
print(f" 东财概念板块: {dc_count} 个")
|
||
print(f" 同花顺概念: {ths_count} 个")
|
||
|
||
# 数据特点对比
|
||
print(f"\n📈 数据特点对比:")
|
||
print(f" 东财概念:")
|
||
print(f" - 更新频率: 每日更新")
|
||
print(f" - 数据字段: 涨跌幅、市值、上涨下跌股数等")
|
||
print(f" - 适用场景: 实时概念轮动分析")
|
||
|
||
print(f" 同花顺概念:")
|
||
print(f" - 更新频率: 每日更新")
|
||
print(f" - 数据字段: 指数价格、涨跌幅、成交量等")
|
||
print(f" - 适用场景: 概念指数走势分析")
|
||
|
||
except Exception as e:
|
||
logger.error(f"对比概念数据源失败: {e}")
|
||
|
||
|
||
def main():
|
||
"""主函数"""
|
||
logger.info("🚀 开始探索同花顺概念板块数据...")
|
||
|
||
# 初始化Tushare数据获取器
|
||
token = "0ed6419a00d8923dc19c0b58fc92d94c9a0696949ab91a13aa58a0cc"
|
||
fetcher = TushareFetcher(token=token)
|
||
|
||
# 1. 探索同花顺接口
|
||
ths_index = explore_ths_interfaces(fetcher)
|
||
|
||
print("\n" + "="*80 + "\n")
|
||
|
||
# 2. 计算同花顺概念过去7个交易日排名
|
||
get_ths_concept_7day_ranking(fetcher)
|
||
|
||
print("\n" + "="*80 + "\n")
|
||
|
||
# 3. 对比数据源
|
||
compare_concept_sources(fetcher)
|
||
|
||
logger.info("✅ 探索完成!")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main() |