# Indicator 重要决策记录

本文档记录 Indicator 模块在设计与演进过程中的关键决策，便于后续维护与扩展时参考。

---

## Decision 1：复用 `pandas-ta-classic`，而不是自行实现指标库

**日期：** 2025-12-19  
**状态：** 已实施

### 背景

- 技术指标种类多且实现细节复杂（平滑、边界处理、缺失值处理等）。
- 开源社区已有成熟的指标库（如 `pandas-ta-classic`），覆盖 150+ 指标。

### 方案

- 选用 `pandas-ta-classic` 作为唯一指标实现库：
  - 不在项目内重复实现 MA/RSI/MACD 等算法
  - 通过 `IndicatorService` 作为 Proxy 统一封装

### 理由

1. **减少维护成本**：不需要维护一整套指标实现
2. **标准化**：指标计算结果与社区惯例一致，便于对齐外部文档与经验
3. **专注业务**：把精力放在指标的使用和组合上，而非底层数学实现

---

## Decision 2：采用静态工具类 `IndicatorService`，而不是面向实例的服务对象

**日期：** 2025-12-19  
**状态：** 已实施

### 背景

- 指标计算本身是**纯函数性质**：输入 K 线 → 输出指标值，不依赖内部状态。
- 多个调用方（Strategy / Tag / Analyzer）需要在各种上下文中频繁调用。

### 方案

- 将 `IndicatorService` 设计为**无状态工具类**：
  - 所有 API 通过 `@classmethod` 或 `@staticmethod` 暴露
  - 不需要在 DI 容器或上下文中注册实例

### 理由

1. **使用简单**：任何地方只需 `from core.modules.indicator import IndicatorService`
2. **更易测试**：不涉及复杂依赖注入或生命周期管理
3. **语义清晰**：强调「指标计算」是纯粹的函数式操作

---

## Decision 3：双层 API 设计（便捷 API + 通用 API）

**日期：** 2025-12-19  
**状态：** 已实施

### 背景

- 业务侧日常只需要少数几个常用指标，但框架希望保留对全部指标的访问能力。
- 若为每个指标都写一层 wrapper，会导致 API 体积巨大、维护成本高。

### 方案

- 采用「双层 API」设计：
  - **便捷 API**：为常用指标提供一层友好封装（命名统一、默认参数合理）
  - **通用 API**：`calculate(indicator_name, klines, **params)` 支持任意指标名

### 理由

1. **兼顾易用性与完备性**：常用指标用便捷 API，少见指标用通用 API
2. **易于扩展**：新增便捷 API 只是一层薄 wrapper，不影响底层实现
3. **避免 API 爆炸**：不用为每一个指标都暴露一个「一等公民」方法

---

## Decision 4：统一输入/输出数据格式，屏蔽 `pandas` 细节

**日期：** 2025-12-19  
**状态：** 已实施

### 背景

- `pandas-ta-classic` 的输入通常是 `pandas.Series` / `DataFrame`，输出也是 `Series` / `DataFrame`。
- 业务模块更习惯使用 Python 原生结构（`List[Dict]`）和简单的 `List[float]`。

### 方案

- 将 Indicator 模块的边界定义为：
  - **输入**：`List[Dict]` 格式的 K 线
  - **输出**：
    - 单列指标：`List[float]`
    - 多列指标：`Dict[str, List[float]]`
- 所有 `pandas` 相关逻辑全部封装在 `IndicatorService` 内部。

### 理由

1. **降低耦合**：上层模块不需要依赖 `pandas` 细节
2. **统一风格**：与 DataSource / DataManager 等模块的数据风格保持一致
3. **便于替换底层库**：未来如需替换 `pandas-ta-classic`，对业务侧影响最小

---

## Decision 5：对数据不足或计算失败时返回 `None`，而不是抛异常

**日期：** 2025-12-19  
**状态：** 已实施

### 背景

- 很多指标在数据量不足时无法计算出有效结果（如 MA20 需要至少 20 条数据）。
- 如果在这类情况下抛异常，会大幅增加业务逻辑的异常处理负担。

### 方案

- 当出现以下情况时，统一返回 `None`：
  - 输入数据条数不足以支撑指标计算
  - 底层库抛出的可预期错误（参数错误、数据格式问题等）
- 由调用方自行决定如何处理 `None`（跳过、回退、打默认值等）。

### 理由

1. **调用体验更好**：指标计算失败是业务可预期状态，使用返回值表达更自然
2. **简化控制流**：避免在业务逻辑中大量 `try/except` 包围指标调用
3. **可观测性更好**：可以通过日志记录 `None` 情况，而不中断整体流程

---

## 相关文档

- 架构设计文档：`architecture/core_modules/indicator/architecture.md`
- 使用说明：`core/modules/indicator/README.md`
