313 lines
11 KiB
Python
313 lines
11 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
高级强势板块筛选器
|
||
筛选条件:
|
||
1. 本周收阳(周涨幅>0)
|
||
2. 周线级别创阶段新高(20周新高)
|
||
3. 成交额巨大(超过1000亿)
|
||
"""
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
import pandas as pd
|
||
import numpy as np
|
||
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_trading_dates(days_back=100):
|
||
"""获取过去N个交易日"""
|
||
dates = []
|
||
current = datetime.now()
|
||
|
||
while len(dates) < days_back:
|
||
if current.weekday() < 5: # 周一到周五
|
||
dates.append(current.strftime('%Y%m%d'))
|
||
current -= timedelta(days=1)
|
||
|
||
return sorted(dates) # 升序返回
|
||
|
||
|
||
def filter_index_concepts(ths_concepts):
|
||
"""过滤掉指数型板块"""
|
||
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)
|
||
filtered_concepts = ths_concepts[ths_concepts['name'].apply(is_concept_plate)]
|
||
filtered_count = len(filtered_concepts)
|
||
|
||
logger.info(f"过滤指数型板块: {original_count} -> {filtered_count} 个")
|
||
return filtered_concepts
|
||
|
||
|
||
def analyze_concept_strength(fetcher: TushareFetcher, concept_info, trading_dates):
|
||
"""分析单个概念的强势特征"""
|
||
ts_code = concept_info['ts_code']
|
||
name = concept_info['name']
|
||
|
||
try:
|
||
# 获取过去20周的数据(约100个交易日)
|
||
start_date = trading_dates[0]
|
||
end_date = trading_dates[-1]
|
||
|
||
daily_data = fetcher.pro.ths_daily(
|
||
ts_code=ts_code,
|
||
start_date=start_date,
|
||
end_date=end_date
|
||
)
|
||
|
||
if daily_data.empty or len(daily_data) < 20:
|
||
return None
|
||
|
||
# 按日期排序
|
||
daily_data = daily_data.sort_values('trade_date')
|
||
daily_data.reset_index(drop=True, inplace=True)
|
||
|
||
# 1. 计算本周涨幅(最近5个交易日)
|
||
recent_data = daily_data.tail(5)
|
||
if len(recent_data) < 2:
|
||
return None
|
||
|
||
week_start_close = recent_data.iloc[0]['close']
|
||
week_end_close = recent_data.iloc[-1]['close']
|
||
week_change = (week_end_close - week_start_close) / week_start_close * 100
|
||
|
||
# 2. 检查是否本周收阳
|
||
is_weekly_positive = week_change > 0
|
||
|
||
# 3. 计算20周新高(约100个交易日)
|
||
current_close = daily_data.iloc[-1]['close']
|
||
past_20weeks_high = daily_data['high'].max()
|
||
is_20week_high = current_close >= past_20weeks_high * 0.99 # 允许1%的误差
|
||
|
||
# 4. 计算成交额(最近5日平均)
|
||
recent_turnover = recent_data['vol'].mean() * recent_data['close'].mean() # 简化计算
|
||
turnover_100yi = recent_turnover / 100000000 # 转换为亿元
|
||
|
||
# 5. 计算技术指标
|
||
# RSI相对强弱指数
|
||
rsi = calculate_rsi(daily_data['close'].values)
|
||
|
||
# 20日均线趋势
|
||
ma20 = daily_data['close'].rolling(20).mean()
|
||
ma20_trend = (ma20.iloc[-1] - ma20.iloc[-10]) / ma20.iloc[-10] * 100 if len(ma20) >= 20 else 0
|
||
|
||
# 波动率
|
||
volatility = daily_data['pct_change'].std() * np.sqrt(250) # 年化波动率
|
||
|
||
return {
|
||
'ts_code': ts_code,
|
||
'name': name,
|
||
'week_change': week_change,
|
||
'is_weekly_positive': is_weekly_positive,
|
||
'is_20week_high': is_20week_high,
|
||
'avg_turnover_yi': turnover_100yi,
|
||
'current_close': current_close,
|
||
'rsi': rsi,
|
||
'ma20_trend': ma20_trend,
|
||
'volatility': volatility,
|
||
'data_length': len(daily_data)
|
||
}
|
||
|
||
except Exception as e:
|
||
logger.debug(f"分析 {name} 失败: {e}")
|
||
return None
|
||
|
||
|
||
def calculate_rsi(prices, period=14):
|
||
"""计算RSI指标"""
|
||
try:
|
||
delta = np.diff(prices)
|
||
gain = np.where(delta > 0, delta, 0)
|
||
loss = np.where(delta < 0, -delta, 0)
|
||
|
||
avg_gain = np.mean(gain[-period:]) if len(gain) >= period else 0
|
||
avg_loss = np.mean(loss[-period:]) if len(loss) >= period else 0
|
||
|
||
if avg_loss == 0:
|
||
return 100
|
||
|
||
rs = avg_gain / avg_loss
|
||
rsi = 100 - (100 / (1 + rs))
|
||
return rsi
|
||
except:
|
||
return 50 # 默认值
|
||
|
||
|
||
def find_strong_sectors(fetcher: TushareFetcher):
|
||
"""寻找强势板块"""
|
||
try:
|
||
logger.info("🔍 开始寻找强势板块...")
|
||
|
||
# 获取交易日期
|
||
trading_dates = get_trading_dates(100)
|
||
logger.info(f"分析周期: {trading_dates[0]} 到 {trading_dates[-1]} (100个交易日)")
|
||
|
||
# 获取同花顺概念列表
|
||
ths_concepts = fetcher.pro.ths_index(exchange='A', type='N')
|
||
if ths_concepts.empty:
|
||
logger.error("未获取到概念数据")
|
||
return
|
||
|
||
# 过滤指数型概念
|
||
ths_concepts = filter_index_concepts(ths_concepts)
|
||
|
||
# 分析概念强度
|
||
strong_concepts = []
|
||
total_concepts = min(80, len(ths_concepts)) # 分析前80个概念
|
||
logger.info(f"分析 {total_concepts} 个概念...")
|
||
|
||
for i, (_, concept) in enumerate(ths_concepts.head(total_concepts).iterrows()):
|
||
if i % 10 == 0:
|
||
logger.info(f"进度: {i+1}/{total_concepts}")
|
||
|
||
result = analyze_concept_strength(fetcher, concept, trading_dates)
|
||
if result:
|
||
strong_concepts.append(result)
|
||
|
||
if not strong_concepts:
|
||
logger.warning("未找到符合条件的强势板块")
|
||
return
|
||
|
||
# 转换为DataFrame
|
||
df = pd.DataFrame(strong_concepts)
|
||
|
||
# 强势板块筛选
|
||
logger.info("🚀 应用强势板块筛选条件...")
|
||
|
||
# 条件1:本周收阳
|
||
weekly_positive = df[df['is_weekly_positive']]
|
||
logger.info(f"本周收阳概念: {len(weekly_positive)} 个")
|
||
|
||
# 条件2:20周新高
|
||
new_high_concepts = df[df['is_20week_high']]
|
||
logger.info(f"20周新高概念: {len(new_high_concepts)} 个")
|
||
|
||
# 条件3:成交额超过1000亿(这里设置为10亿,因为单个概念1000亿太高)
|
||
high_turnover = df[df['avg_turnover_yi'] >= 10]
|
||
logger.info(f"成交额超过10亿概念: {len(high_turnover)} 个")
|
||
|
||
# 综合强势板块(满足至少2个条件)
|
||
df['strength_score'] = (
|
||
df['is_weekly_positive'].astype(int) +
|
||
df['is_20week_high'].astype(int) +
|
||
(df['avg_turnover_yi'] >= 10).astype(int)
|
||
)
|
||
|
||
# 按强势得分和周涨幅排序
|
||
strong_sectors = df[df['strength_score'] >= 2].sort_values(['strength_score', 'week_change'], ascending=[False, False])
|
||
|
||
# 显示结果
|
||
display_strong_sectors(df, strong_sectors, weekly_positive, new_high_concepts, high_turnover)
|
||
|
||
return df
|
||
|
||
except Exception as e:
|
||
logger.error(f"寻找强势板块失败: {e}")
|
||
|
||
|
||
def display_strong_sectors(df, strong_sectors, weekly_positive, new_high_concepts, high_turnover):
|
||
"""显示强势板块分析结果"""
|
||
|
||
print("\n" + "="*100)
|
||
print("🔍 强势板块综合分析报告")
|
||
print("="*100)
|
||
|
||
# 1. 综合强势板块(满足多个条件)
|
||
if not strong_sectors.empty:
|
||
print(f"\n🚀 综合强势板块TOP10(满足2+条件):")
|
||
print(f"{'排名':<4} {'概念名称':<25} {'周涨幅':<10} {'强势分':<8} {'RSI':<8} {'成交额(亿)':<12} {'条件':<20}")
|
||
print("-" * 100)
|
||
|
||
for i, (_, concept) in enumerate(strong_sectors.head(10).iterrows()):
|
||
rank = i + 1
|
||
name = concept['name'][:23] + '..' if len(concept['name']) > 23 else concept['name']
|
||
week_chg = f"{concept['week_change']:+.2f}%"
|
||
score = f"{concept['strength_score']}/3"
|
||
rsi = f"{concept['rsi']:.1f}"
|
||
turnover = f"{concept['avg_turnover_yi']:.1f}"
|
||
|
||
conditions = []
|
||
if concept['is_weekly_positive']:
|
||
conditions.append("周阳")
|
||
if concept['is_20week_high']:
|
||
conditions.append("新高")
|
||
if concept['avg_turnover_yi'] >= 10:
|
||
conditions.append("大额")
|
||
|
||
condition_str = "+".join(conditions)
|
||
|
||
print(f"{rank:<4} {name:<25} {week_chg:<10} {score:<8} {rsi:<8} {turnover:<12} {condition_str:<20}")
|
||
|
||
# 2. 分类展示
|
||
print(f"\n📊 分类统计:")
|
||
print(f" 本周收阳: {len(weekly_positive)} 个")
|
||
print(f" 20周新高: {len(new_high_concepts)} 个")
|
||
print(f" 大成交额: {len(high_turnover)} 个")
|
||
print(f" 综合强势: {len(strong_sectors)} 个")
|
||
|
||
# 3. 本周收阳TOP10
|
||
if not weekly_positive.empty:
|
||
top_weekly = weekly_positive.sort_values('week_change', ascending=False)
|
||
print(f"\n📈 本周收阳TOP10:")
|
||
for i, (_, concept) in enumerate(top_weekly.head(10).iterrows()):
|
||
print(f" {i+1:2d}. {concept['name']}: {concept['week_change']:+.2f}%")
|
||
|
||
# 4. 20周新高概念
|
||
if not new_high_concepts.empty:
|
||
print(f"\n🎯 20周新高概念TOP10:")
|
||
new_high_sorted = new_high_concepts.sort_values('week_change', ascending=False)
|
||
for i, (_, concept) in enumerate(new_high_sorted.head(10).iterrows()):
|
||
print(f" {i+1:2d}. {concept['name']}: {concept['week_change']:+.2f}% (RSI: {concept['rsi']:.1f})")
|
||
|
||
# 5. 大成交额概念
|
||
if not high_turnover.empty:
|
||
print(f"\n💰 大成交额概念TOP10:")
|
||
turnover_sorted = high_turnover.sort_values('avg_turnover_yi', ascending=False)
|
||
for i, (_, concept) in enumerate(turnover_sorted.head(10).iterrows()):
|
||
print(f" {i+1:2d}. {concept['name']}: {concept['avg_turnover_yi']:.1f}亿 ({concept['week_change']:+.2f}%)")
|
||
|
||
# 6. 技术面强势
|
||
strong_tech = df[(df['rsi'] > 60) & (df['ma20_trend'] > 0)].sort_values('week_change', ascending=False)
|
||
if not strong_tech.empty:
|
||
print(f"\n📊 技术面强势概念TOP10:")
|
||
for i, (_, concept) in enumerate(strong_tech.head(10).iterrows()):
|
||
print(f" {i+1:2d}. {concept['name']}: RSI {concept['rsi']:.1f}, MA20趋势 {concept['ma20_trend']:+.2f}%")
|
||
|
||
|
||
def main():
|
||
"""主函数"""
|
||
logger.info("🚀 开始高级强势板块分析...")
|
||
|
||
# 初始化Tushare数据获取器
|
||
token = "0ed6419a00d8923dc19c0b58fc92d94c9a0696949ab91a13aa58a0cc"
|
||
fetcher = TushareFetcher(token=token)
|
||
|
||
# 寻找强势板块
|
||
result_df = find_strong_sectors(fetcher)
|
||
|
||
if result_df is not None:
|
||
logger.info("✅ 强势板块分析完成!")
|
||
else:
|
||
logger.error("❌ 强势板块分析失败!")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main() |