feat(factor): M7.1 因子定义入库 + /api/factors 读库(目录契约源)

- factor_definition 表(migration b7f2a5e81c33,MySQL 已应用):name 主键 + 元数据
  (formula/brief/frequency/lookback/direction/requires JSON/version)+ FactorDefinition
  entity(from_registry_def 由代码注册表构造)
- FactorRepository Protocol + SQLAlchemy 实现(幂等 upsert/list/get)
- /api/factors 改读 DB;目录为空自动 seed 注册表(幂等)—— 保留自定义因子登记能力
  (计算仍须代码注册,引用未注册因子照常 FactorError,防伪因子)
- tests/test_factor_catalog.py(repo 幂等/roundtrip/registry seed、API seed+字段齐全);
  test_api 的 client fixture 补 tmp sqlite session(factors 读库);全量 pytest 通过
This commit is contained in:
Simon
2026-09-09 00:28:16 +08:00
parent 0ab9038570
commit 8f47b5b603
11 changed files with 360 additions and 18 deletions
+13 -17
View File
@@ -1,26 +1,22 @@
"""因子目录 API:/api/factors。"""
"""因子目录 API:/api/factors(M7.1 起读 DB factor_definition)。
目录为空时自动从代码注册表 seed(幂等);随后可登记自定义因子元数据。
响应为 FactorDefinition 实体(含 requires 列表等)。
"""
from __future__ import annotations
from fastapi import APIRouter
from app.quant.factors import list_factors
from app.api.deps import DbSession, FactorRepoDep
from app.application.services.factor_catalog import seed_registry_factors
from app.domain.entities.factor import FactorDefinition
router = APIRouter(prefix="/factors", tags=["factors"])
@router.get("", summary="因子目录(含元数据)")
def list_factor_catalog() -> list[dict]:
return [
{
"name": d.name,
"description": d.description,
"brief": d.brief,
"formula": d.formula,
"frequency": d.frequency,
"lookback": d.lookback,
"direction": d.direction,
"requires": list(d.requires),
}
for d in list_factors()
]
@router.get("", summary="因子目录(含元数据,来自 factor_definition 表)")
def list_factor_catalog(factor_repo: FactorRepoDep, session: DbSession) -> list[FactorDefinition]:
if not factor_repo.list():
seed_registry_factors(factor_repo, session) # 首次:从代码注册表 seed
return factor_repo.list()