from sqlalchemy import ( BigInteger, Boolean, CheckConstraint, Column, Date, DateTime, ForeignKey, ForeignKeyConstraint, Index, Integer, Numeric, String, Text, UniqueConstraint, func, text, ) from sqlalchemy.dialects.postgresql import JSONB from app.services.db import Base class AppUser(Base): __tablename__ = "app_user" id = Column(String, primary_key=True) username = Column(String, nullable=False, unique=True) password_hash = Column(String, nullable=False) is_admin = Column(Boolean, nullable=False, server_default=text("false")) is_super_admin = Column(Boolean, nullable=False, server_default=text("false")) role = Column(String, nullable=False, server_default=text("'USER'")) __table_args__ = ( CheckConstraint("role IN ('USER','ADMIN','SUPER_ADMIN')", name="chk_app_user_role"), Index("idx_app_user_role", "role"), Index("idx_app_user_is_admin", "is_admin"), Index("idx_app_user_is_super_admin", "is_super_admin"), ) class AppSession(Base): __tablename__ = "app_session" id = Column(String, primary_key=True) user_id = Column(String, ForeignKey("app_user.id", ondelete="CASCADE"), nullable=False) created_at = Column(DateTime(timezone=True), nullable=False) last_seen_at = Column(DateTime(timezone=True)) expires_at = Column(DateTime(timezone=True), nullable=False) __table_args__ = ( Index("idx_app_session_user_id", "user_id"), Index("idx_app_session_expires_at", "expires_at"), ) class UserBroker(Base): __tablename__ = "user_broker" user_id = Column(String, ForeignKey("app_user.id", ondelete="CASCADE"), primary_key=True) broker = Column(String) connected = Column(Boolean, nullable=False, server_default=text("false")) access_token = Column(Text) connected_at = Column(DateTime(timezone=True)) api_key = Column(Text) user_name = Column(Text) broker_user_id = Column(Text) pending_broker = Column(Text) pending_api_key = Column(Text) pending_api_secret = Column(Text) pending_started_at = Column(DateTime(timezone=True)) __table_args__ = ( Index("idx_user_broker_broker", "broker"), Index("idx_user_broker_connected", "connected"), ) class ZerodhaSession(Base): __tablename__ = "zerodha_session" id = Column(BigInteger, primary_key=True, autoincrement=True) user_id = Column(String, ForeignKey("app_user.id", ondelete="CASCADE"), nullable=False) linked_at = Column(DateTime(timezone=True), nullable=False) api_key = Column(Text) access_token = Column(Text) request_token = Column(Text) user_name = Column(Text) broker_user_id = Column(Text) __table_args__ = ( Index("idx_zerodha_session_user_id", "user_id"), Index("idx_zerodha_session_linked_at", "linked_at"), ) class ZerodhaRequestToken(Base): __tablename__ = "zerodha_request_token" user_id = Column(String, ForeignKey("app_user.id", ondelete="CASCADE"), primary_key=True) request_token = Column(Text, nullable=False) class StrategyRun(Base): __tablename__ = "strategy_run" run_id = Column(String, primary_key=True) user_id = Column(String, ForeignKey("app_user.id", ondelete="CASCADE"), nullable=False) created_at = Column(DateTime(timezone=True), nullable=False, server_default=func.now()) started_at = Column(DateTime(timezone=True)) stopped_at = Column(DateTime(timezone=True)) status = Column(String, nullable=False) strategy = Column(String) mode = Column(String) broker = Column(String) meta = Column(JSONB) __table_args__ = ( UniqueConstraint("user_id", "run_id", name="uq_strategy_run_user_run"), CheckConstraint("status IN ('RUNNING','STOPPED','ERROR')", name="chk_strategy_run_status"), Index("idx_strategy_run_user_status", "user_id", "status"), Index("idx_strategy_run_user_created", "user_id", "created_at"), Index( "uq_one_running_run_per_user", "user_id", unique=True, postgresql_where=text("status = 'RUNNING'"), ), ) class StrategyConfig(Base): __tablename__ = "strategy_config" id = Column(BigInteger, primary_key=True, autoincrement=True) strategy = Column(String) sip_amount = Column(Numeric) sip_frequency_value = Column(Integer) sip_frequency_unit = Column(String) mode = Column(String) broker = Column(String) active = Column(Boolean) frequency = Column(Text) frequency_days = Column(Integer) unit = Column(String) next_run = Column(DateTime(timezone=True)) user_id = Column(String, ForeignKey("app_user.id", ondelete="CASCADE"), nullable=False) run_id = Column(String, ForeignKey("strategy_run.run_id", ondelete="CASCADE"), nullable=False) __table_args__ = ( UniqueConstraint("user_id", "run_id", name="uq_strategy_config_user_run"), ) class StrategyLog(Base): __tablename__ = "strategy_log" seq = Column(BigInteger, primary_key=True) ts = Column(DateTime(timezone=True), nullable=False) level = Column(String) category = Column(String) event = Column(String) message = Column(Text) user_id = Column(String, ForeignKey("app_user.id", ondelete="CASCADE"), nullable=False) run_id = Column(String, ForeignKey("strategy_run.run_id", ondelete="CASCADE"), nullable=False) meta = Column(JSONB) __table_args__ = ( Index("idx_strategy_log_ts", "ts"), Index("idx_strategy_log_event", "event"), Index("idx_strategy_log_user_run_ts", "user_id", "run_id", "ts"), ) class EngineStatus(Base): __tablename__ = "engine_status" id = Column(BigInteger, primary_key=True, autoincrement=True) user_id = Column(String, ForeignKey("app_user.id", ondelete="CASCADE"), nullable=False) run_id = Column(String, nullable=False) status = Column(String, nullable=False) last_updated = Column(DateTime(timezone=True), nullable=False) __table_args__ = ( UniqueConstraint("user_id", "run_id", name="uq_engine_status_user_run"), ForeignKeyConstraint( ["user_id", "run_id"], ["strategy_run.user_id", "strategy_run.run_id"], ondelete="CASCADE", ), Index("idx_engine_status_user_run", "user_id", "run_id"), ) class EngineState(Base): __tablename__ = "engine_state" id = Column(BigInteger, primary_key=True, autoincrement=True) user_id = Column(String, ForeignKey("app_user.id", ondelete="CASCADE"), nullable=False) run_id = Column(String, nullable=False) total_invested = Column(Numeric) nifty_units = Column(Numeric) gold_units = Column(Numeric) last_sip_ts = Column(DateTime(timezone=True)) last_run = Column(DateTime(timezone=True)) __table_args__ = ( UniqueConstraint("user_id", "run_id", name="uq_engine_state_user_run"), ForeignKeyConstraint( ["user_id", "run_id"], ["strategy_run.user_id", "strategy_run.run_id"], ondelete="CASCADE", ), ) class EngineStatePaper(Base): __tablename__ = "engine_state_paper" id = Column(BigInteger, primary_key=True, autoincrement=True) user_id = Column(String, ForeignKey("app_user.id", ondelete="CASCADE"), nullable=False) run_id = Column(String, nullable=False) initial_cash = Column(Numeric) cash = Column(Numeric) total_invested = Column(Numeric) nifty_units = Column(Numeric) gold_units = Column(Numeric) last_sip_ts = Column(DateTime(timezone=True)) last_run = Column(DateTime(timezone=True)) sip_frequency_value = Column(Integer) sip_frequency_unit = Column(String) __table_args__ = ( UniqueConstraint("user_id", "run_id", name="uq_engine_state_paper_user_run"), ForeignKeyConstraint( ["user_id", "run_id"], ["strategy_run.user_id", "strategy_run.run_id"], ondelete="CASCADE", ), CheckConstraint("cash >= 0", name="chk_engine_state_paper_cash_non_negative"), ) class EngineEvent(Base): __tablename__ = "engine_event" id = Column(BigInteger, primary_key=True, autoincrement=True) ts = Column(DateTime(timezone=True), nullable=False) event = Column(String) data = Column(JSONB) message = Column(Text) meta = Column(JSONB) user_id = Column(String, ForeignKey("app_user.id", ondelete="CASCADE"), nullable=False) run_id = Column(String, ForeignKey("strategy_run.run_id", ondelete="CASCADE"), nullable=False) __table_args__ = ( Index("idx_engine_event_ts", "ts"), Index("idx_engine_event_user_run_ts", "user_id", "run_id", "ts"), ) class PaperBrokerAccount(Base): __tablename__ = "paper_broker_account" id = Column(BigInteger, primary_key=True, autoincrement=True) user_id = Column(String, ForeignKey("app_user.id", ondelete="CASCADE"), nullable=False) run_id = Column(String, nullable=False) cash = Column(Numeric, nullable=False) __table_args__ = ( UniqueConstraint("user_id", "run_id", name="uq_paper_broker_account_user_run"), ForeignKeyConstraint( ["user_id", "run_id"], ["strategy_run.user_id", "strategy_run.run_id"], ondelete="CASCADE", ), CheckConstraint("cash >= 0", name="chk_paper_broker_cash_non_negative"), ) class PaperPosition(Base): __tablename__ = "paper_position" user_id = Column(String, primary_key=True) run_id = Column(String, primary_key=True) symbol = Column(String, primary_key=True) qty = Column(Numeric, nullable=False) avg_price = Column(Numeric) last_price = Column(Numeric) updated_at = Column(DateTime(timezone=True), nullable=False, server_default=func.now()) __table_args__ = ( ForeignKeyConstraint( ["user_id", "run_id"], ["strategy_run.user_id", "strategy_run.run_id"], ondelete="CASCADE", ), CheckConstraint("qty > 0", name="chk_paper_position_qty_positive"), UniqueConstraint("user_id", "run_id", "symbol", name="uq_paper_position_scope"), Index("idx_paper_position_user_run", "user_id", "run_id"), ) class PaperOrder(Base): __tablename__ = "paper_order" id = Column(String, primary_key=True) user_id = Column(String, ForeignKey("app_user.id", ondelete="CASCADE"), nullable=False) run_id = Column(String, nullable=False) symbol = Column(String, nullable=False) side = Column(String, nullable=False) qty = Column(Numeric, nullable=False) price = Column(Numeric) status = Column(String, nullable=False) timestamp = Column("timestamp", DateTime(timezone=True), nullable=False) logical_time = Column(DateTime(timezone=True), nullable=False) __table_args__ = ( ForeignKeyConstraint( ["user_id", "run_id"], ["strategy_run.user_id", "strategy_run.run_id"], ondelete="CASCADE", ), UniqueConstraint("user_id", "run_id", "id", name="uq_paper_order_scope_id"), UniqueConstraint( "user_id", "run_id", "logical_time", "symbol", "side", name="uq_paper_order_logical_key", ), CheckConstraint("qty > 0", name="chk_paper_order_qty_positive"), CheckConstraint("price >= 0", name="chk_paper_order_price_non_negative"), Index("idx_paper_order_ts", "timestamp"), Index("idx_paper_order_user_run_ts", "user_id", "run_id", "timestamp"), ) class PaperTrade(Base): __tablename__ = "paper_trade" id = Column(String, primary_key=True) order_id = Column(String) user_id = Column(String, ForeignKey("app_user.id", ondelete="CASCADE"), nullable=False) run_id = Column(String, nullable=False) symbol = Column(String, nullable=False) side = Column(String, nullable=False) qty = Column(Numeric, nullable=False) price = Column(Numeric, nullable=False) timestamp = Column("timestamp", DateTime(timezone=True), nullable=False) logical_time = Column(DateTime(timezone=True), nullable=False) __table_args__ = ( ForeignKeyConstraint( ["user_id", "run_id"], ["strategy_run.user_id", "strategy_run.run_id"], ondelete="CASCADE", ), ForeignKeyConstraint( ["user_id", "run_id", "order_id"], ["paper_order.user_id", "paper_order.run_id", "paper_order.id"], ondelete="CASCADE", ), UniqueConstraint("user_id", "run_id", "id", name="uq_paper_trade_scope_id"), UniqueConstraint( "user_id", "run_id", "logical_time", "symbol", "side", name="uq_paper_trade_logical_key", ), CheckConstraint("qty > 0", name="chk_paper_trade_qty_positive"), CheckConstraint("price >= 0", name="chk_paper_trade_price_non_negative"), Index("idx_paper_trade_ts", "timestamp"), Index("idx_paper_trade_user_run_ts", "user_id", "run_id", "timestamp"), ) class PaperEquityCurve(Base): __tablename__ = "paper_equity_curve" user_id = Column(String, primary_key=True) run_id = Column(String, primary_key=True) timestamp = Column("timestamp", DateTime(timezone=True), nullable=False) logical_time = Column(DateTime(timezone=True), primary_key=True) equity = Column(Numeric, nullable=False) pnl = Column(Numeric) __table_args__ = ( ForeignKeyConstraint( ["user_id", "run_id"], ["strategy_run.user_id", "strategy_run.run_id"], ondelete="CASCADE", ), Index("idx_paper_equity_curve_ts", "timestamp"), Index("idx_paper_equity_curve_user_run_ts", "user_id", "run_id", "timestamp"), ) class MTMLedger(Base): __tablename__ = "mtm_ledger" user_id = Column(String, primary_key=True) run_id = Column(String, primary_key=True) timestamp = Column("timestamp", DateTime(timezone=True), nullable=False) logical_time = Column(DateTime(timezone=True), primary_key=True) nifty_units = Column(Numeric) gold_units = Column(Numeric) nifty_price = Column(Numeric) gold_price = Column(Numeric) nifty_value = Column(Numeric) gold_value = Column(Numeric) portfolio_value = Column(Numeric) total_invested = Column(Numeric) pnl = Column(Numeric) __table_args__ = ( ForeignKeyConstraint( ["user_id", "run_id"], ["strategy_run.user_id", "strategy_run.run_id"], ondelete="CASCADE", ), Index("idx_mtm_ledger_ts", "timestamp"), Index("idx_mtm_ledger_user_run_ts", "user_id", "run_id", "timestamp"), ) class EventLedger(Base): __tablename__ = "event_ledger" id = Column(BigInteger, primary_key=True, autoincrement=True) user_id = Column(String, ForeignKey("app_user.id", ondelete="CASCADE"), nullable=False) run_id = Column(String, nullable=False) timestamp = Column("timestamp", DateTime(timezone=True), nullable=False) logical_time = Column(DateTime(timezone=True), nullable=False) event = Column(String, nullable=False) nifty_units = Column(Numeric) gold_units = Column(Numeric) nifty_price = Column(Numeric) gold_price = Column(Numeric) amount = Column(Numeric) __table_args__ = ( ForeignKeyConstraint( ["user_id", "run_id"], ["strategy_run.user_id", "strategy_run.run_id"], ondelete="CASCADE", ), UniqueConstraint("user_id", "run_id", "event", "logical_time", name="uq_event_ledger_event_time"), Index("idx_event_ledger_user_run_logical", "user_id", "run_id", "logical_time"), Index("idx_event_ledger_ts", "timestamp"), Index("idx_event_ledger_user_run_ts", "user_id", "run_id", "timestamp"), ) class MarketClose(Base): __tablename__ = "market_close" symbol = Column(String, primary_key=True) date = Column(Date, primary_key=True) close = Column(Numeric, nullable=False) __table_args__ = ( Index("idx_market_close_symbol", "symbol"), Index("idx_market_close_date", "date"), ) class AdminAuditLog(Base): __tablename__ = "admin_audit_log" id = Column(BigInteger, primary_key=True, autoincrement=True) ts = Column(DateTime(timezone=True), nullable=False, server_default=func.now()) actor_user_hash = Column(Text, nullable=False) target_user_hash = Column(Text, nullable=False) target_username_hash = Column(Text) action = Column(Text, nullable=False) meta = Column(JSONB) class AdminRoleAudit(Base): __tablename__ = "admin_role_audit" id = Column(BigInteger, primary_key=True, autoincrement=True) actor_user_id = Column(String, nullable=False) target_user_id = Column(String, nullable=False) old_role = Column(String, nullable=False) new_role = Column(String, nullable=False) changed_at = Column(DateTime(timezone=True), nullable=False, server_default=func.now())