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) # ============================================================ # 批量获取