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:
@@ -11,6 +11,7 @@ from fastapi import Depends
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.application.services.selection_service import SelectionService
|
||||
from app.domain.repositories.factor import FactorRepository
|
||||
from app.domain.repositories.jobs import ExperimentRepository, JobRepository
|
||||
from app.domain.repositories.market import (
|
||||
DailyBarRepository,
|
||||
@@ -18,6 +19,9 @@ from app.domain.repositories.market import (
|
||||
StockRepository,
|
||||
)
|
||||
from app.domain.repositories.selection import SelectionRepository
|
||||
from app.infrastructure.persistence.sqlalchemy.repositories.factor_impl import (
|
||||
SqlAlchemyFactorRepository,
|
||||
)
|
||||
from app.infrastructure.persistence.sqlalchemy.repositories.market_impl import (
|
||||
SqlAlchemyDailyBarRepository,
|
||||
SqlAlchemyFinancialRepository,
|
||||
@@ -70,12 +74,17 @@ def _selection_repo_factory(session: DbSession) -> SelectionRepository:
|
||||
return SqlAlchemySelectionRepository(session)
|
||||
|
||||
|
||||
def _factor_repo_factory(session: DbSession) -> FactorRepository:
|
||||
return SqlAlchemyFactorRepository(session)
|
||||
|
||||
|
||||
StockRepoDep = Annotated[StockRepository, Depends(_stock_repo_factory)]
|
||||
DailyRepoDep = Annotated[DailyBarRepository, Depends(_daily_repo_factory)]
|
||||
EngineDep = Annotated[QuantEngine, Depends(_engine_factory)]
|
||||
ResearchServiceDep = Annotated[ResearchService, Depends(_service_factory)]
|
||||
SelectionServiceDep = Annotated[SelectionService, Depends(_selection_service_factory)]
|
||||
SelectionRepoDep = Annotated[SelectionRepository, Depends(_selection_repo_factory)]
|
||||
FactorRepoDep = Annotated[FactorRepository, Depends(_factor_repo_factory)]
|
||||
|
||||
|
||||
def _job_repo_factory(session: DbSession):
|
||||
|
||||
+13
-17
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user