#!/usr/bin/env python3 """ A股短期题材选股 - 详细诊断版本 显示每个股票的筛选过程 """ import asyncio import sys import os from datetime import datetime, timedelta sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from app.utils.logger import logger from app.config import get_settings from app.astock_agent.tushare_client import get_tushare_client async def diagnose_sector_stocks(): """诊断板块成分股的筛选过程""" print("\n" + "=" * 60) print("🔍 A股选股详细诊断") print("=" * 60) settings = get_settings() ts_client = get_tushare_client(settings.tushare_token) # 1. 测试获取一个板块的成分股 print("\n【测试】获取智能电网板块成分股...") try: # 使用智能电网板块代码(与选股器一致) sector_code = "885311.TI" members_df = ts_client.get_sector_members(sector_code) if members_df.empty: print("❌ 无法获取板块成分股") return stock_codes = members_df['con_code'].tolist()[:10] # 只测试前10只 print(f"✓ 获取到 {len(stock_codes)} 只成分股(测试前10只)") print(f"股票代码: {stock_codes}") # 2. 获取这些股票的实时行情 print("\n【测试】获取实时行情...") today = datetime.now().strftime('%Y%m%d') yesterday = (datetime.now() - timedelta(days=10)).strftime('%Y%m%d') all_stocks_data = [] for stock_code in stock_codes: try: daily_df = ts_client.pro.daily( ts_code=stock_code, start_date=yesterday, end_date=today ) if daily_df.empty: print(f" ⚠️ {stock_code}: 无历史数据") continue daily_df = daily_df.sort_values('trade_date') latest = daily_df.iloc[-1] stock_info = { 'ts_code': stock_code, 'name': latest.get('name', ''), 'close': float(latest['close']), 'pct_chg': float(latest['pct_chg']), 'vol': float(latest['vol']), 'amount': float(latest['amount']) * 1000, 'trade_date': str(latest['trade_date']) } all_stocks_data.append(stock_info) except Exception as e: print(f" ❌ {stock_code}: 获取失败 - {e}") continue print(f"\n✓ 成功获取 {len(all_stocks_data)} 只股票的行情") # 3. 获取每日指标 print("\n【测试】获取每日指标(换手率等)...") basic_df = ts_client.pro.daily_basic( ts_code=','.join(stock_codes), trade_date=all_stocks_data[0]['trade_date'], fields='ts_code,trade_date,turnover_rate,pe,pb' ) if basic_df.empty: print("⚠️ 无法获取每日指标数据") else: print(f"✓ 获取到 {len(basic_df)} 只股票的每日指标") # 4. 逐个检查筛选条件 print("\n【测试】逐个检查筛选条件...") print("=" * 80) for stock_info in all_stocks_data: stock_code = stock_info['ts_code'] name = stock_info['name'] close = stock_info['close'] pct_chg = stock_info['pct_chg'] vol = stock_info['vol'] amount = stock_info['amount'] print(f"\n🔍 {name}({stock_code}):") print(f" 日期: {stock_info['trade_date']}") print(f" 现价: ¥{close:.2f}, 涨跌幅: {pct_chg:+.2f}%") # 检查1: ST股票 if 'ST' in name or '退' in name: print(f" ❌ ST/退市股,被过滤") continue print(f" ✓ 不是ST/退市股") # 检查2: 换手率 basic_row = basic_df[basic_df['ts_code'] == stock_code] if not basic_row.empty: turnover = float(basic_row.iloc[0].get('turnover_rate', 0)) print(f" 换手率: {turnover:.2f}%") if 1.0 <= turnover <= 20.0: print(f" ✓ 换手率符合") else: print(f" ❌ 换手率不符合(需要1%-20%)") continue else: print(f" ⚠️ 无换手率数据") turnover = 0 # 检查3: MA多头排列 try: start_date = (datetime.now() - timedelta(days=60)).strftime('%Y%m%d') daily_df = ts_client.pro.daily( ts_code=stock_code, start_date=start_date, end_date=today ) if daily_df.empty or len(daily_df) < 30: print(f" ❌ 历史数据不足(需要30天以上),无法计算MA") continue daily_df = daily_df.sort_values('trade_date').reset_index(drop=True) close_series = daily_df['close'] vol_series = daily_df['vol'] ma5 = close_series.rolling(window=5).mean().iloc[-1] ma10 = close_series.rolling(window=10).mean().iloc[-1] ma20 = close_series.rolling(window=20).mean().iloc[-1] print(f" MA5: ¥{ma5:.2f}, MA10: ¥{ma10:.2f}, MA20: ¥{ma20:.2f}") if ma5 > ma20: print(f" ✓ MA趋势符合(MA5 > MA20)") else: print(f" ❌ MA趋势不符合(需要 MA5 > MA20)") continue # 检查4: 量能 ma5_vol = vol_series.rolling(window=5).mean().iloc[-1] volume_ratio = vol / ma5_vol if ma5_vol > 0 else 1 print(f" 量比: {volume_ratio:.2f}") if volume_ratio >= 0.7: print(f" ✓ 量能符合(≥0.7)") else: print(f" ❌ 量能不足(量比需要≥0.7)") continue # 检查5: 市值 if turnover > 0: market_cap = amount / (turnover / 100) market_cap_yi = market_cap / 100000000 print(f" 市值: {market_cap_yi:.2f}亿") if 30 <= market_cap_yi <= 1000: print(f" ✓ 市值符合") else: print(f" ❌ 市值不符合(需要30-1000亿)") continue print(f" ✅✅✅ {name}({stock_code}) 通过所有筛选条件!") except Exception as e: print(f" ❌ 计算技术指标失败: {e}") import traceback print(traceback.format_exc()) except Exception as e: print(f"❌ 诊断失败: {e}") import traceback traceback.print_exc() async def main(): """主函数""" await diagnose_sector_stocks() print("\n" + "=" * 60) print("诊断完成") print("=" * 60) if __name__ == "__main__": asyncio.run(main())