summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backend/app/schemas.py81
1 files changed, 79 insertions, 2 deletions
diff --git a/backend/app/schemas.py b/backend/app/schemas.py
index 351de06..27e56b4 100644
--- a/backend/app/schemas.py
+++ b/backend/app/schemas.py
@@ -1,7 +1,7 @@
"""Pydantic response schemas for the Prism v2 Overview API."""
-from typing import Literal
+from typing import ClassVar, Literal
-from pydantic import BaseModel, Field
+from pydantic import BaseModel, Field, model_validator
class SearchResult(BaseModel):
@@ -96,6 +96,8 @@ class TickerOverview(BaseModel):
range_52w: Range52Week
short_interest: ShortInterest
meta: OverviewMeta
+ currency: str = "USD"
+ currency_warning: str | None = None
class HistoryPoint(BaseModel):
@@ -126,6 +128,54 @@ class FinancialsResponse(BaseModel):
income: FinancialStatement
balance: FinancialStatement
cash_flow: FinancialStatement
+ currency: str = "USD"
+ currency_warning: str | None = None
+
+
+class AdvancedDcfInputs(BaseModel):
+ _projection_arrays: ClassVar[tuple[str, ...]] = (
+ "revenue_growth",
+ "ebitda_margin",
+ "dna_pct_revenue",
+ "capex_pct_revenue",
+ "nwc_chg_pct_delta_rev",
+ "tax_rate",
+ )
+
+ base_revenue: float = Field(gt=0)
+ revenue_growth: list[float] = Field(min_length=1)
+ ebitda_margin: list[float] = Field(min_length=1)
+ dna_pct_revenue: list[float] = Field(min_length=1)
+ capex_pct_revenue: list[float] = Field(min_length=1)
+ nwc_chg_pct_delta_rev: list[float] = Field(min_length=1)
+ tax_rate: list[float] = Field(min_length=1)
+ wacc: float
+ terminal_growth: float
+ projection_years: int = Field(default=5, ge=1)
+
+ @model_validator(mode="after")
+ def projection_arrays_cover_horizon(self) -> "AdvancedDcfInputs":
+ short_fields = [
+ field_name
+ for field_name in self._projection_arrays
+ if len(getattr(self, field_name)) < self.projection_years
+ ]
+ if short_fields:
+ fields = ", ".join(short_fields)
+ raise ValueError(f"projection_years requires assumptions for every year: {fields}")
+ return self
+
+
+class SensitivityMatrix(BaseModel):
+ wacc: list[float] = Field(min_length=5, max_length=5)
+ terminal_growth: list[float] = Field(min_length=5, max_length=5)
+ implied_prices: list[list[float | None]] = Field(min_length=5, max_length=5)
+
+ @model_validator(mode="after")
+ def implied_prices_are_five_by_five(self) -> "SensitivityMatrix":
+ if any(len(row) != 5 for row in self.implied_prices):
+ raise ValueError("implied_prices must contain five WACC rows with five terminal-growth columns")
+ return self
class DcfResult(BaseModel):
@@ -144,6 +194,8 @@ class DcfResult(BaseModel):
wacc: float = 0.10
terminal_growth: float = 0.03
projection_years: int = 5
+ sensitivity: SensitivityMatrix | None = None
+ advanced_inputs: AdvancedDcfInputs | None = None
class MultipleResult(BaseModel):
@@ -163,6 +215,29 @@ class ValuationResponse(BaseModel):
ev_ebitda: MultipleResult
ev_revenue: MultipleResult
price_to_book: MultipleResult
+ currency: str = "USD"
+ currency_warning: str | None = None
+
+
+class WaccResponse(BaseModel):
+ symbol: str
+ available: bool = True
+ wacc: float | None = None
+ cost_of_equity: float | None = None
+ after_tax_cost_of_debt: float | None = None
+ risk_free_rate: float = 0.044
+ beta: float = 1.0
+ equity_risk_premium: float = 0.055
+ cost_of_debt: float = 0.05
+ tax_rate: float = 0.21
+ market_cap: float | None = None
+ total_debt: float | None = None
+ cash: float | None = None
+ net_debt: float | None = None
+ equity_weight: float | None = None
+ debt_weight: float | None = None
+ currency: str = "USD"
+ currency_warning: str | None = None
class WatchlistItem(BaseModel):
@@ -204,6 +279,8 @@ class RatiosResponse(BaseModel):
interest_coverage: RatioPoint
dividend_yield: RatioPoint
dividend_payout: RatioPoint
+ currency: str = "USD"
+ currency_warning: str | None = None
class InsiderTransaction(BaseModel):