Files
worldquant-alpha-system/backend/app/models.py
T

439 lines
21 KiB
Python
Raw Normal View History

"""Platform snapshots and local research are intentionally stored separately."""
from datetime import datetime, timezone
from sqlalchemy import (
JSON,
Boolean,
DateTime,
Float,
ForeignKey,
Index,
Integer,
String,
Text,
UniqueConstraint,
)
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
def now() -> datetime:
return datetime.now(timezone.utc)
class Base(DeclarativeBase):
pass
class Admin(Base):
__tablename__ = "admins"
id: Mapped[int] = mapped_column(primary_key=True, default=1)
username: Mapped[str] = mapped_column(String(100), unique=True)
password_hash: Mapped[str] = mapped_column(Text)
class LoginSession(Base):
__tablename__ = "login_sessions"
token_hash: Mapped[str] = mapped_column(String(64), primary_key=True)
expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), index=True)
class Account(Base):
__tablename__ = "accounts"
id: Mapped[int] = mapped_column(primary_key=True, default=1)
email: Mapped[str | None] = mapped_column(String(254))
password_encrypted: Mapped[str | None] = mapped_column(Text)
wq_user_id: Mapped[str | None] = mapped_column(String(100))
profile: Mapped[dict] = mapped_column(JSON, default=dict)
connection_status: Mapped[str] = mapped_column(String(30), default="disconnected")
connection_error: Mapped[str | None] = mapped_column(Text)
verification_url: Mapped[str | None] = mapped_column(Text)
last_synced_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
display_name: Mapped[str] = mapped_column(String(100), default="研究员")
theme: Mapped[str] = mapped_column(String(10), default="light")
timezone: Mapped[str] = mapped_column(String(64), default="Asia/Shanghai")
page_size: Mapped[int] = mapped_column(Integer, default=25)
class Alpha(Base):
__tablename__ = "alphas"
id: Mapped[str] = mapped_column(String(100), primary_key=True)
name: Mapped[str | None] = mapped_column(Text)
expression: Mapped[str | None] = mapped_column(Text)
selection: Mapped[str | None] = mapped_column(Text)
combo: Mapped[str | None] = mapped_column(Text)
alpha_type: Mapped[str | None] = mapped_column(String(50), index=True)
language: Mapped[str | None] = mapped_column(String(50))
stage: Mapped[str | None] = mapped_column(String(50), index=True)
status: Mapped[str | None] = mapped_column(String(80), index=True)
hidden: Mapped[bool] = mapped_column(Boolean, default=False)
region: Mapped[str | None] = mapped_column(String(50), index=True)
universe: Mapped[str | None] = mapped_column(String(100))
settings: Mapped[dict] = mapped_column(JSON, default=dict)
is_metrics: Mapped[dict] = mapped_column(JSON, default=dict)
os_metrics: Mapped[dict] = mapped_column(JSON, default=dict)
checks: Mapped[list] = mapped_column(JSON, default=list)
sharpe: Mapped[float | None] = mapped_column(Float, index=True)
fitness: Mapped[float | None] = mapped_column(Float, index=True)
returns: Mapped[float | None] = mapped_column(Float)
turnover: Mapped[float | None] = mapped_column(Float)
margin: Mapped[float | None] = mapped_column(Float)
drawdown: Mapped[float | None] = mapped_column(Float)
date_created: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), index=True)
date_submitted: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
synced_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
raw: Mapped[dict] = mapped_column(JSON, default=dict)
__table_args__ = (Index("ix_alphas_region_status", "region", "status"),)
class Research(Base):
__tablename__ = "research"
alpha_id: Mapped[str] = mapped_column(ForeignKey("alphas.id"), primary_key=True)
note: Mapped[str] = mapped_column(Text, default="")
tags: Mapped[list] = mapped_column(JSON, default=list)
favorite: Mapped[bool] = mapped_column(Boolean, default=False)
state: Mapped[str] = mapped_column(String(30), default="inbox", index=True)
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
version: Mapped[int] = mapped_column(Integer, default=1, server_default="1")
class Pnl(Base):
__tablename__ = "pnl_cache"
alpha_id: Mapped[str] = mapped_column(ForeignKey("alphas.id"), primary_key=True)
raw: Mapped[dict] = mapped_column(JSON)
points: Mapped[list] = mapped_column(JSON)
fetched_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
class ResearchTag(Base):
__tablename__ = "research_tags"
alpha_id: Mapped[str] = mapped_column(ForeignKey("alphas.id"), primary_key=True)
tag: Mapped[str] = mapped_column(String(60), primary_key=True, index=True)
class SelfCorrelation(Base):
__tablename__ = "self_correlations"
alpha_id: Mapped[str] = mapped_column(ForeignKey("alphas.id"), primary_key=True)
region: Mapped[str | None] = mapped_column(String(50), index=True)
result: Mapped[dict] = mapped_column(JSON)
stale: Mapped[bool] = mapped_column(Boolean, default=False)
calculated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
class Job(Base):
__tablename__ = "sync_jobs"
id: Mapped[str] = mapped_column(String(36), primary_key=True)
kind: Mapped[str] = mapped_column(String(30))
status: Mapped[str] = mapped_column(String(30), default="queued", index=True)
payload: Mapped[dict] = mapped_column(JSON, default=dict)
checkpoint: Mapped[dict] = mapped_column(JSON, default=dict)
processed: Mapped[int] = mapped_column(Integer, default=0)
failed: Mapped[int] = mapped_column(Integer, default=0)
total: Mapped[int | None] = mapped_column(Integer)
error: Mapped[str | None] = mapped_column(Text)
next_retry_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
cancel_requested: Mapped[bool] = mapped_column(Boolean, default=False)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
class JobItem(Base):
__tablename__ = "job_items"
job_id: Mapped[str] = mapped_column(ForeignKey("sync_jobs.id"), primary_key=True)
alpha_id: Mapped[str] = mapped_column(String(100), primary_key=True)
error: Mapped[str | None] = mapped_column(Text)
class AISettings(Base):
__tablename__ = "ai_settings"
id: Mapped[int] = mapped_column(primary_key=True, default=1)
base_url: Mapped[str] = mapped_column(Text, default="")
api_key_encrypted: Mapped[str | None] = mapped_column(Text)
model: Mapped[str] = mapped_column(String(200), default="")
protocol: Mapped[str] = mapped_column(String(30), default="chat_completions")
enabled: Mapped[bool] = mapped_column(Boolean, default=False)
revision: Mapped[int] = mapped_column(Integer, default=1)
tested_revision: Mapped[int | None] = mapped_column(Integer)
test_results: Mapped[dict] = mapped_column(JSON, default=dict)
class AIConversation(Base):
__tablename__ = "ai_conversations"
id: Mapped[str] = mapped_column(String(36), primary_key=True)
admin_id: Mapped[int] = mapped_column(ForeignKey("admins.id"), default=1)
title: Mapped[str] = mapped_column(String(100), default="新会话")
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
class AIRun(Base):
__tablename__ = "ai_runs"
id: Mapped[str] = mapped_column(String(36), primary_key=True)
conversation_id: Mapped[str] = mapped_column(ForeignKey("ai_conversations.id"), index=True)
request_id: Mapped[str] = mapped_column(String(100))
status: Mapped[str] = mapped_column(String(30), default="running")
context: Mapped[dict] = mapped_column(JSON, default=dict)
model: Mapped[str] = mapped_column(String(200))
settings_revision: Mapped[int] = mapped_column(Integer)
model_messages: Mapped[list] = mapped_column(JSON, default=list)
history_start: Mapped[int] = mapped_column(Integer, default=0)
usage: Mapped[dict] = mapped_column(JSON, default=dict)
elapsed_ms: Mapped[int] = mapped_column(Integer, default=0)
error: Mapped[str | None] = mapped_column(Text)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
__table_args__ = (UniqueConstraint("conversation_id", "request_id"),)
class AIMessage(Base):
__tablename__ = "ai_messages"
id: Mapped[str] = mapped_column(String(36), primary_key=True)
conversation_id: Mapped[str] = mapped_column(ForeignKey("ai_conversations.id"), index=True)
run_id: Mapped[str] = mapped_column(ForeignKey("ai_runs.id"), index=True)
role: Mapped[str] = mapped_column(String(20))
parts: Mapped[list] = mapped_column(JSON, default=list)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
class AIToolCall(Base):
__tablename__ = "ai_tool_calls"
id: Mapped[str] = mapped_column(String(36), primary_key=True)
run_id: Mapped[str] = mapped_column(ForeignKey("ai_runs.id"), index=True)
admin_id: Mapped[int] = mapped_column(ForeignKey("admins.id"), default=1)
call_id: Mapped[str] = mapped_column(String(200))
name: Mapped[str] = mapped_column(String(100))
arguments: Mapped[dict] = mapped_column(JSON)
preview: Mapped[dict] = mapped_column(JSON, default=dict)
result: Mapped[dict | None] = mapped_column(JSON)
status: Mapped[str] = mapped_column(String(30), default="pending")
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
__table_args__ = (UniqueConstraint("run_id", "call_id"),)
class BacktestConfig(Base):
__tablename__ = "backtest_config"
id: Mapped[int] = mapped_column(primary_key=True, default=1)
concurrency: Mapped[int] = mapped_column(Integer, default=3)
batch_size: Mapped[int] = mapped_column(Integer, default=8)
version: Mapped[int] = mapped_column(Integer, default=1)
blocked_reason: Mapped[str | None] = mapped_column(Text)
blocked_until: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
class BacktestDraft(Base):
__tablename__ = "backtest_drafts"
id: Mapped[str] = mapped_column(String(36), primary_key=True)
version: Mapped[int] = mapped_column(Integer, default=1)
name: Mapped[str] = mapped_column(String(200))
source: Mapped[dict] = mapped_column(JSON)
candidates: Mapped[list] = mapped_column(JSON)
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
class BacktestPreview(Base):
__tablename__ = "backtest_previews"
id: Mapped[str] = mapped_column(String(36), primary_key=True)
version: Mapped[int] = mapped_column(Integer, default=1)
name: Mapped[str] = mapped_column(String(200))
source: Mapped[dict] = mapped_column(JSON)
candidates: Mapped[list] = mapped_column(JSON)
batches: Mapped[list] = mapped_column(JSON)
batch_size: Mapped[int] = mapped_column(Integer)
digest: Mapped[str] = mapped_column(String(64))
duplicates: Mapped[list] = mapped_column(JSON)
ai_context: Mapped[dict] = mapped_column(JSON, default=dict)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
class BacktestRun(Base):
__tablename__ = "backtest_runs"
id: Mapped[str] = mapped_column(String(36), primary_key=True)
preview_id: Mapped[str] = mapped_column(ForeignKey("backtest_previews.id"), unique=True)
idempotency_key: Mapped[str] = mapped_column(String(100), unique=True)
name: Mapped[str] = mapped_column(String(200))
source: Mapped[dict] = mapped_column(JSON)
ai_context: Mapped[dict] = mapped_column(JSON, default=dict)
control: Mapped[str] = mapped_column(String(20), default="active")
status: Mapped[str] = mapped_column(String(30), default="queued", index=True)
version: Mapped[int] = mapped_column(Integer, default=1)
event_seq: Mapped[int] = mapped_column(Integer, default=0)
total: Mapped[int] = mapped_column(Integer)
batch_size: Mapped[int] = mapped_column(Integer)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
class SimulationAttempt(Base):
__tablename__ = "simulation_attempts"
id: Mapped[str] = mapped_column(String(36), primary_key=True)
run_id: Mapped[str] = mapped_column(ForeignKey("backtest_runs.id"), index=True)
ordinal: Mapped[int] = mapped_column(Integer)
state: Mapped[str] = mapped_column(String(30), default="queued", index=True)
payload: Mapped[list] = mapped_column(JSON)
progress_url: Mapped[str | None] = mapped_column(Text)
remote_complete: Mapped[bool] = mapped_column(Boolean, default=False)
children: Mapped[list] = mapped_column(JSON, default=list)
receipts: Mapped[dict] = mapped_column(JSON, default=dict)
poll_count: Mapped[int] = mapped_column(Integer, default=0)
submit_count: Mapped[int] = mapped_column(Integer, default=0)
next_poll_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
error: Mapped[str | None] = mapped_column(Text)
error_code: Mapped[str | None] = mapped_column(String(50))
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
__table_args__ = (UniqueConstraint("run_id", "ordinal"),)
class BacktestItem(Base):
__tablename__ = "backtest_items"
id: Mapped[str] = mapped_column(String(36), primary_key=True)
run_id: Mapped[str] = mapped_column(ForeignKey("backtest_runs.id"), index=True)
attempt_id: Mapped[str] = mapped_column(ForeignKey("simulation_attempts.id"), index=True)
client_item_id: Mapped[str] = mapped_column(String(100))
ordinal: Mapped[int] = mapped_column(Integer)
expression: Mapped[str] = mapped_column(Text)
settings: Mapped[dict] = mapped_column(JSON)
fingerprint: Mapped[str] = mapped_column(String(64), index=True)
platform_status: Mapped[str] = mapped_column(String(30), default="pending")
collection_status: Mapped[str] = mapped_column(String(30), default="pending")
persistence_status: Mapped[str] = mapped_column(String(30), default="pending")
simulation_id: Mapped[str | None] = mapped_column(String(100))
alpha_id: Mapped[str | None] = mapped_column(String(100))
error: Mapped[str | None] = mapped_column(Text)
__table_args__ = (UniqueConstraint("run_id", "client_item_id"),)
class BacktestResult(Base):
__tablename__ = "backtest_results"
item_id: Mapped[str] = mapped_column(ForeignKey("backtest_items.id"), primary_key=True)
attempt_id: Mapped[str] = mapped_column(ForeignKey("simulation_attempts.id"))
alpha_id: Mapped[str] = mapped_column(ForeignKey("alphas.id"), index=True)
snapshot: Mapped[dict] = mapped_column(JSON)
observed_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
complete: Mapped[bool] = mapped_column(Boolean, default=True)
class BacktestEvent(Base):
__tablename__ = "backtest_events"
run_id: Mapped[str] = mapped_column(ForeignKey("backtest_runs.id"), primary_key=True)
seq: Mapped[int] = mapped_column(Integer, primary_key=True)
kind: Mapped[str] = mapped_column(String(50))
payload: Mapped[dict] = mapped_column(JSON)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
class CatalogScope(Base):
__tablename__ = "catalog_scopes"
key: Mapped[str] = mapped_column(String(200), primary_key=True)
scope: Mapped[dict] = mapped_column(JSON)
catalog_version: Mapped[str | None] = mapped_column(String(36))
synced_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
class CatalogBatch(Base):
__tablename__ = "catalog_batches"
id: Mapped[str] = mapped_column(ForeignKey("sync_jobs.id"), primary_key=True)
scope_key: Mapped[str] = mapped_column(ForeignKey("catalog_scopes.key"), index=True)
dataset_id: Mapped[str | None] = mapped_column(String(200))
complete: Mapped[bool] = mapped_column(Boolean, default=False)
count: Mapped[int] = mapped_column(Integer, default=0)
completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
class CatalogDataset(Base):
__tablename__ = "catalog_datasets"
scope_key: Mapped[str] = mapped_column(ForeignKey("catalog_scopes.key"), primary_key=True)
id: Mapped[str] = mapped_column(String(200), primary_key=True)
field_version: Mapped[str | None] = mapped_column(ForeignKey("catalog_batches.id"))
class CatalogEntry(Base):
"""Immutable published snapshots; staging rows remain invisible until batch completion."""
__tablename__ = "catalog_entries"
batch_id: Mapped[str] = mapped_column(ForeignKey("catalog_batches.id"), primary_key=True)
id: Mapped[str] = mapped_column(String(200), primary_key=True)
name: Mapped[str | None] = mapped_column(Text)
category: Mapped[str | None] = mapped_column(String(200))
subcategory: Mapped[str | None] = mapped_column(String(200))
field_type: Mapped[str | None] = mapped_column(String(100))
coverage: Mapped[float | None] = mapped_column(Float)
user_count: Mapped[int | None] = mapped_column(Integer)
alpha_count: Mapped[int | None] = mapped_column(Integer)
field_count: Mapped[int | None] = mapped_column(Integer)
description: Mapped[str | None] = mapped_column(Text)
unit: Mapped[str | None] = mapped_column(Text)
synced_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
class CatalogNote(Base):
__tablename__ = "catalog_notes"
scope_key: Mapped[str] = mapped_column(ForeignKey("catalog_scopes.key"), primary_key=True)
dataset_id: Mapped[str] = mapped_column(String(200), primary_key=True)
# Empty field_id denotes the dataset; platform identifiers cannot be empty.
field_id: Mapped[str] = mapped_column(String(200), primary_key=True, default="")
note: Mapped[str] = mapped_column(Text, default="")
version: Mapped[int] = mapped_column(Integer, default=1)
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
class TemplateInput(Base):
__tablename__ = "template_inputs"
id: Mapped[str] = mapped_column(String(36), primary_key=True)
scope_key: Mapped[str] = mapped_column(ForeignKey("catalog_scopes.key"), index=True)
dataset_id: Mapped[str] = mapped_column(String(200))
collection_version: Mapped[str] = mapped_column(ForeignKey("catalog_batches.id"))
selection: Mapped[str] = mapped_column(String(20))
field_ids: Mapped[list] = mapped_column(JSON)
field_types: Mapped[dict] = mapped_column(JSON)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
class CatalogResource(Base):
"""Read-only upstream metadata snapshots; local annotations live separately."""
__tablename__ = "catalog_resources"
key: Mapped[str] = mapped_column(String(250), primary_key=True)
kind: Mapped[str] = mapped_column(String(30), index=True)
content: Mapped[dict] = mapped_column(JSON)
fetched_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
class OperatorNote(Base):
__tablename__ = "operator_notes"
name: Mapped[str] = mapped_column(String(200), primary_key=True)
note: Mapped[str] = mapped_column(Text, default="")
favorite: Mapped[bool] = mapped_column(Boolean, default=False)
version: Mapped[int] = mapped_column(Integer, default=1)
class ResearchAsset(Base):
"""Stable identity for typed templates, feature plans, views and workflow definitions."""
__tablename__ = "research_assets"
id: Mapped[str] = mapped_column(String(36), primary_key=True)
kind: Mapped[str] = mapped_column(String(30), index=True)
name: Mapped[str] = mapped_column(String(200))
version: Mapped[int] = mapped_column(Integer, default=1)
archived: Mapped[bool] = mapped_column(Boolean, default=False)
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
class ResearchRevision(Base):
__tablename__ = "research_revisions"
asset_id: Mapped[str] = mapped_column(ForeignKey("research_assets.id"), primary_key=True)
version: Mapped[int] = mapped_column(Integer, primary_key=True)
content: Mapped[dict] = mapped_column(JSON)
provenance: Mapped[dict] = mapped_column(JSON, default=dict)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)
class ResearchExperiment(Base):
"""Immutable generated candidates; never masquerade as platform Alpha records."""
__tablename__ = "research_experiments"
id: Mapped[str] = mapped_column(String(36), primary_key=True)
name: Mapped[str] = mapped_column(String(200))
kind: Mapped[str] = mapped_column(String(30), index=True)
hypothesis: Mapped[str] = mapped_column(Text)
inputs: Mapped[list] = mapped_column(JSON)
parents: Mapped[list] = mapped_column(JSON)
candidates: Mapped[list] = mapped_column(JSON)
evidence: Mapped[dict] = mapped_column(JSON)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=now)