fix: 修复数据获取架构逻辑Bug
修复内容: 1. Bug #1: TushareSource.fetch(adj='raw') ETF 无法获取 - 在 adj='raw' 分支优先判断 ETF - ETF 代码现在正确路由到 fetch_etf() 2. Bug #2: is_china_index 判断范围过宽 - 添加 ETF 排除逻辑 - ETF 不再被误判为指数 3. 接口一致性:CCXTSource 添加 adj 参数 - fetch(code, start, end, adj='raw', timeframe) - 加密货币仅支持 adj='raw' - UniversalDataFetcher._fetch_crypto() 同步更新 影响: - ETF 原始价格数据获取恢复正常 - 类型判断逻辑更准确 - 数据源接口签名统一
This commit is contained in:
@@ -146,6 +146,7 @@ class CCXTSource:
|
|||||||
code: str,
|
code: str,
|
||||||
start_date: str,
|
start_date: str,
|
||||||
end_date: str,
|
end_date: str,
|
||||||
|
adj: str = 'raw',
|
||||||
timeframe: str = '1d'
|
timeframe: str = '1d'
|
||||||
) -> Optional[pd.DataFrame]:
|
) -> Optional[pd.DataFrame]:
|
||||||
"""
|
"""
|
||||||
@@ -155,11 +156,16 @@ class CCXTSource:
|
|||||||
code: 加密货币代码(BTC, ETH 等)
|
code: 加密货币代码(BTC, ETH 等)
|
||||||
start_date: 开始日期 YYYY-MM-DD
|
start_date: 开始日期 YYYY-MM-DD
|
||||||
end_date: 结束日期 YYYY-MM-DD
|
end_date: 结束日期 YYYY-MM-DD
|
||||||
|
adj: 复权类型(加密货币仅支持 'raw')
|
||||||
timeframe: K 线周期(1d, 1h, 4h, 15m, 1m)
|
timeframe: K 线周期(1d, 1h, 4h, 15m, 1m)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
DataFrame with columns: date, open, high, low, close, volume
|
DataFrame with columns: date, open, high, low, close, volume
|
||||||
"""
|
"""
|
||||||
|
# 校验 adj 参数(加密货币仅支持 raw)
|
||||||
|
if adj != 'raw':
|
||||||
|
raise ValueError(f"加密货币不支持复权,adj='{adj}' 无效,仅支持 'raw'")
|
||||||
|
|
||||||
if not CCXT_AVAILABLE:
|
if not CCXT_AVAILABLE:
|
||||||
print(f"⚠️ ccxt 未安装,无法获取 {code}")
|
print(f"⚠️ ccxt 未安装,无法获取 {code}")
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -188,7 +188,10 @@ class TushareSource:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def is_china_index(self, code: str) -> bool:
|
def is_china_index(self, code: str) -> bool:
|
||||||
"""判断是否为A股指数"""
|
"""判断是否为A股指数(排除 ETF)"""
|
||||||
|
# 先排除 ETF
|
||||||
|
if self._is_etf_code(code):
|
||||||
|
return False
|
||||||
return code.endswith(".SH") or code.endswith(".SZ") or code.endswith(".SS") or code.endswith(".CSI")
|
return code.endswith(".SH") or code.endswith(".SZ") or code.endswith(".SS") or code.endswith(".CSI")
|
||||||
|
|
||||||
def is_futures(self, code: str) -> bool:
|
def is_futures(self, code: str) -> bool:
|
||||||
@@ -237,7 +240,10 @@ class TushareSource:
|
|||||||
|
|
||||||
# 原始数据
|
# 原始数据
|
||||||
if adj == 'raw':
|
if adj == 'raw':
|
||||||
if self.is_china_index(code):
|
# 优先判断 ETF(修复:ETF 原始数据获取)
|
||||||
|
if self._is_etf_code(code):
|
||||||
|
return self.fetch_etf(code, start_date, end_date)
|
||||||
|
elif self.is_china_index(code):
|
||||||
return self.fetch_index(code, start_date, end_date)
|
return self.fetch_index(code, start_date, end_date)
|
||||||
elif self.is_futures(code):
|
elif self.is_futures(code):
|
||||||
return self.fetch_futures(code, start_date, end_date)
|
return self.fetch_futures(code, start_date, end_date)
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ class UniversalDataFetcher:
|
|||||||
elif asset_type == AssetType.FUTURES:
|
elif asset_type == AssetType.FUTURES:
|
||||||
return self._fetch_futures(code, start_date, end_date, adj)
|
return self._fetch_futures(code, start_date, end_date, adj)
|
||||||
elif asset_type == AssetType.CRYPTO:
|
elif asset_type == AssetType.CRYPTO:
|
||||||
return self._fetch_crypto(code, start_date, end_date, timeframe)
|
return self._fetch_crypto(code, start_date, end_date, adj, timeframe)
|
||||||
else:
|
else:
|
||||||
print(f"⚠️ 未知资产类型: {code} -> {asset_type}")
|
print(f"⚠️ 未知资产类型: {code} -> {asset_type}")
|
||||||
return None
|
return None
|
||||||
@@ -440,6 +440,7 @@ class UniversalDataFetcher:
|
|||||||
code: str,
|
code: str,
|
||||||
start_date: str,
|
start_date: str,
|
||||||
end_date: str,
|
end_date: str,
|
||||||
|
adj: str = 'raw',
|
||||||
timeframe: str = '1d'
|
timeframe: str = '1d'
|
||||||
) -> Optional[pd.DataFrame]:
|
) -> Optional[pd.DataFrame]:
|
||||||
"""
|
"""
|
||||||
@@ -450,11 +451,13 @@ class UniversalDataFetcher:
|
|||||||
- 需要通过 socks2http 将 SOCKS5 转 HTTP 代理
|
- 需要通过 socks2http 将 SOCKS5 转 HTTP 代理
|
||||||
- 必须指定 timeframe
|
- 必须指定 timeframe
|
||||||
- 不缓存(每次实时下载)
|
- 不缓存(每次实时下载)
|
||||||
|
- 加密货币仅支持 adj='raw'(无复权)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
code: 加密货币代码(BTC, ETH)
|
code: 加密货币代码(BTC, ETH)
|
||||||
start_date: 开始日期
|
start_date: 开始日期
|
||||||
end_date: 结束日期
|
end_date: 结束日期
|
||||||
|
adj: 复权类型(仅支持 'raw')
|
||||||
timeframe: K线周期(1d, 1h, 4h, 15m, 1m)
|
timeframe: K线周期(1d, 1h, 4h, 15m, 1m)
|
||||||
"""
|
"""
|
||||||
# 延迟初始化加密货币数据源
|
# 延迟初始化加密货币数据源
|
||||||
@@ -463,7 +466,7 @@ class UniversalDataFetcher:
|
|||||||
socks_port = self.ssh_config.get('local_port', 1080)
|
socks_port = self.ssh_config.get('local_port', 1080)
|
||||||
self._crypto = get_crypto_source(socks_port=socks_port)
|
self._crypto = get_crypto_source(socks_port=socks_port)
|
||||||
|
|
||||||
return self._crypto.fetch(code, start_date, end_date, timeframe)
|
return self._crypto.fetch(code, start_date, end_date, adj, timeframe)
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# 批量获取
|
# 批量获取
|
||||||
|
|||||||
Reference in New Issue
Block a user