From 3697c9d38be677c1664ca249ea8de04c8f7fc6c8 Mon Sep 17 00:00:00 2001 From: aszerW Date: Sat, 23 May 2026 21:46:01 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E6=9E=B6=E6=9E=84=E9=80=BB=E8=BE=91Bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复内容: 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 原始价格数据获取恢复正常 - 类型判断逻辑更准确 - 数据源接口签名统一 --- datasource/ccxt_source.py | 6 ++++++ datasource/tushare_source.py | 10 ++++++++-- datasource/universal_fetcher.py | 7 +++++-- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/datasource/ccxt_source.py b/datasource/ccxt_source.py index e2344cd..4477175 100644 --- a/datasource/ccxt_source.py +++ b/datasource/ccxt_source.py @@ -146,6 +146,7 @@ class CCXTSource: code: str, start_date: str, end_date: str, + adj: str = 'raw', timeframe: str = '1d' ) -> Optional[pd.DataFrame]: """ @@ -155,11 +156,16 @@ class CCXTSource: code: 加密货币代码(BTC, ETH 等) start_date: 开始日期 YYYY-MM-DD end_date: 结束日期 YYYY-MM-DD + adj: 复权类型(加密货币仅支持 'raw') timeframe: K 线周期(1d, 1h, 4h, 15m, 1m) Returns: DataFrame with columns: date, open, high, low, close, volume """ + # 校验 adj 参数(加密货币仅支持 raw) + if adj != 'raw': + raise ValueError(f"加密货币不支持复权,adj='{adj}' 无效,仅支持 'raw'") + if not CCXT_AVAILABLE: print(f"⚠️ ccxt 未安装,无法获取 {code}") return None diff --git a/datasource/tushare_source.py b/datasource/tushare_source.py index c7c8131..4af5eb9 100644 --- a/datasource/tushare_source.py +++ b/datasource/tushare_source.py @@ -188,7 +188,10 @@ class TushareSource: return None 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") def is_futures(self, code: str) -> bool: @@ -237,7 +240,10 @@ class TushareSource: # 原始数据 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) elif self.is_futures(code): return self.fetch_futures(code, start_date, end_date) diff --git a/datasource/universal_fetcher.py b/datasource/universal_fetcher.py index b3dc0d2..4c128c8 100644 --- a/datasource/universal_fetcher.py +++ b/datasource/universal_fetcher.py @@ -255,7 +255,7 @@ class UniversalDataFetcher: elif asset_type == AssetType.FUTURES: return self._fetch_futures(code, start_date, end_date, adj) 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: print(f"⚠️ 未知资产类型: {code} -> {asset_type}") return None @@ -440,6 +440,7 @@ class UniversalDataFetcher: code: str, start_date: str, end_date: str, + adj: str = 'raw', timeframe: str = '1d' ) -> Optional[pd.DataFrame]: """ @@ -450,11 +451,13 @@ class UniversalDataFetcher: - 需要通过 socks2http 将 SOCKS5 转 HTTP 代理 - 必须指定 timeframe - 不缓存(每次实时下载) + - 加密货币仅支持 adj='raw'(无复权) Args: code: 加密货币代码(BTC, ETH) start_date: 开始日期 end_date: 结束日期 + adj: 复权类型(仅支持 'raw') timeframe: K线周期(1d, 1h, 4h, 15m, 1m) """ # 延迟初始化加密货币数据源 @@ -463,7 +466,7 @@ class UniversalDataFetcher: socks_port = self.ssh_config.get('local_port', 1080) 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) # ============================================================ # 批量获取