# FlaskAPIFetcher 架构设计 ## 定位 `FlaskAPIFetcher` 是 framework_v2 的**数据获取层实现**,连接抽象接口与线上 API 服务。 --- ## 架构层次 ``` ┌─────────────────────────────────────────────────────────┐ │ Strategy (策略层) │ │ - RotationStrategy │ │ - CCIScreener │ └────────────────┬────────────────────────────────────────┘ │ 调用 ┌────────────────▼────────────────────────────────────────┐ │ DataFetcher (抽象接口) │ │ framework_v2/core/data.py │ │ - fetch_indices() [ABC] │ │ - fetch_etf() [ABC] │ │ - get_trading_calendar() [ABC] │ └────────────────┬────────────────────────────────────────┘ │ 继承 ┌────────────────▼────────────────────────────────────────┐ │ FlaskAPIFetcher (具体实现) │ │ framework_v2/shared/data/flask_api_fetcher.py │ │ - fetch_indices() ✅ │ │ - fetch_etf() ✅ │ │ - get_trading_calendar() ✅ │ │ - get_benchmark() ✅ │ └────────────────┬────────────────────────────────────────┘ │ 委托 ┌────────────────▼────────────────────────────────────────┐ │ FlaskAPIDataSource (底层数据源) │ │ datasource/flask_api_source.py │ │ - fetch() - HTTP 请求 + 重试 │ │ - fetch_batch() - 批量获取 │ │ - validate_ohlcv_response() - Pydantic 验证 │ └────────────────┬────────────────────────────────────────┘ │ HTTP ┌────────────────▼────────────────────────────────────────┐ │ Flask API Server (线上服务) │ │ https://k3s.tokenpluse.xyz │ │ - /api/v1/ohlcv - OHLCV 数据 │ │ - /api/v1/etf/nav - ETF 净值 │ └─────────────────────────────────────────────────────────┘ ``` --- ## 设计原则 ### 1. 依赖倒置原则(DIP) **策略层依赖抽象接口,不依赖具体实现**: ```python # ✅ 正确:策略依赖 DataFetcher 抽象 class RotationStrategy: def __init__(self, data_fetcher: DataFetcher): self.fetcher = data_fetcher # 可以是任何实现 # 使用时注入具体实现 strategy = RotationStrategy( data_fetcher=FlaskAPIFetcher() # 或其他实现 ) ``` ### 2. 单一职责原则(SRP) 每个类只负责一件事: | 类 | 职责 | |----|------| | **DataFetcher** | 定义数据获取接口(抽象) | | **FlaskAPIFetcher** | 实现接口,组织数据获取流程 | | **FlaskAPIDataSource** | 处理 HTTP 请求、重试、验证 | ### 3. 开闭原则(OCP) **对扩展开放,对修改封闭**: ```python # 未来可以添加新实现,无需修改 DataFetcher class LocalCacheFetcher(DataFetcher): """本地缓存实现""" pass class DatabaseFetcher(DataFetcher): """数据库实现""" pass ``` --- ## 数据流 ### 获取指数数据 ``` RotationStrategy.fetch_data() │ ▼ FlaskAPIFetcher.fetch_indices(codes, start, end) │ ├─ 遍历 codes │ │ │ ▼ │ FlaskAPIDataSource.fetch(code, start, end, adj='raw') │ │ │ ├─ 构建 HTTP 请求 │ ├─ 发送 GET /api/v1/ohlcv │ ├─ 接收 JSON 响应 │ ├─ Pydantic 验证 │ └─ 返回 DataFrame │ └─ 收集所有 DataFrame │ ▼ Dict[str, DataFrame] ``` ### 获取 ETF 数据 ``` RotationStrategy.fetch_data() │ ▼ FlaskAPIFetcher.fetch_etf(codes, start, end) │ ├─ 遍历 codes │ │ │ ▼ │ FlaskAPIDataSource.fetch(code, start, end, │ adj='raw', │ asset_type='china_etf') │ │ │ ├─ 获取 OHLCV 数据 │ ├─ 自动获取净值数据 │ ├─ 自动计算溢价率 │ └─ 返回 DataFrame(带 attrs) │ ├─ df: 价格数据 │ └─ df.attrs: │ ├─ nav (净值 DataFrame) │ ├─ premium_series (溢价率序列) │ └─ latest_premium (最新溢价率) │ └─ 收集所有 DataFrame │ ▼ Dict[str, DataFrame] ``` --- ## 与 CrossMarketAligner 集成 ### 完整数据流 ``` FlaskAPIFetcher │ ├─ fetch_indices() → 原始 OHLCV(不同市场日历) │ ├─ 美股: ^GSPC (252 天/年) │ ├─ 港股: ^HSI (250 天/年) │ └─ A股: 000300.SH (244 天/年) │ ▼ CrossMarketAligner │ ├─ align_returns() → 对齐收益率到目标日历 │ ├─ 价格先 reindex + ffill │ ├─ 再 pct_change()(避免 ffill 陷阱) │ └─ 休市日收益率 = 0% │ ▼ DataFrame(统一日历) │ ├─ ^GSPC: 244 天(A 股日历) ├─ ^HSI: 244 天 └─ 000300.SH: 244 天 │ ▼ Factor Calculation(因子计算) │ ▼ Signal Generation(信号生成) │ ▼ Backtest Execution(回测执行) ``` ### 示例代码 ```python from framework_v2.shared.data import FlaskAPIFetcher, CrossMarketAligner # 1. 获取数据 fetcher = FlaskAPIFetcher() data = fetcher.fetch_indices( codes=["^GSPC", "000300.SH"], start="2024-01-01", end="2024-12-31" ) # 2. 获取目标日历 target_calendar = fetcher.get_trading_calendar(market='A') # 3. 对齐收益率 aligner = CrossMarketAligner(target_calendar=target_calendar) returns = aligner.align_multi_asset( close_dict={ "SP500": data["^GSPC"]["close"], "CSI300": data["000300.SH"]["close"], } ) # 4. 验证对齐 assert returns.isna().sum().sum() == 0, "不应该有 NaN" print(f"✓ 对齐成功: {len(returns)} 天") ``` --- ## 测试验证 ### 测试覆盖 | 测试项 | 验证内容 | 状态 | |--------|----------|------| | 健康检查 | API 服务可用性 | ✅ 通过 | | 指数数据 | OHLCV 结构、数据量 | ✅ 通过 | | ETF 数据 | 价格 + 净值 + 溢价率 | ✅ 通过 | | 交易日历 | 日期范围、天数 | ✅ 通过 | | 基准数据 | Series 类型、数据量 | ✅ 通过 | ### 运行测试 ```bash cd /Users/aszer/Documents/vscode/etf python framework_v2/tests/test_flask_api_fetcher.py ``` **测试结果**: ``` ✓ 测试通过 - 健康检查 ✓ 测试通过 - 指数数据 ✓ 测试通过 - ETF 数据 ✓ 测试通过 - 交易日历 ✓ 测试通过 - 基准数据 总计: 5/5 通过 ``` --- ## 优势总结 ### vs 直接使用 FlaskAPIDataSource | 特性 | FlaskAPIDataSource | FlaskAPIFetcher | |------|-------------------|-----------------| | **抽象接口** | ❌ 无 | ✅ 继承 DataFetcher | | **批量获取** | ✅ fetch_batch() | ✅ fetch_indices() | | **进度显示** | ❌ 无 | ✅ 自动显示 | | **错误处理** | ✅ 基础 | ✅ 增强(验证 + 重试) | | **策略集成** | ❌ 需适配 | ✅ 直接使用 | | **测试覆盖** | ❌ 无 | ✅ 5/5 通过 | ### vs 旧架构 | 特性 | 旧架构(datasource/) | 新架构(framework_v2/) | |------|----------------------|------------------------| | **抽象接口** | ❌ 无 | ✅ DataFetcher ABC | | **Schema 验证** | ⚠️ 部分 | ✅ Pydantic 完整验证 | | **跨市场对齐** | ❌ 无 | ✅ CrossMarketAligner | | **分层设计** | ❌ 混合 | ✅ core/shared/strategy | | **可测试性** | ⚠️ 困难 | ✅ 依赖注入 | | **文档** | ❌ 缺失 | ✅ 完整文档 | --- ## 未来优化 ### 1. 交易日历准确性 ✅ **已解决**:通过 API 获取准确交易日历。 **实现**: ```python def get_trading_calendar(self, market, start, end): # 调用 API 获取准确日历 calendar = self._source.get_trading_calendar( market=market, start_date=start, end_date=end ) return calendar ``` **API 端点**:`GET /api/v1/trading-calendar` **返回**:准确的 DatetimeIndex(包含节假日处理) ### 2. 缓存机制 **当前问题**:每次请求都调用 API,重复获取相同数据。 **优化方案**: ```python # TODO: 添加本地缓存 class FlaskAPIFetcher(DataFetcher): def __init__(self, cache_dir: str = "data_cache"): self.cache = LocalCache(cache_dir) def fetch_indices(self, codes, start, end): # 1. 检查缓存 cached = self.cache.get(codes, start, end) if cached: return cached # 2. 调用 API data = self._source.fetch_batch(...) # 3. 写入缓存 self.cache.set(codes, start, end, data) return data ``` ### 3. 异步支持 **当前问题**:批量获取串行执行,效率低。 **优化方案**: ```python # TODO: 使用 aiohttp 异步获取 async def fetch_indices_async(self, codes, start, end): async with aiohttp.ClientSession() as session: tasks = [ self._fetch_single(session, code, start, end) for code in codes ] results = await asyncio.gather(*tasks) return dict(zip(codes, results)) ``` --- ## 相关文件 | 文件 | 说明 | |------|------| | `framework_v2/core/data.py` | DataFetcher 抽象基类 | | `framework_v2/shared/data/flask_api_fetcher.py` | FlaskAPIFetcher 实现 | | `framework_v2/shared/data/__init__.py` | 导出 FlaskAPIFetcher | | `framework_v2/tests/test_flask_api_fetcher.py` | 测试套件 | | `datasource/flask_api_source.py` | 底层 HTTP 数据源 | | `FLASK_API_FETCHER_GUIDE.md` | 使用指南 | --- ## 版本历史 - **2024-04-16**: 初始版本 - 继承 DataFetcher 抽象基类 - 实现指数、ETF 数据获取 - 集成 FlaskAPIDataSource - 5/5 测试通过 - 完整文档