first commit
This commit is contained in:
25
app/models/__init__.py
Normal file
25
app/models/__init__.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from app.models.ticker import Ticker
|
||||
from app.models.ohlcv import OHLCVRecord
|
||||
from app.models.user import User
|
||||
from app.models.sentiment import SentimentScore
|
||||
from app.models.fundamental import FundamentalData
|
||||
from app.models.score import DimensionScore, CompositeScore
|
||||
from app.models.sr_level import SRLevel
|
||||
from app.models.trade_setup import TradeSetup
|
||||
from app.models.watchlist import WatchlistEntry
|
||||
from app.models.settings import SystemSetting, IngestionProgress
|
||||
|
||||
__all__ = [
|
||||
"Ticker",
|
||||
"OHLCVRecord",
|
||||
"User",
|
||||
"SentimentScore",
|
||||
"FundamentalData",
|
||||
"DimensionScore",
|
||||
"CompositeScore",
|
||||
"SRLevel",
|
||||
"TradeSetup",
|
||||
"WatchlistEntry",
|
||||
"SystemSetting",
|
||||
"IngestionProgress",
|
||||
]
|
||||
24
app/models/fundamental.py
Normal file
24
app/models/fundamental.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, Float, ForeignKey
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class FundamentalData(Base):
|
||||
__tablename__ = "fundamental_data"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
ticker_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("tickers.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
pe_ratio: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||
revenue_growth: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||
earnings_surprise: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||
market_cap: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||
fetched_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False
|
||||
)
|
||||
|
||||
ticker = relationship("Ticker", back_populates="fundamental_data")
|
||||
30
app/models/ohlcv.py
Normal file
30
app/models/ohlcv.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from datetime import date, datetime
|
||||
|
||||
from sqlalchemy import BigInteger, Date, DateTime, Float, ForeignKey, Index, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class OHLCVRecord(Base):
|
||||
__tablename__ = "ohlcv_records"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("ticker_id", "date", name="uq_ohlcv_ticker_date"),
|
||||
Index("ix_ohlcv_ticker_date", "ticker_id", "date"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
ticker_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("tickers.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
date: Mapped[date] = mapped_column(Date, nullable=False)
|
||||
open: Mapped[float] = mapped_column(Float, nullable=False)
|
||||
high: Mapped[float] = mapped_column(Float, nullable=False)
|
||||
low: Mapped[float] = mapped_column(Float, nullable=False)
|
||||
close: Mapped[float] = mapped_column(Float, nullable=False)
|
||||
volume: Mapped[int] = mapped_column(BigInteger, nullable=False)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=datetime.utcnow, nullable=False
|
||||
)
|
||||
|
||||
ticker = relationship("Ticker", back_populates="ohlcv_records")
|
||||
40
app/models/score.py
Normal file
40
app/models/score.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import Boolean, DateTime, Float, ForeignKey, String, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class DimensionScore(Base):
|
||||
__tablename__ = "dimension_scores"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
ticker_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("tickers.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
dimension: Mapped[str] = mapped_column(String(50), nullable=False)
|
||||
score: Mapped[float] = mapped_column(Float, nullable=False)
|
||||
is_stale: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||
computed_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False
|
||||
)
|
||||
|
||||
ticker = relationship("Ticker", back_populates="dimension_scores")
|
||||
|
||||
|
||||
class CompositeScore(Base):
|
||||
__tablename__ = "composite_scores"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
ticker_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("tickers.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
score: Mapped[float] = mapped_column(Float, nullable=False)
|
||||
is_stale: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||
weights_json: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
computed_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False
|
||||
)
|
||||
|
||||
ticker = relationship("Ticker", back_populates="composite_scores")
|
||||
23
app/models/sentiment.py
Normal file
23
app/models/sentiment.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, ForeignKey, Integer, String
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class SentimentScore(Base):
|
||||
__tablename__ = "sentiment_scores"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
ticker_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("tickers.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
classification: Mapped[str] = mapped_column(String(20), nullable=False)
|
||||
confidence: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
source: Mapped[str] = mapped_column(String(100), nullable=False)
|
||||
timestamp: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False
|
||||
)
|
||||
|
||||
ticker = relationship("Ticker", back_populates="sentiment_scores")
|
||||
35
app/models/settings.py
Normal file
35
app/models/settings.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from datetime import date, datetime
|
||||
|
||||
from sqlalchemy import Date, DateTime, ForeignKey, String, Text, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class SystemSetting(Base):
|
||||
__tablename__ = "system_settings"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
key: Mapped[str] = mapped_column(String(100), unique=True, nullable=False)
|
||||
value: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False
|
||||
)
|
||||
|
||||
|
||||
class IngestionProgress(Base):
|
||||
__tablename__ = "ingestion_progress"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("ticker_id", name="uq_ingestion_progress_ticker"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
ticker_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("tickers.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
last_ingested_date: Mapped[date] = mapped_column(Date, nullable=False)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False
|
||||
)
|
||||
|
||||
ticker = relationship("Ticker", back_populates="ingestion_progress")
|
||||
24
app/models/sr_level.py
Normal file
24
app/models/sr_level.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, Float, ForeignKey, Integer, String
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class SRLevel(Base):
|
||||
__tablename__ = "sr_levels"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
ticker_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("tickers.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
price_level: Mapped[float] = mapped_column(Float, nullable=False)
|
||||
type: Mapped[str] = mapped_column(String(20), nullable=False)
|
||||
strength: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
detection_method: Mapped[str] = mapped_column(String(50), nullable=False)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=datetime.utcnow, nullable=False
|
||||
)
|
||||
|
||||
ticker = relationship("Ticker", back_populates="sr_levels")
|
||||
27
app/models/ticker.py
Normal file
27
app/models/ticker.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import String, DateTime
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class Ticker(Base):
|
||||
__tablename__ = "tickers"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
symbol: Mapped[str] = mapped_column(String(10), unique=True, nullable=False)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=datetime.utcnow, nullable=False
|
||||
)
|
||||
|
||||
# Relationships (cascade deletes)
|
||||
ohlcv_records = relationship("OHLCVRecord", back_populates="ticker", cascade="all, delete-orphan")
|
||||
sentiment_scores = relationship("SentimentScore", back_populates="ticker", cascade="all, delete-orphan")
|
||||
fundamental_data = relationship("FundamentalData", back_populates="ticker", cascade="all, delete-orphan")
|
||||
sr_levels = relationship("SRLevel", back_populates="ticker", cascade="all, delete-orphan")
|
||||
dimension_scores = relationship("DimensionScore", back_populates="ticker", cascade="all, delete-orphan")
|
||||
composite_scores = relationship("CompositeScore", back_populates="ticker", cascade="all, delete-orphan")
|
||||
trade_setups = relationship("TradeSetup", back_populates="ticker", cascade="all, delete-orphan")
|
||||
watchlist_entries = relationship("WatchlistEntry", back_populates="ticker", cascade="all, delete-orphan")
|
||||
ingestion_progress = relationship("IngestionProgress", back_populates="ticker", cascade="all, delete-orphan", uselist=False)
|
||||
26
app/models/trade_setup.py
Normal file
26
app/models/trade_setup.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, Float, ForeignKey, String
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class TradeSetup(Base):
|
||||
__tablename__ = "trade_setups"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
ticker_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("tickers.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
direction: Mapped[str] = mapped_column(String(10), nullable=False)
|
||||
entry_price: Mapped[float] = mapped_column(Float, nullable=False)
|
||||
stop_loss: Mapped[float] = mapped_column(Float, nullable=False)
|
||||
target: Mapped[float] = mapped_column(Float, nullable=False)
|
||||
rr_ratio: Mapped[float] = mapped_column(Float, nullable=False)
|
||||
composite_score: Mapped[float] = mapped_column(Float, nullable=False)
|
||||
detected_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), nullable=False
|
||||
)
|
||||
|
||||
ticker = relationship("Ticker", back_populates="trade_setups")
|
||||
24
app/models/user.py
Normal file
24
app/models/user.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import Boolean, DateTime, String
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = "users"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
username: Mapped[str] = mapped_column(String(100), unique=True, nullable=False)
|
||||
password_hash: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
role: Mapped[str] = mapped_column(String(20), nullable=False, default="user")
|
||||
has_access: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=datetime.utcnow, nullable=False
|
||||
)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False
|
||||
)
|
||||
|
||||
watchlist_entries = relationship("WatchlistEntry", back_populates="user", cascade="all, delete-orphan")
|
||||
28
app/models/watchlist.py
Normal file
28
app/models/watchlist.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, ForeignKey, String, UniqueConstraint
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class WatchlistEntry(Base):
|
||||
__tablename__ = "watchlist_entries"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("user_id", "ticker_id", name="uq_watchlist_user_ticker"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
user_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("users.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
ticker_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("tickers.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
entry_type: Mapped[str] = mapped_column(String(10), nullable=False)
|
||||
added_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=datetime.utcnow, nullable=False
|
||||
)
|
||||
|
||||
user = relationship("User", back_populates="watchlist_entries")
|
||||
ticker = relationship("Ticker", back_populates="watchlist_entries")
|
||||
Reference in New Issue
Block a user