mirror of
https://github.com/ZhuLinsen/daily_stock_analysis
synced 2026-09-20 10:53:33 +08:00
fix(#155): resolve missing volume ratio and improve real-time data sources
- akshare_fetcher: Parse volume_ratio (49), PE (39), PB (46), and market cap (44/45) from Tencent API. - efinance_fetcher: Add parsing for volume_ratio, PE, and market capitalization. - base.py: Integrated Tushare Pro as an optional real-time quote source. - config: Reordered default priority to prioritize Tencent (tencent > akshare_sina > efinance > akshare_em). - documentation: Updated .env.example and GitHub Actions workflow with real-time source priority instructions. This fix ensures critical indicators like volume ratio (量比) and turnover rate are correctly fetched and displayed across different data sources. Closes #155
This commit is contained in:
22
.env.example
22
.env.example
@@ -213,3 +213,25 @@ WEBUI_PORT=8000
|
||||
# Example: Prioritize Yahoo Finance for US stocks
|
||||
# YFINANCE_PRIORITY=0
|
||||
# EFINANCE_PRIORITY=99
|
||||
|
||||
# ===========================================
|
||||
# 实时行情数据源优先级配置
|
||||
# ===========================================
|
||||
# 用于获取量比、换手率、市盈率等实时数据
|
||||
# 可选数据源(逗号分隔,按顺序尝试):
|
||||
# - tencent: 腾讯财经,有量比/换手率/PE/PB,单股查询稳定(推荐首选)
|
||||
# - akshare_sina: 新浪财经,基本行情,无量比,但非常稳定
|
||||
# - efinance: 东财(efinance库),有量比,全量拉取易被封
|
||||
# - akshare_em: 东财(akshare库),数据最全,全量拉取易被封
|
||||
# - tushare: Tushare Pro,需要2000积分,数据全面(付费用户推荐)
|
||||
#
|
||||
# 默认优先级:tencent > akshare_sina > efinance > akshare_em
|
||||
# 如果有 Tushare Pro 高积分账号,可将 tushare 放在首位:
|
||||
# REALTIME_SOURCE_PRIORITY=tushare,tencent,akshare_sina,efinance,akshare_em
|
||||
# REALTIME_SOURCE_PRIORITY=tencent,akshare_sina,efinance,akshare_em
|
||||
|
||||
# 是否启用实时行情(关闭后使用历史收盘价分析)
|
||||
# ENABLE_REALTIME_QUOTE=true
|
||||
|
||||
# 是否启用筹码分布(该接口不稳定,云端部署建议关闭)
|
||||
# ENABLE_CHIP_DISTRIBUTION=true
|
||||
|
||||
3
.github/workflows/daily_analysis.yml
vendored
3
.github/workflows/daily_analysis.yml
vendored
@@ -142,7 +142,8 @@ jobs:
|
||||
# GitHub Actions 环境建议关闭不稳定的数据源
|
||||
ENABLE_CHIP_DISTRIBUTION: 'false'
|
||||
# 实时行情数据源优先级(腾讯有量比且稳定,推荐首选)
|
||||
# 可选值: tencent, akshare_sina, efinance, akshare_em
|
||||
# 可选值: tencent, akshare_sina, efinance, akshare_em, tushare
|
||||
# 如果有 Tushare Pro 高积分账号,可将 tushare 放在首位
|
||||
REALTIME_SOURCE_PRIORITY: ${{ vars.REALTIME_SOURCE_PRIORITY || 'tencent,akshare_sina,efinance,akshare_em' }}
|
||||
|
||||
run: |
|
||||
|
||||
@@ -986,10 +986,11 @@ class AkshareFetcher(BaseFetcher):
|
||||
|
||||
circuit_breaker.record_success(source_key)
|
||||
|
||||
# 腾讯数据字段顺序(部分):
|
||||
# 腾讯数据字段顺序(完整):
|
||||
# 1:名称 2:代码 3:最新价 4:昨收 5:今开 6:成交量(手) 7:外盘 8:内盘
|
||||
# 9:买一价 10:买一量 ... 30:最高 31:最低 32:涨跌幅(%) 33:涨跌额
|
||||
# 38:换手率(%) 39:市盈率 44:振幅
|
||||
# 9-28:买卖五档 30:时间戳 31:涨跌额 32:涨跌幅(%) 33:今开 34:最高 35:最低/成交量/成交额
|
||||
# 36:成交量(手) 37:成交额(万) 38:换手率(%) 39:市盈率 43:振幅(%)
|
||||
# 44:流通市值(亿) 45:总市值(亿) 46:市净率 47:涨停价 48:跌停价 49:量比
|
||||
# 使用 realtime_types.py 中的统一转换函数
|
||||
quote = UnifiedRealtimeQuote(
|
||||
code=stock_code,
|
||||
@@ -1000,15 +1001,20 @@ class AkshareFetcher(BaseFetcher):
|
||||
change_amount=safe_float(fields[31]) if len(fields) > 31 else None,
|
||||
volume=safe_int(fields[6]) * 100 if fields[6] else None, # 腾讯返回的是手,转为股
|
||||
open_price=safe_float(fields[5]),
|
||||
high=safe_float(fields[33]) if len(fields) > 33 else None,
|
||||
low=safe_float(fields[34]) if len(fields) > 34 else None,
|
||||
high=safe_float(fields[34]) if len(fields) > 34 else None,
|
||||
low=safe_float(fields[35].split('/')[0]) if len(fields) > 35 and '/' in str(fields[35]) else safe_float(fields[35]) if len(fields) > 35 else None,
|
||||
pre_close=safe_float(fields[4]),
|
||||
turnover_rate=safe_float(fields[38]) if len(fields) > 38 else None,
|
||||
amplitude=safe_float(fields[43]) if len(fields) > 43 else None,
|
||||
volume_ratio=safe_float(fields[49]) if len(fields) > 49 else None, # 量比
|
||||
pe_ratio=safe_float(fields[39]) if len(fields) > 39 else None, # 市盈率
|
||||
pb_ratio=safe_float(fields[46]) if len(fields) > 46 else None, # 市净率
|
||||
circ_mv=safe_float(fields[44]) * 100000000 if len(fields) > 44 and fields[44] else None, # 流通市值(亿->元)
|
||||
total_mv=safe_float(fields[45]) * 100000000 if len(fields) > 45 and fields[45] else None, # 总市值(亿->元)
|
||||
)
|
||||
|
||||
logger.info(f"[实时行情-腾讯] {stock_code} {quote.name}: 价格={quote.price}, "
|
||||
f"涨跌={quote.change_pct}%, 换手率={quote.turnover_rate}%")
|
||||
f"涨跌={quote.change_pct}%, 量比={quote.volume_ratio}, 换手率={quote.turnover_rate}%")
|
||||
return quote
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -575,6 +575,14 @@ class DataFetcherManager:
|
||||
quote = fetcher.get_realtime_quote(stock_code, source="tencent")
|
||||
break
|
||||
|
||||
elif source == "tushare":
|
||||
# 尝试 TushareFetcher(需要 Tushare Pro 积分)
|
||||
for fetcher in self._fetchers:
|
||||
if fetcher.name == "TushareFetcher":
|
||||
if hasattr(fetcher, 'get_realtime_quote'):
|
||||
quote = fetcher.get_realtime_quote(stock_code)
|
||||
break
|
||||
|
||||
if quote is not None and quote.has_basic_data():
|
||||
logger.info(f"[实时行情] {stock_code} 成功获取 (来源: {source})")
|
||||
return quote
|
||||
|
||||
@@ -516,6 +516,11 @@ class EfinanceFetcher(BaseFetcher):
|
||||
high_col = '最高' if '最高' in df.columns else 'high'
|
||||
low_col = '最低' if '最低' in df.columns else 'low'
|
||||
open_col = '开盘' if '开盘' in df.columns else 'open'
|
||||
# efinance 也返回量比、市盈率、市值等字段
|
||||
vol_ratio_col = '量比' if '量比' in df.columns else 'volume_ratio'
|
||||
pe_col = '市盈率' if '市盈率' in df.columns else 'pe_ratio'
|
||||
total_mv_col = '总市值' if '总市值' in df.columns else 'total_mv'
|
||||
circ_mv_col = '流通市值' if '流通市值' in df.columns else 'circ_mv'
|
||||
|
||||
quote = UnifiedRealtimeQuote(
|
||||
code=stock_code,
|
||||
@@ -531,10 +536,14 @@ class EfinanceFetcher(BaseFetcher):
|
||||
high=safe_float(row.get(high_col)),
|
||||
low=safe_float(row.get(low_col)),
|
||||
open_price=safe_float(row.get(open_col)),
|
||||
volume_ratio=safe_float(row.get(vol_ratio_col)), # 量比
|
||||
pe_ratio=safe_float(row.get(pe_col)), # 市盈率
|
||||
total_mv=safe_float(row.get(total_mv_col)), # 总市值
|
||||
circ_mv=safe_float(row.get(circ_mv_col)), # 流通市值
|
||||
)
|
||||
|
||||
logger.info(f"[实时行情-efinance] {stock_code} {quote.name}: 价格={quote.price}, 涨跌={quote.change_pct}%, "
|
||||
f"换手率={quote.turnover_rate}%")
|
||||
f"量比={quote.volume_ratio}, 换手率={quote.turnover_rate}%")
|
||||
return quote
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -144,7 +144,12 @@ class Config:
|
||||
# 筹码分布开关(该接口不稳定,云端部署建议关闭)
|
||||
enable_chip_distribution: bool = True
|
||||
# 实时行情数据源优先级(逗号分隔)
|
||||
realtime_source_priority: str = "akshare_sina,tencent,efinance,akshare_em"
|
||||
# 推荐顺序:tencent > akshare_sina > efinance > akshare_em > tushare
|
||||
# - tencent: 腾讯财经,有量比/换手率/市盈率等,单股查询稳定(推荐)
|
||||
# - akshare_sina: 新浪财经,基本行情稳定,但无量比
|
||||
# - efinance/akshare_em: 东财全量接口,数据最全但容易被封
|
||||
# - tushare: Tushare Pro,需要2000积分,数据全面(付费用户可优先使用)
|
||||
realtime_source_priority: str = "tencent,akshare_sina,efinance,akshare_em"
|
||||
# 实时行情缓存时间(秒)
|
||||
realtime_cache_ttl: int = 600
|
||||
# 熔断器冷却时间(秒)
|
||||
@@ -387,9 +392,11 @@ class Config:
|
||||
enable_realtime_quote=os.getenv('ENABLE_REALTIME_QUOTE', 'true').lower() == 'true',
|
||||
enable_chip_distribution=os.getenv('ENABLE_CHIP_DISTRIBUTION', 'true').lower() == 'true',
|
||||
# 实时行情数据源优先级:
|
||||
# - akshare_sina/tencent: 单股票直连查询,轻量级,推荐放前面
|
||||
# - efinance/akshare_em: 全量拉取,数据丰富但负载大
|
||||
realtime_source_priority=os.getenv('REALTIME_SOURCE_PRIORITY', 'akshare_sina,tencent,efinance,akshare_em'),
|
||||
# - tencent: 腾讯财经,有量比/换手率/PE/PB等,单股查询稳定(推荐)
|
||||
# - akshare_sina: 新浪财经,基本行情稳定,但无量比
|
||||
# - efinance/akshare_em: 东财全量接口,数据最全但容易被封
|
||||
# - tushare: Tushare Pro,需要2000积分,数据全面
|
||||
realtime_source_priority=os.getenv('REALTIME_SOURCE_PRIORITY', 'tencent,akshare_sina,efinance,akshare_em'),
|
||||
realtime_cache_ttl=int(os.getenv('REALTIME_CACHE_TTL', '600')),
|
||||
circuit_breaker_cooldown=int(os.getenv('CIRCUIT_BREAKER_COOLDOWN', '300'))
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user