first commit
This commit is contained in:
1
app/schemas/__init__.py
Normal file
1
app/schemas/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
41
app/schemas/admin.py
Normal file
41
app/schemas/admin.py
Normal file
@@ -0,0 +1,41 @@
|
||||
"""Admin request/response schemas."""
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class UserManagement(BaseModel):
|
||||
"""Schema for user access management."""
|
||||
has_access: bool
|
||||
|
||||
|
||||
class PasswordReset(BaseModel):
|
||||
"""Schema for resetting a user's password."""
|
||||
new_password: str = Field(..., min_length=6)
|
||||
|
||||
|
||||
class CreateUserRequest(BaseModel):
|
||||
"""Schema for admin-created user accounts."""
|
||||
username: str = Field(..., min_length=1)
|
||||
password: str = Field(..., min_length=6)
|
||||
role: str = Field(default="user", pattern=r"^(user|admin)$")
|
||||
has_access: bool = False
|
||||
|
||||
|
||||
class RegistrationToggle(BaseModel):
|
||||
"""Schema for toggling registration on/off."""
|
||||
enabled: bool
|
||||
|
||||
|
||||
class SystemSettingUpdate(BaseModel):
|
||||
"""Schema for updating a system setting."""
|
||||
value: str = Field(..., min_length=1)
|
||||
|
||||
|
||||
class DataCleanupRequest(BaseModel):
|
||||
"""Schema for data cleanup — delete records older than N days."""
|
||||
older_than_days: int = Field(..., gt=0)
|
||||
|
||||
|
||||
class JobToggle(BaseModel):
|
||||
"""Schema for enabling/disabling a scheduled job."""
|
||||
enabled: bool
|
||||
18
app/schemas/auth.py
Normal file
18
app/schemas/auth.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""Auth request/response schemas."""
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class RegisterRequest(BaseModel):
|
||||
username: str = Field(..., min_length=1)
|
||||
password: str = Field(..., min_length=6)
|
||||
|
||||
|
||||
class LoginRequest(BaseModel):
|
||||
username: str
|
||||
password: str
|
||||
|
||||
|
||||
class TokenResponse(BaseModel):
|
||||
access_token: str
|
||||
token_type: str = "bearer"
|
||||
13
app/schemas/common.py
Normal file
13
app/schemas/common.py
Normal file
@@ -0,0 +1,13 @@
|
||||
"""Shared API schemas used across all endpoints."""
|
||||
|
||||
from typing import Any, Literal
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class APIEnvelope(BaseModel):
|
||||
"""Standard JSON envelope for all API responses."""
|
||||
|
||||
status: Literal["success", "error"]
|
||||
data: Any | None = None
|
||||
error: str | None = None
|
||||
18
app/schemas/fundamental.py
Normal file
18
app/schemas/fundamental.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""Pydantic schemas for fundamental data endpoints."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class FundamentalResponse(BaseModel):
|
||||
"""Envelope-ready fundamental data response."""
|
||||
|
||||
symbol: str
|
||||
pe_ratio: float | None = None
|
||||
revenue_growth: float | None = None
|
||||
earnings_surprise: float | None = None
|
||||
market_cap: float | None = None
|
||||
fetched_at: datetime | None = None
|
||||
49
app/schemas/indicator.py
Normal file
49
app/schemas/indicator.py
Normal file
@@ -0,0 +1,49 @@
|
||||
"""Pydantic schemas for technical indicator endpoints."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date
|
||||
from typing import Any, Literal
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class IndicatorRequest(BaseModel):
|
||||
"""Query parameters for indicator computation."""
|
||||
|
||||
start_date: date | None = None
|
||||
end_date: date | None = None
|
||||
period: int | None = None
|
||||
|
||||
|
||||
class IndicatorResult(BaseModel):
|
||||
"""Raw indicator values plus normalized score."""
|
||||
|
||||
indicator_type: str
|
||||
values: dict[str, Any]
|
||||
score: float = Field(ge=0, le=100)
|
||||
bars_used: int
|
||||
|
||||
|
||||
class IndicatorResponse(BaseModel):
|
||||
"""Envelope-ready indicator response."""
|
||||
|
||||
symbol: str
|
||||
indicator: IndicatorResult
|
||||
|
||||
|
||||
class EMACrossResult(BaseModel):
|
||||
"""EMA cross signal details."""
|
||||
|
||||
short_ema: float
|
||||
long_ema: float
|
||||
short_period: int
|
||||
long_period: int
|
||||
signal: Literal["bullish", "bearish", "neutral"]
|
||||
|
||||
|
||||
class EMACrossResponse(BaseModel):
|
||||
"""Envelope-ready EMA cross response."""
|
||||
|
||||
symbol: str
|
||||
ema_cross: EMACrossResult
|
||||
31
app/schemas/ohlcv.py
Normal file
31
app/schemas/ohlcv.py
Normal file
@@ -0,0 +1,31 @@
|
||||
"""OHLCV request/response schemas."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime as _dt
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class OHLCVCreate(BaseModel):
|
||||
symbol: str = Field(..., description="Ticker symbol (e.g. AAPL)")
|
||||
date: _dt.date = Field(..., description="Trading date (YYYY-MM-DD)")
|
||||
open: float = Field(..., ge=0, description="Opening price")
|
||||
high: float = Field(..., ge=0, description="High price")
|
||||
low: float = Field(..., ge=0, description="Low price")
|
||||
close: float = Field(..., ge=0, description="Closing price")
|
||||
volume: int = Field(..., ge=0, description="Trading volume")
|
||||
|
||||
|
||||
class OHLCVResponse(BaseModel):
|
||||
id: int
|
||||
ticker_id: int
|
||||
date: _dt.date
|
||||
open: float
|
||||
high: float
|
||||
low: float
|
||||
close: float
|
||||
volume: int
|
||||
created_at: _dt.datetime
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
52
app/schemas/score.py
Normal file
52
app/schemas/score.py
Normal file
@@ -0,0 +1,52 @@
|
||||
"""Pydantic schemas for scoring endpoints."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class DimensionScoreResponse(BaseModel):
|
||||
"""A single dimension score."""
|
||||
|
||||
dimension: str
|
||||
score: float
|
||||
is_stale: bool
|
||||
computed_at: datetime | None = None
|
||||
|
||||
|
||||
class ScoreResponse(BaseModel):
|
||||
"""Full score response for a ticker: composite + all dimensions."""
|
||||
|
||||
symbol: str
|
||||
composite_score: float | None = None
|
||||
composite_stale: bool = False
|
||||
weights: dict[str, float] = {}
|
||||
dimensions: list[DimensionScoreResponse] = []
|
||||
missing_dimensions: list[str] = []
|
||||
computed_at: datetime | None = None
|
||||
|
||||
|
||||
class WeightUpdateRequest(BaseModel):
|
||||
"""Request to update dimension weights."""
|
||||
|
||||
weights: dict[str, float] = Field(
|
||||
...,
|
||||
description="Dimension name → weight mapping. All weights must be positive.",
|
||||
)
|
||||
|
||||
|
||||
class RankingEntry(BaseModel):
|
||||
"""A single entry in the rankings list."""
|
||||
|
||||
symbol: str
|
||||
composite_score: float
|
||||
dimensions: list[DimensionScoreResponse] = []
|
||||
|
||||
|
||||
class RankingResponse(BaseModel):
|
||||
"""Rankings response: tickers sorted by composite score descending."""
|
||||
|
||||
rankings: list[RankingEntry] = []
|
||||
weights: dict[str, float] = {}
|
||||
30
app/schemas/sentiment.py
Normal file
30
app/schemas/sentiment.py
Normal file
@@ -0,0 +1,30 @@
|
||||
"""Pydantic schemas for sentiment endpoints."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class SentimentScoreResult(BaseModel):
|
||||
"""A single sentiment score record."""
|
||||
|
||||
id: int
|
||||
classification: Literal["bullish", "bearish", "neutral"]
|
||||
confidence: int = Field(ge=0, le=100)
|
||||
source: str
|
||||
timestamp: datetime
|
||||
|
||||
|
||||
class SentimentResponse(BaseModel):
|
||||
"""Envelope-ready sentiment response."""
|
||||
|
||||
symbol: str
|
||||
scores: list[SentimentScoreResult]
|
||||
count: int
|
||||
dimension_score: float | None = Field(
|
||||
None, ge=0, le=100, description="Time-decay weighted sentiment dimension score"
|
||||
)
|
||||
lookback_hours: float
|
||||
27
app/schemas/sr_level.py
Normal file
27
app/schemas/sr_level.py
Normal file
@@ -0,0 +1,27 @@
|
||||
"""Pydantic schemas for S/R level endpoints."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class SRLevelResult(BaseModel):
|
||||
"""A single support/resistance level."""
|
||||
|
||||
id: int
|
||||
price_level: float
|
||||
type: Literal["support", "resistance"]
|
||||
strength: int = Field(ge=0, le=100)
|
||||
detection_method: Literal["volume_profile", "pivot_point", "merged"]
|
||||
created_at: datetime
|
||||
|
||||
|
||||
class SRLevelResponse(BaseModel):
|
||||
"""Envelope-ready S/R levels response."""
|
||||
|
||||
symbol: str
|
||||
levels: list[SRLevelResult]
|
||||
count: int
|
||||
17
app/schemas/ticker.py
Normal file
17
app/schemas/ticker.py
Normal file
@@ -0,0 +1,17 @@
|
||||
"""Ticker request/response schemas."""
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class TickerCreate(BaseModel):
|
||||
symbol: str = Field(..., description="NASDAQ ticker symbol (e.g. AAPL)")
|
||||
|
||||
|
||||
class TickerResponse(BaseModel):
|
||||
id: int
|
||||
symbol: str
|
||||
created_at: datetime
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
21
app/schemas/trade_setup.py
Normal file
21
app/schemas/trade_setup.py
Normal file
@@ -0,0 +1,21 @@
|
||||
"""Pydantic schemas for trade setup endpoints."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class TradeSetupResponse(BaseModel):
|
||||
"""A single trade setup detected by the R:R scanner."""
|
||||
|
||||
id: int
|
||||
symbol: str
|
||||
direction: str
|
||||
entry_price: float
|
||||
stop_loss: float
|
||||
target: float
|
||||
rr_ratio: float
|
||||
composite_score: float
|
||||
detected_at: datetime
|
||||
36
app/schemas/watchlist.py
Normal file
36
app/schemas/watchlist.py
Normal file
@@ -0,0 +1,36 @@
|
||||
"""Pydantic schemas for watchlist endpoints."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
class SRLevelSummary(BaseModel):
|
||||
"""Compact SR level for watchlist entry."""
|
||||
|
||||
price_level: float
|
||||
type: Literal["support", "resistance"]
|
||||
strength: int = Field(ge=0, le=100)
|
||||
|
||||
|
||||
class DimensionScoreSummary(BaseModel):
|
||||
"""Compact dimension score for watchlist entry."""
|
||||
|
||||
dimension: str
|
||||
score: float
|
||||
|
||||
|
||||
class WatchlistEntryResponse(BaseModel):
|
||||
"""A single watchlist entry with enriched data."""
|
||||
|
||||
symbol: str
|
||||
entry_type: Literal["auto", "manual"]
|
||||
composite_score: float | None = None
|
||||
dimensions: list[DimensionScoreSummary] = []
|
||||
rr_ratio: float | None = None
|
||||
rr_direction: str | None = None
|
||||
sr_levels: list[SRLevelSummary] = []
|
||||
added_at: datetime
|
||||
Reference in New Issue
Block a user