You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

456 lines
20 KiB
Python

"""SQLAlchemy 2.0 declarative models for ACE-F."""
from __future__ import annotations
import datetime as dt
import uuid
from sqlalchemy import (
BigInteger,
Boolean,
Date,
DateTime,
ForeignKey,
Index,
Integer,
Numeric,
Text,
UniqueConstraint,
)
from sqlalchemy.dialects.postgresql import JSONB, UUID
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
# ---------------------------------------------------------------------------
# Phase 3 imports (used below in new model definitions)
# ---------------------------------------------------------------------------
def _utcnow() -> dt.datetime:
return dt.datetime.now(tz=dt.UTC)
class Base(DeclarativeBase):
pass
class IssuerMaster(Base):
__tablename__ = "issuer_master"
issuer_id: Mapped[str] = mapped_column(Text, primary_key=True)
cik: Mapped[str | None] = mapped_column(Text, unique=True, nullable=True)
ticker: Mapped[str | None] = mapped_column(Text, nullable=True)
issuer_name: Mapped[str] = mapped_column(Text, nullable=False)
exchange: Mapped[str | None] = mapped_column(Text, nullable=True)
country_code: Mapped[str | None] = mapped_column(Text, nullable=True)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
created_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, nullable=False
)
updated_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, onupdate=_utcnow, nullable=False
)
symbols: Mapped[list[SymbolMaster]] = relationship(back_populates="issuer")
class SymbolMaster(Base):
__tablename__ = "symbol_master"
symbol_id: Mapped[str] = mapped_column(Text, primary_key=True)
issuer_id: Mapped[str | None] = mapped_column(
Text, ForeignKey("issuer_master.issuer_id"), nullable=True
)
ticker: Mapped[str] = mapped_column(Text, nullable=False)
venue: Mapped[str | None] = mapped_column(Text, nullable=True)
asset_type: Mapped[str | None] = mapped_column(Text, nullable=True)
currency: Mapped[str | None] = mapped_column(Text, nullable=True)
start_date: Mapped[dt.date | None] = mapped_column(Date, nullable=True)
end_date: Mapped[dt.date | None] = mapped_column(Date, nullable=True)
is_primary: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
created_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, nullable=False
)
updated_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, onupdate=_utcnow, nullable=False
)
issuer: Mapped[IssuerMaster | None] = relationship(back_populates="symbols")
class JobRun(Base):
__tablename__ = "job_runs"
__table_args__ = (Index("ix_job_runs_job_name_run_date", "job_name", "run_date"),)
job_run_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
)
job_name: Mapped[str] = mapped_column(Text, nullable=False)
source_name: Mapped[str | None] = mapped_column(Text, nullable=True)
run_date: Mapped[dt.date | None] = mapped_column(Date, nullable=True)
status: Mapped[str] = mapped_column(Text, nullable=False)
started_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, nullable=False
)
finished_at_utc: Mapped[dt.datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True
)
records_seen: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
records_written: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
records_skipped: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
error_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
error_summary: Mapped[str | None] = mapped_column(Text, nullable=True)
metadata_json: Mapped[dict] = mapped_column(JSONB, default=dict, nullable=False)
class Document(Base):
__tablename__ = "documents"
__table_args__ = (
UniqueConstraint("accession_no", "form_type", name="uq_documents_accession_form"),
Index("ix_documents_issuer_filing_date", "issuer_id", "filing_date"),
Index("ix_documents_form_type_filing_date", "form_type", "filing_date"),
)
document_id: Mapped[str] = mapped_column(Text, primary_key=True)
source_name: Mapped[str] = mapped_column(Text, nullable=False)
issuer_id: Mapped[str | None] = mapped_column(
Text, ForeignKey("issuer_master.issuer_id"), nullable=True
)
symbol_id: Mapped[str | None] = mapped_column(
Text, ForeignKey("symbol_master.symbol_id"), nullable=True
)
accession_no: Mapped[str | None] = mapped_column(Text, nullable=True)
form_type: Mapped[str] = mapped_column(Text, nullable=False)
filing_date: Mapped[dt.date] = mapped_column(Date, nullable=False)
accepted_at_utc: Mapped[dt.datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True
)
primary_document_name: Mapped[str | None] = mapped_column(Text, nullable=True)
parsed_status: Mapped[str] = mapped_column(Text, default="pending", nullable=False)
created_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, nullable=False
)
updated_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, onupdate=_utcnow, nullable=False
)
exhibits: Mapped[list[DocumentExhibit]] = relationship(back_populates="document")
events: Mapped[list[Event]] = relationship(back_populates="primary_document")
class DocumentExhibit(Base):
__tablename__ = "document_exhibits"
exhibit_id: Mapped[str] = mapped_column(Text, primary_key=True)
document_id: Mapped[str] = mapped_column(
Text, ForeignKey("documents.document_id"), nullable=False
)
exhibit_code: Mapped[str] = mapped_column(Text, nullable=False)
exhibit_name: Mapped[str | None] = mapped_column(Text, nullable=True)
created_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, nullable=False
)
document: Mapped[Document] = relationship(back_populates="exhibits")
class ExhibitCache(Base):
__tablename__ = "exhibit_cache"
__table_args__ = (
UniqueConstraint("accession_no", "exhibit_type", name="uq_exhibit_cache_accession_type"),
)
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
accession_no: Mapped[str] = mapped_column(Text, nullable=False)
exhibit_type: Mapped[str] = mapped_column(Text, nullable=False)
content_hash: Mapped[str] = mapped_column(Text, nullable=False)
cache_path: Mapped[str] = mapped_column(Text, nullable=False)
fetched_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, nullable=False
)
class Event(Base):
__tablename__ = "events"
__table_args__ = (
Index("ix_events_event_type_date", "event_type", "event_date"),
Index("ix_events_primary_document_id", "primary_document_id"),
)
event_id: Mapped[str] = mapped_column(Text, primary_key=True)
issuer_id: Mapped[str | None] = mapped_column(
Text, ForeignKey("issuer_master.issuer_id"), nullable=True
)
symbol_id: Mapped[str | None] = mapped_column(
Text, ForeignKey("symbol_master.symbol_id"), nullable=True
)
primary_document_id: Mapped[str] = mapped_column(
Text, ForeignKey("documents.document_id"), nullable=False
)
event_type: Mapped[str] = mapped_column(Text, nullable=False)
event_direction: Mapped[str] = mapped_column(Text, nullable=False)
event_date: Mapped[dt.date] = mapped_column(Date, nullable=False)
filed_at_utc: Mapped[dt.datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True
)
parser_version: Mapped[str] = mapped_column(Text, nullable=False)
parse_confidence: Mapped[float | None] = mapped_column(Numeric, nullable=True)
status: Mapped[str] = mapped_column(Text, default="pending", nullable=False)
created_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, nullable=False
)
updated_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, onupdate=_utcnow, nullable=False
)
primary_document: Mapped[Document] = relationship(back_populates="events")
parses: Mapped[list[EventParse]] = relationship(back_populates="event")
feature_snapshots: Mapped[list[FeatureSnapshot]] = relationship(back_populates="event")
class EventParse(Base):
__tablename__ = "event_parses"
event_parse_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
event_id: Mapped[str] = mapped_column(
Text, ForeignKey("events.event_id"), nullable=False
)
parser_kind: Mapped[str] = mapped_column(Text, nullable=False)
parser_version: Mapped[str] = mapped_column(Text, nullable=False)
schema_version: Mapped[str] = mapped_column(Text, nullable=False)
output_json: Mapped[dict] = mapped_column(JSONB, nullable=False)
validation_status: Mapped[str] = mapped_column(Text, nullable=False)
validation_errors: Mapped[dict | None] = mapped_column(JSONB, nullable=True)
created_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, nullable=False
)
event: Mapped[Event] = relationship(back_populates="parses")
class MacroSeries(Base):
__tablename__ = "macro_series"
series_id: Mapped[str] = mapped_column(Text, primary_key=True)
title: Mapped[str | None] = mapped_column(Text, nullable=True)
frequency: Mapped[str | None] = mapped_column(Text, nullable=True)
units: Mapped[str | None] = mapped_column(Text, nullable=True)
source_name: Mapped[str] = mapped_column(Text, nullable=False)
metadata_json: Mapped[dict] = mapped_column(JSONB, default=dict, nullable=False)
created_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, nullable=False
)
observations: Mapped[list[MacroObservation]] = relationship(back_populates="series")
class MacroObservation(Base):
__tablename__ = "macro_observations"
__table_args__ = (
UniqueConstraint("series_id", "observation_date", name="uq_macro_obs_series_date"),
)
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
series_id: Mapped[str] = mapped_column(
Text, ForeignKey("macro_series.series_id"), nullable=False
)
observation_date: Mapped[dt.date] = mapped_column(Date, nullable=False)
value: Mapped[float | None] = mapped_column(Numeric, nullable=True)
created_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, nullable=False
)
series: Mapped[MacroSeries] = relationship(back_populates="observations")
class ShortSaleDaily(Base):
__tablename__ = "short_sale_daily"
__table_args__ = (
UniqueConstraint(
"ticker_raw", "trade_date", "source_name", name="uq_short_sale_ticker_date_source"
),
Index("ix_short_sale_ticker_date", "ticker_raw", "trade_date"),
)
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
symbol_id: Mapped[str | None] = mapped_column(
Text, ForeignKey("symbol_master.symbol_id"), nullable=True
)
ticker_raw: Mapped[str] = mapped_column(Text, nullable=False)
trade_date: Mapped[dt.date] = mapped_column(Date, nullable=False)
short_volume: Mapped[int] = mapped_column(BigInteger, nullable=False)
short_exempt_volume: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
total_volume: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
source_name: Mapped[str] = mapped_column(Text, nullable=False)
created_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, nullable=False
)
class FeatureSnapshot(Base):
__tablename__ = "feature_snapshots"
feature_snapshot_id: Mapped[int] = mapped_column(
BigInteger, primary_key=True, autoincrement=True
)
event_id: Mapped[str] = mapped_column(
Text, ForeignKey("events.event_id"), nullable=False
)
snapshot_name: Mapped[str] = mapped_column(Text, nullable=False)
snapshot_version: Mapped[str] = mapped_column(Text, nullable=False)
feature_json: Mapped[dict] = mapped_column(JSONB, nullable=False)
created_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, nullable=False
)
event: Mapped[Event] = relationship(back_populates="feature_snapshots")
class OrderPlan(Base):
__tablename__ = "order_plans"
order_plan_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
event_id: Mapped[str] = mapped_column(
Text, ForeignKey("events.event_id"), nullable=False
)
symbol_id: Mapped[str] = mapped_column(
Text, ForeignKey("symbol_master.symbol_id"), nullable=False
)
side: Mapped[str] = mapped_column(Text, nullable=False)
planned_entry_date: Mapped[dt.date] = mapped_column(Date, nullable=False)
planned_order_type: Mapped[str] = mapped_column(Text, nullable=False)
planned_price: Mapped[float | None] = mapped_column(Numeric, nullable=True)
stop_price: Mapped[float | None] = mapped_column(Numeric, nullable=True)
take_profit_price: Mapped[float | None] = mapped_column(Numeric, nullable=True)
quantity_plan: Mapped[float | None] = mapped_column(Numeric, nullable=True)
status: Mapped[str] = mapped_column(Text, default="draft", nullable=False)
created_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, nullable=False
)
class SyncCheckpoint(Base):
__tablename__ = "sync_checkpoints"
__table_args__ = (UniqueConstraint("domain", name="uq_sync_checkpoints_domain"),)
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
domain: Mapped[str] = mapped_column(Text, nullable=False)
last_sync_at_utc: Mapped[dt.datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True
)
last_sync_params: Mapped[dict] = mapped_column(JSONB, default=dict, nullable=False)
status: Mapped[str] = mapped_column(Text, nullable=False)
created_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, nullable=False
)
updated_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, onupdate=_utcnow, nullable=False
)
# ---------------------------------------------------------------------------
# Phase 3 models
# ---------------------------------------------------------------------------
class LLMCallCache(Base):
"""Cache of LLM API calls keyed by content hash."""
__tablename__ = "llm_call_cache"
cache_key: Mapped[str] = mapped_column(Text, primary_key=True)
document_id: Mapped[str | None] = mapped_column(Text, nullable=True)
model_name: Mapped[str] = mapped_column(Text, nullable=False)
prompt_version: Mapped[str] = mapped_column(Text, nullable=False)
schema_version: Mapped[str] = mapped_column(Text, nullable=False)
raw_prompt: Mapped[str] = mapped_column(Text, nullable=False)
raw_response: Mapped[str] = mapped_column(Text, nullable=False)
normalized_json: Mapped[dict] = mapped_column(JSONB, nullable=False)
token_usage_json: Mapped[dict] = mapped_column(JSONB, nullable=False)
elapsed_ms: Mapped[int] = mapped_column(Integer, nullable=False)
created_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, nullable=False
)
class ReviewItem(Base):
"""Human-in-the-loop review queue item."""
__tablename__ = "review_items"
__table_args__ = (
Index("ix_review_items_entity_open", "entity_type", "entity_id", "status"),
Index("ix_review_items_status_priority", "status", "priority"),
)
review_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
)
entity_type: Mapped[str] = mapped_column(Text, nullable=False)
entity_id: Mapped[str] = mapped_column(Text, nullable=False)
priority: Mapped[str] = mapped_column(Text, nullable=False)
reason_codes: Mapped[list] = mapped_column(JSONB, nullable=False)
status: Mapped[str] = mapped_column(Text, default="open", nullable=False)
assigned_to: Mapped[str | None] = mapped_column(Text, nullable=True)
snapshot_refs: Mapped[dict] = mapped_column(JSONB, nullable=False)
suggested_overrides: Mapped[dict | None] = mapped_column(JSONB, nullable=True)
reviewer_id: Mapped[str | None] = mapped_column(Text, nullable=True)
resolution_type: Mapped[str | None] = mapped_column(Text, nullable=True)
field_overrides: Mapped[dict | None] = mapped_column(JSONB, nullable=True)
root_cause: Mapped[str | None] = mapped_column(Text, nullable=True)
notes: Mapped[str | None] = mapped_column(Text, nullable=True)
resolved_at: Mapped[dt.datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True
)
created_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, nullable=False
)
updated_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, onupdate=_utcnow, nullable=False
)
class EventLabel(Base):
"""Forward-return labels for a parsed event."""
__tablename__ = "event_labels"
__table_args__ = (
UniqueConstraint(
"event_id", "entry_convention", "label_version",
name="uq_event_labels_event_convention_version",
),
)
label_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
event_id: Mapped[str] = mapped_column(
Text, ForeignKey("events.event_id"), nullable=False
)
entry_convention: Mapped[str] = mapped_column(Text, nullable=False)
reaction_date: Mapped[dt.date | None] = mapped_column(Date, nullable=True)
entry_date: Mapped[dt.date | None] = mapped_column(Date, nullable=True)
entry_price: Mapped[float | None] = mapped_column(Numeric, nullable=True)
fwd_return_1d: Mapped[float | None] = mapped_column(Numeric, nullable=True)
fwd_return_3d: Mapped[float | None] = mapped_column(Numeric, nullable=True)
fwd_return_5d: Mapped[float | None] = mapped_column(Numeric, nullable=True)
hit_pos_1r_within_3d: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
hit_neg_1r_within_3d: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
close_up_after_3d: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
close_up_after_5d: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
mfe_3d: Mapped[float | None] = mapped_column(Numeric, nullable=True)
mae_3d: Mapped[float | None] = mapped_column(Numeric, nullable=True)
mfe_5d: Mapped[float | None] = mapped_column(Numeric, nullable=True)
mae_5d: Mapped[float | None] = mapped_column(Numeric, nullable=True)
bars_to_mfe_3d: Mapped[int | None] = mapped_column(Integer, nullable=True)
bars_to_mae_3d: Mapped[int | None] = mapped_column(Integer, nullable=True)
days_to_peak_close_5d: Mapped[int | None] = mapped_column(Integer, nullable=True)
label_status: Mapped[str] = mapped_column(Text, nullable=False)
invalid_event_for_labeling: Mapped[bool] = mapped_column(
Boolean, default=False, nullable=False
)
risk_model_name: Mapped[str | None] = mapped_column(Text, nullable=True)
initial_stop_price: Mapped[float | None] = mapped_column(Numeric, nullable=True)
initial_r_value: Mapped[float | None] = mapped_column(Numeric, nullable=True)
label_version: Mapped[str] = mapped_column(Text, nullable=False)
created_at_utc: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=_utcnow, nullable=False
)
event: Mapped[Event] = relationship()