# 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 测试通过
  - 完整文档
