mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-11-05 17:01:58 +00:00
Seprate Intrinio financials data tags into a different command (#5723)
* add `ebit` field in income statement standard model * only add required data tags * remove calculations data * use `name` key instead of `tag` key * set `period` as required in data * remove `period` and `cik` from provider models * add `equity/fundamental/search_financial_attributes` with tests * clean descriptions file * add `equity/fundamental/financial_attributes` with tests * set `value` as `Optional`
This commit is contained in:
@@ -964,7 +964,7 @@ def test_equity_news(params, headers):
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"params",
|
||||
[({"symbol": "AAPL", "limit": 100})],
|
||||
[({"symbol": "AAPL", "limit": 100, "provider": "fmp"})],
|
||||
)
|
||||
@pytest.mark.integration
|
||||
def test_equity_fundamental_multiples(params, headers):
|
||||
@@ -977,6 +977,52 @@ def test_equity_fundamental_multiples(params, headers):
|
||||
assert result.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"params",
|
||||
[
|
||||
({"query": "ebit", "limit": 100, "provider": "intrinio"}),
|
||||
],
|
||||
)
|
||||
@pytest.mark.integration
|
||||
def test_equity_fundamental_search_financial_attributes(params, obb):
|
||||
params = {p: v for p, v in params.items() if v}
|
||||
|
||||
query_str = get_querystring(params, [])
|
||||
url = f"http://0.0.0.0:8000/api/v1/equity/fundamental/search_financial_attributes?{query_str}"
|
||||
result = requests.get(url, headers=headers, timeout=10)
|
||||
assert isinstance(result, requests.Response)
|
||||
assert result.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"params",
|
||||
[
|
||||
(
|
||||
{
|
||||
"provider": "intrinio",
|
||||
"symbol": "AAPL",
|
||||
"tag": "ebit",
|
||||
"period": "annual",
|
||||
"limit": 1000,
|
||||
"type": None,
|
||||
"start_date": "2013-01-01",
|
||||
"end_date": "2023-01-01",
|
||||
"sort": "desc",
|
||||
}
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.integration
|
||||
def test_equity_fundamental_financial_attributes(params, obb):
|
||||
params = {p: v for p, v in params.items() if v}
|
||||
|
||||
query_str = get_querystring(params, [])
|
||||
url = f"http://0.0.0.0:8000/api/v1/equity/fundamental/financial_attributes?{query_str}"
|
||||
result = requests.get(url, headers=headers, timeout=10)
|
||||
assert isinstance(result, requests.Response)
|
||||
assert result.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"params",
|
||||
[
|
||||
|
||||
@@ -894,7 +894,7 @@ def test_equity_news(params, obb):
|
||||
@pytest.mark.parametrize(
|
||||
"params",
|
||||
[
|
||||
({"symbol": "AAPL", "limit": 100}),
|
||||
({"symbol": "AAPL", "limit": 100, "provider": "fmp"}),
|
||||
],
|
||||
)
|
||||
@pytest.mark.integration
|
||||
@@ -905,6 +905,46 @@ def test_equity_fundamental_multiples(params, obb):
|
||||
assert len(result.results) > 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"params",
|
||||
[
|
||||
({"query": "ebit", "limit": 100, "provider": "intrinio"}),
|
||||
],
|
||||
)
|
||||
@pytest.mark.integration
|
||||
def test_equity_fundamental_search_financial_attributes(params, obb):
|
||||
result = obb.equity.fundamental.search_financial_attributes(**params)
|
||||
assert result
|
||||
assert isinstance(result, OBBject)
|
||||
assert len(result.results) > 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"params",
|
||||
[
|
||||
(
|
||||
{
|
||||
"provider": "intrinio",
|
||||
"symbol": "AAPL",
|
||||
"tag": "ebit",
|
||||
"period": "annual",
|
||||
"limit": 1000,
|
||||
"type": None,
|
||||
"start_date": "2013-01-01",
|
||||
"end_date": "2023-01-01",
|
||||
"sort": "desc",
|
||||
}
|
||||
),
|
||||
],
|
||||
)
|
||||
@pytest.mark.integration
|
||||
def test_equity_fundamental_financial_attributes(params, obb):
|
||||
result = obb.equity.fundamental.financial_attributes(**params)
|
||||
assert result
|
||||
assert isinstance(result, OBBject)
|
||||
assert len(result.results) > 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"params",
|
||||
[
|
||||
|
||||
@@ -103,6 +103,28 @@ def employee_count(
|
||||
return OBBject(results=Query(**locals()).execute())
|
||||
|
||||
|
||||
@router.command(model="SearchFinancialAttributes")
|
||||
def search_financial_attributes(
|
||||
cc: CommandContext,
|
||||
provider_choices: ProviderChoices,
|
||||
standard_params: StandardParams,
|
||||
extra_params: ExtraParams,
|
||||
) -> OBBject[BaseModel]:
|
||||
"""Search financial attributes for financial statements."""
|
||||
return OBBject(results=Query(**locals()).execute())
|
||||
|
||||
|
||||
@router.command(model="FinancialAttributes")
|
||||
def financial_attributes(
|
||||
cc: CommandContext,
|
||||
provider_choices: ProviderChoices,
|
||||
standard_params: StandardParams,
|
||||
extra_params: ExtraParams,
|
||||
) -> OBBject[BaseModel]:
|
||||
"""Fetch the value of financial attributes for a selected company and fiscal period."""
|
||||
return OBBject(results=Query(**locals()).execute())
|
||||
|
||||
|
||||
@router.command(model="IncomeStatement")
|
||||
def income(
|
||||
cc: CommandContext,
|
||||
|
||||
@@ -62,9 +62,7 @@ class BalanceSheetData(Data):
|
||||
accepted_date: Optional[datetime] = Field(
|
||||
default=None, description="Accepted date."
|
||||
)
|
||||
period: Optional[str] = Field(
|
||||
default=None, description="Reporting period of the statement."
|
||||
)
|
||||
period: str = Field(default=None, description="Reporting period of the statement.")
|
||||
|
||||
cash_and_cash_equivalents: Optional[StrictFloat] = Field(
|
||||
default=None, description="Cash and cash equivalents"
|
||||
|
||||
@@ -51,9 +51,7 @@ class CashFlowStatementData(Data):
|
||||
default=None, description=DATA_DESCRIPTIONS.get("symbol", "")
|
||||
)
|
||||
date: dateType = Field(description=DATA_DESCRIPTIONS.get("date" ""))
|
||||
period: Optional[str] = Field(
|
||||
default=None, description="Reporting period of the statement."
|
||||
)
|
||||
period: str = Field(default=None, description="Reporting period of the statement.")
|
||||
cik: Optional[str] = Field(
|
||||
default=None, description="Central Index Key (CIK) of the company."
|
||||
)
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
"""Financial Attributes standard model."""
|
||||
|
||||
from datetime import date as dateType
|
||||
from typing import Literal, Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from openbb_provider.abstract.data import Data
|
||||
from openbb_provider.abstract.query_params import QueryParams
|
||||
from openbb_provider.utils.descriptions import DATA_DESCRIPTIONS, QUERY_DESCRIPTIONS
|
||||
|
||||
|
||||
class FinancialAttributesQueryParams(QueryParams):
|
||||
"""Financial Attributes Query."""
|
||||
|
||||
symbol: str = Field(description=QUERY_DESCRIPTIONS.get("symbol"))
|
||||
tag: str = Field(description=QUERY_DESCRIPTIONS.get("tag"))
|
||||
period: Optional[Literal["annual", "quarter"]] = Field(
|
||||
default="annual", description=QUERY_DESCRIPTIONS.get("period")
|
||||
)
|
||||
limit: Optional[int] = Field(
|
||||
default=1000, description=QUERY_DESCRIPTIONS.get("limit")
|
||||
)
|
||||
type: Optional[str] = Field(
|
||||
default=None, description="Filter by type, when applicable."
|
||||
)
|
||||
start_date: Optional[dateType] = Field(
|
||||
default=None, description=QUERY_DESCRIPTIONS.get("start_date")
|
||||
)
|
||||
end_date: Optional[dateType] = Field(
|
||||
default=None, description=QUERY_DESCRIPTIONS.get("end_date")
|
||||
)
|
||||
sort: Optional[Literal["asc", "desc"]] = Field(
|
||||
default="desc", description="Sort order."
|
||||
)
|
||||
|
||||
|
||||
class FinancialAttributesData(Data):
|
||||
"""Financial Attributes Data."""
|
||||
|
||||
date: dateType = Field(description=DATA_DESCRIPTIONS.get("date"))
|
||||
value: Optional[float] = Field(default=None, description="The value of the data.")
|
||||
@@ -54,9 +54,7 @@ class IncomeStatementData(Data):
|
||||
description=DATA_DESCRIPTIONS.get("date", "")
|
||||
+ " In this case, the date of the income statement."
|
||||
)
|
||||
period: Optional[str] = Field(
|
||||
default=None, description="Period of the income statement."
|
||||
)
|
||||
period: str = Field(default=None, description="Period of the income statement.")
|
||||
cik: Optional[str] = Field(default=None, description="Central Index Key.")
|
||||
|
||||
revenue: Optional[StrictFloat] = Field(default=None, description="Revenue.")
|
||||
@@ -95,6 +93,10 @@ class IncomeStatementData(Data):
|
||||
depreciation_and_amortization: Optional[StrictFloat] = Field(
|
||||
default=None, description="Depreciation and amortization."
|
||||
)
|
||||
ebit: Optional[StrictFloat] = Field(
|
||||
default=None,
|
||||
description="Earnings before interest, and taxes.",
|
||||
)
|
||||
ebitda: Optional[StrictFloat] = Field(
|
||||
default=None,
|
||||
description="Earnings before interest, taxes, depreciation and amortization.",
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
"""Search Financial Attributes standard model."""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from openbb_provider.abstract.data import Data
|
||||
from openbb_provider.abstract.query_params import QueryParams
|
||||
from openbb_provider.utils.descriptions import QUERY_DESCRIPTIONS
|
||||
|
||||
|
||||
class SearchFinancialAttributesQueryParams(QueryParams):
|
||||
"""Search Financial Attributes Query."""
|
||||
|
||||
query: str = Field(description="Query to search for.")
|
||||
limit: Optional[int] = Field(
|
||||
default=1000, description=QUERY_DESCRIPTIONS.get("limit")
|
||||
)
|
||||
|
||||
|
||||
class SearchFinancialAttributesData(Data):
|
||||
"""Search Financial Attributes Data."""
|
||||
|
||||
id: str = Field(description="ID of the financial attribute.")
|
||||
name: str = Field(description="Name of the financial attribute.")
|
||||
tag: str = Field(description="Tag of the financial attribute.")
|
||||
statement_code: str = Field(description="Code of the financial statement.")
|
||||
statement_type: Optional[str] = Field(
|
||||
default=None, description="Type of the financial statement."
|
||||
)
|
||||
parent_name: Optional[str] = Field(
|
||||
default=None, description="Parent's name of the financial attribute."
|
||||
)
|
||||
sequence: Optional[int] = Field(
|
||||
default=None, description="Sequence of the financial statement."
|
||||
)
|
||||
factor: Optional[str] = Field(
|
||||
default=None, description="Unit of the financial attribute."
|
||||
)
|
||||
transaction: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Transaction type (credit/debit) of the financial attribute.",
|
||||
)
|
||||
type: Optional[str] = Field(
|
||||
default=None, description="Type of the financial attribute."
|
||||
)
|
||||
unit: Optional[str] = Field(
|
||||
default=None, description="Unit of the financial attribute."
|
||||
)
|
||||
@@ -5,8 +5,6 @@ QUERY_DESCRIPTIONS = {
|
||||
"start_date": "Start date of the data, in YYYY-MM-DD format.",
|
||||
"end_date": "End date of the data, in YYYY-MM-DD format.",
|
||||
"interval": "Time interval of the data to return.",
|
||||
# "weekly": "Whether to return weekly data.",
|
||||
# "monthly": "Whether to return monthly data.",
|
||||
"period": "Time period of the data to return.",
|
||||
"date": "A specific date to get data for.",
|
||||
"limit": "The number of data entries to return.",
|
||||
|
||||
@@ -7,11 +7,17 @@ from openbb_intrinio.models.company_news import IntrinioCompanyNewsFetcher
|
||||
from openbb_intrinio.models.currency_pairs import IntrinioCurrencyPairsFetcher
|
||||
from openbb_intrinio.models.equity_historical import IntrinioEquityHistoricalFetcher
|
||||
from openbb_intrinio.models.equity_quote import IntrinioEquityQuoteFetcher
|
||||
from openbb_intrinio.models.financial_attributes import (
|
||||
IntrinioFinancialAttributesFetcher,
|
||||
)
|
||||
from openbb_intrinio.models.fred_indices import IntrinioFredIndicesFetcher
|
||||
from openbb_intrinio.models.global_news import IntrinioGlobalNewsFetcher
|
||||
from openbb_intrinio.models.income_statement import IntrinioIncomeStatementFetcher
|
||||
from openbb_intrinio.models.options_chains import IntrinioOptionsChainsFetcher
|
||||
from openbb_intrinio.models.options_unusual import IntrinioOptionsUnusualFetcher
|
||||
from openbb_intrinio.models.search_financial_attributes import (
|
||||
IntrinioSearchFinancialAttributesFetcher,
|
||||
)
|
||||
from openbb_provider.abstract.provider import Provider
|
||||
|
||||
intrinio_provider = Provider(
|
||||
@@ -28,10 +34,12 @@ intrinio_provider = Provider(
|
||||
"CurrencyPairs": IntrinioCurrencyPairsFetcher,
|
||||
"EquityHistorical": IntrinioEquityHistoricalFetcher,
|
||||
"EquityQuote": IntrinioEquityQuoteFetcher,
|
||||
"FinancialAttributes": IntrinioFinancialAttributesFetcher,
|
||||
"FredIndices": IntrinioFredIndicesFetcher,
|
||||
"GlobalNews": IntrinioGlobalNewsFetcher,
|
||||
"IncomeStatement": IntrinioIncomeStatementFetcher,
|
||||
"OptionsChains": IntrinioOptionsChainsFetcher,
|
||||
"OptionsUnusual": IntrinioOptionsUnusualFetcher,
|
||||
"SearchFinancialAttributes": IntrinioSearchFinancialAttributesFetcher,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -85,24 +85,16 @@ class IntrinioBalanceSheetFetcher(
|
||||
|
||||
def get_financial_statement_data(period: str, data: List[Dict]) -> None:
|
||||
statement_data: Dict = {}
|
||||
calculations_data: Dict = {}
|
||||
|
||||
intrinio_id = f"{query.symbol}-{statement_code}-{period}"
|
||||
statement_url = f"{base_url}/fundamentals/{intrinio_id}/standardized_financials?api_key={api_key}"
|
||||
statement_data = get_data_one(statement_url, **kwargs)
|
||||
|
||||
intrinio_id = f"{query.symbol}-calculations-{period}"
|
||||
calculations_url = f"{base_url}/fundamentals/{intrinio_id}/standardized_financials?api_key={api_key}"
|
||||
calculations_data = get_data_one(calculations_url, **kwargs)
|
||||
|
||||
data.append(
|
||||
{
|
||||
"date": statement_data["fundamental"]["end_date"],
|
||||
"period": statement_data["fundamental"]["fiscal_period"],
|
||||
"cik": statement_data["fundamental"]["company"]["cik"],
|
||||
"symbol": statement_data["fundamental"]["company"]["ticker"],
|
||||
"financials": statement_data["standardized_financials"]
|
||||
+ calculations_data["standardized_financials"],
|
||||
"financials": statement_data["standardized_financials"],
|
||||
}
|
||||
)
|
||||
|
||||
@@ -122,13 +114,11 @@ class IntrinioBalanceSheetFetcher(
|
||||
sub_dict: Dict[str, Any] = {}
|
||||
|
||||
for sub_item in item["financials"]:
|
||||
field_name = alias_generators.to_snake(sub_item["data_tag"]["tag"])
|
||||
field_name = alias_generators.to_snake(sub_item["data_tag"]["name"])
|
||||
sub_dict[field_name] = float(sub_item["value"])
|
||||
|
||||
sub_dict["date"] = item["date"]
|
||||
sub_dict["period"] = item["period"]
|
||||
sub_dict["cik"] = item["cik"]
|
||||
sub_dict["symbol"] = item["symbol"]
|
||||
|
||||
# Intrinio does not return Q4 data but FY data instead
|
||||
if query.period == "quarter" and item["period"] == "FY":
|
||||
|
||||
@@ -101,24 +101,16 @@ class IntrinioCashFlowStatementFetcher(
|
||||
|
||||
def get_financial_statement_data(period: str, data: List[Dict]) -> None:
|
||||
statement_data: Dict = {}
|
||||
calculations_data: Dict = {}
|
||||
|
||||
intrinio_id = f"{query.symbol}-{statement_code}-{period}"
|
||||
statement_url = f"{base_url}/fundamentals/{intrinio_id}/standardized_financials?api_key={api_key}"
|
||||
statement_data = get_data_one(statement_url, **kwargs)
|
||||
|
||||
intrinio_id = f"{query.symbol}-calculations-{period}"
|
||||
calculations_url = f"{base_url}/fundamentals/{intrinio_id}/standardized_financials?api_key={api_key}"
|
||||
calculations_data = get_data_one(calculations_url, **kwargs)
|
||||
|
||||
data.append(
|
||||
{
|
||||
"date": statement_data["fundamental"]["end_date"],
|
||||
"period": statement_data["fundamental"]["fiscal_period"],
|
||||
"cik": statement_data["fundamental"]["company"]["cik"],
|
||||
"symbol": statement_data["fundamental"]["company"]["ticker"],
|
||||
"financials": statement_data["standardized_financials"]
|
||||
+ calculations_data["standardized_financials"],
|
||||
"financials": statement_data["standardized_financials"],
|
||||
}
|
||||
)
|
||||
|
||||
@@ -143,8 +135,6 @@ class IntrinioCashFlowStatementFetcher(
|
||||
|
||||
sub_dict["date"] = item["date"]
|
||||
sub_dict["period"] = item["period"]
|
||||
sub_dict["cik"] = item["cik"]
|
||||
sub_dict["symbol"] = item["symbol"]
|
||||
|
||||
# Intrinio does not return Q4 data but FY data instead
|
||||
if query.period == "quarter" and item["period"] == "FY":
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
"""Intrinio Financial Attributes model."""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from openbb_intrinio.utils.helpers import get_data_many
|
||||
from openbb_provider.abstract.fetcher import Fetcher
|
||||
from openbb_provider.standard_models.financial_attributes import (
|
||||
FinancialAttributesData,
|
||||
FinancialAttributesQueryParams,
|
||||
)
|
||||
from openbb_provider.utils.helpers import get_querystring
|
||||
|
||||
|
||||
class IntrinioFinancialAttributesQueryParams(FinancialAttributesQueryParams):
|
||||
"""Intrinio Financial Attributes Query."""
|
||||
|
||||
__alias_dict__ = {"sort": "sort_order", "limit": "page_size"}
|
||||
|
||||
|
||||
class IntrinioFinancialAttributesData(FinancialAttributesData):
|
||||
"""Intrinio Financial Attributes Data."""
|
||||
|
||||
|
||||
class IntrinioFinancialAttributesFetcher(
|
||||
Fetcher[
|
||||
IntrinioFinancialAttributesQueryParams,
|
||||
List[IntrinioFinancialAttributesData],
|
||||
]
|
||||
):
|
||||
"""Transform the query, extract and transform the data from the Intrinio endpoints."""
|
||||
|
||||
@staticmethod
|
||||
def transform_query(
|
||||
params: Dict[str, Any]
|
||||
) -> IntrinioFinancialAttributesQueryParams:
|
||||
"""Transform the query params."""
|
||||
transformed_params = params
|
||||
|
||||
now = datetime.now().date()
|
||||
if params.get("start_date") is None:
|
||||
transformed_params["start_date"] = now - relativedelta(years=5)
|
||||
|
||||
if params.get("end_date") is None:
|
||||
transformed_params["end_date"] = now
|
||||
|
||||
return IntrinioFinancialAttributesQueryParams(**transformed_params)
|
||||
|
||||
@staticmethod
|
||||
def extract_data(
|
||||
query: IntrinioFinancialAttributesQueryParams,
|
||||
credentials: Optional[Dict[str, str]],
|
||||
**kwargs: Any,
|
||||
) -> List[Dict]:
|
||||
"""Return the raw data from the Intrinio endpoint."""
|
||||
api_key = credentials.get("intrinio_api_key") if credentials else ""
|
||||
frequency = "yearly" if query.period == "annual" else "quarterly"
|
||||
data: List[Dict] = []
|
||||
|
||||
base_url = "https://api-v2.intrinio.com"
|
||||
query_str = get_querystring(query.model_dump(by_alias=True), ["frequency"])
|
||||
query_str = f"{query_str}&frequency={frequency}"
|
||||
|
||||
url = f"{base_url}/historical_data/{query.symbol}/{query.tag}?{query_str}&api_key={api_key}"
|
||||
# data = get_data_one(url).get("historical_data", [])
|
||||
data = get_data_many(url, "historical_data")
|
||||
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def transform_data(
|
||||
query: IntrinioFinancialAttributesQueryParams,
|
||||
data: List[Dict],
|
||||
**kwargs: Any,
|
||||
) -> List[IntrinioFinancialAttributesData]:
|
||||
"""Return the transformed data."""
|
||||
return [IntrinioFinancialAttributesData.model_validate(item) for item in data]
|
||||
@@ -28,6 +28,9 @@ class IntrinioIncomeStatementData(IncomeStatementData):
|
||||
__alias_dict__ = {
|
||||
"research_and_development_expenses": "ResearchAndDevelopmentExpense",
|
||||
"selling_general_and_administrative_expenses": "SellingGeneralAndAdministrativeExpense",
|
||||
"ebit": "earnings before interest and taxes (ebit)",
|
||||
"ebitda": "earnings before interest, taxes, depreciation and amortization (ebitda)",
|
||||
"ebitda_margin": "ebitda margin",
|
||||
"operating_income": "OperatingIncomeLoss",
|
||||
"income_before_tax": "IncomeLossFromContinuingOperationsBeforeIncomeTaxesExtraordinaryItemsNoncontrollingInterest", # noqa: E501
|
||||
"eps_diluted": "EarningsPerShareDiluted",
|
||||
@@ -59,6 +62,7 @@ class IntrinioIncomeStatementFetcher(
|
||||
api_key = credentials.get("intrinio_api_key") if credentials else ""
|
||||
statement_code = "income_statement"
|
||||
period_type = "FY" if query.period == "annual" else "QTR"
|
||||
data_tags = ["ebit", "ebitda", "ebitdamargin"]
|
||||
|
||||
fundamentals_data: Dict = {}
|
||||
data: List[Dict] = []
|
||||
@@ -81,7 +85,7 @@ class IntrinioIncomeStatementFetcher(
|
||||
|
||||
def get_financial_statement_data(period: str, data: List[Dict]) -> None:
|
||||
statement_data: Dict = {}
|
||||
calculations_data: Dict = {}
|
||||
calculations_data: List = []
|
||||
|
||||
intrinio_id = f"{query.symbol}-{statement_code}-{period}"
|
||||
statement_url = f"{base_url}/fundamentals/{intrinio_id}/standardized_financials?api_key={api_key}"
|
||||
@@ -89,16 +93,21 @@ class IntrinioIncomeStatementFetcher(
|
||||
|
||||
intrinio_id = f"{query.symbol}-calculations-{period}"
|
||||
calculations_url = f"{base_url}/fundamentals/{intrinio_id}/standardized_financials?api_key={api_key}"
|
||||
calculations_data = get_data_one(calculations_url, **kwargs)
|
||||
calculations_data = get_data_one(calculations_url, **kwargs).get(
|
||||
"standardized_financials", []
|
||||
)
|
||||
calculations_data = [
|
||||
item
|
||||
for item in calculations_data
|
||||
if item["data_tag"]["tag"] in data_tags
|
||||
]
|
||||
|
||||
data.append(
|
||||
{
|
||||
"date": statement_data["fundamental"]["end_date"],
|
||||
"period": statement_data["fundamental"]["fiscal_period"],
|
||||
"cik": statement_data["fundamental"]["company"]["cik"],
|
||||
"symbol": statement_data["fundamental"]["company"]["ticker"],
|
||||
"financials": statement_data["standardized_financials"]
|
||||
+ calculations_data["standardized_financials"],
|
||||
+ calculations_data,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -123,8 +132,6 @@ class IntrinioIncomeStatementFetcher(
|
||||
|
||||
sub_dict["date"] = item["date"]
|
||||
sub_dict["period"] = item["period"]
|
||||
sub_dict["cik"] = item["cik"]
|
||||
sub_dict["symbol"] = item["symbol"]
|
||||
|
||||
# Intrinio does not return Q4 data but FY data instead
|
||||
if query.period == "quarter" and item["period"] == "FY":
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
"""Intrinio Search Financial Attributes model."""
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from openbb_intrinio.utils.helpers import get_data_one
|
||||
from openbb_provider.abstract.fetcher import Fetcher
|
||||
from openbb_provider.standard_models.search_financial_attributes import (
|
||||
SearchFinancialAttributesData,
|
||||
SearchFinancialAttributesQueryParams,
|
||||
)
|
||||
from openbb_provider.utils.helpers import get_querystring
|
||||
|
||||
|
||||
class IntrinioSearchFinancialAttributesQueryParams(
|
||||
SearchFinancialAttributesQueryParams
|
||||
):
|
||||
"""Intrinio Search Financial Attributes Query."""
|
||||
|
||||
__alias_dict__ = {"limit": "page_size"}
|
||||
|
||||
|
||||
class IntrinioSearchFinancialAttributesData(SearchFinancialAttributesData):
|
||||
"""Intrinio Search Financial Attributes Data."""
|
||||
|
||||
__alias_dict__ = {
|
||||
"parent_name": "parent",
|
||||
"transaction": "balance",
|
||||
}
|
||||
|
||||
|
||||
class IntrinioSearchFinancialAttributesFetcher(
|
||||
Fetcher[
|
||||
IntrinioSearchFinancialAttributesQueryParams,
|
||||
List[IntrinioSearchFinancialAttributesData],
|
||||
]
|
||||
):
|
||||
"""Transform the query, extract and transform the data from the Intrinio endpoints."""
|
||||
|
||||
@staticmethod
|
||||
def transform_query(
|
||||
params: Dict[str, Any]
|
||||
) -> IntrinioSearchFinancialAttributesQueryParams:
|
||||
"""Transform the query params."""
|
||||
return IntrinioSearchFinancialAttributesQueryParams(**params)
|
||||
|
||||
@staticmethod
|
||||
def extract_data(
|
||||
query: IntrinioSearchFinancialAttributesQueryParams,
|
||||
credentials: Optional[Dict[str, str]],
|
||||
**kwargs: Any,
|
||||
) -> List[Dict]:
|
||||
"""Return the raw data from the Intrinio endpoint."""
|
||||
api_key = credentials.get("intrinio_api_key") if credentials else ""
|
||||
data: List[Dict] = []
|
||||
|
||||
base_url = "https://api-v2.intrinio.com"
|
||||
query_str = get_querystring(query.model_dump(by_alias=True), [])
|
||||
|
||||
url = f"{base_url}/data_tags/search?{query_str}&api_key={api_key}"
|
||||
data = get_data_one(url).get("tags", [])
|
||||
# Intrinio doesn't return the correct number of results when using the limit parameter
|
||||
# Temporary fix until they fix it
|
||||
data = data[: query.limit]
|
||||
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def transform_data(
|
||||
query: IntrinioSearchFinancialAttributesQueryParams,
|
||||
data: List[Dict],
|
||||
**kwargs: Any,
|
||||
) -> List[IntrinioSearchFinancialAttributesData]:
|
||||
"""Return the transformed data."""
|
||||
return [
|
||||
IntrinioSearchFinancialAttributesData.model_validate(item) for item in data
|
||||
]
|
||||
@@ -0,0 +1,36 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- '*/*'
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Connection:
|
||||
- keep-alive
|
||||
method: GET
|
||||
uri: https://api-v2.intrinio.com/historical_data/AAPL/ebit?api_key=MOCK_API_KEY&end_date=2023-01-01&frequency=yearly&page_size=1000&period=annual&provider=intrinio&sort_order=desc&start_date=2013-01-01&symbol=AAPL&tag=ebit
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
H4sIAOjTU2UAA3TQ2wrCMAwG4Hfp9ZQkPaTdq4iMokMHZYpuIoy9u7U3LaPrVWg+/qZZxH14T4/X
|
||||
cPGhu/rJi/a0iFj0ohUERAdwB1KiER8f5niJ6BAkpHOEtSkxJqwLDI6A6xgSNhkbhhhds+iStYXV
|
||||
LOu5aJN12TK5nYGR/1ZCkavAuro1200YlEx1q7d/Y9Ko61Yly9lqqezOvHK7Bx3fyrnnRoz9d+qe
|
||||
/hZ74xzC+gMAAP//AwAceBCU4AEAAA==
|
||||
headers:
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Tue, 14 Nov 2023 20:09:12 GMT
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
Vary:
|
||||
- Origin,Accept-Encoding
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
@@ -0,0 +1,48 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: null
|
||||
headers:
|
||||
Accept:
|
||||
- '*/*'
|
||||
Accept-Encoding:
|
||||
- gzip, deflate, br
|
||||
Connection:
|
||||
- keep-alive
|
||||
method: GET
|
||||
uri: https://api-v2.intrinio.com/data_tags/search?api_key=MOCK_API_KEY&page_size=1000&query=ebit
|
||||
response:
|
||||
body:
|
||||
string: !!binary |
|
||||
H4sIALG6U2UAA9SYa0/bMBSG/0qUT6AxAVVB0G9liSagKxcxSDtNlZOcGrPETm2nlyD++5xek7Qd
|
||||
mdQm7Uf7xI4f+/Vr+7zrEmGh136968TVa3Gp08Qt3rT0I50iH1SdiTglFAvNhi7joF1TCRyE1BB1
|
||||
tSc0BKEdmFfXT4eqiWqvWoBNpCoIiST4QGXHYW7ck4M8J/SQJIyKVFyOgjjeJRRRhyBPBQPEVUSv
|
||||
0dDz1KfQC4E6MCt3kSMZn5Vs5KFEcNqbD5ITR3UVUjWcmh4KV/84SoBWoj59jMoAJdQNhRpdUaQv
|
||||
o1NjkCRVFNrD8YP2nbOBfE3x9FgPz2rLBpsPZAoWAHdUlwhDmu82OjOGVgF8m1VoXrxn66VJ8QLP
|
||||
AFtqkmkxplGf07mqWrKY0UU7sHiCeX3VZrTg63oMyTSagSt1b7R1tM2uWy4yho1eP6vJFXrcNzFW
|
||||
ossf51kv2ShXSSbSxPeDRkKKDUbx1yfgvrZGlJ7cxx2ndHlHi8UsYfepxWw0cEalPxDHhKZU6s+q
|
||||
yl67gLMukcgmHpGjz8VqRKy95C4b5dvsov0nnonrdxcZkzHqq2zGRftmoCau3rS3zlaaibYGlQRc
|
||||
E+Q6X6Gwl/6ppNm6wUURlmCdJyP7/sJa0uea67SL9vHBwPEJui2KsRybca27qrtsMyvOCBft6Smh
|
||||
3kWc4m0jlnvQX1qoBUmlxkmJgBMB2jPyQpjZjnZgPh9nshP9ifPsAHRfDXX8swWwH3qSBF72dYGv
|
||||
2EXxuJuVcW5aO+oFS3kLRTjPPJnDAKiAlJYlI9MwDHdgaR3WBx7L9l9nSjXib0b20bht0M0uai5O
|
||||
5Ui3NEfu9GiSTzxSN4eAgxpjDDHOM9Z9xiWJJhUHE0c7zHjZDqz6pynHXmTWz3MkV4ufiYKzzE3r
|
||||
/M9bVvoNEEL7hgJz+Oku8NSnDgpguI/7/mV0+tjOPsPLgC/BC2yrHVQTiZZ1R5m6scwOs5TA+zv0
|
||||
Msp9oj1GPkvlXYqELukUV6Z/c5Jg/klBmR242j0Hn4S+0OpCgJxDhtN4MA27624t03F1xCuA7MzD
|
||||
edF1yaTqIf73eLrmc3B6tpgB/YuemAB9NpZpt+kRrPS634pb7c9OEO+H8Qx+/AUAAP//AwBIw/g6
|
||||
VhwAAA==
|
||||
headers:
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Encoding:
|
||||
- gzip
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Tue, 14 Nov 2023 18:21:37 GMT
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
Vary:
|
||||
- Origin,Accept-Encoding
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
version: 1
|
||||
@@ -9,11 +9,17 @@ from openbb_intrinio.models.company_news import IntrinioCompanyNewsFetcher
|
||||
from openbb_intrinio.models.currency_pairs import IntrinioCurrencyPairsFetcher
|
||||
from openbb_intrinio.models.equity_historical import IntrinioEquityHistoricalFetcher
|
||||
from openbb_intrinio.models.equity_quote import IntrinioEquityQuoteFetcher
|
||||
from openbb_intrinio.models.financial_attributes import (
|
||||
IntrinioFinancialAttributesFetcher,
|
||||
)
|
||||
from openbb_intrinio.models.fred_indices import IntrinioFredIndicesFetcher
|
||||
from openbb_intrinio.models.global_news import IntrinioGlobalNewsFetcher
|
||||
from openbb_intrinio.models.income_statement import IntrinioIncomeStatementFetcher
|
||||
from openbb_intrinio.models.options_chains import IntrinioOptionsChainsFetcher
|
||||
from openbb_intrinio.models.options_unusual import IntrinioOptionsUnusualFetcher
|
||||
from openbb_intrinio.models.search_financial_attributes import (
|
||||
IntrinioSearchFinancialAttributesFetcher,
|
||||
)
|
||||
|
||||
test_credentials = UserService().default_user_settings.credentials.model_dump(
|
||||
mode="json"
|
||||
@@ -148,3 +154,31 @@ def test_intrinio_calendar_ipo_fetcher(credentials=test_credentials):
|
||||
fetcher = IntrinioCalendarIpoFetcher()
|
||||
result = fetcher.test(params, credentials)
|
||||
assert result is None
|
||||
|
||||
|
||||
@pytest.mark.record_http
|
||||
def test_intrinio_search_financial_attributes(credentials=test_credentials):
|
||||
params = {"query": "ebit"}
|
||||
|
||||
fetcher = IntrinioSearchFinancialAttributesFetcher()
|
||||
result = fetcher.test(params, credentials)
|
||||
assert result is None
|
||||
|
||||
|
||||
@pytest.mark.record_http
|
||||
def test_intrinio_financial_attributes(credentials=test_credentials):
|
||||
params = {
|
||||
"provider": "intrinio",
|
||||
"symbol": "AAPL",
|
||||
"tag": "ebit",
|
||||
"period": "annual",
|
||||
"limit": 1000,
|
||||
"type": None,
|
||||
"start_date": date(2013, 1, 1),
|
||||
"end_date": date(2023, 1, 1),
|
||||
"sort": "desc",
|
||||
}
|
||||
|
||||
fetcher = IntrinioFinancialAttributesFetcher()
|
||||
result = fetcher.test(params, credentials)
|
||||
assert result is None
|
||||
|
||||
Reference in New Issue
Block a user