Files
qlib/backend/app/domain/repositories/market.py
T
Simon 75c5472c31 feat(selection): M6.2 条件选股(method=condition + 财务可见性防护)
- quant/selection.run_condition_selection:结构化条件 AND 求值 —— 字段域
  static.*(行业/市场…)、技术列与派生量(close/volume/ma20/ma60)、已注册因子
  (momentum_60 等)、fundamental.*(announce_date<=as_of 的最新已公告财务值);
  条件支持 value 字面量与 ref 字段比较(如 close > ma60);结果带 filter_status/reason
- SelectionQuery 校验调整:condition 模式为纯过滤(不再强制 top_n/top_pct)
- FinancialRepository 新增 list_announced_many(批量防未来函数读取)+ SQLAlchemy 实现;
  SelectionService 注入 financial_repo 并按 announce_date 取每股最新一版
- tests/test_selection_condition.py:8 例(行业 in/ne、动量>0、close>ma60 ref、阈值、
  ROE 过滤且未来公告不可见、缺财务 repo 报错、更早 as_of 排除);全量 pytest 通过
2026-09-09 00:16:45 +08:00

105 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""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 的记录 —— 未来函数红线。"""
def list_announced_many(
self,
symbols: Sequence[str],
as_of_date: date,
) -> list[FinancialIndicator]:
"""批量版:返回这些股票 announce_date <= as_of_date 的全部记录。
供选股/截面研究一次性取财务字段(调用方按需取每 symbol 最新一版)。
实现可选 —— 未提供时 SelectionService 回退逐只 list_announced。
"""
class SyncLogRepository(Protocol):
def add(self, log: SyncLog) -> SyncLog: ...
def recent(self, source: str | None = None, limit: int = 20) -> list[SyncLog]: ...