# Renew Interval 需求总结与分析

**版本**：2.1  
**最后更新**：2026-01-23

---

## 设计原则

### 框架实现 vs 用户实现

**框架实现（配置化）**：
- ✅ **间隔更新**：超过N个时间单位没更新的，触发更新（per entity）
- ✅ **固定时间更新**：超过了某个/某几个固定日期的，补全固定时间点的数据（per entity）

**用户实现（钩子函数）**：
- ⚠️ **随机/带游标抽样**：太灵活，抽样可能需要额外记录，通过 `on_before_fetch` 钩子实现
- ⚠️ **特殊条件刷新**：条件未知，可能状况太多，通过 `on_before_fetch` 钩子实现

**设计理由**：
- 框架只实现最通用、最常见的需求，保持简洁性
- 复杂、灵活的场景通过钩子暴露给用户，保持可扩展性
- 避免框架过度设计，降低学习成本和维护成本

---

## 核心需求（框架实现）

### 需求1：间隔更新（Interval-based Renew）

**定义**：对上次更新时间超过 N 个时间单位的数据进行更新

**配置示例**：
```json
{
  "renew_interval": {
    "enabled": true,
    "unit": "day",      // day/week/month/quarter
    "value": 30,        // 间隔值
    "field": "date"     // 用于检查的日期字段（可选，默认 "date"）
  }
}
```

**使用场景**：
1. **复权因子事件**（`adj_factor_event`）
   - 更新不频繁，可以间隔 N 天/周更新一次
   - 当前实现：`update_threshold_days = 15`（至少15天未更新才更新）
   - 数据特征：变化频率低，不需要频繁更新

2. **切片数据**（`stock_index_indicator_weight`）
   - 股票指数的成分和占比、股票分类等
   - 不需要经常更新，可以超过 N 天未更新再更新
   - 当前实现：至少30天才更新一次
   - 数据特征：变化频率低，但需要定期更新以反映变化

3. **K线周期数据**（`kline`）
   - 周线：至少间隔1周才更新
   - 月线：至少间隔1个月才更新
   - 数据特征：不同周期有不同的更新频率要求

---

### 需求2：固定时间全量更新（Fixed-time Full Renew）

**定义**：在固定时间点进行全量更新（忽略间隔检查）

**配置示例**：
```json
{
  "renew_schedule": {
    "enabled": true,
    "type": "monthly",      // monthly/weekly/daily
    "months": [1, 4, 7, 10],  // 每年这些月份（1=1月，4=4月等）
    "day": 1,              // 每月几号（1-31），可选
    "force_all": true       // 是否强制更新所有实体（忽略批次）
  }
}
```

**使用场景**：
1. **公司财务数据**（`corporate_finance`）
   - 财报季度月份（1、4、7、10月）需要全量更新
   - 平时可以 rolling 一部分（批次更新）
   - 数据特征：季度数据，在财报发布月份需要及时全量更新

2. **GDP/季度数据**（`gdp`）
   - 季度数据，可能在固定月份发布
   - 需要固定时间全量更新以确保数据及时性
   - 数据特征：季度数据，发布有固定时间规律

3. **切片数据**（`stock_index_indicator_weight`）
   - 可以固定时间切片（如每月1号、每季度第一个交易日）
   - 确保定期更新以反映指数成分变化
   - 数据特征：变化频率低，但有固定更新周期

---

## 详细场景分析

### 场景1：复权因子事件（adj_factor_event）

**数据特征**：
- 更新频率：低（复权事件不频繁发生）
- 数据重要性：高（影响K线复权计算）
- 更新策略：间隔更新（至少N天未更新才更新）

**当前实现**：
- `update_threshold_days = 15`（至少15天未更新才更新）
- 通过 SQL 查询找出超过阈值未更新的股票

**需求匹配**：✅ 需求1（间隔更新）

**配置建议**：
```json
{
  "renew_mode": "incremental",
  "renew_interval": {
    "enabled": true,
    "unit": "day",
    "value": 15,
    "field": "last_update"
  }
}
```

---

### 场景2：公司财务数据（corporate_finance）

**数据特征**：
- 更新频率：季度（财报发布有固定时间）
- 数据重要性：高（财务指标分析）
- 更新策略：**混合策略**（固定时间全量 + 平时批次）

**当前实现**：
- 批次轮转：`renew_rolling_batch = 8`（每次处理约1/8的股票）
- 滚动窗口：`rolling_quarters = 3`（每次刷新最近3个季度）
- **缺失**：固定时间全量更新逻辑（需要在财报季度月份全量更新）

**需求匹配**：✅ 需求1（间隔更新）+ ✅ 需求2（固定时间全量更新）

**配置建议**：
```json
{
  "renew_mode": "incremental",
  "date_format": "quarter",
  "renew_schedule": {
    "enabled": true,
    "type": "monthly",
    "months": [1, 4, 7, 10],  // 财报季度月份
    "day": 1,
    "force_all": true          // 强制更新所有股票
  },
  "renew_batch": {
    "enabled": true,
    "batch_size": 8            // 非财报月份使用批次更新
  },
  "rolling_quarters": 3
}
```

---

### 场景3：月/季度数据（price_indexes, gdp）

**数据特征**：
- 更新频率：月度/季度
- 数据重要性：中高（宏观经济指标）
- 更新策略：滚动刷新（rolling）

**当前实现**：
- `price_indexes`: `rolling_unit: "month"`, `rolling_length: 12`（滚动刷新最近12个月）
- `gdp`: `rolling_unit: "quarter"`, `rolling_length: 4`（滚动刷新最近4个季度）

**需求匹配**：✅ 需求1（间隔更新，通过 rolling 实现）

**配置建议**：
```json
{
  "renew_mode": "rolling",
  "rolling_unit": "month",
  "rolling_length": 12
}
```

**注意**：rolling 模式本身已经实现了"间隔更新"的效果（只更新最近N个时间单位），但可能需要额外的"固定时间全量更新"来确保数据及时性。

---

### 场景4：切片数据（stock_index_indicator_weight）

**数据特征**：
- 更新频率：低（指数成分变化不频繁）
- 数据重要性：中（影响指数计算）
- 更新策略：**混合策略**（间隔更新 + 可选固定时间）

**当前实现**：
- 至少30天才更新一次（间隔检查）
- 通过 SQL 查询找出超过30天未更新的股票

**需求匹配**：✅ 需求1（间隔更新）+ ✅ 需求2（可选固定时间全量更新）

**配置建议**：
```json
{
  "renew_mode": "incremental",
  "renew_interval": {
    "enabled": true,
    "unit": "day",
    "value": 30,
    "field": "date"
  },
  "renew_schedule": {
    "enabled": true,
    "type": "monthly",
    "months": [1],           // 每月1号
    "day": 1,
    "force_all": true        // 可选：固定时间全量更新
  }
}
```

---

### 场景5：K线周期数据（kline）

**数据特征**：
- 更新频率：不同周期不同（daily/weekly/monthly）
- 数据重要性：高（核心价格数据）
- 更新策略：增量更新 + 周期间隔检查

**当前实现**：
- 日线：每天更新（增量）
- 周线：至少间隔1周才更新
- 月线：至少间隔1个月才更新

**需求匹配**：✅ 需求1（间隔更新，按周期不同）

**配置建议**：
```json
{
  "renew_mode": "incremental",
  "renew_interval": {
    "enabled": true,
    "unit": "week",          // 周线：至少1周
    "value": 1,
    "field": "date",
    "term": "weekly"         // 按周期分别配置
  }
}
```

**注意**：K线有多个周期（daily/weekly/monthly），需要支持按周期分别配置间隔。

---

## 潜在遗漏场景分析

### 场景6：高频数据 + 固定时间修正（潜在需求）

**数据特征**：
- 更新频率：高（每天更新）
- 数据重要性：高
- 更新策略：增量更新 + 固定时间全量修正

**示例**：
- K线数据：每天增量更新，但每月1号全量刷新最近1个月（修正可能的错误数据）
- 复权因子：每天增量更新，但每周一全量刷新最近1周

**当前状态**：❌ 未实现

**需求匹配**：✅ 需求1（间隔更新）+ ✅ 需求2（固定时间全量更新）

**配置建议**：
```json
{
  "renew_mode": "incremental",
  "renew_schedule": {
    "enabled": true,
    "type": "monthly",
    "months": [1],           // 每月1号
    "day": 1,
    "force_all": true,
    "date_range": {           // 可选：限制全量更新的日期范围
      "unit": "month",
      "value": 1              // 只刷新最近1个月
    }
  }
}
```

---

### 场景7：工作日/交易日固定更新（潜在需求）

**数据特征**：
- 更新频率：工作日/交易日
- 数据重要性：中高
- 更新策略：固定时间更新（但基于交易日历）

**示例**：
- 每周第一个交易日更新
- 每月第一个交易日更新
- 每季度第一个交易日更新

**当前状态**：❌ 未实现

**需求匹配**：✅ 需求2（固定时间全量更新，但需要交易日历支持）

**配置建议**：
```json
{
  "renew_schedule": {
    "enabled": true,
    "type": "trading_day",   // 交易日模式
    "frequency": "weekly",    // 每周
    "day_of_week": 1,        // 第一个交易日
    "force_all": true
  }
}
```

---

### 场景8：条件触发更新（潜在需求）

**数据特征**：
- 更新频率：条件触发
- 数据重要性：中
- 更新策略：基于条件的更新

**示例**：
- 当某个依赖数据更新后，触发更新
- 当数据量达到阈值后，触发全量更新
- 当检测到数据异常后，触发修正更新

**当前状态**：❌ 未实现（可能通过依赖注入实现）

**需求匹配**：⚠️ 超出当前需求范围，但值得考虑

---

## 需求总结

### 已覆盖的场景

1. ✅ **间隔更新**（需求1）
   - 复权因子事件：至少N天未更新才更新
   - 切片数据：至少N天未更新才更新
   - K线周期数据：不同周期不同间隔

2. ✅ **固定时间全量更新**（需求2）
   - 公司财务：财报季度月份全量更新
   - 切片数据：可选固定时间全量更新

3. ✅ **混合策略**
   - 公司财务：固定时间全量 + 平时批次更新

### 潜在遗漏的场景

1. ⚠️ **高频数据 + 固定时间修正**
   - 每天增量更新，但固定时间全量刷新最近N个时间单位
   - 用于修正可能的错误数据

2. ⚠️ **工作日/交易日固定更新**
   - 基于交易日历的固定时间更新
   - 需要交易日历支持

3. ⚠️ **条件触发更新**
   - 基于依赖数据更新的触发
   - 基于数据异常的触发

---

## 实现优先级建议

### 优先级1：核心需求（必须实现）

1. **间隔更新**（需求1）
   - 使用频率：⭐⭐⭐⭐⭐（高频）
   - 代码重复度：⭐⭐⭐⭐（高）
   - 实现复杂度：⭐⭐⭐（中等）
   - **收益明显**：消除多个 handler 中的重复代码

2. **固定时间全量更新**（需求2）
   - 使用频率：⭐⭐⭐⭐（中高频）
   - 代码重复度：⭐⭐（中等）
   - 实现复杂度：⭐⭐（低-中等）
   - **有明确需求**：`corporate_finance` 需要财报季度全量更新

### 优先级2：增强功能（建议实现）

3. **高频数据 + 固定时间修正**
   - 使用频率：⭐⭐⭐（中频）
   - 代码重复度：⭐（低）
   - 实现复杂度：⭐⭐⭐（中等）
   - **潜在价值**：提高数据质量

### 优先级3：扩展功能（可选实现）

4. **工作日/交易日固定更新**
   - 使用频率：⭐⭐（低频）
   - 代码重复度：⭐（低）
   - 实现复杂度：⭐⭐⭐⭐（高，需要交易日历支持）
   - **价值有限**：当前需求不明确

5. **条件触发更新**
   - 使用频率：⭐（极低频）
   - 代码重复度：⭐（低）
   - 实现复杂度：⭐⭐⭐⭐⭐（很高）
   - **超出范围**：可能通过依赖注入实现

---

## 用户实现场景（通过钩子函数）

### 场景1：随机/带游标抽样

**数据特征**：
- 数据量：大（需要分批处理）
- 更新频率：低（不需要频繁更新）
- 更新策略：批次轮转（每次处理一部分）

**实现方式**：通过 `on_before_fetch` 钩子实现

**示例**：`corporate_finance` 的批次轮转逻辑
```python
def on_before_fetch(self, context: Dict[str, Any], apis: List[ApiJob]) -> List[ApiJob]:
    # 用户自己实现批次轮转逻辑
    all_stocks = context.get("stock_list", [])
    batch_size = len(all_stocks) // self.renew_rolling_batch
    batch_offset = self._get_batch_offset()  # 从缓存读取
    effective_stocks = self._select_batch(all_stocks, batch_offset, batch_size)
    # ... 创建 ApiJobs
```

**设计理由**：
- 抽样逻辑太灵活，不同场景需求不同
- 批次游标需要额外记录（缓存、数据库等），框架不应该强制管理
- 通过钩子暴露，用户可以根据业务需求灵活实现

---

### 场景2：特殊条件刷新

**数据特征**：
- 更新条件：复杂、多变
- 更新策略：基于业务逻辑的条件判断

**实现方式**：通过 `on_before_fetch` 钩子实现

**示例场景**：
- 当某个依赖数据更新后，触发更新
- 当数据量达到阈值后，触发全量更新
- 当检测到数据异常后，触发修正更新

**设计理由**：
- 条件未知，可能状况太多
- 不同业务场景的条件判断逻辑差异很大
- 框架不应该内置复杂的条件判断逻辑
- 通过钩子暴露，用户可以根据业务需求灵活实现

---

## 总结

### 统一配置结构

```json
{
  "renew_mode": "incremental",  // incremental/rolling/refresh
  
  // 需求1：间隔更新
  "renew_interval": {
    "enabled": true,
    "unit": "day",              // day/week/month/quarter
    "value": 30,                 // 间隔值
    "field": "date",             // 用于检查的日期字段（可选）
    "term": "weekly"             // 可选：按周期分别配置（用于kline）
  },
  
  // 需求2：固定时间全量更新
  "renew_schedule": {
    "enabled": true,
    "type": "monthly",           // monthly/weekly/daily/trading_day
    "months": [1, 4, 7, 10],     // 每年这些月份（仅用于monthly）
    "day": 1,                    // 每月几号（仅用于monthly）
    "day_of_week": 1,           // 每周几（仅用于weekly）
    "force_all": true,           // 是否强制更新所有实体
    "date_range": {               // 可选：限制全量更新的日期范围
      "unit": "month",
      "value": 1
    }
  },
  
  // 批次更新（与固定时间配合使用）
  "renew_batch": {
    "enabled": true,
    "batch_size": 8,             // 批次大小
    "cache_key": "handler_name_batch_offset"  // 缓存键名（可选）
  },
  
  // Rolling 模式配置（如果使用rolling模式）
  "rolling_unit": "month",
  "rolling_length": 12
}
```

---

## 总结

### 核心需求覆盖情况

✅ **需求1（间隔更新）**：已覆盖
- 复权因子事件
- 切片数据
- K线周期数据

✅ **需求2（固定时间全量更新）**：已覆盖
- 公司财务（财报季度月份）
- 切片数据（可选）

### 用户实现场景（通过钩子函数）

⚠️ **随机/带游标抽样**：通过 `on_before_fetch` 钩子实现
- 示例：`corporate_finance` 的批次轮转逻辑
- 设计理由：抽样逻辑太灵活，批次游标需要额外记录

⚠️ **特殊条件刷新**：通过 `on_before_fetch` 钩子实现
- 示例：基于依赖数据更新、数据异常等条件触发
- 设计理由：条件未知，可能状况太多

### 下一步行动

1. **实现框架核心功能**：
   - 间隔更新（需求1）
   - 固定时间全量更新（需求2）

2. **文档说明用户实现方式**：
   - 在架构文档中说明如何通过 `on_before_fetch` 实现复杂场景
   - 提供 `corporate_finance` 作为批次轮转的参考示例
