This commit is contained in:
songzhuoyuan
2026-08-11 20:41:31 +08:00
parent 2ce13e5bae
commit befdc32aea
24 changed files with 1311 additions and 530 deletions
+160 -86
View File
@@ -329,11 +329,56 @@ class MarketSentimentDataFetcher:
}
def _get_turnover_rate(self, symbol):
"""获取换手率数据(支持akshare和tushare自动切换"""
"""获取换手率数据(优先tushare,失败时使用akshare"""
try:
# 优先使用akshare获取最近的换手率数据
print(f" [Akshare] 正在获取换手率数据...")
# 获取A股实时行情数据(不需要参数)
# 优先使用tusharedaily_basic,取最近10个交易日保证非交易日也有数据
if data_source_manager.tushare_available:
try:
print(f" [Tushare] 正在获取换手率数据(主要数据源)...")
ts_code = data_source_manager._convert_to_ts_code(symbol)
end_date = datetime.now().strftime('%Y%m%d')
start_date = (datetime.now() - timedelta(days=10)).strftime('%Y%m%d')
df = data_source_manager.tushare_api.daily_basic(
ts_code=ts_code,
start_date=start_date,
end_date=end_date
)
if df is not None and not df.empty:
row = df.iloc[0]
turnover_rate = row.get('turnover_rate', 'N/A')
# 解读换手率
interpretation = ""
if turnover_rate != 'N/A':
try:
turnover = float(turnover_rate)
if turnover > 20:
interpretation = "换手率极高(>20%),资金活跃度极高,可能存在炒作"
elif turnover > 10:
interpretation = "换手率较高(>10%),交易活跃"
elif turnover > 5:
interpretation = "换手率正常(5%-10%),交易适中"
elif turnover > 2:
interpretation = "换手率偏低(2%-5%),交易相对清淡"
else:
interpretation = "换手率很低(<2%),交易清淡"
except:
pass
print(f" [Tushare] ✅ 成功获取换手率: {turnover_rate}%")
return {
"current_turnover_rate": turnover_rate,
"interpretation": interpretation
}
else:
print(f" [Tushare] ❌ 未获取到换手率,尝试备用数据源")
except Exception as te:
print(f" [Tushare] ❌ 获取失败: {te}")
# tushare失败,回退akshare
print(f" [Akshare] 正在获取换手率数据(备用数据源)...")
df = ak.stock_zh_a_spot_em()
if df is not None and not df.empty:
stock_data = df[df['代码'] == symbol]
@@ -366,57 +411,42 @@ class MarketSentimentDataFetcher:
}
except Exception as e:
print(f" [Akshare] ❌ 获取换手率失败: {e}")
# akshare失败,尝试tushare
if data_source_manager.tushare_available:
try:
print(f" [Tushare] 正在获取换手率数据(备用数据源)...")
ts_code = data_source_manager._convert_to_ts_code(symbol)
# 获取最近一个交易日的数据
df = data_source_manager.tushare_api.daily_basic(
ts_code=ts_code,
trade_date=datetime.now().strftime('%Y%m%d')
)
if df is not None and not df.empty:
row = df.iloc[0]
turnover_rate = row.get('turnover_rate', 'N/A')
# 解读换手率
interpretation = ""
if turnover_rate != 'N/A':
try:
turnover = float(turnover_rate)
if turnover > 20:
interpretation = "换手率极高(>20%),资金活跃度极高,可能存在炒作"
elif turnover > 10:
interpretation = "换手率较高(>10%),交易活跃"
elif turnover > 5:
interpretation = "换手率正常(5%-10%),交易适中"
elif turnover > 2:
interpretation = "换手率偏低(2%-5%),交易相对清淡"
else:
interpretation = "换手率很低(<2%),交易清淡"
except:
pass
print(f" [Tushare] ✅ 成功获取换手率: {turnover_rate}%")
return {
"current_turnover_rate": turnover_rate,
"interpretation": interpretation
}
except Exception as te:
print(f" [Tushare] ❌ 获取失败: {te}")
return None
def _get_market_index_sentiment(self):
"""获取大盘指数情绪(支持akshare和tushare自动切换)"""
try:
# 优先使用akshare获取上证指数实时数据
print(f" [Akshare] 正在获取大盘指数数据...")
# 使用正确的symbol参数
# 优先使用tushareindex_daily,取最近10个交易日保证非交易日也有数据
if data_source_manager.tushare_available:
try:
print(f" [Tushare] 正在获取大盘指数数据(主要数据源)...")
# 获取上证指数数据
end_date = datetime.now().strftime('%Y%m%d')
start_date = (datetime.now() - timedelta(days=10)).strftime('%Y%m%d')
df = data_source_manager.tushare_api.index_daily(
ts_code='000001.SH',
start_date=start_date,
end_date=end_date
)
if df is not None and not df.empty:
row = df.iloc[0]
change_pct = row.get('pct_chg', 0)
print(f" [Tushare] ✅ 成功获取大盘指数涨跌幅: {change_pct}%")
return {
"index_name": "上证指数",
"change_percent": change_pct
}
else:
print(f" [Tushare] ❌ 未获取到大盘指数,尝试备用数据源")
except Exception as te:
print(f" [Tushare] ❌ 获取失败: {te}")
# tushare失败,回退akshare
print(f" [Akshare] 正在获取大盘指数数据(备用数据源)...")
df = ak.stock_zh_index_spot_em(symbol="上证系列指数")
if df is not None and not df.empty:
# 查找上证指数(代码为000001
@@ -470,30 +500,6 @@ class MarketSentimentDataFetcher:
}
except Exception as e:
print(f" [Akshare] ❌ 获取大盘指数失败: {e}")
# akshare失败,尝试tushare
if data_source_manager.tushare_available:
try:
print(f" [Tushare] 正在获取大盘指数数据(备用数据源)...")
# 获取上证指数数据
df = data_source_manager.tushare_api.index_daily(
ts_code='000001.SH',
start_date=datetime.now().strftime('%Y%m%d'),
end_date=datetime.now().strftime('%Y%m%d')
)
if df is not None and not df.empty:
row = df.iloc[0]
change_pct = row.get('pct_chg', 0)
print(f" [Tushare] ✅ 成功获取大盘指数涨跌幅: {change_pct}%")
return {
"index_name": "上证指数",
"change_percent": change_pct
}
except Exception as te:
print(f" [Tushare] ❌ 获取失败: {te}")
return None
@@ -503,19 +509,50 @@ class MarketSentimentDataFetcher:
# 获取今日涨停和跌停统计
today = datetime.now().strftime('%Y%m%d')
# 获取涨停股票
try:
limit_up_df = ak.stock_zt_pool_em(date=today)
limit_up_count = len(limit_up_df) if limit_up_df is not None and not limit_up_df.empty else 0
except:
limit_up_count = 0
limit_up_count = 0
limit_down_count = 0
# 获取跌停股票
try:
limit_down_df = ak.stock_zt_pool_dtgc_em(date=today)
limit_down_count = len(limit_down_df) if limit_down_df is not None and not limit_down_df.empty else 0
except:
limit_down_count = 0
# 优先使用tushare的涨跌停列表
if data_source_manager.tushare_available:
try:
print(f" [Tushare] 正在获取涨跌停数据(主要数据源)...")
df_ll = data_source_manager.tushare_api.limit_list_d(trade_date=today)
if df_ll is not None and not df_ll.empty:
if 'limit_type' in df_ll.columns:
limit_up_count = int((df_ll['limit_type'].fillna('') == 'U').sum())
limit_down_count = int((df_ll['limit_type'].fillna('') == 'D').sum())
print(f" [Tushare] ✅ 成功获取涨跌停: 涨停{limit_up_count} / 跌停{limit_down_count}")
elif 'pct_chg' in df_ll.columns:
limit_up_count = int((df_ll['pct_chg'] >= 9.5).sum())
limit_down_count = int((df_ll['pct_chg'] <= -9.5).sum())
print(f" [Tushare] ✅ 成功获取涨跌停: 涨停{limit_up_count} / 跌停{limit_down_count}")
elif '涨跌幅' in df_ll.columns:
limit_up_count = int((df_ll['涨跌幅'] >= 9.5).sum())
limit_down_count = int((df_ll['涨跌幅'] <= -9.5).sum())
print(f" [Tushare] ✅ 成功获取涨跌停: 涨停{limit_up_count} / 跌停{limit_down_count}")
else:
# 无法识别的列结构,按0处理并回退akshare
print(f" [Tushare] ⚠ 涨跌停返回列无法识别: {list(df_ll.columns)[:10]},尝试备用数据源")
else:
print(f" [Tushare] ❌ 未获取到涨跌停数据,尝试备用数据源")
except Exception as e:
print(f" [Tushare] ❌ 获取涨跌停数据失败: {e}")
# tushare不可用或失败时,回退akshare
if limit_up_count == 0 and limit_down_count == 0:
# 获取涨停股票
try:
limit_up_df = ak.stock_zt_pool_em(date=today)
limit_up_count = len(limit_up_df) if limit_up_df is not None and not limit_up_df.empty else 0
except:
limit_up_count = 0
# 获取跌停股票
try:
limit_down_df = ak.stock_zt_pool_dtgc_em(date=today)
limit_down_count = len(limit_down_df) if limit_down_df is not None and not limit_down_df.empty else 0
except:
limit_down_count = 0
# 计算涨跌停比例
if limit_up_count + limit_down_count > 0:
@@ -549,6 +586,44 @@ class MarketSentimentDataFetcher:
def _get_margin_trading_data(self, symbol):
"""获取融资融券数据"""
try:
# 优先使用tushare的个股融资融券明细
if data_source_manager.tushare_available:
try:
print(f" [Tushare] 正在获取融资融券数据(主要数据源)...")
ts_code = data_source_manager._convert_to_ts_code(symbol)
end_date = datetime.now().strftime('%Y%m%d')
start_date = (datetime.now() - timedelta(days=15)).strftime('%Y%m%d')
df = data_source_manager.tushare_api.margin_detail(
ts_code=ts_code,
start_date=start_date,
end_date=end_date
)
if df is not None and not df.empty:
latest = df.iloc[0]
margin_balance = latest.get('rzye', 0) or 0
short_balance = latest.get('rqye', 0) or 0
# 解读融资融券
interpretation = []
if margin_balance > short_balance * 10:
interpretation.append("融资余额远大于融券余额,投资者看多情绪强")
elif margin_balance > short_balance * 3:
interpretation.append("融资余额大于融券余额,投资者偏看多")
else:
interpretation.append("融资融券相对平衡")
print(f" [Tushare] ✅ 成功获取融资融券数据")
return {
"margin_balance": margin_balance,
"short_balance": short_balance,
"interpretation": interpretation,
"date": str(latest.get('trade_date', datetime.now().strftime('%Y-%m-%d')))
}
else:
print(f" [Tushare] ❌ 未获取到融资融券数据,尝试备用数据源")
except Exception as e:
print(f" [Tushare] ❌ 获取融资融券数据失败: {e}")
# 获取个股融资融券数据(尝试多个API)
try:
# 方法1:获取沪深融资融券明细
@@ -762,4 +837,3 @@ if __name__ == "__main__":
print(formatted_text)
else:
print(f"\n获取失败: {sentiment_data.get('error', '未知错误')}")