diff --git a/openbb_providers/README.md b/openbb_providers/README.md new file mode 100644 index 00000000000..b4e245327fd --- /dev/null +++ b/openbb_providers/README.md @@ -0,0 +1,144 @@ +# Table of contents + +- [Table of contents](#table-of-contents) +- [Temporary glossary](#temporary-glossary) +- [openbb-provider](#openbb-provider) + - [Functional Requirements](#functional-requirements) +- [How to use it?](#how-to-use-it) + - [Prerequisites](#prerequisites) + - [1. Defining : OpenBB QueryParams](#1-defining--openbb-queryparams) + - [2. Querying the Provider Table : OpenBB QueryParams -\> OpenBB Data](#2-querying-the-provider-table--openbb-queryparams---openbb-data) +- [Different ways of usage](#different-ways-of-usage) + - [1. OpenBB QueryParams -\> OpenBB Data](#1-openbb-queryparams---openbb-data) + - [2. OpenBB QueryParams -\> Provider Data](#2-openbb-queryparams---provider-data) + - [3. Provider QueryParams -\> OpenBB Data](#3-provider-queryparams---openbb-data) + - [4. Provider QueryParams -\> Provider Data](#4-provider-queryparams---provider-data) +- [Data Mapping](#data-mapping) +- [Package extension : Adding a new Provider extension](#package-extension--adding-a-new-provider-extension) + - [1. Build a Python package](#1-build-a-python-package) + - [2. Entrypoint](#2-entrypoint) + - [3. Install the extension](#3-install-the-extension) + +# Temporary glossary + +- **Provider** : A Provider is a Financial Data Provider such as FMP, Polygon, Benzinga, etc. +- **QueryParams** : A set of parameters that define a request to a Provider. It is defined by the user. +- **Provider QueryParams** : A set of parameters which are defined by the Provider. +- **Data** : A standardized set of data that is returned by a Provider and then standardized. +- **Provider Data** : Provider Data is a raw set of data that is returned by a Provider as is. + +# openbb-provider + +This library aim to give a coherent access to Financial Data Providers. + +It provides coherence on: + +- QueryParams : one can define QueryParams and use them on multiple Financial Data Providers. +- Data : one can process the data provided by multiple Financial Data Providers as if there were from the same one. + +## Functional Requirements + +Functional requirements are the following: + +- Provide a standardized access to similar Data from multiple Data Providers. +- Use the same QueryParams to query similar Data from multiple Providers. +- Get similar Data from multiple Provider and process it as if there were from the same Provider. + +# How to use it? + +### Prerequisites + +- Python 3.9 or higher +- .env file with the following variables: + - FMP_API_KEY + - POLYGON_API_KEY + - BENZINGA_API_KEY +- Install the dependencies: + - `pip install poetry==1.4.1` + - `poetry install` + +### 1. Defining : OpenBB QueryParams + +```python +from openbb_provider.model.data.stock_eod import StockEODQueryParams + +query = StockEODQueryParams( + symbol="AAPL", + start_date="2022-01-01", +) +``` + +### 2. Querying the Provider Table : OpenBB QueryParams -> OpenBB Data + +```python +from openbb_provider.provider.provider_registry import provider_registry + +data = provider_registry.fetch(provider_name="fmp", query=query) +print(data) +``` + +# Different ways of usage + +### 1. OpenBB QueryParams -> OpenBB Data + +This is the most common way of usage that the SDK v4 users will use. + +```python +provider_registry.fetch(provider_name="fmp", query=query) +``` + +### 2. OpenBB QueryParams -> Provider Data + +This way uses the standardized query to obtain the raw data from the Provider. This is useful when the user wants specific data that is not available in the standardized data. + +```python +provider_registry.fetch_provider_data(provider_name="fmp", query=query) +``` + +### 3. Provider QueryParams -> OpenBB Data + +This way uses the Provider QueryParams to obtain the standardized data. This is useful when the user wants to use a specific query field that is not available in the standardized query. + +```python +provider_registry.standardized(provider_name="fmp", query=provider_query) +``` + +### 4. Provider QueryParams -> Provider Data + +This way uses the Provider QueryParams to obtain the raw data from the Provider. This is useful when the user wants specific data that is not available in the standardized data and wants to use a specific query field that is not available in the standardized query. It is the same a using the Provider directly. + +This will primarily be used for integration tests so that we can notice Provider API changes in time and adjust the mapping accordingly. + +```python +provider_registry.simple(provider_name="fmp", query=provider_query) +``` + +# Data Mapping + +The data mapping is done in the following [file](https://docs.google.com/spreadsheets/d/1AhmQWGRDqORk8nlcNclcnCuPpWsdMuaHwxujMMdzcsk/edit#gid=152728452 +). + +# Package extension : Adding a new Provider extension + +## 1. Build a Python package + +```bash +poetry new opebb-provider-your_extension +``` + +## 2. Entrypoint + +Add an entrypoint for the extension inside your `pyproject.toml` file + +```toml +# File pyproject.toml +... +[tool.poetry.extensions."openbb_provider_extension"] +extension_name_space = "my_extension.provider:provider_module" +``` + +## 3. Install the extension + +```bash +poetry install +``` diff --git a/openbb_providers/openbb_provider/__init__.py b/openbb_providers/openbb_provider/__init__.py new file mode 100644 index 00000000000..4499867c136 --- /dev/null +++ b/openbb_providers/openbb_provider/__init__.py @@ -0,0 +1,2 @@ +"""OpenBB Provider Package.""" +from . import metadata, model, provider, settings # noqa: F401 diff --git a/openbb_providers/openbb_provider/metadata.py b/openbb_providers/openbb_provider/metadata.py new file mode 100644 index 00000000000..3658bc03f54 --- /dev/null +++ b/openbb_providers/openbb_provider/metadata.py @@ -0,0 +1,11 @@ +"""Metadata for the parameters of the Pydantic models.""" + +DESCRIPTIONS = { + "symbol": "Symbol to get data for.", + "start_date": "Start date of the data.", + "end_date": "End date of the data.", + "weekly": "Whether to return weekly data.", + "monthly": "Whether to return monthly data.", + "period": "Period of the data to return (quarterly or annually).", + "date": "A specific date to get data for.", +} diff --git a/openbb_providers/openbb_provider/model/__init__.py b/openbb_providers/openbb_provider/model/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_providers/openbb_provider/model/abstract/__init__.py b/openbb_providers/openbb_provider/model/abstract/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_providers/openbb_provider/model/abstract/data.py b/openbb_providers/openbb_provider/model/abstract/data.py new file mode 100644 index 00000000000..6aaca4f431f --- /dev/null +++ b/openbb_providers/openbb_provider/model/abstract/data.py @@ -0,0 +1,23 @@ +# IMPORT STANDARD + +# IMPORT THIRD-PARTY +from pydantic import BaseModel, Extra + +# IMPORT INTERNAL + + +class Data(BaseModel): + """The OpenBB Standardized Data Model.""" + + def __repr__(self): + return f"{self.__class__.__name__}({', '.join([f'{k}={v}' for k, v in self.dict().items()])})" + + class Config: + extra = Extra.allow + + +class QueryParams(BaseModel): + """The OpenBB Standardized QueryParams Model that holds the query input parameters.""" + + def __repr__(self): + return f"{self.__class__.__name__}({', '.join([f'{k}={v}' for k, v in self.dict().items()])})" diff --git a/openbb_providers/openbb_provider/model/abstract/provider_data.py b/openbb_providers/openbb_provider/model/abstract/provider_data.py new file mode 100644 index 00000000000..1766c62ca71 --- /dev/null +++ b/openbb_providers/openbb_provider/model/abstract/provider_data.py @@ -0,0 +1,34 @@ +# IMPORT STANDARD + +# IMPORT THIRD-PARTY +from pydantic import BaseModel, Extra + +# IMPORT INTERNAL + + +class ProviderData(BaseModel): + """The Provider-specific data model.""" + + def __repr__(self): + return f"{self.__class__.__name__}({', '.join([f'{k}={v}' for k, v in self.dict().items()])})" + + class Config: + extra = Extra.allow + allow_population_by_field_name = True + + +class ProviderQueryParams(BaseModel): + """The Provider-specific query parameter model.""" + + def __post_init__(self): + # check the fields that were received vs the fields that are allowed + # if there are any fields that were received that are not allowed, raise an error + allowed_fields = set(self.__fields__.keys()) + received_fields = set(self.__dict__.keys()) + if not received_fields.issubset(allowed_fields): + raise ValueError( + f"Received fields that are not allowed: {received_fields - allowed_fields}" + ) + + def __repr__(self): + return f"{self.__class__.__name__}({', '.join([f'{k}={v}' for k, v in self.dict().items()])})" diff --git a/openbb_providers/openbb_provider/model/data/__init__.py b/openbb_providers/openbb_provider/model/data/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_providers/openbb_provider/model/data/analyst_estimates.py b/openbb_providers/openbb_provider/model/data/analyst_estimates.py new file mode 100644 index 00000000000..d26fde2c342 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/analyst_estimates.py @@ -0,0 +1,103 @@ +"""Analyst estimates data model.""" + +# IMPORT STANDARD +from datetime import date as dateType +from typing import Literal + +# IMPORT THIRD-PARTY +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class AnalystEstimatesQueryParams(QueryParams, BaseSymbol): + """Analyst Estimates query. + + Parameter + --------- + symbol : str + The symbol of the company. + period : Literal["quarter", "annual"] + The period of the analyst estimates. + limit : int + The limit of the analyst estimates. + """ + + __name__ = "AnalystEstimatesQueryParams" + period: Literal["quarter", "annual"] = "annual" + limit: int = 30 + + +class AnalystEstimatesData(Data, BaseSymbol): + """Analyst estimates data. + + Returns + ------- + symbol : str + The symbol of the asset. + date : date + The date of the analyst estimates. + estimated_revenue_low : int + The estimated revenue low of the analyst estimates. + estimated_revenue_high : int + The estimated revenue high of the analyst estimates. + estimated_revenue_avg : int + The estimated revenue average of the analyst estimates. + estimated_ebitda_low : int + The estimated EBITDA low of the analyst estimates. + estimated_ebitda_high : int + The estimated EBITDA high of the analyst estimates. + estimated_ebitda_avg : int + The estimated EBITDA average of the analyst estimates. + estimated_ebit_low : int + The estimated EBIT low of the analyst estimates. + estimated_ebit_high : int + The estimated EBIT high of the analyst estimates. + estimated_ebit_avg : int + The estimated EBIT average of the analyst estimates. + estimated_net_income_low : int + The estimated net income low of the analyst estimates. + estimated_net_income_high : int + The estimated net income high of the analyst estimates. + estimated_net_income_avg : int + The estimated net income average of the analyst estimates. + estimated_sga_expense_low : int + The estimated SGA expense low of the analyst estimates. + estimated_sga_expense_high : int + The estimated SGA expense high of the analyst estimates. + estimated_sga_expense_avg : int + The estimated SGA expense average of the analyst estimates. + estimated_eps_avg : float + The estimated EPS average of the analyst estimates. + estimated_eps_high : float + The estimated EPS high of the analyst estimates. + estimated_eps_low : float + The estimated EPS low of the analyst estimates. + number_analyst_estimated_revenue : int + The number of analysts who estimated revenue of the analyst estimates. + number_analysts_estimated_eps : int + The number of analysts who estimated EPS of the analyst estimates. + """ + + symbol: str + date: dateType + estimated_revenue_low: int + estimated_revenue_high: int + estimated_revenue_avg: int + estimated_ebitda_low: int + estimated_ebitda_high: int + estimated_ebitda_avg: int + estimated_ebit_low: int + estimated_ebit_high: int + estimated_ebit_avg: int + estimated_net_income_low: int + estimated_net_income_high: int + estimated_net_income_avg: int + estimated_sga_expense_low: int + estimated_sga_expense_high: int + estimated_sga_expense_avg: int + estimated_eps_avg: float + estimated_eps_high: float + estimated_eps_low: float + number_analyst_estimated_revenue: int + number_analysts_estimated_eps: int diff --git a/openbb_providers/openbb_provider/model/data/balance_sheet.py b/openbb_providers/openbb_provider/model/data/balance_sheet.py new file mode 100644 index 00000000000..bbe00754378 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/balance_sheet.py @@ -0,0 +1,73 @@ +"""Balance Sheet Data Model.""" + +# IMPORT STANDARD +from datetime import date as dateType +from typing import Optional + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data +from openbb_provider.model.data.base import FinancialStatementQueryParams + +# IMPORT THIRD PARTY + + +class BalanceSheetQueryParams(FinancialStatementQueryParams): + """Cash flow statement query. + + Parameter + --------- + symbol : str + The symbol of the company. + period : Literal["annually", "quarterly"] + The period of the balance sheet. + """ + + __name__ = "BalanceSheetQueryParams" + + +class BalanceSheetData(Data): + """Return Balance Sheet Data. + + Returns + ------- + date : date + The date of the balance sheet. + cik : Optional[int] + The CIK of the balance sheet. + assets : Optional[int] + The assets of the balance sheet. + current_assets : Optional[int] + The current assets of the balance sheet. + current_liabilities : Optional[int] + The current liabilities of the balance sheet. + equity : Optional[int] + The equity of the balance sheet. + equity_attributable_to_noncontrolling_interest : Optional[int] + The equity attributable to noncontrolling interest of the balance sheet. + equity_attributable_to_parent : Optional[int] + The equity attributable to parent of the balance sheet. + liabilities : Optional[int] + The liabilities of the balance sheet. + liabilities_and_equity : Optional[int] + The liabilities and equity of the balance sheet. + noncurrent_assets : Optional[int] + The noncurrent assets of the balance sheet. + noncurrent_liabilities : Optional[int] + The noncurrent liabilities of the balance sheet. + """ + + date: dateType + # symbol: List[Optional[str]] + # currency: List[Optional[str]] + cik: Optional[int] + # period: List[Optional[str]] + assets: Optional[int] + current_assets: Optional[int] + current_liabilities: Optional[int] + equity: Optional[int] + equity_attributable_to_noncontrolling_interest: Optional[int] + equity_attributable_to_parent: Optional[int] + liabilities: Optional[int] + liabilities_and_equity: Optional[int] + noncurrent_assets: Optional[int] + noncurrent_liabilities: Optional[int] diff --git a/openbb_providers/openbb_provider/model/data/base.py b/openbb_providers/openbb_provider/model/data/base.py new file mode 100644 index 00000000000..eb4799fb2b5 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/base.py @@ -0,0 +1,24 @@ +# IMPORT STANDARD +from typing import Literal + +# IMPORT THIRD-PARTY +from pydantic import BaseModel, Field, validator + +# IMPORT INTERNAL +from openbb_provider.metadata import DESCRIPTIONS +from openbb_provider.model.abstract.data import QueryParams + + +class BaseSymbol(BaseModel): + symbol: str = Field(min_length=1, description=DESCRIPTIONS.get("symbol", "")) + + @validator("symbol", pre=True) + def upper_symbol(cls, v: str): # pylint: disable=E0213 + return v.upper() + + +class FinancialStatementQueryParams(QueryParams, BaseSymbol): + __name__ = "FinancialStatementQueryParams" + period: Literal["annually", "quarterly"] = Field( + default="annually", description=DESCRIPTIONS.get("period", "") + ) diff --git a/openbb_providers/openbb_provider/model/data/cash_flows.py b/openbb_providers/openbb_provider/model/data/cash_flows.py new file mode 100644 index 00000000000..4d6a611de05 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/cash_flows.py @@ -0,0 +1,74 @@ +"""Cash Flow Statement Data Model.""" + +# IMPORT STANDARD +from datetime import date as dateType +from typing import Optional + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data +from openbb_provider.model.data.base import FinancialStatementQueryParams + + +class CashFlowStatementQueryParams(FinancialStatementQueryParams): + """Cash flow statement query. + + Parameter + --------- + symbol : str + The symbol of the company. + period : Literal["annually", "quarterly"] + The period of the income statement. + """ + + __name__ = "CashFlowStatementQueryParams" + + +class CashFlowStatementData(Data): + """Return Cash Flow Statement Data. + + Returns + ------- + date : date + The date of the cash flow statement. + symbol : str + The symbol of the company. + currency : Optional[str] + The currency of the cash flow statement. + cik : Optional[int] + The Central Index Key (CIK) of the company. + period : Optional[str] + The period of the cash flow statement. + exchange_gain_losses : Optional[int] + The exchange gain losses of the cash flow statement. + net_cash_flow : Optional[int] + The net cash flow of the cash flow statement. + net_cash_flow_continuing : Optional[int] + The net cash flow continuing of the cash flow statement. + net_cash_flow_from_financing_activities : Optional[int] + The net cash flow from financing activities of the cash flow statement. + net_cash_flow_from_financing_activities_continuing : Optional[int] + The net cash flow from financing activities continuing of the cash flow statement. + net_cash_flow_from_investing_activities : Optional[int] + The net cash flow from investing activities of the cash flow statement. + net_cash_flow_from_investing_activities_continuing : Optional[int] + The net cash flow from investing activities continuing of the cash flow statement. + net_cash_flow_from_operating_activities : Optional[int] + The net cash flow from operating activities of the cash flow statement. + net_cash_flow_from_operating_activities_continuing : Optional[int] + The net cash flow from operating activities continuing of the cash flow statement. + """ + + date: dateType + symbol: str + currency: Optional[str] = None + cik: Optional[int] + period: Optional[str] + exchange_gain_losses: Optional[int] = None + net_cash_flow: Optional[int] + net_cash_flow_continuing: Optional[int] + net_cash_flow_from_financing_activities: Optional[int] + net_cash_flow_from_financing_activities_continuing: Optional[int] + net_cash_flow_from_investing_activities: Optional[int] + net_cash_flow_from_investing_activities_continuing: Optional[int] + net_cash_flow_from_operating_activities: Optional[int] + net_cash_flow_from_operating_activities_continuing: Optional[int] diff --git a/openbb_providers/openbb_provider/model/data/company_overview.py b/openbb_providers/openbb_provider/model/data/company_overview.py new file mode 100644 index 00000000000..f239e328836 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/company_overview.py @@ -0,0 +1,139 @@ +"""Company Overview Data Model.""" + +# IMPORT STANDARD +from datetime import date +from typing import Optional + +# IMPORT THIRD-PARTY +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class CompanyOverviewQueryParams(QueryParams, BaseSymbol): + """Company overview query. + + Parameter + --------- + symbol : str + The symbol of the company. + """ + + __name__ = "CompanyOverviewQueryParams" + + +class CompanyOverviewData(Data): + """Company Overview data. + + Returns + ------- + symbol : str + The symbol of the company. + price : float + The price of the company. + beta : float + The beta of the company. + vol_avg : int + The volume average of the company. + mkt_cap : int + The market capitalization of the company. + last_div : float + The last dividend of the company. + range : str + The range of the company. + changes : float + The changes of the company. + company_name : str + The company name of the company. + currency : str + The currency of the company. + cik : Optional[str] + The CIK of the company. + isin : Optional[str] + The ISIN of the company. + cusip : Optional[str] + The CUSIP of the company. + exchange : str + The exchange of the company. + exchange_short_name : str + The exchange short name of the company. + industry : str + The industry of the company. + website : str + The website of the company. + description : str + The description of the company. + ceo : str + The CEO of the company. + sector : str + The sector of the company. + country : str + The country of the company. + full_time_employees : str + The full time employees of the company. + phone : str + The phone of the company. + address : str + The address of the company. + city : str + The city of the company. + state : str + The state of the company. + zip : str + The zip of the company. + dcf_diff : float + The discounted cash flow difference of the company. + dcf : float + The discounted cash flow of the company. + image : str + The image of the company. + ipo_date : date + The IPO date of the company. + default_image : bool + If the image is the default image. + is_etf : bool + If the company is an ETF. + is_actively_trading : bool + If the company is actively trading. + is_adr : bool + If the company is an ADR. + is_fund : bool + If the company is a fund. + """ + + symbol: str + price: float + beta: float + vol_avg: int + mkt_cap: int + last_div: float + range: str + changes: float + company_name: str + currency: str + cik: Optional[str] + isin: Optional[str] + cusip: Optional[str] + exchange: str + exchange_short_name: str + industry: str + website: str + description: str + ceo: str + sector: str + country: str + full_time_employees: str + phone: str + address: str + city: str + state: str + zip: str + dcf_diff: float + dcf: float + image: str + ipo_date: date + default_image: bool + is_etf: bool + is_actively_trading: bool + is_adr: bool + is_fund: bool diff --git a/openbb_providers/openbb_provider/model/data/crypto_eod.py b/openbb_providers/openbb_provider/model/data/crypto_eod.py new file mode 100644 index 00000000000..c0aa27b76b8 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/crypto_eod.py @@ -0,0 +1,64 @@ +"""Crypto aggregate end of day price data model.""" + +# IMPORT STANDARD +from datetime import date, datetime +from typing import Optional + +# IMPORT THIRD-PARTY +from pydantic import Field, PositiveFloat + +from openbb_provider.metadata import DESCRIPTIONS + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class CryptoEODQueryParams(QueryParams, BaseSymbol): + """Crypto end of day query. + + Parameter + --------- + symbol : str + The symbol of the company. + start_date : date + The start date of the stock data from which to retrieve the data. + end_date : Optional[date] + The end date of the stock data up to which to retrieve the data. + """ + + __name__ = "CryptoEODQueryParams" + start_date: date = Field(description=DESCRIPTIONS.get("start_date", "")) + end_date: Optional[date] = Field( + description=DESCRIPTIONS.get("end_date", ""), default=date.today() + ) + + +class CryptoEODData(Data): + """Crypto end of day price data. + + Returns + ------- + date : datetime + The date of the stock. + open : PositiveFloat + The open price of the stock. + high : PositiveFloat + The high price of the stock. + low : PositiveFloat + The low price of the stock. + close : PositiveFloat + The close price of the stock. + adj_close : Optional[PositiveFloat] + The adjusted close price of the stock. + volume : PositiveFloat + The volume of the stock. + """ + + date: datetime + open: PositiveFloat + high: PositiveFloat + low: PositiveFloat + close: PositiveFloat + adj_close: Optional[PositiveFloat] + volume: PositiveFloat diff --git a/openbb_providers/openbb_provider/model/data/crypto_price.py b/openbb_providers/openbb_provider/model/data/crypto_price.py new file mode 100644 index 00000000000..21ae86eec9e --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/crypto_price.py @@ -0,0 +1,60 @@ +"""Crypto Price Data Model.""" + +# IMPORT STANDARD +from datetime import date, datetime +from typing import Optional + +# IMPORT THIRD-PARTY +from pydantic import Field, PositiveFloat + +from openbb_provider.metadata import DESCRIPTIONS + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class CryptoPriceQueryParams(QueryParams, BaseSymbol): + """Crypto price query. + + Parameter + --------- + symbol : str + The symbol of the company. + start_date : date + The start date of the query. + end_date : Optional[date] + The end date of the query. + """ + + __name__ = "CryptoPriceQueryParams" + + # These fields only work with Polygon + start_date: Optional[date] = Field(description=DESCRIPTIONS.get("start_date", "")) + end_date: Optional[date] = Field( + description=DESCRIPTIONS.get("end_date", ""), default=date.today() + ) + + +class CryptoPriceData(Data): + """Crypto price data. + + Returns + ------- + open : PositiveFloat + The open price of the stock. + high : PositiveFloat + The high price of the stock. + low : PositiveFloat + The low price of the stock. + close : PositiveFloat + The close price of the stock. + date : datetime + The date of the stock. + """ + + open: PositiveFloat + high: PositiveFloat + low: PositiveFloat + close: PositiveFloat + date: datetime diff --git a/openbb_providers/openbb_provider/model/data/earnings_calendar.py b/openbb_providers/openbb_provider/model/data/earnings_calendar.py new file mode 100644 index 00000000000..d7de0321868 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/earnings_calendar.py @@ -0,0 +1,61 @@ +"""Earnings calendar data model.""" + +# IMPORT STANDARD +from datetime import date as dateType +from typing import Optional + +# IMPORT THIRD-PARTY +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class EarningsCalendarQueryParams(QueryParams, BaseSymbol): + """Earnings calendar rating query model. + + Parameter + --------- + symbol : str + The symbol of the company. + limit : int + """ + + __name__ = "EarningsCalendarQueryParams" + + limit: Optional[int] + + +class EarningsCalendarData(Data, BaseSymbol): + """Earnings calendar data. + + Returns + ------- + symbol : str + The symbol of the asset. + date : date + The date of the earnings calendar. + eps : float + The EPS of the earnings calendar. + eps_estimated : float + The estimated EPS of the earnings calendar. + time : str + The time of the earnings calendar. + revenue : int + The revenue of the earnings calendar. + revenue_estimated : int + The estimated revenue of the earnings calendar. + updated_from_date : date + The updated from date of the earnings calendar. + fiscal_date_ending : date + The fiscal date ending of the earnings calendar. + """ + + date: dateType + symbol: str + eps: Optional[float] + eps_estimated: Optional[float] + time: str + revenue: Optional[int] + revenue_estimated: Optional[int] + updated_from_date: Optional[dateType] + fiscal_date_ending: dateType diff --git a/openbb_providers/openbb_provider/model/data/earnings_call_transcript.py b/openbb_providers/openbb_provider/model/data/earnings_call_transcript.py new file mode 100644 index 00000000000..f04bd87eae2 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/earnings_call_transcript.py @@ -0,0 +1,60 @@ +"""Earnings call transcript data model.""" + +# IMPORT STANDARD +from datetime import datetime + +# IMPORT THIRD-PARTY +from pydantic import validator + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class EarningsCallTranscriptQueryParams(QueryParams, BaseSymbol): + """Earnings call transcript rating query model. + + Parameter + --------- + symbol : str + The symbol of the company. + year : int + The year of the earnings call transcript. + """ + + __name__ = "EarningsCallTranscriptQueryParams" + + year: int + + @validator("year", pre=True) + def time_validate(cls, v: int): # pylint: disable=E0213 + current_year = datetime.now().year + if v > current_year: + return current_year + if v < 1950: + return current_year + return v + + +class EarningsCallTranscriptData(Data, BaseSymbol): + """Earnings call transcript data. + + Returns + ------- + symbol : str + The symbol of the asset. + quarter : int + The quarter of the earnings call transcript. + year : int + The year of the earnings call transcript. + date : datetime + The date of the earnings call transcript. + content : str + The content of the earnings call transcript. + """ + + symbol: str + quarter: int + year: int + date: datetime + content: str diff --git a/openbb_providers/openbb_provider/model/data/esg_risk_rating.py b/openbb_providers/openbb_provider/model/data/esg_risk_rating.py new file mode 100644 index 00000000000..7df29a7eb1c --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/esg_risk_rating.py @@ -0,0 +1,53 @@ +"""ESG Risk Rating data model.""" + +# IMPORT STANDARD +from typing import Literal + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + +# IMPORT THIRD-PARTY + + +class ESGRiskRatingQueryParams(QueryParams, BaseSymbol): + """ESG risk rating query model. + + Parameter + --------- + symbol : str + The symbol of the company. + """ + + __name__ = "ESGRiskRatingQueryParams" + + +class ESGRiskRatingData(Data, BaseSymbol): + """ESG Risk Rating data. + + Returns + ------- + symbol : str + The symbol of the asset. + cik : int + The CIK of the ESG Risk Rating. + company_name : str + The company name of the ESG Risk Rating. + industry : str + The industry of the ESG Risk Rating. + year : int + The year of the ESG Risk Rating. + esg_risk_rating : str + The ESG Risk Rating of the ESG Risk Rating. + industry_rank : str + The industry rank of the ESG Risk Rating. + """ + + cik: int + company_name: str + industry: str + year: int + esg_risk_rating: Literal[ + "A+", "A", "A-", "B+", "B", "B-", "C+", "C", "C-", "D+", "D", "D-", "F" + ] + industry_rank: str diff --git a/openbb_providers/openbb_provider/model/data/esg_score.py b/openbb_providers/openbb_provider/model/data/esg_score.py new file mode 100644 index 00000000000..806912a5474 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/esg_score.py @@ -0,0 +1,64 @@ +"""ESG Score data model.""" + +# IMPORT STANDARD +from datetime import date, datetime + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + +# IMPORT THIRD-PARTY + + +class ESGScoreQueryParams(QueryParams, BaseSymbol): + """ESG score query model. + + Parameter + --------- + symbol : str + The symbol of the company. + """ + + __name__ = "ESGScoreQueryParams" + + +class ESGScoreData(Data): + """ESG Score data. + + Returns + ------- + symbol : str + The symbol of the asset. + cik : int + The CIK of the ESG Score. + company_name : str + The company name of the ESG Score. + form_type : str + The form type of the ESG Score. + accepted_date : datetime + The accepted date of the ESG Score. + date : date + The date of the ESG Score. + environmental_score : float + The environmental score of the ESG Score. + social_score : float + The social score of the ESG Score. + governance_score : float + The governance score of the ESG Score. + esg_score : float + The ESG score of the ESG Score. + url : str + The URL of the ESG Score. + """ + + symbol: str + cik: int + company_name: str + form_type: str + accepted_date: datetime + date: date + environmental_score: float + social_score: float + governance_score: float + esg_score: float + url: str diff --git a/openbb_providers/openbb_provider/model/data/esg_sector.py b/openbb_providers/openbb_provider/model/data/esg_sector.py new file mode 100644 index 00000000000..a51372efd97 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/esg_sector.py @@ -0,0 +1,48 @@ +"""ESG Sector data model.""" + +# IMPORT STANDARD + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams + +# IMPORT THIRD-PARTY + + +class ESGSectorQueryParams(QueryParams): + """ESG sector query model. + + Parameter + --------- + year : int + The year to get ESG information for + """ + + __name__ = "ESGSectorQueryParams" + year: int + + +class ESGSectorData(Data): + """ESG Sector data. + + Returns + ------- + year : int + The year of the ESG Sector. + sector : str + The sector of the ESG Sector. + environmental_score : float + The environmental score of the ESG Sector. + social_score : float + The social score of the ESG Sector. + governance_score : float + The governance score of the ESG Sector. + esg_score : float + The ESG score of the ESG Sector. + """ + + year: int + sector: str + environmental_score: float + social_score: float + governance_score: float + esg_score: float diff --git a/openbb_providers/openbb_provider/model/data/executive_compensation.py b/openbb_providers/openbb_provider/model/data/executive_compensation.py new file mode 100644 index 00000000000..a0512f8e73d --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/executive_compensation.py @@ -0,0 +1,69 @@ +"""Executive Compensation Data Model.""" + +# IMPORT STANDARD +from datetime import date as dateType +from typing import Optional + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class ExecutiveCompensationQueryParams(QueryParams, BaseSymbol): + """Executive Compensation query model. + + Parameter + --------- + symbol : str + The symbol of the company. + """ + + __name__ = "ExecutiveCompensationQueryParams" + + +class ExecutiveCompensationData(Data): + """Return Executive Compensation Data. + + Returns + ------- + cik : Optional[str] + The Central Index Key (CIK) of the company. + symbol : str + The symbol of the company. + filing_date : dateType + The date of the filing. + accepted_date : dateType + The date the filing was accepted. + name_and_position : str + The name and position of the executive. + year : int + The year of the compensation. + salary : float + The salary of the executive. + bonus : float + The bonus of the executive. + stock_award : float + The stock award of the executive. + incentive_plan_compensation : float + The incentive plan compensation of the executive. + all_other_compensation : float + The all other compensation of the executive. + total : float + The total compensation of the executive. + url : str + The URL of the filing. + """ + + cik: Optional[str] + symbol: str + filing_date: dateType + accepted_date: dateType + name_and_position: str + year: int + salary: float + bonus: float + stock_award: float + incentive_plan_compensation: float + all_other_compensation: float + total: float + url: str diff --git a/openbb_providers/openbb_provider/model/data/forex_eod.py b/openbb_providers/openbb_provider/model/data/forex_eod.py new file mode 100644 index 00000000000..607b63dbbc0 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/forex_eod.py @@ -0,0 +1,64 @@ +"""Forex aggregate end of day price data model.""" + +# IMPORT STANDARD +from datetime import date, datetime +from typing import Optional + +# IMPORT THIRD-PARTY +from pydantic import Field, PositiveFloat + +from openbb_provider.metadata import DESCRIPTIONS + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class ForexEODQueryParams(QueryParams, BaseSymbol): + """Forex end of day query. + + Parameter + --------- + symbol : str + The symbol of the company. + start_date : date + The start date of the stock data from which to retrieve the data. + end_date : Optional[date] + The end date of the stock data up to which to retrieve the data. + """ + + __name__ = "ForexEODQueryParams" + start_date: date = Field(description=DESCRIPTIONS.get("start_date", "")) + end_date: Optional[date] = Field( + description=DESCRIPTIONS.get("end_date", ""), default=date.today() + ) + + +class ForexEODData(Data): + """Forex end of day price data. + + Returns + ------- + date : datetime + The date of the stock. + open : PositiveFloat + The open price of the stock. + high : PositiveFloat + The high price of the stock. + low : PositiveFloat + The low price of the stock. + close : PositiveFloat + The close price of the stock. + adj_close : Optional[PositiveFloat] + The adjusted close price of the stock. + volume : PositiveFloat + The volume of the stock. + """ + + date: datetime + open: PositiveFloat + high: PositiveFloat + low: PositiveFloat + close: PositiveFloat + adj_close: Optional[PositiveFloat] + volume: PositiveFloat diff --git a/openbb_providers/openbb_provider/model/data/forex_price.py b/openbb_providers/openbb_provider/model/data/forex_price.py new file mode 100644 index 00000000000..61ffd409ea2 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/forex_price.py @@ -0,0 +1,60 @@ +"""Forex Price Data Model.""" + +# IMPORT STANDARD +from datetime import date, datetime +from typing import Optional + +# IMPORT THIRD-PARTY +from pydantic import Field, PositiveFloat + +from openbb_provider.metadata import DESCRIPTIONS + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class ForexPriceQueryParams(QueryParams, BaseSymbol): + """Forex price query. + + Parameter + --------- + symbol : str + The symbol of the company. + start_date : date + The start date of the query. + end_date : Optional[date] + The end date of the query. + """ + + __name__ = "ForexPriceQueryParams" + + # These fields only work with Polygon + start_date: date = Field(description=DESCRIPTIONS.get("start_date", "")) + end_date: Optional[date] = Field( + description=DESCRIPTIONS.get("end_date", ""), default=date.today() + ) + + +class ForexPriceData(Data): + """Forex price data. + + Returns + ------- + open : PositiveFloat + The open price of the stock. + high : PositiveFloat + The high price of the stock. + low : PositiveFloat + The low price of the stock. + close : PositiveFloat + The close price of the stock. + date : datetime + The date of the stock. + """ + + open: PositiveFloat + high: PositiveFloat + low: PositiveFloat + close: PositiveFloat + date: datetime diff --git a/openbb_providers/openbb_provider/model/data/global_news.py b/openbb_providers/openbb_provider/model/data/global_news.py new file mode 100644 index 00000000000..54b49cf70b3 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/global_news.py @@ -0,0 +1,48 @@ +"""Global News Data Model.""" + +# IMPORT STANDARD +from datetime import datetime +from typing import Optional + +# IMPORT THIRD-PARTY +from pydantic import Field, NonNegativeInt + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams + + +class GlobalNewsQueryParams(QueryParams): + """Global news query. + + Parameter + --------- + page : NonNegativeInt + The page of the global news to be retrieved. + """ + + __name__ = "GlobalNewsQueryParams" + page: NonNegativeInt = Field(default=0) + + +class GlobalNewsData(Data): + """Return Global News Data. + + Returns + ------- + date : datetime + The published date of the news. + title : str + The title of the news. + image : Optional[str] + The image URL of the news. + text : str + The text/body of the news. + url : str + The URL of the news. + """ + + date: datetime + title: str + image: Optional[str] + text: str + url: str diff --git a/openbb_providers/openbb_provider/model/data/historical_dividends.py b/openbb_providers/openbb_provider/model/data/historical_dividends.py new file mode 100644 index 00000000000..b52b88aa75a --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/historical_dividends.py @@ -0,0 +1,52 @@ +"""Historical dividends data model.""" + +# IMPORT STANDARD +from datetime import date as dateType +from typing import Optional + +# IMPORT THIRD-PARTY +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class HistoricalDividendsQueryParams(QueryParams, BaseSymbol): + """Historical dividends query. + + Parameter + --------- + symbol : str + The symbol of the company. + """ + + __name__ = "HistoricalDividendsQueryParams" + + +class HistoricalDividendsData(Data): + """Historical dividends data. + + Returns + ------- + date : date + The date of the historical dividends. + label : str + The label of the historical dividends. + adj_dividend : float + The adjusted dividend of the historical dividends. + dividend : float + The dividend of the historical dividends. + record_date : date + The record date of the historical dividends. + payment_date : date + The payment date of the historical dividends. + declaration_date : date + The declaration date of the historical dividends. + """ + + date: dateType + label: str + adj_dividend: float + dividend: float + record_date: Optional[dateType] + payment_date: Optional[dateType] + declaration_date: Optional[dateType] diff --git a/openbb_providers/openbb_provider/model/data/historical_stock_splits.py b/openbb_providers/openbb_provider/model/data/historical_stock_splits.py new file mode 100644 index 00000000000..6d2f5759fab --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/historical_stock_splits.py @@ -0,0 +1,42 @@ +"""Historical stock splits data model.""" + +# IMPORT STANDARD +from datetime import date as dateType + +# IMPORT THIRD-PARTY +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class HistoricalStockSplitsQueryParams(QueryParams, BaseSymbol): + """Historical Stock Splits query model. + + Parameter + --------- + symbol : str + The symbol of the company. + """ + + __name__ = "HistoricalStockSplitsQueryParams" + + +class HistoricalStockSplitsData(Data): + """Historical stock splits data. + + Returns + ------- + date : date + The date of the historical stock splits. + label : str + The label of the historical stock splits. + numerator : float + The numerator of the historical stock splits. + denominator : float + The denominator of the historical stock splits. + """ + + date: dateType + label: str + numerator: float + denominator: float diff --git a/openbb_providers/openbb_provider/model/data/income_statement.py b/openbb_providers/openbb_provider/model/data/income_statement.py new file mode 100644 index 00000000000..aa5dda5165d --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/income_statement.py @@ -0,0 +1,122 @@ +"""Income Statement Data Model.""" + +# IMPORT STANDARD +from datetime import date as dateType +from typing import Optional + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data +from openbb_provider.model.data.base import FinancialStatementQueryParams + + +class IncomeStatementQueryParams(FinancialStatementQueryParams): + """Income statement query. + + Parameter + --------- + symbol : str + The symbol of the company. + period : Literal["annually", "quarterly"] + The period of the income statement. + """ + + __name__ = "IncomeStatementQueryParams" + + +class IncomeStatementData(Data): + """Income Statement Data. + + Returns + ------- + date : date + The date of the income statement. + symbol : str + The symbol of the company. + currency : Optional[str] + The currency of the income statement. + cik : Optional[int] + The Central Index Key (CIK) of the company. + filing_date : date + The filing date of the income statement. + accepted_date : Optional[date] + The accepted date of the income statement. + period : Optional[str] + The period of the income statement. + revenue : Optional[int] + The revenue of the income statement. + cost_of_revenue : Optional[int] + The cost of revenue of the income statement. + gross_profit : Optional[int] + The gross profit of the income statement. + research_and_development_expenses : Optional[int] + The research and development expenses of the income statement. + general_and_administrative_expenses : Optional[int] + The general and administrative expenses of the income statement. + selling_and_marketing_expenses : Optional[float] + The selling and marketing expenses of the income statement. + selling_general_and_administrative_expenses : Optional[int] + The selling general and administrative expenses of the income statement. + other_expenses : Optional[int] + The other expenses of the income statement. + operating_expenses : Optional[int] + The operating expenses of the income statement. + cost_and_expenses : Optional[int] + The cost and expenses of the income statement. + depreciation_and_amortization : Optional[int] + The depreciation and amortization of the income statement. + ebitda : Optional[int] + The earnings before interest, taxes, depreciation, and amortization of the income statement. + operating_income : Optional[int] + The operating income of the income statement. + total_other_income_expenses_net : Optional[int] + The total other income expenses net of the income statement. + income_before_tax : Optional[int] + The income before tax of the income statement. + income_tax_expense : Optional[int] + The income tax expense of the income statement. + net_income : Optional[int] + The net income of the income statement. + eps : Optional[float] + The earnings per share of the income statement. + eps_diluted : Optional[float] + The earnings per share diluted of the income statement. + interest_expense : Optional[int] + The interest expense of the income statement. + interest_income : Optional[int] + The interest income of the income statement. + weighted_average_shares_outstanding : Optional[int] + The weighted average shares outstanding of the income statement. + weighted_average_shares_outstanding_dil : Optional[int] + The weighted average shares outstanding diluted of the income statement. + """ + + date: dateType + symbol: str + currency: Optional[str] = None + cik: Optional[int] + filing_date: dateType + accepted_date: Optional[dateType] = None + period: Optional[str] + revenue: Optional[int] + cost_of_revenue: Optional[int] + gross_profit: Optional[int] + research_and_development_expenses: Optional[int] = None + general_and_administrative_expenses: Optional[int] = None + selling_and_marketing_expenses: Optional[float] = None + selling_general_and_administrative_expenses: Optional[int] = None + other_expenses: Optional[int] = None + operating_expenses: Optional[int] + cost_and_expenses: Optional[int] + depreciation_and_amortization: Optional[int] = None + ebitda: Optional[int] = None + operating_income: Optional[int] = None + total_other_income_expenses_net: Optional[int] = None + income_before_tax: Optional[int] + income_tax_expense: Optional[int] + net_income: Optional[int] + eps: Optional[float] + eps_diluted: Optional[float] + interest_expense: Optional[int] = None + interest_income: Optional[int] = None + weighted_average_shares_outstanding: Optional[int] = None + weighted_average_shares_outstanding_dil: Optional[int] = None diff --git a/openbb_providers/openbb_provider/model/data/institutional_ownership.py b/openbb_providers/openbb_provider/model/data/institutional_ownership.py new file mode 100644 index 00000000000..7dca2b126d3 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/institutional_ownership.py @@ -0,0 +1,131 @@ +"""Institutional Ownership Data Model.""" + +# IMPORT STANDARD +from datetime import date as dateType +from typing import Optional + +# IMPORT THIRD-PARTY +from pydantic import Field + +from openbb_provider.metadata import DESCRIPTIONS + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class InstitutionalOwnershipQueryParams(QueryParams, BaseSymbol): + """Institutional Ownership QueryParams. + + Parameter + --------- + symbol : str + The symbol of the company. + include_current_quarter : bool + Whether to include the current quarter. Default is False. + date : Optional[dateType] + A specific date to get data for. + """ + + __name__ = "InstitutionalOwnershipQueryParams" + include_current_quarter: bool = Field( + default=False, description="Include current quarter data." + ) + date: Optional[dateType] = Field(description=DESCRIPTIONS.get("date", "")) + + +class InstitutionalOwnershipData(Data): + """Institutional Ownership data. + + Returns + ------- + symbol : str + The symbol of the company. + cik : str + The CIK of the company. + date : dateType + The date of the institutional ownership. + investors_holding : int + The number of investors holding the stock. + last_investors_holding : int + The number of investors holding the stock in the last quarter. + investors_holding_change : int + The change in the number of investors holding the stock. + number_of_13f_shares : int + The number of 13F shares. + last_number_of_13f_shares : int + The number of 13F shares in the last quarter. + number_of_13f_shares_change : int + The change in the number of 13F shares. + total_invested : float + The total amount invested. + last_total_invested : float + The total amount invested in the last quarter. + total_invested_change : float + The change in the total amount invested. + ownership_percent : float + The ownership percent. + last_ownership_percent : float + The ownership percent in the last quarter. + ownership_percent_change : float + The change in the ownership percent. + new_positions : int + The number of new positions. + last_new_positions : int + The number of new positions in the last quarter. + new_positions_change : int + The change in the number of new positions. + increased_positions : int + The number of increased positions. + last_increased_positions : int + The number of increased positions in the last quarter. + increased_positions_change : int + The change in the number of increased positions. + closed_positions : int + The number of closed positions. + last_closed_positions : int + The number of closed positions in the last quarter. + closed_positions_change : int + The change in the number of closed positions. + reduced_positions : int + The number of reduced positions. + last_reduced_positions : int + The number of reduced positions in the last quarter. + """ + + symbol: str = Field(min_lenght=1) + cik: Optional[str] = Field(min_lenght=1) + date: dateType + investors_holding: int + last_investors_holding: int + investors_holding_change: int + number_of_13f_shares: Optional[int] + last_number_of_13f_shares: Optional[int] + number_of_13f_shares_change: Optional[int] + total_invested: float + last_total_invested: float + total_invested_change: float + ownership_percent: float + last_ownership_percent: float + ownership_percent_change: float + new_positions: int + last_new_positions: int + new_positions_change: int + increased_positions: int + last_increased_positions: int + increased_positions_change: int + closed_positions: int + last_closed_positions: int + closed_positions_change: int + reduced_positions: int + last_reduced_positions: int + reduced_positions_change: int + total_calls: int + last_total_calls: int + total_calls_change: int + total_puts: int + last_total_puts: int + total_puts_change: int + put_call_ratio: float + last_put_call_ratio: float + put_call_ratio_change: float diff --git a/openbb_providers/openbb_provider/model/data/key_executives.py b/openbb_providers/openbb_provider/model/data/key_executives.py new file mode 100644 index 00000000000..c69aff808ce --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/key_executives.py @@ -0,0 +1,60 @@ +"""Key Executives Data Model.""" + +# IMPORT STANDARD +from datetime import datetime +from typing import Optional + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class KeyExecutivesQueryParams(QueryParams, BaseSymbol): + """Key executives query. + + Parameter + --------- + symbol : str + The symbol of the company. + key_executive_name : Optional[str] + The name of the key executive to be retrieved. + key_executive_title : Optional[str] + The title of the key executive to be retrieved. + key_executive_title_since : Optional[datetime] + The period since the position of the key executive to be retrieved. + key_executive_year_born : Optional[datetime] + The year born of the key executive to be retrieved. + key_executive_gender: Optional[str] + The gender of the key executive to be retrieved. + """ + + __name__ = "KeyExecutivesQueryParams" + key_executive_name: Optional[str] + key_executive_title: Optional[str] + key_executive_title_since: Optional[datetime] + key_executive_year_born: Optional[datetime] + key_executive_gender: Optional[str] + + +class KeyExecutivesData(Data): + """Key Executives Data. + + Returns + ------- + name : Optional[str] + The name of the key executive. + title : Optional[str] + The title of the key executive. + title_since : Optional[datetime] + The title since of the key executive. + year_born : Optional[datetime] + The year born of the key executive. + gender : Optional[str] + The gender of the key executive. + """ + + name: Optional[str] + title: Optional[str] + title_since: Optional[datetime] + year_born: Optional[datetime] + gender: Optional[str] diff --git a/openbb_providers/openbb_provider/model/data/key_metrics.py b/openbb_providers/openbb_provider/model/data/key_metrics.py new file mode 100644 index 00000000000..ad1d2a28a26 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/key_metrics.py @@ -0,0 +1,212 @@ +"""Key Metrics Data Model.""" + +# IMPORT STANDARD +from datetime import date as DateType +from typing import Literal, Optional + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class KeyMetricsQueryParams(QueryParams, BaseSymbol): + """Key Metrics query model. + + Parameter + --------- + symbol : str + The symbol of the company. + """ + + __name__ = "KeyMetricsQueryParams" + period: Literal["annually", "quarterly"] = "annually" + limit: Optional[int] = None + + +class KeyMetricsData(Data): + """Return Key Metrics Data. + + Returns + ------- + symbol : str + The symbol of the stock. + date : DateType + The date of the key metrics. + period : str + The period of the key metrics. + revenue_per_share : float + The revenue per share of the stock. + net_income_per_share : float + The net income per share of the stock. + operating_cash_flow_per_share : float + The operating cash flow per share of the stock. + free_cash_flow_per_share : float + The free cash flow per share of the stock. + cash_per_share : float + The cash per share of the stock. + book_value_per_share : float + The book value per share of the stock. + tangible_book_value_per_share : float + The tangible book value per share of the stock. + shareholders_equity_per_share : float + The shareholders equity per share of the stock. + interest_debt_per_share : float + The interest debt per share of the stock. + market_cap : float + The market cap of the stock. + enterprise_value : float + The enterprise value of the stock. + pe_ratio : float + The PE ratio of the stock. + price_to_sales_ratio : float + The price to sales ratio of the stock. + pocf_ratio : float + The POCF ratio of the stock. + pfcf_ratio : float + The PFCF ratio of the stock. + pb_ratio : float + The PB ratio of the stock. + ptb_ratio : float + The PTB ratio of the stock. + ev_to_sales : float + The EV to sales of the stock. + enterprise_value_over_ebitda : float + The enterprise value over EBITDA of the stock. + ev_to_operating_cash_flow : float + The EV to operating cash flow of the stock. + ev_to_free_cash_flow : float + The EV to free cash flow of the stock. + earnings_yield : float + The earnings yield of the stock. + free_cash_flow_yield : float + The free cash flow yield of the stock. + debt_to_equity : float + The debt to equity of the stock. + debt_to_assets : float + The debt to assets of the stock. + net_debt_to_ebitda : float + The net debt to EBITDA of the stock. + current_ratio : float + The current ratio of the stock. + interest_coverage : float + The interest coverage of the stock. + income_quality : float + The income quality of the stock. + dividend_yield : float + The dividend yield of the stock. + payout_ratio : float + The payout ratio of the stock. + sales_general_and_administrative_to_revenue : float + The sales general and administrative to revenue of the stock. + research_and_developement_to_revenue : float + The research and development to revenue of the stock. + intangibles_to_total_assets : float + The intangibles to total assets of the stock. + capex_to_operating_cash_flow : float + The capex to operating cash flow of the stock. + capex_to_revenue : float + The capex to revenue of the stock. + capex_to_depreciation : float + The capex to depreciation of the stock. + stock_based_compensation_to_revenue : float + The stock based compensation to revenue of the stock. + graham_number : float + The graham number of the stock. + roic : float + The ROIC of the stock. + return_on_tangible_assets : float + The return on tangible assets of the stock. + graham_net_net : float + The graham net net of the stock. + working_capital : float + The working capital of the stock. + tangible_asset_value : float + The tangible asset value of the stock. + net_current_asset_value : float + The net current asset value of the stock. + invested_capital : float + The invested capital of the stock. + average_receivables : float + The average receivables of the stock. + average_payables : float + The average payables of the stock. + average_inventory : float + The average inventory of the stock. + days_sales_outstanding : float + The days sales outstanding of the stock. + days_payables_outstanding : float + The days payables outstanding of the stock. + days_of_inventory_on_hand : float + The days of inventory on hand of the stock. + receivables_turnover : float + The receivables turnover of the stock. + payables_turnover : float + The payables turnover of the stock. + inventory_turnover : float + The inventory turnover of the stock. + roe : float + The ROE of the stock. + capex_per_share : float + The capex per share of the stock. + """ + + symbol: str + date: DateType + period: str + revenue_per_share: Optional[float] + net_income_per_share: Optional[float] + operating_cash_flow_per_share: Optional[float] + free_cash_flow_per_share: Optional[float] + cash_per_share: Optional[float] + book_value_per_share: Optional[float] + tangible_book_value_per_share: Optional[float] + shareholders_equity_per_share: Optional[float] + interest_debt_per_share: Optional[float] + market_cap: Optional[float] + enterprise_value: Optional[float] + pe_ratio: Optional[float] + price_to_sales_ratio: Optional[float] + pocf_ratio: Optional[float] + pfcf_ratio: Optional[float] + pb_ratio: Optional[float] + ptb_ratio: Optional[float] + ev_to_sales: Optional[float] + enterprise_value_over_ebitda: Optional[float] + ev_to_operating_cash_flow: Optional[float] + ev_to_free_cash_flow: Optional[float] + earnings_yield: Optional[float] + free_cash_flow_yield: Optional[float] + debt_to_equity: Optional[float] + debt_to_assets: Optional[float] + net_debt_to_ebitda: Optional[float] + current_ratio: Optional[float] + interest_coverage: Optional[float] + income_quality: Optional[float] + dividend_yield: Optional[float] + payout_ratio: Optional[float] + sales_general_and_administrative_to_revenue: Optional[float] + research_and_development_to_revenue: Optional[float] + intangibles_to_total_assets: Optional[float] + capex_to_operating_cash_flow: Optional[float] + capex_to_revenue: Optional[float] + capex_to_depreciation: Optional[float] + stock_based_compensation_to_revenue: Optional[float] + graham_number: Optional[float] + roic: Optional[float] + return_on_tangible_assets: Optional[float] + graham_net_net: Optional[float] + working_capital: Optional[float] + tangible_asset_value: Optional[float] + net_current_asset_value: Optional[float] + invested_capital: Optional[float] + average_receivables: Optional[float] + average_payables: Optional[float] + average_inventory: Optional[float] + days_sales_outstanding: Optional[float] + days_payables_outstanding: Optional[float] + days_of_inventory_on_hand: Optional[float] + receivables_turnover: Optional[float] + payables_turnover: Optional[float] + inventory_turnover: Optional[float] + roe: Optional[float] + capex_per_share: Optional[float] diff --git a/openbb_providers/openbb_provider/model/data/major_indices_eod.py b/openbb_providers/openbb_provider/model/data/major_indices_eod.py new file mode 100644 index 00000000000..0a05eb4920b --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/major_indices_eod.py @@ -0,0 +1,64 @@ +"""Major indices aggregate end of day price data model.""" + +# IMPORT STANDARD +from datetime import date, datetime +from typing import Optional + +# IMPORT THIRD-PARTY +from pydantic import Field, PositiveFloat + +from openbb_provider.metadata import DESCRIPTIONS + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class MajorIndicesEODQueryParams(QueryParams, BaseSymbol): + """Major Indices end of day query. + + Parameter + --------- + symbol : str + The symbol of the company. + start_date : date + The start date of the stock data from which to retrieve the data. + end_date : Optional[date] + The end date of the stock data up to which to retrieve the data. + """ + + __name__ = "MajorIndicesEODQueryParams" + start_date: date = Field(description=DESCRIPTIONS.get("start_date", "")) + end_date: Optional[date] = Field( + description=DESCRIPTIONS.get("end_date", ""), default=date.today() + ) + + +class MajorIndicesEODData(Data): + """Major Indices end of day price data. + + Returns + ------- + date : datetime + The date of the stock. + open : PositiveFloat + The open price of the stock. + high : PositiveFloat + The high price of the stock. + low : PositiveFloat + The low price of the stock. + close : PositiveFloat + The close price of the stock. + adj_close : Optional[PositiveFloat] + The adjusted close price of the stock. + volume : Optional[PositiveFloat] + The volume of the stock. + """ + + date: datetime + open: PositiveFloat + high: PositiveFloat + low: PositiveFloat + close: PositiveFloat + adj_close: Optional[PositiveFloat] + volume: Optional[PositiveFloat] diff --git a/openbb_providers/openbb_provider/model/data/major_indices_price.py b/openbb_providers/openbb_provider/model/data/major_indices_price.py new file mode 100644 index 00000000000..c9fd38f79f4 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/major_indices_price.py @@ -0,0 +1,60 @@ +"""Major Indices Price Data Model.""" + +# IMPORT STANDARD +from datetime import date, datetime +from typing import Optional + +# IMPORT THIRD-PARTY +from pydantic import Field, PositiveFloat + +from openbb_provider.metadata import DESCRIPTIONS + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class MajorIndicesPriceQueryParams(QueryParams, BaseSymbol): + """Major indices price query. + + Parameter + --------- + symbol : str + The symbol of the company. + start_date : date + The start date of the query. + end_date : Optional[date] + The end date of the query. + """ + + __name__ = "MajorIndicesPriceQueryParams" + + # These fields only work with Polygon + start_date: date = Field(description=DESCRIPTIONS.get("start_date", "")) + end_date: Optional[date] = Field( + description=DESCRIPTIONS.get("end_date", ""), default=date.today() + ) + + +class MajorIndicesPriceData(Data): + """Major Indices price data. + + Returns + ------- + open : PositiveFloat + The open price of the stock. + high : PositiveFloat + The high price of the stock. + low : PositiveFloat + The low price of the stock. + close : PositiveFloat + The close price of the stock. + date : datetime + The date of the stock. + """ + + open: PositiveFloat + high: PositiveFloat + low: PositiveFloat + close: PositiveFloat + date: datetime diff --git a/openbb_providers/openbb_provider/model/data/price_target.py b/openbb_providers/openbb_provider/model/data/price_target.py new file mode 100644 index 00000000000..b38a773bd3b --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/price_target.py @@ -0,0 +1,64 @@ +"""Price target data model.""" + +# IMPORT STANDARD +from datetime import datetime +from typing import Optional + +# IMPORT THIRD-PARTY +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class PriceTargetQueryParams(QueryParams, BaseSymbol): + """Price Target query. + + Parameter + --------- + symbol : str + The symbol of the company. + """ + + __name__ = "PriceTargetQueryParams" + + +class PriceTargetData(Data, BaseSymbol): + """Price target data. + + Returns + ------- + symbol : str + The symbol of the asset. + published_date : datetime + The published date of the price target. + news_url : str + The news URL of the price target. + news_title : str + The news title of the price target. + analyst_name : str + The analyst name of the price target. + price_target : float + The price target of the price target. + adj_price_target : float + The adjusted price target of the price target. + price_when_posted : float + The price when posted of the price target. + news_publisher : str + The news publisher of the price target. + news_base_url : str + The news base URL of the price target. + analyst_company : str + The analyst company of the price target. + """ + + symbol: str + published_date: datetime + news_url: str + news_title: Optional[str] + analyst_name: Optional[str] + price_target: float + adj_price_target: float + price_when_posted: float + news_publisher: str + news_base_url: str + analyst_company: str diff --git a/openbb_providers/openbb_provider/model/data/price_target_consensus.py b/openbb_providers/openbb_provider/model/data/price_target_consensus.py new file mode 100644 index 00000000000..98279c4906d --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/price_target_consensus.py @@ -0,0 +1,43 @@ +"""Price target consensus data model.""" + +# IMPORT STANDARD +# IMPORT THIRD-PARTY +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class PriceTargetConsensusQueryParams(QueryParams, BaseSymbol): + """Price Target Consensus query. + + Parameter + --------- + symbol : str + The symbol of the company. + """ + + __name__ = "PriceTargetConsensusQueryParams" + + +class PriceTargetConsensusData(Data): + """Price target consensus data. + + Returns + ------- + symbol : str + The symbol of the asset. + target_high : float + The high target of the price target consensus. + target_low : float + The low target of the price target consensus. + target_consensus : float + The consensus target of the price target consensus. + target_median : float + The median target of the price target consensus. + """ + + symbol: str + target_high: float + target_low: float + target_consensus: float + target_median: float diff --git a/openbb_providers/openbb_provider/model/data/revenue_business_line.py b/openbb_providers/openbb_provider/model/data/revenue_business_line.py new file mode 100644 index 00000000000..9c34d654504 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/revenue_business_line.py @@ -0,0 +1,52 @@ +"""Revenue by business line data model.""" + +# IMPORT STANDARD +from datetime import date +from typing import Dict, Literal + +# IMPORT THIRD-PARTY +from pydantic import Field + +from openbb_provider.metadata import DESCRIPTIONS + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class RevenueBusinessLineQueryParams(QueryParams, BaseSymbol): + """Revenue business line query. + + + Parameter + --------- + symbol : str + The symbol of the company. + period : Literal["quarterly", "annually"] + The period of the income statement. + structure : Literal["hierarchical", "flat"] + The structure of the revenue business line. Should always be flat. + """ + + __name__ = "RevenueBusinessLineQueryParams" + period: Literal["quarterly", "annually"] = Field( + default="quarterly", description=DESCRIPTIONS.get("period", "") + ) + structure: Literal["hierarchical", "flat"] = Field( + default="flat" + ) # should always be flat + + +class RevenueBusinessLineData(Data): + """Revenue by business line data. + + Returns + ------- + date : date + The date of the revenue. + data_and_service : Dict[str, int] + The data and service of the revenue. + """ + + date: date + data_and_service: Dict[str, int] diff --git a/openbb_providers/openbb_provider/model/data/revenue_geographic.py b/openbb_providers/openbb_provider/model/data/revenue_geographic.py new file mode 100644 index 00000000000..1ade3f3e53a --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/revenue_geographic.py @@ -0,0 +1,63 @@ +"""Revenue by geographic segments data model.""" + +# IMPORT STANDARD +from datetime import date +from typing import Literal, Optional + +# IMPORT THIRD-PARTY +from pydantic import Field + +from openbb_provider.metadata import DESCRIPTIONS + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class RevenueGeographicQueryParams(QueryParams, BaseSymbol): + """Revenue geographic query. + + Parameter + --------- + symbol : str + The symbol of the company. + period : Literal["quarterly", "annually"] + The period of the income statement. + structure : Literal["hierarchical", "flat"] + The structure of the revenue geographic. Should always be flat. + """ + + __name__ = "RevenueGeographicQueryParams" + period: Literal["quarterly", "annually"] = Field( + default="quarterly", description=DESCRIPTIONS.get("period", "") + ) + structure: Literal["hierarchical", "flat"] = Field( + default="flat" + ) # should always be flat + + +class RevenueGeographicData(Data): + """Revenue by geographic segments data. + + Returns + ------- + date : date + The date of the revenue. + americas : Optional[int] + The revenue of the America segment. + europe : Optional[int] + The revenue of the Europe segment. + greater_china : Optional[int] + The revenue of the Greater China segment. + japan : Optional[int] + The revenue of the Japan segment. + rest_of_asia_pacific : Optional[int] + The revenue of the Rest of Asia Pacific segment. + """ + + date: date + americas: Optional[int] + europe: Optional[int] + greater_china: Optional[int] + japan: Optional[int] + rest_of_asia_pacific: Optional[int] diff --git a/openbb_providers/openbb_provider/model/data/sec_filings.py b/openbb_providers/openbb_provider/model/data/sec_filings.py new file mode 100644 index 00000000000..2e300ce069b --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/sec_filings.py @@ -0,0 +1,61 @@ +"""SEC filings data model.""" + +# IMPORT STANDARD +from datetime import date as dateType +from typing import Optional + +# IMPORT THIRD-PARTY +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class SECFilingsQueryParams(QueryParams, BaseSymbol): + """SEC Filings query model. + Parameter + --------- + symbol : str + The symbol of the company. + type : str + The type of the SEC filing form. (full list: https://www.sec.gov/forms) + page : int + The page of the results. + limit : int + The limit of the results. + """ + + __name__ = "SECFilingsQueryParams" + symbol: str + type: Optional[str] = None + page: Optional[int] = 0 + limit: Optional[int] = None + + +class SECFilingsData(Data): + """SEC filings data. + + Returns + ------- + symbol : str + The symbol of the stock. + filling_date : date + The filling date of the SEC filing. + accepted_date : date + The accepted date of the SEC filing. + cik : str + The CIK of the SEC filing. + type : str + The type of the SEC filing. + link : str + The link of the SEC filing. + final_link : str + The final link of the SEC filing. + """ + + symbol: str + filling_date: dateType + accepted_date: dateType + cik: Optional[str] + type: str + link: str + final_link: str diff --git a/openbb_providers/openbb_provider/model/data/share_statistics.py b/openbb_providers/openbb_provider/model/data/share_statistics.py new file mode 100644 index 00000000000..45ed3321726 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/share_statistics.py @@ -0,0 +1,47 @@ +"""Share Statistics Data Model.""" + +# IMPORT STANDARD +from datetime import date as dateType + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class ShareStatisticsQueryParams(QueryParams, BaseSymbol): + """Share Statistics query. + + Parameter + --------- + symbol : str + The symbol of the company. + """ + + __name__ = "ShareStatisticsQueryParams" + + +class ShareStatisticsData(Data): + """Return Share Statistics Data. + + Returns + ------- + symbol : str + The symbol of the company. + date : dateType + The date of the share statistics. + free_float : float + The free float of the company. + float_shares : float + The float shares of the company. + outstanding_shares : float + The outstanding shares of the company. + source : str + The source of the data. + """ + + symbol: str + date: dateType + free_float: float + float_shares: float + outstanding_shares: float + source: str diff --git a/openbb_providers/openbb_provider/model/data/stock_eod.py b/openbb_providers/openbb_provider/model/data/stock_eod.py new file mode 100644 index 00000000000..fc368241c24 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/stock_eod.py @@ -0,0 +1,63 @@ +"""Stock aggregate end of day price data model.""" + +# IMPORT STANDARD +from datetime import date, datetime +from typing import Optional + +# IMPORT THIRD-PARTY +from pydantic import Field, PositiveFloat + +from openbb_provider.metadata import DESCRIPTIONS + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class StockEODQueryParams(QueryParams, BaseSymbol): + """Stock end of day query. + Parameter + --------- + symbol : str + The symbol of the company. + start_date : date + The start date of the stock data from which to retrieve the data. + end_date : Optional[date] + The end date of the stock data up to which to retrieve the data. + """ + + __name__ = "StockEODQueryParams" + start_date: date = Field(description=DESCRIPTIONS.get("start_date", "")) + end_date: Optional[date] = Field( + description=DESCRIPTIONS.get("end_date", ""), default=date.today() + ) + + +class StockEODData(Data): + """Stock end of day price data. + + Returns + ------- + date : datetime + The date of the stock. + open : PositiveFloat + The open price of the stock. + high : PositiveFloat + The high price of the stock. + low : PositiveFloat + The low price of the stock. + close : PositiveFloat + The close price of the stock. + adj_close : Optional[PositiveFloat] + The adjusted close price of the stock. + volume : PositiveFloat + The volume of the stock. + """ + + date: datetime + open: PositiveFloat + high: PositiveFloat + low: PositiveFloat + close: PositiveFloat + adj_close: Optional[PositiveFloat] + volume: PositiveFloat diff --git a/openbb_providers/openbb_provider/model/data/stock_insider_trading.py b/openbb_providers/openbb_provider/model/data/stock_insider_trading.py new file mode 100644 index 00000000000..2b55aabca54 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/stock_insider_trading.py @@ -0,0 +1,115 @@ +"""Stock insider trading data model.""" + +# IMPORT STANDARD +from datetime import date, datetime +from enum import Enum +from typing import List, Optional + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams + +# IMPORT THIRD-PARTY + + +class TransactionTypes(str, Enum): + Award = "A-Award" + Conversion = "C-Conversion" + Return = "D-Return" + ExpireShort = "E-ExpireShort" + InKind = "F-InKind" + Gift = "G-Gift" + ExpireLong = "H-ExpireLong" + Discretionary = "I-Discretionary" + Other = "J-Other" + Small = "L-Small" + Exempt = "M-Exempt" + OutOfTheMoney = "O-OutOfTheMoney" + Purchase = "P-Purchase" + Sale = "S-Sale" + Tender = "U-Tender" + Will = "W-Will" + InTheMoney = "X-InTheMoney" + Trust = "Z-Trust" + + +class StockInsiderTradingQueryParams(QueryParams): + """Stock Insider Trading query. + + + Parameter + --------- + transactionType : List[TransactionTypes] + The type of the transaction. Possible values are: + A-Award, C-Conversion, D-Return, E-ExpireShort, F-InKind, G-Gift, H-ExpireLong + I-Discretionary, J-Other, L-Small, M-Exempt, O-OutOfTheMoneym P-Purchase + S-Sale, U-Tender, W-Will, X-InTheMoney, Z-Trust + symbol : Optional[str] + The symbol of the company. + reportingCik : Optional[int] + The CIK of the reporting owner. + companyCik : Optional[int] + The CIK of the company owner. + page: int + The page number to get + """ + + __name__ = "StockInsiderTradingQueryParams" + transactionType: List[TransactionTypes] = [] + symbol: Optional[str] + reportingCik: Optional[int] + companyCik: Optional[int] + page: int = 0 + + +class StockInsiderTradingData(Data): + """Stock insider trading data. + + Returns + ------- + symbol : str + The symbol of the asset. + filingDate : datetime + The filing date of the stock insider trading. + transactionDate : date + The transaction date of the stock insider trading. + reportingCik : int + The reporting CIK of the stock insider trading. + transactionType : str + The transaction type of the stock insider trading. + securitiesOwned : int + The securities owned of the stock insider trading. + companyCik : int + The company CIK of the stock insider trading. + reportingName : str + The reporting name of the stock insider trading. + typeOfOwner : str + The type of owner of the stock insider trading. + acquistionOrDisposition : str + The acquistion or disposition of the stock insider trading. + formType : str + The form type of the stock insider trading. + securitiesTransacted : float + The securities transacted of the stock insider trading. + price : float + The price of the stock insider trading. + securityName : str + The security name of the stock insider trading. + link : str + The link of the stock insider trading. + """ + + symbol: str + filingDate: datetime + transactionDate: date + reportingCik: int + transactionType: str + securitiesOwned: int + companyCik: int + reportingName: str + typeOfOwner: str + acquistionOrDisposition: str + formType: str + securitiesTransacted: float + price: float + securityName: str + link: str diff --git a/openbb_providers/openbb_provider/model/data/stock_news.py b/openbb_providers/openbb_provider/model/data/stock_news.py new file mode 100644 index 00000000000..b2934e54573 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/stock_news.py @@ -0,0 +1,58 @@ +"""Stock News Data Model.""" + +# IMPORT STANDARD +from datetime import date +from typing import Optional + +# IMPORT THIRD-PARTY +from pydantic import Field, validator + +from openbb_provider.metadata import DESCRIPTIONS + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams + + +class StockNewsQueryParams(QueryParams): + """Stock news query. + + + Parameter + --------- + symbols : str + The symbols of the company. + page : int + The page of the stock news to be retrieved. + """ + + __name__ = "StockNewsQueryParams" + symbols: str = Field(min_length=1, description=DESCRIPTIONS.get("symbols", "")) + page: int = Field(default=0) + + @validator("symbols", pre=True) + def time_validate(cls, v: str): # pylint: disable=E0213 + return v.upper() + + +class StockNewsData(Data): + """Stock News data. + + Returns + ------- + date : date + The published date of the news. + title : str + The title of the news. + image : Optional[str] + The image URL of the news. + text : str + The text/body of the news. + url : str + The URL of the news. + """ + + date: date + title: str + image: Optional[str] + text: Optional[str] + url: str diff --git a/openbb_providers/openbb_provider/model/data/stock_ownership.py b/openbb_providers/openbb_provider/model/data/stock_ownership.py new file mode 100644 index 00000000000..8556c63b0c6 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/stock_ownership.py @@ -0,0 +1,190 @@ +"""Stock owner data model.""" + +# IMPORT STANDARD +from datetime import date as dateType +from datetime import datetime + +# IMPORT THIRD-PARTY +from pydantic import validator + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +def most_recent_quarter(base: dateType = dateType.today()) -> dateType: + """Get the most recent quarter date. + + Parameter + --------- + base : date + The date to get the most recent quarter. + + Returns + ------- + date : date + The most recent quarter date. + """ + + base = min(base, dateType.today()) # This prevents dates from being in the future + exacts = [(3, 31), (6, 30), (9, 30), (12, 31)] + for exact in exacts: + if base.month == exact[0] and base.day == exact[1]: + return base + if base.month < 4: + return dateType(base.year - 1, 12, 31) + if base.month < 7: + return dateType(base.year, 3, 31) + if base.month < 10: + return dateType(base.year, 6, 30) + return dateType(base.year, 9, 30) + + +class StockOwnershipQueryParams(QueryParams, BaseSymbol): + """Stock ownership query. + + + Parameter + --------- + symbol : string + The symbol of the company. + page : int + The page number to get + date : date + The CIK of the company owner. + """ + + __name__ = "StockOwnershipQueryParams" + page: int = 0 + date: dateType = most_recent_quarter() + + @validator("date", pre=True) + def time_validate(cls, v: str): # pylint: disable=E0213 + base = datetime.strptime(v, "%Y-%m-%d").date() + return most_recent_quarter(base) + + +class StockOwnershipData(Data): + """Stock ownership data. + + Returns + ------- + date : date + The date of the stock ownership. + cik : int + The cik of the stock ownership. + filingDate : date + The filing date of the stock ownership. + investorName : str + The investor name of the stock ownership. + symbol : str + The symbol of the stock ownership. + securityName : str + The security name of the stock ownership. + typeOfSecurity : str + The type of security of the stock ownership. + securityCusip : str + The security cusip of the stock ownership. + sharesType : str + The shares type of the stock ownership. + putCallShare : str + The put call share of the stock ownership. + investmentDiscretion : str + The investment discretion of the stock ownership. + industryTitle : str + The industry title of the stock ownership. + weight : float + The weight of the stock ownership. + lastWeight : float + The last weight of the stock ownership. + changeInWeight : float + The change in weight of the stock ownership. + changeInWeightPercentage : float + The change in weight percentage of the stock ownership. + marketValue : int + The market value of the stock ownership. + lastMarketValue : int + The last market value of the stock ownership. + changeInMarketValue : int + The change in market value of the stock ownership. + changeInMarketValuePercentage : float + The change in market value percentage of the stock ownership. + sharesNumber : int + The shares number of the stock ownership. + lastSharesNumber : int + The last shares number of the stock ownership. + changeInSharesNumber : float + The change in shares number of the stock ownership. + changeInSharesNumberPercentage : float + The change in shares number percentage of the stock ownership. + quarterEndPrice : float + The quarter end price of the stock ownership. + avgPricePaid : float + The average price paid of the stock ownership. + isNew : bool + Is the stock ownership new. + isSoldOut : bool + Is the stock ownership sold out. + ownership : float + How much is the ownership. + lastOwnership : float + The last ownership amount. + changeInOwnership : float + The change in ownership amount. + changeInOwnershipPercentage : float + The change in ownership percentage. + holdingPeriod : int + The holding period of the stock ownership. + firstAdded : date + The first added date of the stock ownership. + performance : float + The performance of the stock ownership. + performancePercentage : float + The performance percentage of the stock ownership. + lastPerformance : float + The last performance of the stock ownership. + changeInPerformance : float + The change in performance of the stock ownership. + isCountedForPerformance : bool + Is the stock ownership counted for performance. + """ + + date: dateType + cik: int + filingDate: dateType + investorName: str + symbol: str + securityName: str + typeOfSecurity: str + securityCusip: str + sharesType: str + putCallShare: str + investmentDiscretion: str + industryTitle: str + weight: float + lastWeight: float + changeInWeight: float + changeInWeightPercentage: float + marketValue: int + lastMarketValue: int + changeInMarketValue: int + changeInMarketValuePercentage: float + sharesNumber: int + lastSharesNumber: int + changeInSharesNumber: float + changeInSharesNumberPercentage: float + quarterEndPrice: float + avgPricePaid: float + isNew: bool + isSoldOut: bool + ownership: float + lastOwnership: float + changeInOwnership: float + changeInOwnershipPercentage: float + holdingPeriod: int + firstAdded: dateType + performance: float + performancePercentage: float + lastPerformance: float + changeInPerformance: float + isCountedForPerformance: bool diff --git a/openbb_providers/openbb_provider/model/data/stock_price.py b/openbb_providers/openbb_provider/model/data/stock_price.py new file mode 100644 index 00000000000..50fa97b2cd9 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/stock_price.py @@ -0,0 +1,60 @@ +"""Stock Price Data Model.""" + +# IMPORT STANDARD +from datetime import date, datetime +from typing import Optional + +# IMPORT THIRD-PARTY +from pydantic import Field, PositiveFloat + +from openbb_provider.metadata import DESCRIPTIONS + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.data.base import BaseSymbol + + +class StockPriceQueryParams(QueryParams, BaseSymbol): + """Stock price query. + + Parameter + --------- + symbol : str + The symbol of the company. + start_date : date + The start date of the query. + end_date : Optional[date] + The end date of the query. + """ + + __name__ = "StockPriceQueryParams" + + # These fields only work with Polygon + start_date: date = Field(description=DESCRIPTIONS.get("start_date", "")) + end_date: Optional[date] = Field( + description=DESCRIPTIONS.get("end_date", ""), default=date.today() + ) + + +class StockPriceData(Data): + """Stock price data. + + Returns + ------- + open : PositiveFloat + The open price of the stock. + high : PositiveFloat + The high price of the stock. + low : PositiveFloat + The low price of the stock. + close : PositiveFloat + The close price of the stock. + date : datetime + The date of the stock. + """ + + open: PositiveFloat + high: PositiveFloat + low: PositiveFloat + close: PositiveFloat + date: datetime diff --git a/openbb_providers/openbb_provider/model/data/treasury_rates.py b/openbb_providers/openbb_provider/model/data/treasury_rates.py new file mode 100644 index 00000000000..b5b1f385cd0 --- /dev/null +++ b/openbb_providers/openbb_provider/model/data/treasury_rates.py @@ -0,0 +1,81 @@ +"""Treasury Rates Data Model.""" + +# IMPORT STANDARD +from datetime import date as dateType +from datetime import datetime +from typing import Optional + +# IMPORT THIRD-PARTY +from pydantic import Field + +from openbb_provider.metadata import DESCRIPTIONS + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams + + +class TreasuryRatesQueryParams(QueryParams): + """Treasury Rates query. + + Parameter + --------- + start_date : str + Start date of the data. + end_date : str + End date of the data. Default is today. + """ + + __name__ = "TreasuryRatesQueryParams" + start_date: str = Field(default=None, description=DESCRIPTIONS["start_date"]) + end_date: Optional[str] = Field( + default=datetime.today().strftime("%Y-%m-%d"), + description=DESCRIPTIONS["end_date"], + ) + + +class TreasuryRatesData(Data): + """Return Treasury Rates Data. + + Returns + ------- + date : dateType + The date of the treasury rates. + month_1 : float + The 1 month treasury rate. + month_2 : float + The 2 month treasury rate. + month_3 : float + The 3 month treasury rate. + month_6 : float + The 6 month treasury rate. + year_1 : float + The 1 year treasury rate. + year_2 : float + The 2 year treasury rate. + year_3 : float + The 3 year treasury rate. + year_5 : float + The 5 year treasury rate. + year_7 : float + The 7 year treasury rate. + year_10 : float + The 10 year treasury rate. + year_20 : float + The 20 year treasury rate. + year_30 : float + The 30 year treasury rate. + """ + + date: dateType + month_1: float + month_2: float + month_3: float + month_6: float + year_1: float + year_2: float + year_3: float + year_5: float + year_7: float + year_10: float + year_20: float + year_30: float diff --git a/openbb_providers/openbb_provider/provider/__init__.py b/openbb_providers/openbb_provider/provider/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_providers/openbb_provider/provider/abstract/__init__.py b/openbb_providers/openbb_provider/provider/abstract/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openbb_providers/openbb_provider/provider/abstract/fetcher.py b/openbb_providers/openbb_provider/provider/abstract/fetcher.py new file mode 100644 index 00000000000..ccd8207ce35 --- /dev/null +++ b/openbb_providers/openbb_provider/provider/abstract/fetcher.py @@ -0,0 +1,87 @@ +"""Abstract class for the fetcher.""" + +# IMPORT STANDARD +from typing import Dict, Generic, List, Optional, TypeVar, Union + +# IMPORT THIRD-PARTY +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import Data, QueryParams +from openbb_provider.model.abstract.provider_data import ( + ProviderData, + ProviderQueryParams, +) + +QueryParamsType = TypeVar("QueryParamsType", bound=QueryParams) +DataType = TypeVar("DataType", bound=Data) +ProviderQueryParamsType = TypeVar("ProviderQueryParamsType", bound=ProviderQueryParams) +ProviderDataType = TypeVar("ProviderDataType", bound=ProviderData) +ReturnType = Union[List[ProviderDataType], ProviderDataType] +UDataType = Union[List[DataType], DataType] + + +class Fetcher( + Generic[ + QueryParamsType, + DataType, + ProviderQueryParamsType, + ProviderDataType, + ] +): + @staticmethod + def transform_query( + query: QueryParamsType, + extra_params: Optional[Dict] = None, + ) -> ProviderQueryParamsType: + raise NotImplementedError("Method `transform_query` not implemented.") + + @staticmethod + def extract_data(query: ProviderQueryParamsType, api_key: str) -> ReturnType: + raise NotImplementedError("Method `extract_data` not implemented.") + + @staticmethod + def transform_data(data: ReturnType) -> UDataType: + raise NotImplementedError("Method `transform_query` not implemented.") + + @classmethod + def fetch_data( + cls, query: QueryParamsType, extra_params: Optional[Dict], api_key: str + ) -> UDataType: + provider_query = cls.transform_query(query=query, extra_params=extra_params) + provider_data = cls.extract_data(query=provider_query, api_key=api_key) + data = cls.transform_data(data=provider_data) + return data + + @classmethod + def fetch_provider_data( + cls, query: QueryParamsType, extra_params: Optional[Dict], api_key: str + ) -> ReturnType: + provider_query = cls.transform_query(query=query, extra_params=extra_params) + provider_data = cls.extract_data(query=provider_query, api_key=api_key) + return provider_data + + @classmethod + def standardized(cls, query: ProviderQueryParamsType, api_key: str) -> UDataType: + provider_data = cls.extract_data(query=query, api_key=api_key) + data = cls.transform_data(data=provider_data) + return data + + @classmethod + def simple(cls, query: ProviderQueryParamsType, api_key: str) -> ReturnType: + provider_data = cls.extract_data(query=query, api_key=api_key) + return provider_data + + @classmethod + def get_query_type(cls): + return cls.transform_query.__annotations__["query"] + + @classmethod + def get_data_type(cls): + return cls.transform_data.__annotations__["return"] + + @classmethod + def get_provider_query_type(cls): + return cls.extract_data.__annotations__["query"] + + @classmethod + def get_provider_data_type(cls): + return cls.extract_data.__annotations__["return"] diff --git a/openbb_providers/openbb_provider/provider/abstract/provider.py b/openbb_providers/openbb_provider/provider/abstract/provider.py new file mode 100644 index 00000000000..dbfdaddb379 --- /dev/null +++ b/openbb_providers/openbb_provider/provider/abstract/provider.py @@ -0,0 +1,67 @@ +"""Abstract class for providers.""" + +# IMPORT STANDARD +from typing import Any, Dict, List, Literal + +import pkg_resources + +# IMPORT THIRD-PARTY + +# IMPORT INTERNAL + + +def build_provider_name(): + """Builds the provider name Literal from the entry points.""" + extension_names = [] + + for entry_point in pkg_resources.iter_entry_points("openbb_provider_extension"): + extension_names.append(entry_point.name) + + literal_values = () + for extension_name in extension_names: + literal_values += (extension_name,) + + provider_name = Literal[literal_values] # type: ignore + + return provider_name + + +ProviderName = build_provider_name() + + +class ProviderNameType(type): + __args__ = ProviderName.__args__ + __str__ = ProviderName.__str__ + + def __init__(cls, value): + if value not in ProviderName.__args__: + raise ValueError(f"{value} is not a valid provider name.") + cls.value = value + + def __getstate__(cls): + return cls.value + + def __setstate__(cls, state): + cls.value = state + + def __repr__(cls): + return f"ProviderName({cls.value})" + + +class Provider: + QUERIES = ["get_query_type", "get_provider_query_type"] + + def __init__( + self, + name: ProviderNameType, + description: str, + fetcher_list: List[Any], + ) -> None: + self.name = name + self.description = description + the_dict: Dict[str, Any] = {} + for query in self.QUERIES: + the_dict[query] = {getattr(x, query)().__name__: x for x in fetcher_list} + self.fetcher_dict = the_dict + # This is used only for documentation generation + self.fetcher_list = fetcher_list diff --git a/openbb_providers/openbb_provider/provider/provider_helpers.py b/openbb_providers/openbb_provider/provider/provider_helpers.py new file mode 100644 index 00000000000..bb2af6fca97 --- /dev/null +++ b/openbb_providers/openbb_provider/provider/provider_helpers.py @@ -0,0 +1,153 @@ +"""Provider helpers.""" + +# IMPORT STANDARD +import json +import re +from io import StringIO +from typing import Callable, Dict, List, Optional, Type, Union + +# IMPORT THIRD-PARTY +# IMPORT INTERNAL +from openbb_provider.provider.abstract.fetcher import DataType, ProviderDataType + + +def get_querystring(items: dict, exclude: List[str]) -> str: + """Turns a dictionary into a querystring, excluding the keys in the exclude list. + + Parameters: + ----------- + items: dict + The dictionary to be turned into a querystring. + + exclude: List[str] + The keys to be excluded from the querystring. + + Returns: + -------- + str + The querystring. + + """ + for item in exclude: + items.pop(item) + params = {k: v for k, v in items.items() if v is not None} + return "&".join([f"{k}={v}" for k, v in params.items()]) + + +def extract_data(schema, data: list): + new_data: Dict[str, list] = {x: [] for x in schema.__fields__} + for item in data: + for key, value in item.items(): + if key in new_data: + new_data[key].append(value) + return schema(**new_data) + + +def process( + row: ProviderDataType, key: str, processors: Optional[Dict[str, Callable]] = None +): + if not processors: + return getattr(row, key) + if key not in processors: + return getattr(row, key) + return processors[key](getattr(row, key)) + + +def camel_to_snake(name: str) -> str: + "Converts a camelCase string to snake_case." + pattern = re.compile(r"(? bool: + "Checks if a field has an alias." + alias = getattr(field, "alias") + name = getattr(field, "name") + return alias != name + + +def data_transformer( + data: Union[List[ProviderDataType], ProviderDataType], + new_schema: Type[DataType], + processors: Optional[Dict[str, Callable]] = None, +) -> List[DataType]: + """Converts a specific data into the standardised version + + Parameters: + ----------- + data: Union[List[ProviderDataType], ProviderDataType] + A list of pydantic schemas + + new_schema: DataType + The standardised pydantic schema + + processors: Optional[Dict[str, str]] + A dictionary with fields and custom processing functions + + Returns: + -------- + List[DataType] + A list of the newly formatted schemas + + """ + the_data = data[0] if isinstance(data, list) else data + fields = the_data.__dict__.keys() + final_mapping = {x: camel_to_snake(x) for x in fields} + mapping = { + attr: field.alias + for attr, field in the_data.__fields__.items() + if check_alias(field) + } + if mapping: + for key, value in mapping.items(): + final_mapping[key] = value + if not isinstance(data, list): + return convert_schema(data, new_schema, final_mapping, processors) + new_data = [] + for row in data: + schema = convert_schema(row, new_schema, final_mapping, processors) + new_data.append(schema) + return new_data + + +class BasicResponse: + def __init__(self, response: StringIO): + # Find a way to get the status code + self.status_code = 200 + response.seek(0) + self.text = response.read() + + def json(self) -> dict: + return json.loads(self.text) + + +def request(url: str) -> BasicResponse: + """ + Request function for PyScript. Pass in Method and make sure to await! + Parameters: + ----------- + url: str + URL to make request to + + Return: + ------- + response: BasicRequest + BasicRequest object with status_code and text attributes + """ + # pylint: disable=import-outside-toplevel + from pyodide.http import open_url + + response = open_url(url) + return BasicResponse(response) diff --git a/openbb_providers/openbb_provider/provider/provider_map.py b/openbb_providers/openbb_provider/provider/provider_map.py new file mode 100644 index 00000000000..d52997b9238 --- /dev/null +++ b/openbb_providers/openbb_provider/provider/provider_map.py @@ -0,0 +1,319 @@ +"""Create a detailed Map of the Provider Registry.""" +from typing import Dict, Tuple + +from openbb_provider.provider.provider_registry import provider_registry + + +def _get_data_type(fetcher) -> str: + """Get the data type of the fetcher.""" + data_type = ( + str(fetcher.get_data_type()) # pylint: disable=use-maxsplit-arg + .split(".")[-1] + .replace("]", "") + .replace("'>", "") + ) + return data_type + + +def _get_query_type(fetcher) -> str: + """Get the query type of the fetcher.""" + query_type = ( + str(fetcher.get_query_type()) # pylint: disable=use-maxsplit-arg + .split(".")[-1] + .replace("'>", "") + ) + return query_type + + +def _get_data_fields(fetcher) -> Tuple[Dict, Dict]: + """Get the standard and provider-specific data fields of the fetcher. + + Parameters + ---------- + fetcher : Fetcher + The fetcher to get the data fields from. + + Returns + ------- + Tuple[Dict, Dict] + A tuple of the provider-specific and standardized data fields. + """ + try: + provider_fields = (fetcher.get_provider_data_type()).__fields__ + except AttributeError: + provider_fields = (fetcher.get_provider_data_type().__args__[0]).__fields__ + try: + standard_fields = (fetcher.get_data_type()).__fields__ + except AttributeError: + standard_fields = (fetcher.get_data_type().__args__[0]).__fields__ + + return provider_fields, standard_fields + + +def _extract_fields( + provider_fields: Dict, + standard_fields: Dict, +) -> Tuple[Dict, Dict]: + """Extract the fields and their details from pydantic models. + + Parameters + ---------- + provider_fields : Dict + The fields of the provider pydantic model. + standard_fields : Dict + The fields of the standardized pydantic model. + + Returns + ------- + Tuple[Dict, Dict] + A tuple of the provider fields and standardized fields. + """ + provider_dict: Dict = {} + standard_dict: Dict = {} + + for field in provider_fields: + provider_dict[provider_fields[field].alias] = { + "type": provider_fields[field].annotation, + "default": provider_fields[field].default + if provider_fields[field].default + else None, + "required": provider_fields[field].required, + } + + for field in standard_fields: + standard_dict[standard_fields[field].alias] = { + "type": standard_fields[field].annotation, + "default": standard_fields[field].default + if standard_fields[field].default + else None, + "required": standard_fields[field].required, + } + + return provider_dict, standard_dict + + +def build_query_params_mapping() -> Dict: + """Build a mapping of the provider registry QueryParams. + + Returns + ------- + Dict : + The mapping is a nested dictionary with the following structure: + { + "QueryParamsType": { + "provider_name": { + "param_name": { + "type": "param_type", + "default": "param_default", + "required": "param_required" + } + } + "openbb": { + "param_name": { + "type": "param_type", + "default": "param_default", + "required": "param_required" + } + } + } + } + """ + provider_registry_mapping = provider_registry.provider_mapping + mapping = {} + for provider in provider_registry_mapping.values(): + for fetcher in provider.fetcher_list: + provider_name = provider.name + + query_type = _get_query_type(fetcher) + + provider_param_fields = (fetcher.get_provider_query_type()).__fields__ + standard_param_fields = (fetcher.get_query_type()).__fields__ + + provider_specific_query_params, standardized_query_params = _extract_fields( + provider_param_fields, + standard_param_fields, + ) + + if query_type not in mapping: + mapping[query_type] = {provider_name: provider_specific_query_params} + mapping[query_type]["openbb"] = standardized_query_params # type: ignore + else: + mapping[query_type][provider_name] = provider_specific_query_params + + return mapping + + +def build_data_mapping() -> Dict: + """Build a mapping of the provider registry Data. + + Returns + ------- + Dict : + The mapping is a nested dictionary with the following structure: + { + "DataType": { + "provider_name": { + "data_field_name": { + "type": "param_type", + "default": "param_default", + "required": "param_required" + } + } + "openbb": { + "data_field_name": { + "type": "param_type", + "default": "param_default", + "required": "param_required" + } + } + } + } + """ + provider_registry_mapping = provider_registry.provider_mapping + mapping = {} + for provider in provider_registry_mapping.values(): + for fetcher in provider.fetcher_list: + provider_name = provider.name + + data_type = _get_data_type(fetcher) + + provider_data_fields, standard_data_fields = _get_data_fields(fetcher) + + provider_specific_data_fields, standardized_data_fields = _extract_fields( + provider_data_fields, + standard_data_fields, + ) + + if data_type not in mapping: + mapping[data_type] = {provider_name: provider_specific_data_fields} + mapping[data_type]["openbb"] = standardized_data_fields # type: ignore + else: + mapping[data_type][provider_name] = provider_specific_data_fields + + return mapping + + +def _get_data_docstring(fetcher) -> Tuple[str, str]: + """Get the data docstring of the fetcher for the provider and standardized data. + + Parameters + ---------- + fetcher : Fetcher + The fetcher to get the data docstring from. + + Returns + ------- + Tuple[str, str] + A tuple of the provider and standardized data docstrings. + """ + provider_data_docstring = (fetcher.get_provider_data_type()).__doc__ + if not provider_data_docstring: + try: + provider_data_docstring = ( + fetcher.get_provider_data_type().__args__[0] + ).__doc__ + except AttributeError: + provider_data_docstring = "" + + standard_data_docstring = (fetcher.get_data_type()).__doc__ + if not standard_data_docstring: + try: + standard_data_docstring = (fetcher.get_data_type().__args__[0]).__doc__ + except AttributeError: + standard_data_docstring = "" + + return provider_data_docstring, standard_data_docstring + + +def build_docstring_mapping() -> Dict: + """Build a mapping of the provider registry docstrings. + + Returns + ------- + Dict : + The mapping is a nested dictionary with the following structure: + { + "DataType": { + "provider_name": "docstring", + "openbb": "docstring" + }, + "QueryParamsType": { + "provider_name": "docstring", + "openbb": "docstring" + } + } + """ + mapping = {} + provider_registry_mapping = provider_registry.provider_mapping + for provider in provider_registry_mapping.values(): + for fetcher in provider.fetcher_list: + provider_name = provider.name + + data_type = _get_data_type(fetcher) + query_type = _get_query_type(fetcher) + + provider_query_docstring = (fetcher.get_provider_query_type()).__doc__ + standard_query_docstring = (fetcher.get_query_type()).__doc__ + + provider_data_docstring, standard_data_docstring = _get_data_docstring( + fetcher + ) + + if data_type not in mapping: + mapping[data_type] = { + provider_name: provider_data_docstring, + "openbb": standard_data_docstring, + } + else: + mapping[data_type][provider_name] = provider_data_docstring + + if query_type not in mapping: + mapping[query_type] = { + provider_name: provider_query_docstring, + "openbb": standard_query_docstring, + } + else: + mapping[query_type][provider_name] = provider_query_docstring + + return mapping + + +def build_provider_mapping() -> Dict: + """Build a mapping of the provider registry. + + Returns + ------- + Dict : + The mapping is a nested dictionary of data and query mappings. + """ + mapping: Dict = {} + data_mapping = build_data_mapping() + query_mapping = build_query_params_mapping() + docstring_mapping = build_docstring_mapping() + + for query_type, providers in query_mapping.items(): + query_type_name = query_type.replace("QueryParams", "") + mapping[query_type_name] = {} + for provider, fields in providers.items(): + mapping[query_type_name][provider] = { + "QueryParams": {}, + "Data": {}, + } + mapping[query_type_name][provider]["QueryParams"] = { + "fields": fields, + "docstring": docstring_mapping[query_type][provider], + } + + for data_type, providers in data_mapping.items(): + data_type_name = data_type.replace("Data", "") + + for provider, fields in providers.items(): + mapping[data_type_name][provider]["Data"] = { + "fields": fields, + "docstring": docstring_mapping[data_type][provider], + } + + return mapping + + +provider_mapping = build_provider_mapping() diff --git a/openbb_providers/openbb_provider/provider/provider_registry.py b/openbb_providers/openbb_provider/provider/provider_registry.py new file mode 100644 index 00000000000..2e62bf39f6d --- /dev/null +++ b/openbb_providers/openbb_provider/provider/provider_registry.py @@ -0,0 +1,256 @@ +"""ProviderRegistry class and Builder class.""" + +# IMPORT STANDARD +from typing import Any, Dict, List, Literal, Optional, Tuple, Union + +import pkg_resources + +# IMPORT INTERNAL +from openbb_provider.model.abstract.data import QueryParams +from openbb_provider.provider.abstract.fetcher import ProviderDataType +from openbb_provider.provider.abstract.provider import Provider, ProviderNameType +from openbb_provider.settings import settings + +# IMPORT THIRD-PARTY + + +orients = Literal["LIST", "RECORDS"] + + +def process( + data: Union[List[ProviderDataType], ProviderDataType], orientation: orients +) -> Any: + """ + Converts the data into the desired orientation. + + Naming is based on the pandas to_dict() method: + https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.from_dict.html + + Parameters + ---------- + data: Union[List[ProviderDataType], ProviderDataType] + The data to be processed. + orientation: orients + The orientation of the data to be returned. + + Returns + ------- + Any + The processed data. + """ + if not isinstance(data, list): + # TODO: how do we want to handle this? + return data + if orientation == "RECORDS": + return data + if orientation == "LIST": + layout = data[0].dict() + new_dict: Dict[str, list] = {x: [] for x in layout} + for item in data: + for key, value in item.dict().items(): + new_dict[key].append(value) + return [new_dict] + raise ValueError(f"Orientation {orientation} is not supported.") + + +class ProviderRegistry: + """A Provider Registry is the "brain" of the openbb_provider library + with which users and the SDK v4 interact for retrieving data. + """ + + def __init__(self) -> None: + self.provider_mapping: Dict[ProviderNameType, Provider] = {} + self.api_keys: Dict[ProviderNameType, str] = {} + + def add(self, provider: Provider) -> None: + if provider.name not in self.provider_mapping: + self.provider_mapping[provider.name] = provider + else: + raise ValueError( + f"Provider {provider.name} is already in the ProviderRegistry." + ) + + def __fetch_factory__( # noqa: PLR0913 + self, + provider_name: ProviderNameType, + query: QueryParams, + extra_params: Optional[Dict], + query_name: str, + data_name: str, # type: ignore + data_orientation: orients, + ): + if not self.provider_mapping: + raise ValueError( + "Provider Mapping is empty. Please confirm plugins are loaded." + ) + try: + provider = self.provider_mapping[provider_name] + except KeyError as e: + raise ValueError( + f"{provider_name} is not part of the ProviderRegistry. " + f"Available providers are: {list(self.provider_mapping.keys())}" + ) from e + api_key = self.api_keys[provider_name] + + fetch_dict = provider.fetcher_dict[query_name] + fetcher = fetch_dict.get(query.__name__) # type: ignore + if fetcher is None: + raise ValueError( + f"This query is not supported by the {provider_name} provider. " + f"Please try another provider: {ProviderNameType.__args__}" + ) + + try: + if data_name == "standardized": + result = getattr(fetcher, data_name)(query, api_key) + else: + result = getattr(fetcher, data_name)(query, extra_params, api_key) + return process(result, data_orientation) + except Exception as e: + raise RuntimeError( + f"The {provider_name} provider failed to fetch the data: {e}" + ) from e + + def fetch( + self, + provider_name: ProviderNameType, + query: QueryParams, + extra_params: Optional[Dict] = None, + data_orientation: orients = "RECORDS", + ): + return self.__fetch_factory__( + provider_name, + query, + extra_params, + "get_query_type", + "fetch_data", + data_orientation, + ) + + def fetch_provider_data( + self, + provider_name: ProviderNameType, + query: QueryParams, + extra_params: Optional[Dict] = None, + data_orientation: orients = "RECORDS", + ): + return self.__fetch_factory__( + provider_name, + query, + extra_params, + "get_query_type", + "fetch_provider_data", + data_orientation, + ) + + def standardized( + self, + provider_name: ProviderNameType, + query: QueryParams, + extra_params: Optional[Dict] = None, + data_orientation: orients = "RECORDS", + ): + return self.__fetch_factory__( + provider_name, + query, + extra_params, + "get_provider_query_type", + "standardized", + data_orientation, + ) + + def simple( + self, + provider_name: ProviderNameType, + query: QueryParams, + extra_params: Optional[Dict] = None, + data_orientation: orients = "RECORDS", + ): + return self.__fetch_factory__( + provider_name, + query, + extra_params, + "get_provider_query_type", + "simple", + data_orientation, + ) + + +class Builder: + """A Builder class builds out the provider table by adding providers and their + API keys to it. It can build multiple different types of Provider Registries + with their own state. + """ + + def __init__(self) -> None: + self.provider_registry = ProviderRegistry() + self.api_keys: Dict[ProviderNameType, str] = {} + + def add_providers(self, providers: List[Provider]): + for provider in providers: + self.provider_registry.add(provider) + return f"{len(providers)} providers have been added." + + def add_api_key(self, provider_name: ProviderNameType, api_key: str) -> str: + if provider_name not in self.api_keys: + self.api_keys[provider_name] = api_key + else: + raise ValueError( + f"API key for {provider_name} is already in the ProviderRegistry." + ) + + self.provider_registry.api_keys[provider_name] = api_key + return f"API key for {provider_name} has been added." + + def add_keys(self, api_keys: Dict[ProviderNameType, str]) -> str: + for provider_name, api_key in api_keys.items(): + self.add_api_key(provider_name, api_key) + + return f"{len(api_keys)} API keys have been added." + + def build(self) -> ProviderRegistry: + return self.provider_registry + + +def load_extensions( + entry_point_group: str = "openbb_provider_extension", +) -> Tuple[list, dict]: + """Load extensions from entry points and their API keys from settings. + + Parameters + ---------- + entry_point_group : str + The entry point group to load extensions from. + + Returns + ------- + Tuple[list, dict] + A tuple of the extensions and their API keys. + """ + extensions = [] + extension_api_keys = {} + + entry_points = pkg_resources.iter_entry_points(entry_point_group) + for entry_point in entry_points: + extensions.append(entry_point.load()) + + for extension in extensions: + extension_name = extension.name + try: + if extension_name in ProviderNameType.__args__: + extension_api_keys[extension_name] = getattr( + settings, f"{extension_name.upper()}_API_KEY" + ) + except AttributeError as exc: + raise ValueError(f"API key for {extension_name} is not set.") from exc + + return extensions, extension_api_keys + + +entry_point_extensions, entry_point_extension_api_keys = load_extensions( + "openbb_provider_extension" +) +builder = Builder() +builder.add_keys(entry_point_extension_api_keys) +builder.add_providers(entry_point_extensions) +provider_registry = builder.build() diff --git a/openbb_providers/openbb_provider/provider/provider_types.py b/openbb_providers/openbb_provider/provider/provider_types.py new file mode 100644 index 00000000000..513789552f9 --- /dev/null +++ b/openbb_providers/openbb_provider/provider/provider_types.py @@ -0,0 +1,7 @@ +"""Provider types helper module.""" + +from typing import List, Optional + +OptListInt = Optional[List[Optional[int]]] +OptListStr = Optional[List[Optional[str]]] +OptListFloat = Optional[List[Optional[float]]] diff --git a/openbb_providers/openbb_provider/settings.py b/openbb_providers/openbb_provider/settings.py new file mode 100644 index 00000000000..a7230ff82ba --- /dev/null +++ b/openbb_providers/openbb_provider/settings.py @@ -0,0 +1,32 @@ +"""Settings for the openbb_provider package.""" + +# IMPORT STANDARD +import os +from typing import Optional + +# IMPORT THIRD-PARTY +from pydantic import BaseSettings +from pydantic.error_wrappers import ValidationError + +# IMPORT INTERNAL + + +def create_path(*path: str) -> str: + base_path = os.path.dirname(os.path.abspath(__file__)) + in_between = os.path.dirname(base_path) + default_path = os.path.join(in_between, *path) + return default_path + + +class Settings(BaseSettings): + FMP_API_KEY: Optional[str] = None + POLYGON_API_KEY: Optional[str] = None + BENZINGA_API_KEY: Optional[str] = None + DEBUG_MODE: bool = False + + +try: + settings = Settings() # type: ignore[call-arg] +except ValidationError: + env_path = create_path(".env") + settings = Settings(env_path) # type: ignore[call-arg,misc,arg-type]