"""Repository Protocol(Phase 1 数据层)。 业务层只依赖这些 Protocol;具体实现位于 infrastructure/persistence。 实体一律以 domain.entities 类型进出,禁止把 ORM Model 泄漏到上层。 """ from __future__ import annotations from collections.abc import Iterator, Sequence from datetime import date from typing import Protocol from app.domain.entities.market import ( AdjustFactor, DailyBar, FinancialIndicator, Stock, SyncLog, TradingCalendar, ) class StockRepository(Protocol): def get_by_symbol(self, symbol: str) -> Stock | None: ... def list(self) -> list[Stock]: ... def upsert_many(self, stocks: Sequence[Stock]) -> int: """批量写入,以 symbol 为幂等键,返回写入/更新的行数。""" class TradingCalendarRepository(Protocol): def upsert_many(self, days: Sequence[TradingCalendar]) -> int: ... def list_range(self, start: date, end: date) -> list[TradingCalendar]: ... def is_open(self, day: date) -> bool: ... class DailyBarRepository(Protocol): def upsert_many(self, bars: Sequence[DailyBar]) -> int: ... def get_range(self, symbol: str, start: date, end: date) -> list[DailyBar]: ... def get_range_many(self, symbols: Sequence[str], start: date, end: date) -> list[DailyBar]: """批量区间查询(研究服务装配面板用,避免逐只查询)。""" def stream_range_many_columns( self, symbols: Sequence[str], start: date, end: date, columns: Sequence[str], ) -> Iterator[tuple]: """流式(分批 yield)返回 symbol, trade_date(iso str), 数值列(float) 元组。 研究装配大数据面板专用:只 SELECT 所需列并在 SQL 侧转 REAL, 避免 ORM 对象 / Decimal 全量物化(内存大头,见内存优化专项)。 实现可选——ResearchService 会对缺失该方法的老实现回退到 get_range_many。 """ def latest_date(self, symbol: str) -> date | None: """断点续传用:该股票本地已有数据的最新交易日。""" class AdjustFactorRepository(Protocol): def upsert_many(self, factors: Sequence[AdjustFactor]) -> int: ... def get_range(self, symbol: str, start: date, end: date) -> list[AdjustFactor]: ... class FinancialRepository(Protocol): def upsert_many(self, rows: Sequence[FinancialIndicator]) -> int: ... def list_symbol(self, symbol: str) -> list[FinancialIndicator]: """该股票本地全部财务行(增量判断 / 新浪校验重叠用,量级小)。""" def has_report_period(self, symbol: str, report_date: date) -> bool: """本地是否已含该报告期(最新应披露报告期是否已入库)。""" def list_announced( self, symbol: str, as_of_date: date, report_start: date | None = None, ) -> list[FinancialIndicator]: """只返回 announce_date <= as_of_date 的记录 —— 未来函数红线。""" class SyncLogRepository(Protocol): def add(self, log: SyncLog) -> SyncLog: ... def recent(self, source: str | None = None, limit: int = 20) -> list[SyncLog]: ...