# V2 配置设计文档

## 概述

framework_v2 的配置系统基于 **Pydantic Schema 验证**，解决 V1 配置文件的 10 个问题。

---

## 核心设计原则

### 1. **类型安全**（解决 V1 问题 #2）

```python
# ❌ V1: 无验证
n_days: 25           # 如果是 "25"（字符串）会静默失败

# ✅ V2: Pydantic 验证
class FactorConfig(BaseModel):
    n_days: int = Field(default=25, ge=5, le=250)
```

**验证效果**：
```python
# 错误：超出范围
factor: {n_days: 1000}
# → ValidationError: n_days 必须在 5-250 之间

# 错误：类型错误
factor: {n_days: "25"}
# → ValidationError: n_days 必须是整数
```

---

### 2. **敏感信息环境变量化**（解决 V1 问题 #1）

```yaml
# ❌ V1: 硬编码
flask_api:
  url: "https://k3s.tokenpluse.xyz"
ssh_tunnel:
  host: "8.218.167.69"
  key_path: "hk_ecs.pem"

# ✅ V2: 环境变量
data:
  sources:
    - type: "flask_api"
      url: "${FLASK_API_URL}"    # 从环境变量读取
    - type: "tushare"
      token: "${TUSHARE_TOKEN}"  # 从环境变量读取
```

**环境变量替换**：
```python
# 支持格式
${VAR_NAME}                  # 必需环境变量
${VAR_NAME:default_value}    # 带默认值

# 示例
url: "${FLASK_API_URL}"                   # 必须设置
timeout: "${TIMEOUT:120}"                 # 默认 120
```

---

### 3. **资产池按类别分组**（解决 V1 问题 #3）

```yaml
# ❌ V1: 混合在一起
code_list:
  "399006.SZ":
    name: "创业板指"
    market: "A"
  "GC=F":
    name: "黄金"
    market: "COMMODITY"

# ✅ V2: 按类别分组
asset_pools:
  equity:                        # 股票资产
    "399006.SZ":
      name: "创业板指"
      market: "CN_EQUITY"
    "NDX":
      name: "纳指100"
      market: "US_EQUITY"
  
  commodity:                     # 商品资产
    "GC=F":
      name: "黄金"
      market: "COMMODITY"
  
  fixed_income:                  # 固定收益
    "931862.CSI":
      name: "短债指数"
      market: "FIXED_INCOME"
```

**优势**：
- ✅ 清晰的资产分类
- ✅ 支持分散化策略（每类选 Top-N）
- ✅ 易于扩展新资产类别

---

### 4. **精简注释 + 独立文档**（解决 V1 问题 #4）

```yaml
# ❌ V1: 30 行注释
# 931862.CSI = 中证0-9个月国债指数（短债指数）
# 数据范围：2007-12-31开始，约19年数据
# 久期：极短（<1年），波动极小，熊市防御效果最佳
# ...（共 30 行）
"931862.CSI":
  name: "短债指数"
  etf: null
  market: "BOND"

# ✅ V2: 精简注释 + description 字段
fixed_income:
  "931862.CSI":
    name: "短债指数"
    etf: null
    market: "FIXED_INCOME"
    description: "中证0-9个月国债指数，久期<1年，防御配置"
```

**详细文档独立**：
```
docs/
├── bond_analysis.md           # 债券收益归因分析
├── asset_pool_design.md       # 资产池设计说明
└── threshold_strategy.md      # 阈值策略文档
```

---

### 5. **统一阈值配置**（解决 V1 问题 #6）

```yaml
# ❌ V1: V2 + V3 共存，逻辑混乱
min_score: 0.0                    # V2 固定阈值

bond_threshold:                   # V3 动态阈值
  enabled: true
  bond_code: "931862.CSI"
  ratio: 1.0
  fill_bond: true

# ✅ V2: 统一配置
rotation:
  threshold:
    mode: "dynamic"               # 阈值模式: fixed / dynamic
    fixed_value: 0.0              # mode=fixed 时使用
    
    dynamic:                      # mode=dynamic 时使用
      reference: "931862.CSI"     # 参考标的
      ratio: 1.0                  # 倍数
      fallback_enabled: true      # 回退开关
      fallback_value: 0.0         # 回退值
```

**模式切换**：
```yaml
# 固定阈值模式
threshold:
  mode: "fixed"
  fixed_value: 0.0

# 动态阈值模式
threshold:
  mode: "dynamic"
  dynamic:
    reference: "931862.CSI"
    ratio: 1.0
```

---

### 6. **标准化市场类型**（解决 V1 问题 #8）

```yaml
# ❌ V1: 随意定义
market: "A"         # A股
market: "US"        # 美股
market: "JP"        # 日本

# ✅ V2: 标准枚举
market: "CN_EQUITY"       # 中国股票
market: "US_EQUITY"       # 美国股票
market: "JP_EQUITY"       # 日本股票
market: "EU_EQUITY"       # 欧洲股票
market: "HK_EQUITY"       # 香港股票
market: "COMMODITY"       # 商品
market: "FIXED_INCOME"    # 固定收益
```

**Python 枚举**：
```python
class MarketType(str, Enum):
    CN_EQUITY = "CN_EQUITY"
    US_EQUITY = "US_EQUITY"
    JP_EQUITY = "JP_EQUITY"
    EU_EQUITY = "EU_EQUITY"
    HK_EQUITY = "HK_EQUITY"
    COMMODITY = "COMMODITY"
    FIXED_INCOME = "FIXED_INCOME"
```

---

### 7. **多数据源降级策略**（解决 V1 问题 #9）

```yaml
# ❌ V1: 扁平化配置
flask_api:
  enabled: true
  url: "https://k3s.tokenpluse.xyz"

ssh_tunnel:
  enabled: true
  # ...

# ✅ V2: 优先级列表
data:
  sources:
    # 主数据源
    - type: "flask_api"
      enabled: true
      url: "${FLASK_API_URL}"
      timeout: 120
    
    # 备用数据源
    - type: "tushare"
      enabled: true
      token: "${TUSHARE_TOKEN}"
      timeout: 60
    
    # 降级数据源
    - type: "yfinance"
      enabled: false
      timeout: 120
```

**降级逻辑**：
```python
for source in config.data.sources:
    if not source.enabled:
        continue
    
    try:
        data = fetch_from_source(source)
        return data
    except Exception as e:
        print(f"数据源 {source.type} 失败: {e}")
        continue

raise ValueError("所有数据源均失败")
```

---

### 8. **配置版本控制**（解决 V1 问题 #10）

```yaml
# ❌ V1: 无版本信息
# ETF轮动策略配置
# （无版本）

# ✅ V2: 元数据
metadata:
  version: "1.0.0"
  strategy: "rotation"
  description: "ETF轮动策略 V2 - 基于 framework_v2 架构"
  last_updated: "2024-04-16"
```

---

## Schema 层次结构

```
RotationStrategyConfig
├── metadata: MetadataConfig
│   ├── version: str
│   ├── strategy: str
│   ├── description: str
│   └── last_updated: str
│
├── asset_pools: AssetPool
│   ├── equity: Dict[str, AssetConfig]
│   ├── commodity: Dict[str, AssetConfig]
│   └── fixed_income: Dict[str, AssetConfig]
│
├── benchmark: BenchmarkConfig
│   ├── code: str
│   └── name: str
│
├── backtest: BacktestConfig
│   ├── start_date: str
│   └── end_date: Optional[str]
│
├── factor: FactorConfig
│   ├── type: FactorType
│   ├── n_days: int (5-250)
│   ├── auto_day: bool
│   ├── min_days: int
│   └── max_days: int
│
├── rotation: RotationConfig
│   ├── select_num: int (1-10)
│   ├── diversified: bool
│   └── threshold: ThresholdConfig
│       ├── mode: ThresholdMode
│       ├── fixed_value: float
│       └── dynamic: DynamicThresholdConfig
│           ├── reference: str
│           ├── ratio: float
│           ├── fallback_enabled: bool
│           └── fallback_value: float
│
├── rebalance: RebalanceConfig
│   ├── min_hold_days: int (1-30)
│   ├── score_threshold: float (0-0.5)
│   └── trade_cost: float (0-0.01)
│
├── premium_control: PremiumControlConfig
│   ├── enabled: bool
│   ├── default_threshold: float
│   ├── mode: PremiumMode
│   ├── penalty_factor: float
│   └── market_overrides: Dict[str, MarketPremiumOverride]
│
└── data: DataConfig
    ├── sources: List[DataSourceConfig]
    ├── use_cache: bool
    └── cache_dir: str
```

---

## 使用示例

### 基础使用

```python
from framework_v2.config import load_config

# 加载配置文件
config = load_config('rotation_example.yaml')

# 访问配置
print(f"动量窗口: {config.factor.n_days}")
print(f"选股数量: {config.rotation.select_num}")
print(f"数据源: {len(config.data.sources)} 个")
```

### 验证错误处理

```python
from pydantic import ValidationError

try:
    config = load_config('invalid_config.yaml')
except ValidationError as e:
    print(f"配置验证失败: {e}")
    # 输出详细错误信息
    for error in e.errors():
        print(f"  - {error['loc']}: {error['msg']}")
```

### 环境变量

```bash
# 设置环境变量
export FLASK_API_URL="https://k3s.tokenpluse.xyz"
export TUSHARE_TOKEN="your_token_here"

# 加载配置（自动替换环境变量）
config = load_config('rotation_example.yaml')
```

---

## 文件结构

```
framework_v2/config/
├── __init__.py                # 导出模块
├── schemas.py                 # Pydantic Schema 定义（275 行）
├── loader.py                  # 配置加载器（237 行）
├── rotation_example.yaml      # 示例配置文件（195 行）
└── CONFIG_DESIGN.md           # 配置设计文档（本文件）

framework_v2/tests/
└── test_config.py             # 配置测试（286 行）
```

---

## 测试验证

### 测试结果

```
✓ 测试 1: 加载配置文件 - 通过
✓ 测试 2: 资产池配置 - 通过
✓ 测试 3: 阈值配置 - 通过
✓ 测试 4: 数据源配置 - 通过
✓ 测试 5: 验证错误处理 - 通过
✓ 测试 6: 环境变量替换 - 通过

总计: 6/6 通过
```

### 验证覆盖

| 验证项 | 测试内容 | 状态 |
|--------|----------|------|
| 配置加载 | YAML 解析 + 环境变量替换 | ✅ |
| 类型验证 | n_days 范围、必需字段 | ✅ |
| 资产池 | 股票/商品/债券分类 | ✅ |
| 阈值配置 | 固定/动态模式切换 | ✅ |
| 数据源 | 多源降级配置 | ✅ |
| 错误处理 | ValidationError 捕获 | ✅ |

---

## V1 vs V2 对比

| 特性 | V1 | V2 | 改进 |
|------|----|----|----|
| **类型安全** | ❌ 无验证 | ✅ Pydantic Schema | 早期失败 |
| **敏感信息** | ❌ 硬编码 | ✅ 环境变量 | 安全性 |
| **资产分类** | ❌ 混合 | ✅ 按类别分组 | 可维护性 |
| **注释** | ❌ 冗长（30行） | ✅ 精简 + 独立文档 | 可读性 |
| **阈值逻辑** | ❌ V2/V3 共存 | ✅ 统一配置 | 逻辑清晰 |
| **市场类型** | ❌ 随意字符串 | ✅ 标准枚举 | 一致性 |
| **数据源** | ❌ 扁平化 | ✅ 优先级列表 | 可靠性 |
| **版本控制** | ❌ 无 | ✅ 元数据 | 可追溯 |

---

## 迁移指南

### 从 V1 迁移到 V2

#### 1. 更新配置文件结构

```yaml
# V1
code_list:
  "399006.SZ":
    name: "创业板指"
    etf: "159915.SZ"
    market: "A"

# V2
asset_pools:
  equity:
    "399006.SZ":
      name: "创业板指"
      etf: "159915.SZ"
      market: "CN_EQUITY"
```

#### 2. 更新市场类型

```yaml
# V1 → V2 映射
"A" → "CN_EQUITY"
"US" → "US_EQUITY"
"HK" → "HK_EQUITY"
"COMMODITY" → "COMMODITY"
"BOND" → "FIXED_INCOME"
```

#### 3. 更新阈值配置

```yaml
# V1
min_score: 0.0
bond_threshold:
  enabled: true
  bond_code: "931862.CSI"
  ratio: 1.0

# V2
rotation:
  threshold:
    mode: "dynamic"
    dynamic:
      reference: "931862.CSI"
      ratio: 1.0
```

#### 4. 更新数据源配置

```yaml
# V1
flask_api:
  enabled: true
  url: "https://k3s.tokenpluse.xyz"

# V2
data:
  sources:
    - type: "flask_api"
      enabled: true
      url: "${FLASK_API_URL}"
```

---

## 最佳实践

### 1. 使用环境变量管理敏感信息

```bash
# .env 文件（不要提交到 Git）
FLASK_API_URL=https://k3s.tokenpluse.xyz
TUSHARE_TOKEN=your_token_here
SSH_HOST=8.218.167.69
SSH_KEY_PATH=/path/to/hk_ecs.pem
```

### 2. 为不同环境创建不同配置

```
config/
├── rotation_dev.yaml       # 开发环境
├── rotation_prod.yaml      # 生产环境
└── rotation_test.yaml      # 测试环境
```

### 3. 使用版本控制追踪配置变更

```yaml
metadata:
  version: "1.0.0"
  last_updated: "2024-04-16"
  changelog:
    - "2024-04-16: 初始版本"
```

### 4. 定期验证配置

```bash
# 运行配置测试
python framework_v2/tests/test_config.py
```

---

## 未来优化

1. [ ] 配置热重载（无需重启策略）
2. [ ] 配置 diff 工具（对比版本差异）
3. [ ] 配置 UI 编辑器（可视化配置）
4. [ ] 配置模板系统（快速创建新策略）

---

## 版本历史

- **2024-04-16**: 初始版本
  - Pydantic Schema 验证
  - 环境变量替换
  - 资产池分类
  - 统一阈值配置
  - 多数据源降级
  - 6/6 测试通过
