Files
qlib/backend/app/infrastructure/persistence/sqlalchemy/models/market.py
T
Simon 442999f701 feat(data): 财务/日线同步增量 + 新浪「两边一致」校验兜底 + 逐只进度
- financial 默认增量:按 A 股披露节奏判断已最新并跳过;--full 强制全量重拉
- Tushare fina_indicator 增加报告期窗口与 100 条/请求自动分页(修复老报告期静默截断)
- 新浪兜底收紧为校验兜底:两源重叠历史一致才导入缺失键,行标记 source=sina;
  财务可比字段取 eps/销售毛利率(ROE 两端口径不同不作依据),日线只比较最近重叠交易日
- CLI 输出逐只进度与导入内容描述(来源/行数/报告期与公告区间),失败股票留待重跑
- financial_indicator 增 source 列(迁移 d3f6c9a21b04);新增一致性/分页/服务测试
2026-09-08 21:48:09 +08:00

118 lines
4.9 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.
"""Phase 1 市场数据表模型(SQLAlchemy 2.x 声明式)。
列名与 domain.entities.market 字段一一对应,便于 Repository 双向映射。
Decimal 字段用 Numeric:SQLite 以浮点近似存储,未来 MySQL 下精确。
"""
from __future__ import annotations
from datetime import date, datetime
from decimal import Decimal
from sqlalchemy import (
BigInteger,
Boolean,
Date,
DateTime,
Integer,
Numeric,
String,
UniqueConstraint,
)
from sqlalchemy.orm import Mapped, mapped_column
from app.infrastructure.persistence.sqlalchemy.base import Base
# SQLite 只对 INTEGER PRIMARY KEY 自增;MySQL 下用 BIGINT
PK_INT = BigInteger().with_variant(Integer, "sqlite")
SYMBOL_LEN = 12
class StockModel(Base):
__tablename__ = "stock"
id: Mapped[int] = mapped_column(primary_key=True)
symbol: Mapped[str] = mapped_column(String(SYMBOL_LEN), unique=True, index=True)
name: Mapped[str] = mapped_column(String(64))
industry: Mapped[str | None] = mapped_column(String(64), nullable=True)
area: Mapped[str | None] = mapped_column(String(32), nullable=True)
market: Mapped[str | None] = mapped_column(String(16), nullable=True)
exchange: Mapped[str | None] = mapped_column(String(8), nullable=True)
list_date: Mapped[date] = mapped_column(Date)
delist_date: Mapped[date | None] = mapped_column(Date, nullable=True)
status: Mapped[str] = mapped_column(String(8), default="L")
class TradingCalendarModel(Base):
__tablename__ = "trading_calendar"
id: Mapped[int] = mapped_column(primary_key=True)
calendar_date: Mapped[date] = mapped_column(Date, unique=True, index=True)
is_open: Mapped[bool] = mapped_column(Boolean, default=True)
class StockDailyModel(Base):
"""不复权日线。"""
__tablename__ = "stock_daily"
__table_args__ = (UniqueConstraint("symbol", "trade_date", name="uq_daily_symbol_date"),)
id: Mapped[int] = mapped_column(PK_INT, primary_key=True, autoincrement=True)
symbol: Mapped[str] = mapped_column(String(SYMBOL_LEN), index=True)
trade_date: Mapped[date] = mapped_column(Date, index=True)
source: Mapped[str] = mapped_column(String(16), default="tushare", server_default="tushare")
adjust: Mapped[str] = mapped_column(String(8), default="none", server_default="none")
open: Mapped[Decimal | None] = mapped_column(Numeric(12, 4), nullable=True)
high: Mapped[Decimal | None] = mapped_column(Numeric(12, 4), nullable=True)
low: Mapped[Decimal | None] = mapped_column(Numeric(12, 4), nullable=True)
close: Mapped[Decimal | None] = mapped_column(Numeric(12, 4), nullable=True)
volume: Mapped[Decimal | None] = mapped_column(Numeric(24, 2), nullable=True)
amount: Mapped[Decimal | None] = mapped_column(Numeric(24, 2), nullable=True)
class AdjustFactorModel(Base):
__tablename__ = "adjust_factor"
__table_args__ = (UniqueConstraint("symbol", "trade_date", name="uq_adj_symbol_date"),)
id: Mapped[int] = mapped_column(PK_INT, primary_key=True, autoincrement=True)
symbol: Mapped[str] = mapped_column(String(SYMBOL_LEN), index=True)
trade_date: Mapped[date] = mapped_column(Date, index=True)
factor: Mapped[Decimal] = mapped_column(Numeric(20, 6))
class FinancialIndicatorModel(Base):
"""财务指标快照 —— report_date(报告期) 与 announce_date(公告日) 并存。"""
__tablename__ = "financial_indicator"
__table_args__ = (
UniqueConstraint("symbol", "report_date", "announce_date", name="uq_fin_sym_rep_ann"),
)
id: Mapped[int] = mapped_column(PK_INT, primary_key=True, autoincrement=True)
symbol: Mapped[str] = mapped_column(String(SYMBOL_LEN), index=True)
report_date: Mapped[date] = mapped_column(Date, index=True)
announce_date: Mapped[date] = mapped_column(Date, index=True)
source: Mapped[str] = mapped_column(
String(16), default="tushare", server_default="tushare"
)
eps: Mapped[Decimal | None] = mapped_column(Numeric(12, 4), nullable=True)
roe: Mapped[Decimal | None] = mapped_column(Numeric(10, 4), nullable=True)
total_revenue: Mapped[Decimal | None] = mapped_column(Numeric(24, 2), nullable=True)
net_profit: Mapped[Decimal | None] = mapped_column(Numeric(24, 2), nullable=True)
gross_margin: Mapped[Decimal | None] = mapped_column(Numeric(10, 4), nullable=True)
class SyncLogModel(Base):
__tablename__ = "sync_log"
id: Mapped[int] = mapped_column(PK_INT, primary_key=True, autoincrement=True)
source: Mapped[str] = mapped_column(String(16), index=True)
api: Mapped[str] = mapped_column(String(32))
request_time: Mapped[datetime] = mapped_column(DateTime)
success: Mapped[bool] = mapped_column(Boolean)
failure_reason: Mapped[str | None] = mapped_column(String(500), nullable=True)
row_count: Mapped[int] = mapped_column(default=0)
data_start: Mapped[date | None] = mapped_column(Date, nullable=True)
data_end: Mapped[date | None] = mapped_column(Date, nullable=True)