from datetime import datetime from typing import List, Optional from sqlalchemy import Integer, String, Boolean, Float, DateTime, ForeignKey, Text, JSON from sqlalchemy.orm import Mapped, mapped_column, relationship from app.db.database import Base # ----------------- # 1. System & Config # ----------------- class AiConfig(Base): __tablename__ = "ai_configs" id: Mapped[str] = mapped_column(String, primary_key=True) name: Mapped[str] = mapped_column(String) providerType: Mapped[str] = mapped_column(String) # Gemini, Ollama, OpenAI modelName: Mapped[str] = mapped_column(String) baseUrl: Mapped[Optional[str]] = mapped_column(String, nullable=True) class ApiSettings(Base): __tablename__ = "api_settings" id: Mapped[int] = mapped_column(Integer, primary_key=True, default=1) # Always 1 # Credentials appKey: Mapped[Optional[str]] = mapped_column(String, nullable=True) appSecret: Mapped[Optional[str]] = mapped_column(String, nullable=True) accountNumber: Mapped[Optional[str]] = mapped_column(String, nullable=True) # Integrations useTelegram: Mapped[bool] = mapped_column(Boolean, default=False) telegramToken: Mapped[Optional[str]] = mapped_column(String, nullable=True) telegramChatId: Mapped[Optional[str]] = mapped_column(String, nullable=True) useNaverNews: Mapped[bool] = mapped_column(Boolean, default=False) naverClientId: Mapped[Optional[str]] = mapped_column(String, nullable=True) naverClientSecret: Mapped[Optional[str]] = mapped_column(String, nullable=True) # Configs kisApiDelayMs: Mapped[int] = mapped_column(Integer, default=250) newsScrapIntervalMin: Mapped[int] = mapped_column(Integer, default=10) # Token Storage (Runtime) accessToken: Mapped[Optional[str]] = mapped_column(Text, nullable=True) tokenExpiry: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True) websocketApprovalKey: Mapped[Optional[str]] = mapped_column(Text, nullable=True) # AI Config Relations (Foreign Keys) preferredNewsAiId: Mapped[Optional[str]] = mapped_column(ForeignKey("ai_configs.id"), nullable=True) preferredStockAiId: Mapped[Optional[str]] = mapped_column(ForeignKey("ai_configs.id"), nullable=True) preferredNewsJudgementAiId: Mapped[Optional[str]] = mapped_column(ForeignKey("ai_configs.id"), nullable=True) preferredAutoBuyAiId: Mapped[Optional[str]] = mapped_column(ForeignKey("ai_configs.id"), nullable=True) preferredAutoSellAiId: Mapped[Optional[str]] = mapped_column(ForeignKey("ai_configs.id"), nullable=True) # ----------------- # 2. Account & Portfolio # ----------------- class AccountStatus(Base): __tablename__ = "account_status" id: Mapped[int] = mapped_column(Integer, primary_key=True, default=1) totalAssets: Mapped[float] = mapped_column(Float, default=0.0) buyingPower: Mapped[float] = mapped_column(Float, default=0.0) dailyProfit: Mapped[float] = mapped_column(Float, default=0.0) dailyProfitRate: Mapped[float] = mapped_column(Float, default=0.0) class Holding(Base): __tablename__ = "holdings" stockCode: Mapped[str] = mapped_column(String, primary_key=True) stockName: Mapped[str] = mapped_column(String) quantity: Mapped[int] = mapped_column(Integer) avgPrice: Mapped[float] = mapped_column(Float) currentPrice: Mapped[float] = mapped_column(Float) # Real-time updated profit: Mapped[float] = mapped_column(Float) profitRate: Mapped[float] = mapped_column(Float) marketValue: Mapped[float] = mapped_column(Float) # ----------------- # 3. Market & Discovery # ----------------- class MasterStock(Base): __tablename__ = "master_stocks" code: Mapped[str] = mapped_column(String, primary_key=True) name: Mapped[str] = mapped_column(String) market: Mapped[str] = mapped_column(String) # Domestic, Overseas # Stats per: Mapped[float] = mapped_column(Float, default=0.0) pbr: Mapped[float] = mapped_column(Float, default=0.0) roe: Mapped[float] = mapped_column(Float, default=0.0) marketCap: Mapped[float] = mapped_column(Float, default=0.0) dividendYield: Mapped[float] = mapped_column(Float, default=0.0) # User Data memo: Mapped[Optional[str]] = mapped_column(Text, nullable=True) isHidden: Mapped[bool] = mapped_column(Boolean, default=False) class NewsCache(Base): __tablename__ = "news_cache" news_id: Mapped[str] = mapped_column(String, primary_key=True) # Hashed ID title: Mapped[str] = mapped_column(Text) description: Mapped[str] = mapped_column(Text) link: Mapped[str] = mapped_column(Text) pubDate: Mapped[str] = mapped_column(String) sentiment: Mapped[Optional[str]] = mapped_column(String, nullable=True) relatedThemes: Mapped[Optional[List]] = mapped_column(JSON, nullable=True) relatedStocks: Mapped[Optional[List]] = mapped_column(JSON, nullable=True) class DiscoveryRankingCache(Base): __tablename__ = "discovery_ranking_cache" # Composite Key simulated (category_market string or separate cols) # Using composite PK category: Mapped[str] = mapped_column(String, primary_key=True) market: Mapped[str] = mapped_column(String, primary_key=True) updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.now) items_json: Mapped[str] = mapped_column(Text) # JSON String of StockItem[] class StockStat(Base): __tablename__ = "stock_stats" code: Mapped[str] = mapped_column(String, primary_key=True) tradingValue: Mapped[float] = mapped_column(Float, default=0.0) buyRatio: Mapped[int] = mapped_column(Integer, default=0) sellRatio: Mapped[int] = mapped_column(Integer, default=0) foreignNetBuy: Mapped[int] = mapped_column(Integer, default=0) institutionalNetBuy: Mapped[int] = mapped_column(Integer, default=0) aiScoreBuy: Mapped[int] = mapped_column(Integer, default=0) aiScoreSell: Mapped[int] = mapped_column(Integer, default=0) class StockItem(Base): __tablename__ = "stock_items" code: Mapped[str] = mapped_column(String, primary_key=True) name: Mapped[str] = mapped_column(String) price: Mapped[float] = mapped_column(Float, default=0.0) change: Mapped[float] = mapped_column(Float, default=0.0) changePercent: Mapped[float] = mapped_column(Float, default=0.0) market: Mapped[str] = mapped_column(String) # Domestic, Overseas updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.now) # ----------------- # 4. Watchlist # ----------------- class WatchlistGroup(Base): __tablename__ = "watchlist_groups" id: Mapped[str] = mapped_column(String, primary_key=True) name: Mapped[str] = mapped_column(String) market: Mapped[str] = mapped_column(String) # Domestic, Overseas class WatchlistItem(Base): __tablename__ = "watchlist_items" group_id: Mapped[str] = mapped_column(ForeignKey("watchlist_groups.id"), primary_key=True) stock_code: Mapped[str] = mapped_column(String, primary_key=True) added_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.now) # ----------------- # 5. Trading & Automation # ----------------- class TradeHistory(Base): __tablename__ = "trade_history" id: Mapped[str] = mapped_column(String, primary_key=True) stockCode: Mapped[str] = mapped_column(String) stockName: Mapped[str] = mapped_column(String) type: Mapped[str] = mapped_column(String) # BUY, SELL quantity: Mapped[int] = mapped_column(Integer) price: Mapped[float] = mapped_column(Float) timestamp: Mapped[datetime] = mapped_column(DateTime) status: Mapped[str] = mapped_column(String) # FILLED, CANCELLED class AutoTradeRobot(Base): __tablename__ = "auto_trade_robots" id: Mapped[str] = mapped_column(String, primary_key=True) stockCode: Mapped[str] = mapped_column(String) stockName: Mapped[str] = mapped_column(String) groupId: Mapped[Optional[str]] = mapped_column(String, nullable=True) type: Mapped[str] = mapped_column(String) # ACCUMULATION, TRAILING, etc. frequency: Mapped[str] = mapped_column(String) # DAILY, WEEKLY executionTime: Mapped[str] = mapped_column(String) # HH:MM market: Mapped[str] = mapped_column(String) quantity: Mapped[int] = mapped_column(Integer) specificDay: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) # 0=Monday trailingPercent: Mapped[Optional[float]] = mapped_column(Float, nullable=True) active: Mapped[bool] = mapped_column(Boolean, default=True) class ReservedOrder(Base): __tablename__ = "reserved_orders" id: Mapped[str] = mapped_column(String, primary_key=True) stockCode: Mapped[str] = mapped_column(String) stockName: Mapped[str] = mapped_column(String) type: Mapped[str] = mapped_column(String) # BUY, SELL market: Mapped[str] = mapped_column(String) monitoringType: Mapped[str] = mapped_column(String) # TARGET, TRAILING trailingType: Mapped[Optional[str]] = mapped_column(String, nullable=True) # AMOUNT, PERCENT status: Mapped[str] = mapped_column(String) # MONITORING, TRIGGERED, EXPIRED quantity: Mapped[int] = mapped_column(Integer) triggerPrice: Mapped[Optional[float]] = mapped_column(Float, nullable=True) trailingValue: Mapped[Optional[float]] = mapped_column(Float, nullable=True) stopLossValue: Mapped[Optional[float]] = mapped_column(Float, nullable=True) highestPrice: Mapped[Optional[float]] = mapped_column(Float, nullable=True) # For Trailing lowestPrice: Mapped[Optional[float]] = mapped_column(Float, nullable=True) useStopLoss: Mapped[bool] = mapped_column(Boolean, default=False) sellAll: Mapped[bool] = mapped_column(Boolean, default=False) stopLossType: Mapped[Optional[str]] = mapped_column(String, nullable=True) createdAt: Mapped[datetime] = mapped_column(DateTime, default=datetime.now) expiryDate: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)