- index_weight 表(migration f5e0d1c2b3a4,MySQL 已应用;index_code+date+symbol 唯一) + IndexWeight 实体 + IndexConstituentRepository(members_at:取 <=as_of 最近一期快照, Survivorship-free / 无未来成分;latest_date) - UniverseSpec.index_code + universe.filter_stocks members 交集 + resolve_members; Research/Selection/Signal/Replay 服务注入 index repo(历史成分过滤,选股/回测共用) - tests/test_index_universe.py:快照历史成分(成分变更不入早期结果)、幂等、 空快照期空集、index_code 过滤下 as_of 一致性;全量 pytest 通过
32 lines
1.2 KiB
Python
32 lines
1.2 KiB
Python
"""指数历史成分表(B1)。
|
|
|
|
index_weight:指数成分快照(index_code, trade_date, symbol 唯一)。
|
|
历史成分查询(members_at)取 <= as_of 最近一期快照 —— Survivorship-free Universe。
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import date
|
|
from decimal import Decimal
|
|
|
|
from sqlalchemy import BigInteger, Date, Integer, Numeric, String, UniqueConstraint
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from app.infrastructure.persistence.sqlalchemy.base import Base
|
|
|
|
PK_INT = BigInteger().with_variant(Integer, "sqlite")
|
|
|
|
|
|
class IndexWeightModel(Base):
|
|
__tablename__ = "index_weight"
|
|
__table_args__ = (
|
|
UniqueConstraint("index_code", "trade_date", "symbol", name="uq_idxw_code_date_sym"),
|
|
)
|
|
|
|
id: Mapped[int] = mapped_column(PK_INT, primary_key=True, autoincrement=True)
|
|
index_code: Mapped[str] = mapped_column(String(12), index=True)
|
|
index_name: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
|
trade_date: Mapped[date] = mapped_column(Date, index=True)
|
|
symbol: Mapped[str] = mapped_column(String(12))
|
|
weight: Mapped[Decimal | None] = mapped_column(Numeric(10, 6), nullable=True)
|