From f07e34e1089ebb446f09828884701495ca107247 Mon Sep 17 00:00:00 2001 From: tehcoderer Date: Tue, 8 Aug 2023 18:08:17 -0400 Subject: [PATCH] financial ratios --- .../stocks/openbb_stocks/fa/fa_router.py | 17 ++- .../providers/fmp/openbb_fmp/__init__.py | 2 + .../fmp/openbb_fmp/models/financial_ratios.py | 118 ++++++++++++++++++ .../openbb_fmp/models/historical_dividends.py | 18 +-- .../openbb_fmp/models/historical_employees.py | 14 --- .../fmp/openbb_fmp/models/stock_peers.py | 4 +- .../standard_models/financial_ratios.py | 112 +++++++++++++++++ .../standard_models/historical_dividends.py | 20 ++- .../standard_models/historical_employees.py | 14 ++- 9 files changed, 276 insertions(+), 43 deletions(-) create mode 100644 openbb_sdk/providers/fmp/openbb_fmp/models/financial_ratios.py create mode 100644 openbb_sdk/sdk/provider/openbb_provider/standard_models/financial_ratios.py diff --git a/openbb_sdk/extensions/stocks/openbb_stocks/fa/fa_router.py b/openbb_sdk/extensions/stocks/openbb_stocks/fa/fa_router.py index 19e3cc5f272..3da6c5fe6f1 100644 --- a/openbb_sdk/extensions/stocks/openbb_stocks/fa/fa_router.py +++ b/openbb_sdk/extensions/stocks/openbb_stocks/fa/fa_router.py @@ -328,18 +328,23 @@ def pta( return CommandOutput(results=Query(**locals()).execute()) +@router.command(model="FinancialRatios") +def ratios( + cc: CommandContext, + provider_choices: ProviderChoices, + standard_params: StandardParams, + extra_params: ExtraParams, +) -> CommandOutput[BaseModel]: + """Extensive set of ratios over time.""" + return CommandOutput(results=Query(**locals()).execute()) + + @router.command def rating() -> CommandOutput[Empty]: # type: ignore """Analyst prices and ratings over time of the company.""" return CommandOutput(results=Empty()) -@router.command -def ratios() -> CommandOutput[Empty]: # type: ignore - """Extensive set of ratios over time.""" - return CommandOutput(results=Empty()) - - @router.command def revfc() -> CommandOutput[Empty]: # type: ignore """Earning Estimate by Analysts - Revenue.""" diff --git a/openbb_sdk/providers/fmp/openbb_fmp/__init__.py b/openbb_sdk/providers/fmp/openbb_fmp/__init__.py index 31ec593ef72..d2198fc976b 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/__init__.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/__init__.py @@ -16,6 +16,7 @@ from openbb_fmp.models.esg_risk_rating import FMPESGRiskRatingFetcher from openbb_fmp.models.esg_score import FMPESGScoreFetcher from openbb_fmp.models.esg_sector import FMPESGSectorFetcher from openbb_fmp.models.executive_compensation import FMPExecutiveCompensationFetcher +from openbb_fmp.models.financial_ratios import FMPFinancialRatiosFetcher from openbb_fmp.models.forex_eod import FMPForexEODFetcher from openbb_fmp.models.forex_pairs import FMPForexPairsFetcher from openbb_fmp.models.global_news import FMPGlobalNewsFetcher @@ -101,5 +102,6 @@ fmp_provider = Provider( "MajorIndicesConstituents": FMPMajorIndicesConstituentsFetcher, "DividendCalendar": FMPDividendCalendarFetcher, "StockQuote": FMPStockQuoteFetcher, + "FinancialRatios": FMPFinancialRatiosFetcher, }, ) diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/financial_ratios.py b/openbb_sdk/providers/fmp/openbb_fmp/models/financial_ratios.py new file mode 100644 index 00000000000..51ac343525e --- /dev/null +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/financial_ratios.py @@ -0,0 +1,118 @@ +"""FMP Income Statement Fetcher.""" + + +from typing import Any, Dict, List, Literal, Optional + +from openbb_provider.abstract.fetcher import Fetcher +from openbb_provider.standard_models.base import FinancialStatementQueryParams +from openbb_provider.standard_models.financial_ratios import FinancialRatiosData + +from openbb_fmp.utils.helpers import get_data_many + +PeriodType = Literal["annual", "quarter"] + + +class FMPFinancialRatiosQueryParams(FinancialStatementQueryParams): + """FMP Financial Ratios QueryParams. + + Source: https://financialmodelingprep.com/developer/docs/#Company-Financial-Ratios + + Symbol must be provided. + """ + + +class FMPFinancialRatiosData(FinancialRatiosData): + """FMP Financial Ratios Data.""" + + class Config: + fields = { + "current_ratio": "currentRatio", + "quick_ratio": "quickRatio", + "cash_ratio": "cashRatio", + "days_of_sales_outstanding": "daysOfSalesOutstanding", + "days_of_inventory_outstanding": "daysOfInventoryOutstanding", + "operating_cycle": "operatingCycle", + "days_of_payables_outstanding": "daysOfPayablesOutstanding", + "cash_conversion_cycle": "cashConversionCycle", + "gross_profit_margin": "grossProfitMargin", + "operating_profit_margin": "operatingProfitMargin", + "pretax_profit_margin": "pretaxProfitMargin", + "net_profit_margin": "netProfitMargin", + "effective_tax_rate": "effectiveTaxRate", + "return_on_assets": "returnOnAssets", + "return_on_equity": "returnOnEquity", + "return_on_capital_employed": "returnOnCapitalEmployed", + "net_income_per_ebt": "netIncomePerEBT", + "ebt_per_ebit": "ebtPerEbit", + "ebit_per_revenue": "ebitPerRevenue", + "debt_ratio": "debtRatio", + "debt_equity_ratio": "debtEquityRatio", + "long_term_debt_to_capitalization": "longTermDebtToCapitalization", + "total_debt_to_capitalization": "totalDebtToCapitalization", + "interest_coverage": "interestCoverage", + "cash_flow_to_debt_ratio": "cashFlowToDebtRatio", + "company_equity_multiplier": "companyEquityMultiplier", + "receivables_turnover": "receivablesTurnover", + "payables_turnover": "payablesTurnover", + "inventory_turnover": "inventoryTurnover", + "fixed_asset_turnover": "fixedAssetTurnover", + "asset_turnover": "assetTurnover", + "operating_cash_flow_per_share": "operatingCashFlowPerShare", + "free_cash_flow_per_share": "freeCashFlowPerShare", + "cash_per_share": "cashPerShare", + "payout_ratio": "payoutRatio", + "operating_cash_flow_sales_ratio": "operatingCashFlowSalesRatio", + "free_cash_flow_operating_cash_flow_ratio": "freeCashFlowOperatingCashFlowRatio", + "cash_flow_coverage_ratios": "cashFlowCoverageRatios", + "short_term_coverage_ratios": "shortTermCoverageRatios", + "capital_expenditure_coverage_ratio": "capitalExpenditureCoverageRatio", + "dividend_paid_and_capex_coverage_ratio": "dividendPaidAndCapexCoverageRatio", + "dividend_payout_ratio": "dividendPayoutRatio", + "price_book_value_ratio": "priceBookValueRatio", + "price_to_book_ratio": "priceToBookRatio", + "price_to_sales_ratio": "priceToSalesRatio", + "price_earnings_ratio": "priceEarningsRatio", + "price_to_free_cash_flows_ratio": "priceToFreeCashFlowsRatio", + "price_to_operating_cash_flows_ratio": "priceToOperatingCashFlowsRatio", + "price_cash_flow_ratio": "priceCashFlowRatio", + "price_earnings_to_growth_ratio": "priceEarningsToGrowthRatio", + "price_sales_ratio": "priceSalesRatio", + "dividend_yield": "dividendYield", + "enterprise_value_multiple": "enterpriseValueMultiple", + "price_fair_value": "priceFairValue", + } + + +class FMPFinancialRatiosFetcher( + Fetcher[ + FMPFinancialRatiosQueryParams, + List[FMPFinancialRatiosData], + ] +): + @staticmethod + def transform_query(params: Dict[str, Any]) -> FMPFinancialRatiosQueryParams: + return FMPFinancialRatiosQueryParams(**params) + + @staticmethod + def extract_data( + query: FMPFinancialRatiosQueryParams, + credentials: Optional[Dict[str, str]], + **kwargs: Any, + ) -> List[FMPFinancialRatiosData]: + api_key = credentials.get("fmp_api_key") if credentials else "" + + query.period = "annual" if query.period == "annually" else "quarter" + base_url = "https://financialmodelingprep.com/api/v3" + + url = ( + f"{base_url}/ratios/{query.symbol}?" + f"period={query.period}&limit={query.limit}&apikey={api_key}" + ) + + return get_data_many(url, FMPFinancialRatiosData, **kwargs) + + @staticmethod + def transform_data( + data: List[FMPFinancialRatiosData], + ) -> List[FinancialRatiosData]: + return [FinancialRatiosData.parse_obj(d.dict()) for d in data] diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/historical_dividends.py b/openbb_sdk/providers/fmp/openbb_fmp/models/historical_dividends.py index 3706c68718d..40b2abbbac3 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/historical_dividends.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/historical_dividends.py @@ -1,6 +1,5 @@ """FMP Historical Dividends fetcher.""" -from datetime import datetime from typing import Any, Dict, List, Optional from openbb_provider.abstract.fetcher import Fetcher @@ -8,7 +7,6 @@ from openbb_provider.standard_models.historical_dividends import ( HistoricalDividendsData, HistoricalDividendsQueryParams, ) -from pydantic import validator from openbb_fmp.utils.helpers import create_url, get_data_many @@ -31,18 +29,6 @@ class FMPHistoricalDividendsData(HistoricalDividendsData): "declaration_date": "declarationDate", } - @validator("declarationDate", pre=True, check_fields=False) - def declaration_date_validate(cls, v: str): # pylint: disable=E0213 - return datetime.strptime(v, "%Y-%m-%d") if v else None - - @validator("recordDate", pre=True, check_fields=False) - def record_date_validate(cls, v: str): # pylint: disable=E0213 - return datetime.strptime(v, "%Y-%m-%d") if v else None - - @validator("paymentDate", pre=True, check_fields=False) - def payment_date_validate(cls, v: str): # pylint: disable=E0213 - return datetime.strptime(v, "%Y-%m-%d") if v else None - class FMPHistoricalDividendsFetcher( Fetcher[ @@ -70,5 +56,5 @@ class FMPHistoricalDividendsFetcher( @staticmethod def transform_data( data: List[FMPHistoricalDividendsData], - ) -> List[FMPHistoricalDividendsData]: - return data + ) -> List[HistoricalDividendsData]: + return [HistoricalDividendsData.parse_obj(d.dict()) for d in data] diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/historical_employees.py b/openbb_sdk/providers/fmp/openbb_fmp/models/historical_employees.py index 858b8c02e32..456372abc85 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/historical_employees.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/historical_employees.py @@ -1,7 +1,6 @@ """FMP Historical Employees fetcher.""" -from datetime import datetime from typing import Any, Dict, List, Optional from openbb_provider.abstract.fetcher import Fetcher @@ -9,7 +8,6 @@ from openbb_provider.standard_models.historical_employees import ( HistoricalEmployeesData, HistoricalEmployeesQueryParams, ) -from pydantic import validator from openbb_fmp.utils.helpers import create_url, get_data_many @@ -34,18 +32,6 @@ class FMPHistoricalEmployeesData(HistoricalEmployeesData): "employee_count": "employeeCount", } - @validator("acceptanceTime", pre=True, check_fields=False) - def acceptance_time_validate(cls, v): # pylint: disable=E0213 - return datetime.strptime(v, "%Y-%m-%d %H:%M:%S") - - @validator("periodOfReport", pre=True, check_fields=False) - def period_of_report_validate(cls, v): # pylint: disable=E0213 - return datetime.strptime(v, "%Y-%m-%d") - - @validator("filingDate", pre=True, check_fields=False) - def filing_date_validate(cls, v): # pylint: disable=E0213 - return datetime.strptime(v, "%Y-%m-%d") - class FMPHistoricalEmployeesFetcher( Fetcher[ diff --git a/openbb_sdk/providers/fmp/openbb_fmp/models/stock_peers.py b/openbb_sdk/providers/fmp/openbb_fmp/models/stock_peers.py index a865025f973..ac8b6de00dc 100644 --- a/openbb_sdk/providers/fmp/openbb_fmp/models/stock_peers.py +++ b/openbb_sdk/providers/fmp/openbb_fmp/models/stock_peers.py @@ -50,5 +50,5 @@ class FMPStockPeersFetcher( return get_data_one(url, FMPStockPeersData, **kwargs) @staticmethod - def transform_data(data: FMPStockPeersData) -> FMPStockPeersData: - return data + def transform_data(data: FMPStockPeersData) -> StockPeersData: + return StockPeersData.parse_obj(data.dict()) diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/financial_ratios.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/financial_ratios.py new file mode 100644 index 00000000000..2b40b054a12 --- /dev/null +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/financial_ratios.py @@ -0,0 +1,112 @@ +"""Financial ratios data model.""" + + +from typing import Optional + +from pydantic import Field + +from openbb_provider.abstract.data import Data + + +class FinancialRatiosData(Data): + symbol: str = Field(description="The symbol of the company.") + date: str = Field(description="The date of the financial ratios.") + period: str = Field(description="The period of the financial ratios.") + current_ratio: Optional[float] = Field(description="The current ratio.") + quick_ratio: Optional[float] = Field(description="The quick ratio.") + cash_ratio: Optional[float] = Field(description="The cash ratio.") + days_of_sales_outstanding: Optional[float] = Field( + description="Days of sales outstanding." + ) + days_of_inventory_outstanding: Optional[float] = Field( + description="Days of inventory outstanding." + ) + operating_cycle: Optional[float] = Field(description="Operating cycle.") + days_of_payables_outstanding: Optional[float] = Field( + description="Days of payables outstanding." + ) + cash_conversion_cycle: Optional[float] = Field(description="Cash conversion cycle.") + gross_profit_margin: Optional[float] = Field(description="Gross profit margin.") + operating_profit_margin: Optional[float] = Field( + description="Operating profit margin." + ) + pretax_profit_margin: Optional[float] = Field(description="Pretax profit margin.") + net_profit_margin: Optional[float] = Field(description="Net profit margin.") + effective_tax_rate: Optional[float] = Field(description="Effective tax rate.") + return_on_assets: Optional[float] = Field(description="Return on assets.") + return_on_equity: Optional[float] = Field(description="Return on equity.") + return_on_capital_employed: Optional[float] = Field( + description="Return on capital employed." + ) + net_income_per_ebt: Optional[float] = Field(description="Net income per EBT.") + ebt_per_ebit: Optional[float] = Field(description="EBT per EBIT.") + ebit_per_revenue: Optional[float] = Field(description="EBIT per revenue.") + debt_ratio: Optional[float] = Field(description="Debt ratio.") + debt_equity_ratio: Optional[float] = Field(description="Debt equity ratio.") + long_term_debt_to_capitalization: Optional[float] = Field( + description="Long term debt to capitalization." + ) + total_debt_to_capitalization: Optional[float] = Field( + description="Total debt to capitalization." + ) + interest_coverage: Optional[float] = Field(description="Interest coverage.") + cash_flow_to_debt_ratio: Optional[float] = Field( + description="Cash flow to debt ratio." + ) + company_equity_multiplier: Optional[float] = Field( + description="Company equity multiplier." + ) + receivables_turnover: Optional[float] = Field(description="Receivables turnover.") + payables_turnover: Optional[float] = Field(description="Payables turnover.") + inventory_turnover: Optional[float] = Field(description="Inventory turnover.") + fixed_asset_turnover: Optional[float] = Field(description="Fixed asset turnover.") + asset_turnover: Optional[float] = Field(description="Asset turnover.") + operating_cash_flow_per_share: Optional[float] = Field( + description="Operating cash flow per share." + ) + free_cash_flow_per_share: Optional[float] = Field( + description="Free cash flow per share." + ) + cash_per_share: Optional[float] = Field(description="Cash per share.") + payout_ratio: Optional[float] = Field(description="Payout ratio.") + operating_cash_flow_sales_ratio: Optional[float] = Field( + description="Operating cash flow sales ratio." + ) + free_cash_flow_operating_cash_flow_ratio: Optional[float] = Field( + description="Free cash flow operating cash flow ratio." + ) + cash_flow_coverage_ratios: Optional[float] = Field( + description="Cash flow coverage ratios." + ) + short_term_coverage_ratios: Optional[float] = Field( + description="Short term coverage ratios." + ) + capital_expenditure_coverage_ratio: Optional[float] = Field( + description="Capital expenditure coverage ratio." + ) + dividend_paid_and_capex_coverage_ratio: Optional[float] = Field( + description="Dividend paid and capex coverage ratio." + ) + dividend_payout_ratio: Optional[float] = Field(description="Dividend payout ratio.") + price_book_value_ratio: Optional[float] = Field( + description="Price book value ratio." + ) + price_to_book_ratio: Optional[float] = Field(description="Price to book ratio.") + price_to_sales_ratio: Optional[float] = Field(description="Price to sales ratio.") + price_earnings_ratio: Optional[float] = Field(description="Price earnings ratio.") + price_to_free_cash_flows_ratio: Optional[float] = Field( + description="Price to free cash flows ratio." + ) + price_to_operating_cash_flows_ratio: Optional[float] = Field( + description="Price to operating cash flows ratio." + ) + price_cash_flow_ratio: Optional[float] = Field(description="Price cash flow ratio.") + price_earnings_to_growth_ratio: Optional[float] = Field( + description="Price earnings to growth ratio." + ) + price_sales_ratio: Optional[float] = Field(description="Price sales ratio.") + dividend_yield: Optional[float] = Field(description="Dividend yield.") + enterprise_value_multiple: Optional[float] = Field( + description="Enterprise value multiple." + ) + price_fair_value: Optional[float] = Field(description="Price fair value.") diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/historical_dividends.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/historical_dividends.py index 148e0c0fd59..8089ae45f22 100644 --- a/openbb_sdk/sdk/provider/openbb_provider/standard_models/historical_dividends.py +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/historical_dividends.py @@ -4,7 +4,7 @@ from datetime import date as dateType from typing import Optional -from pydantic import Field +from pydantic import Field, validator from openbb_provider.abstract.data import Data from openbb_provider.abstract.query_params import QueryParams @@ -25,11 +25,23 @@ class HistoricalDividendsData(Data): ) dividend: float = Field(description="The dividend of the historical dividends.") record_date: Optional[dateType] = Field( - description="The record date of the historical dividends." + description="The record date of the historical dividends.", default=None ) payment_date: Optional[dateType] = Field( - description="The payment date of the historical dividends." + description="The payment date of the historical dividends.", default=None ) declaration_date: Optional[dateType] = Field( - description="The declaration date of the historical dividends." + description="The declaration date of the historical dividends.", default=None ) + + @validator("declaration_date", pre=True, check_fields=False) + def declaration_date_validate(cls, v: str): # pylint: disable=E0213 + return dateType.fromisoformat(v) if v else None + + @validator("record_date", pre=True, check_fields=False) + def record_date_validate(cls, v: str): # pylint: disable=E0213 + return dateType.fromisoformat(v) if v else None + + @validator("payment_date", pre=True, check_fields=False) + def payment_date_validate(cls, v: str): # pylint: disable=E0213 + return dateType.fromisoformat(v) if v else None diff --git a/openbb_sdk/sdk/provider/openbb_provider/standard_models/historical_employees.py b/openbb_sdk/sdk/provider/openbb_provider/standard_models/historical_employees.py index ab480f08113..ecaa80f06be 100644 --- a/openbb_sdk/sdk/provider/openbb_provider/standard_models/historical_employees.py +++ b/openbb_sdk/sdk/provider/openbb_provider/standard_models/historical_employees.py @@ -3,7 +3,7 @@ from datetime import date, datetime -from pydantic import Field +from pydantic import Field, validator from openbb_provider.abstract.data import Data from openbb_provider.abstract.query_params import QueryParams @@ -35,3 +35,15 @@ class HistoricalEmployeesData(Data, BaseSymbol): source: str = Field( description="The source URL which retrieves this data for the company." ) + + @validator("acceptance_time", pre=True, check_fields=False) + def acceptance_time_validate(cls, v): # pylint: disable=E0213 + return datetime.strptime(v, "%Y-%m-%d %H:%M:%S") + + @validator("period_of_report", pre=True, check_fields=False) + def period_of_report_validate(cls, v): # pylint: disable=E0213 + return datetime.strptime(v, "%Y-%m-%d") + + @validator("filing_date", pre=True, check_fields=False) + def filing_date_validate(cls, v): # pylint: disable=E0213 + return datetime.strptime(v, "%Y-%m-%d")