fix(datasource): 修正混合数据源导入路径错误
- 修正 strategies.rotation.engine 中 hybrid_source 模块导入路径错误 - 新增 core.datasource 目录下多个数据源实现模块 - 增加 Akshare 数据源支持 A股指数数据拉取 - 实现数据缓存管理机制,支持本地数据缓存读写 - 新增 YFinance 数据源,支持通过 SSH 隧道访问美股和港股数据 - 实现混合数据源支持 A股/Tushare、港美股/YFinance、加密货币/CCXT 的统一访问 - 集成 SSH 隧道管理,支持 SOCKS5 转 HTTP 代理转发 - 新增 socks2http.py 代理转发工具,解决 CCXT 仅支持 HTTP 代理问题 - 修改 rotation.yaml 加密货币注释,明确使用 OKX 现货和 SSH->HTTP 代理访问 - 删除.gitignore中无用的 data/ 忽略规则,保留 test/ 文件夹忽略规则
This commit is contained in:
128
core/datasource/akshare_source.py
Normal file
128
core/datasource/akshare_source.py
Normal file
@@ -0,0 +1,128 @@
|
||||
"""
|
||||
akshare数据源实现(用于CCI筛选等场景)
|
||||
"""
|
||||
|
||||
import time
|
||||
import pandas as pd
|
||||
import akshare as ak
|
||||
from typing import Optional
|
||||
|
||||
from .base import DataSource
|
||||
|
||||
|
||||
class AkshareDataSource(DataSource):
|
||||
"""基于akshare的数据源"""
|
||||
|
||||
def __init__(self, delay: float = 3.0):
|
||||
"""
|
||||
初始化akshare数据源
|
||||
|
||||
Args:
|
||||
delay: 每次请求间隔秒数(避免触发限流)
|
||||
"""
|
||||
self.delay = delay
|
||||
|
||||
def fetch_ohlcv(
|
||||
self,
|
||||
code: str,
|
||||
start_date: str,
|
||||
end_date: str,
|
||||
fields: Optional[list] = None,
|
||||
) -> pd.DataFrame:
|
||||
"""
|
||||
获取指数历史数据(使用akshare的东方财富接口)
|
||||
|
||||
Args:
|
||||
code: 指数代码,如 '000300'(不带后缀)
|
||||
start_date: 起始日期 'YYYY-MM-DD'
|
||||
end_date: 结束日期 'YYYY-MM-DD'
|
||||
|
||||
Returns:
|
||||
DataFrame,包含 date, open, high, low, close, volume
|
||||
"""
|
||||
# 转换日期格式
|
||||
sd = start_date.replace("-", "")
|
||||
ed = end_date.replace("-", "")
|
||||
|
||||
# 去除后缀
|
||||
symbol = code.replace(".SH", "").replace(".SZ", "")
|
||||
|
||||
time.sleep(self.delay)
|
||||
|
||||
try:
|
||||
df = ak.index_zh_a_hist(
|
||||
symbol=symbol,
|
||||
period="daily",
|
||||
start_date=sd,
|
||||
end_date=ed,
|
||||
)
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"akshare查询失败 [{code}]: {e}")
|
||||
|
||||
if df is None or df.empty:
|
||||
raise ValueError(f"akshare返回空数据: {code}")
|
||||
|
||||
# 统一列名
|
||||
df = df.rename(
|
||||
columns={
|
||||
"日期": "date",
|
||||
"开盘": "open",
|
||||
"最高": "high",
|
||||
"最低": "low",
|
||||
"收盘": "close",
|
||||
"成交量": "volume",
|
||||
}
|
||||
)
|
||||
df["date"] = pd.to_datetime(df["date"])
|
||||
df["code"] = code
|
||||
df = df[["date", "code", "open", "high", "low", "close", "volume"]]
|
||||
df = df.sort_values("date").reset_index(drop=True)
|
||||
|
||||
return df
|
||||
|
||||
def fetch_multiple(
|
||||
self,
|
||||
codes: list,
|
||||
start_date: str,
|
||||
end_date: str,
|
||||
) -> tuple[pd.DataFrame, list]:
|
||||
"""批量获取数据"""
|
||||
df_list = []
|
||||
failed = []
|
||||
|
||||
for code in codes:
|
||||
try:
|
||||
df = self.fetch_ohlcv(code, start_date, end_date)
|
||||
df_list.append(df[["date", "code", "close"]].copy())
|
||||
except Exception as e:
|
||||
print(f" ⚠ 跳过 {code}: {e}")
|
||||
failed.append(code)
|
||||
|
||||
if not df_list:
|
||||
raise RuntimeError("所有数据获取失败")
|
||||
|
||||
all_df = pd.concat(df_list, ignore_index=True)
|
||||
data = all_df.pivot(index="date", columns="code", values="close")
|
||||
data = data.sort_index()
|
||||
|
||||
return data, failed
|
||||
|
||||
def fetch_index_list(self) -> pd.DataFrame:
|
||||
"""
|
||||
获取所有A股指数列表
|
||||
|
||||
Returns:
|
||||
DataFrame with index info
|
||||
"""
|
||||
sources = ["沪深重要指数", "上证系列指数", "深证系列指数", "中证系列指数"]
|
||||
df_list = []
|
||||
|
||||
for source in sources:
|
||||
try:
|
||||
df = ak.stock_zh_index_spot_em(symbol=source)
|
||||
df["source"] = source
|
||||
df_list.append(df)
|
||||
except Exception as e:
|
||||
print(f" ⚠ 获取 {source} 失败: {e}")
|
||||
|
||||
return pd.concat(df_list, ignore_index=True) if df_list else pd.DataFrame()
|
||||
Reference in New Issue
Block a user