New Intrinio Endpoints (#5789)

* redefined CalendarDividend standard model

* fix nasdaq calendar dividend model

* removed Filings standard model

* add DiscoveryFilings standard model

* fix fmp calendar dividend and discovery filing models

* add cik description in standard model utils

* redefine CompanyFilings standard model

* fix fmp company filings model

* fix sec company filings model

* remove regulators/sec/filings command

* redefine InsiderTrading standard model

* fix fmp insider trading model

* redefine InstitutionalOwnership standard model

* fix fmp institutional ownership model

* redefine KeyMetrics standard model

* fix fmp key metrics model

* fix fmp market snapshots model

* add attributes standard model and commands

* redefine EquityInfo standard model

* remove SearchFinancialAttributes standard model file

* add new intrinio endpoints

* linting

* pass standard model description test

* fix tests? w/@montezdesousa

* pass fmp fetcher tests

* re-record sec company filings test

* pass intrinio fetcher tests

* re-record intrinio insider trading test

* index integration test

* regulators integration test

* equity integration test

* fix same owner name in intrinio instituional ownership

* re-record intrinio insider training

* crypto price historical integration test reduce end_date
This commit is contained in:
Pratyush Shukla
2023-11-27 18:05:19 +05:30
committed by GitHub
parent a31df4b300
commit 9219a63c00
79 changed files with 12630 additions and 6085 deletions

View File

@@ -58,7 +58,8 @@ class BalanceSheetData(Data):
)
date: dateType = Field(description=DATA_DESCRIPTIONS.get("date", ""))
cik: Optional[str] = Field(
default=None, description="Central Index Key (CIK) of the company."
default=None,
description=DATA_DESCRIPTIONS.get("cik", ""),
)
currency: Optional[str] = Field(default=None, description="Reporting currency.")
filling_date: Optional[dateType] = Field(default=None, description="Filling date.")

View File

@@ -14,7 +14,7 @@ from openbb_core.provider.utils.descriptions import (
)
class DividendCalendarQueryParams(QueryParams):
class CalendarDividendQueryParams(QueryParams):
"""Dividend Calendar Query."""
start_date: Optional[dateType] = Field(
@@ -25,14 +25,17 @@ class DividendCalendarQueryParams(QueryParams):
)
class DividendCalendarData(Data):
class CalendarDividendData(Data):
"""Dividend Calendar Data."""
date: dateType = Field(
description=DATA_DESCRIPTIONS.get("date", "") + " (Ex-Dividend)"
)
symbol: str = Field(description=DATA_DESCRIPTIONS.get("symbol", ""))
name: Optional[str] = Field(description="Name of the entity.", default=None)
amount: Optional[float] = Field(
default=None, description="Dividend amount, per-share."
)
name: Optional[str] = Field(default=None, description="Name of the entity.")
record_date: Optional[dateType] = Field(
default=None,
description="The record date of ownership for eligibility.",
@@ -45,6 +48,3 @@ class DividendCalendarData(Data):
default=None,
description="Declaration date of the dividend.",
)
amount: Optional[float] = Field(
default=None, description="Dividend amount, per-share."
)

View File

@@ -58,7 +58,8 @@ class CashFlowStatementData(Data):
default=None, description="Reporting period of the statement."
)
cik: Optional[str] = Field(
default=None, description="Central Index Key (CIK) of the company."
default=None,
description=DATA_DESCRIPTIONS.get("cik", ""),
)
net_income: Optional[StrictFloat] = Field(default=None, description="Net income.")

View File

@@ -1,11 +1,13 @@
"""Company Filings Standard Model."""
from datetime import date as dateType
from typing import List, Literal, Optional, Set, Union
from datetime import (
date as dateType,
datetime,
)
from typing import List, Optional, Set, Union
from dateutil import parser
from pydantic import Field, field_validator
from pydantic import Field, NonNegativeInt, field_validator
from openbb_core.provider.abstract.data import Data
from openbb_core.provider.abstract.query_params import QueryParams
@@ -14,109 +16,6 @@ from openbb_core.provider.utils.descriptions import (
QUERY_DESCRIPTIONS,
)
SEC_FORM_TYPES = Literal[
"1",
"1-A",
"1-E",
"1-K",
"1-N",
"1-SA",
"1-U",
"1-Z",
"10",
"10-D",
"10-K",
"10-M",
"10-Q",
"11-K",
"12b-25",
"13F",
"13H",
"144",
"15",
"15F",
"17-H",
"18",
"18-K",
"19b-4",
"19b-4(e)",
"19b-7",
"2-E",
"20-F",
"24F-2",
"25",
"3",
"4",
"40-F",
"5",
"6-K",
"7-M",
"8-A",
"8-K",
"8-M",
"9-M",
"ABS-15G",
"ABS-EE",
"ABS DD-15E",
"ADV",
"ADV-E",
"ADV-H",
"ADV-NR",
"ADV-W",
"ATS",
"ATS-N",
"ATS-R",
"BD",
"BD-N",
"BDW",
"C",
"CA-1",
"CB",
"CFPORTAL",
"CRS",
"CUSTODY",
"D",
"F-1",
"F-10",
"F-3",
"F-4",
"F-6",
"F-7",
"F-8",
"F-80",
"F-N",
"F-X",
"ID",
"MA",
"MA-I",
"MA-NR",
"MA-W",
"MSD",
"MSDW",
"N-14",
"N-17D-1",
"N-17f-1",
"N-17f-2",
"N-18f-1",
"N-1A",
"N-2",
"N-23c-3",
"N-27D-1",
"N-3",
"N-4",
"N-5",
"N-54A",
"N-54C",
"N-6",
"N-6EI-1",
"N-6F",
"N-8A",
"N-8B-2",
"N-8B-4",
"N-8F",
"N-CEN",
]
class CompanyFilingsQueryParams(QueryParams):
"""Company Filings Query."""
@@ -124,11 +23,19 @@ class CompanyFilingsQueryParams(QueryParams):
symbol: Optional[str] = Field(
default=None, description=QUERY_DESCRIPTIONS.get("symbol", "")
)
limit: Optional[int] = Field(
default=300, description=QUERY_DESCRIPTIONS.get("limit", "")
form_type: Optional[str] = Field(
default=None,
description=(
"Filter by form type. Visit https://www.sec.gov/forms "
"for a list of supported form types."
),
)
limit: NonNegativeInt = Field(
default=100, description=QUERY_DESCRIPTIONS.get("limit", "")
)
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
def upper_symbol(cls, v: Union[str, List[str], Set[str]]):
"""Convert symbol to uppercase."""
if isinstance(v, str):
@@ -139,14 +46,28 @@ class CompanyFilingsQueryParams(QueryParams):
class CompanyFilingsData(Data):
"""Company Filings Data."""
date: dateType = Field(
description=DATA_DESCRIPTIONS.get("date", "")
+ " In this case, it is the date of the filing."
symbol: Optional[str] = Field(
default=None, description=DATA_DESCRIPTIONS.get("symbol", "")
)
type: str = Field(description="Type of document.")
link: str = Field(description="URL to the document.")
cik: Optional[str] = Field(
default=None, description=DATA_DESCRIPTIONS.get("cik", "")
)
filing_date: dateType = Field(description="Filing date of the SEC report.")
accepted_date: datetime = Field(description="Accepted date of the SEC report.")
report_type: str = Field(description="Type of the SEC report.")
filing_url: str = Field(description="URL to the filing page on the SEC site.")
report_url: str = Field(description="URL to the actual report on the SEC site.")
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
def upper_symbol(cls, v: Union[str, List[str], Set[str]]):
"""Convert symbol to uppercase."""
if isinstance(v, str):
return v.upper()
return ",".join([symbol.upper() for symbol in list(v)]) if v else None
@field_validator("date", mode="before", check_fields=False)
@classmethod
def convert_date(cls, v: str):
"""Convert date to date type."""
return parser.parse(str(v)).date()

View File

@@ -20,6 +20,7 @@ class CompanyOverviewQueryParams(QueryParams):
symbol: str = Field(description=QUERY_DESCRIPTIONS.get("symbol", ""))
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
def upper_symbol(cls, v: Union[str, List[str], Set[str]]):
"""Convert symbol to uppercase."""
if isinstance(v, str):
@@ -55,7 +56,9 @@ class CompanyOverviewData(Data):
currency: Optional[str] = Field(
default=None, description="Currency of the company."
)
cik: Optional[str] = Field(default=None, description="CIK of the company.")
cik: Optional[str] = Field(
default=None, description=DATA_DESCRIPTIONS.get("cik", "")
)
isin: Optional[str] = Field(default=None, description="ISIN of the company.")
cusip: Optional[str] = Field(default=None, description="CUSIP of the company.")
exchange: Optional[str] = Field(
@@ -99,6 +102,7 @@ class CompanyOverviewData(Data):
is_fund: bool = Field(description="If the company is a fund.")
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
def upper_symbol(cls, v: Union[str, List[str], Set[str]]):
"""Convert symbol to uppercase."""
if isinstance(v, str):

View File

@@ -1,4 +1,4 @@
"""Filings Standard Model."""
"""Discovery Filings Standard Model."""
from datetime import (
date as dateType,
@@ -16,8 +16,8 @@ from openbb_core.provider.utils.descriptions import (
)
class FilingsQueryParams(QueryParams):
"""Filings Query."""
class DiscoveryFilingsQueryParams(QueryParams):
"""Discovery Filings Query."""
start_date: Optional[dateType] = Field(
default=None,
@@ -29,30 +29,22 @@ class FilingsQueryParams(QueryParams):
)
form_type: Optional[str] = Field(
default=None,
description="Fuzzy filter by form type. E.g. 10-K, 10, 8, 6-K, etc.",
description=(
"Filter by form type. Visit https://www.sec.gov/forms "
"for a list of supported form types."
),
)
limit: NonNegativeInt = Field(
default=100, description=QUERY_DESCRIPTIONS.get("limit", "")
)
class FilingsData(Data):
"""Filings Data."""
class DiscoveryFilingsData(Data):
"""Discovery Filings Data."""
timestamp: datetime = Field(
description="The timestamp from when the filing was accepted.",
)
symbol: Optional[str] = Field(
default=None,
description=DATA_DESCRIPTIONS.get("symbol", ""),
)
cik: str = Field(
description="The CIK of the filing",
)
title: str = Field(
description="The title of the filing",
)
form_type: str = Field(
description="The form type of the filing",
)
url: Optional[str] = Field(description="The URL of the filing", default=None)
symbol: str = Field(description=DATA_DESCRIPTIONS.get("symbol", ""))
cik: str = Field(description=DATA_DESCRIPTIONS.get("cik", ""))
title: str = Field(description="Title of the filing.")
date: datetime = Field(description=DATA_DESCRIPTIONS.get("date", ""))
form_type: str = Field(description="The form type of the filing")
link: str = Field(description="URL to the filing page on the SEC site.")

View File

@@ -1,5 +1,6 @@
"""Equity Info Standard Model."""
from datetime import date as dateType
from typing import List, Optional, Set, Union
from pydantic import Field, field_validator
@@ -30,29 +31,111 @@ class EquityInfoData(Data):
"""Equity Info Data."""
symbol: str = Field(description=DATA_DESCRIPTIONS.get("symbol", ""))
name: str = Field(description="Name associated with the ticker symbol.")
price: Optional[float] = Field(default=None, description="Last transaction price.")
open: Optional[float] = Field(
default=None, description=DATA_DESCRIPTIONS.get("open", "")
)
high: Optional[float] = Field(
default=None, description=DATA_DESCRIPTIONS.get("high", "")
)
low: Optional[float] = Field(
default=None, description=DATA_DESCRIPTIONS.get("low", "")
)
close: Optional[float] = Field(
default=None, description=DATA_DESCRIPTIONS.get("close", "")
)
change: Optional[float] = Field(
default=None, description="Change in price over the current trading period."
)
change_percent: Optional[float] = Field(
name: Optional[str] = Field(default=None, description="Common name of the company.")
cik: Optional[str] = Field(
default=None,
description="Percent change in price over the current trading period.",
description=DATA_DESCRIPTIONS.get("cik", ""),
)
prev_close: Optional[float] = Field(
default=None, description="Previous closing price."
lei: Optional[str] = Field(
default=None, description="Legal Entity Identifier assigned to the company."
)
legal_name: Optional[str] = Field(
default=None, description="Official legal name of the company."
)
stock_exchange: Optional[str] = Field(
default=None, description="Stock exchange where the company is traded."
)
sic: Optional[int] = Field(
default=None,
description="Standard Industrial Classification code for the company.",
)
short_description: Optional[str] = Field(
default=None, description="Short description of the company."
)
long_description: Optional[str] = Field(
default=None, description="Long description of the company."
)
ceo: Optional[str] = Field(
default=None, description="Chief Executive Officer of the company."
)
company_url: Optional[str] = Field(
default=None, description="URL of the company's website."
)
business_address: Optional[str] = Field(
default=None, description="Address of the company's headquarters."
)
mailing_address: Optional[str] = Field(
default=None, description="Mailing address of the company."
)
business_phone_no: Optional[str] = Field(
default=None, description="Phone number of the company's headquarters."
)
hq_address1: Optional[str] = Field(
default=None, description="Address of the company's headquarters."
)
hq_address2: Optional[str] = Field(
default=None, description="Address of the company's headquarters."
)
hq_address_city: Optional[str] = Field(
default=None, description="City of the company's headquarters."
)
hq_address_postal_code: Optional[str] = Field(
default=None, description="Zip code of the company's headquarters."
)
hq_state: Optional[str] = Field(
default=None, description="State of the company's headquarters."
)
hq_country: Optional[str] = Field(
default=None, description="Country of the company's headquarters."
)
inc_state: Optional[str] = Field(
default=None, description="State in which the company is incorporated."
)
inc_country: Optional[str] = Field(
default=None, description="Country in which the company is incorporated."
)
employees: Optional[int] = Field(
default=None, description="Number of employees working for the company."
)
entity_legal_form: Optional[str] = Field(
default=None, description="Legal form of the company."
)
entity_status: Optional[str] = Field(
default=None, description="Status of the company."
)
latest_filing_date: Optional[dateType] = Field(
default=None, description="Date of the company's latest filing."
)
irs_number: Optional[str] = Field(
default=None, description="IRS number assigned to the company."
)
sector: Optional[str] = Field(
default=None, description="Sector in which the company operates."
)
industry_category: Optional[str] = Field(
default=None, description="Category of industry in which the company operates."
)
industry_group: Optional[str] = Field(
default=None, description="Group of industry in which the company operates."
)
template: Optional[str] = Field(
default=None,
description="Template used to standardize the company's financial statements.",
)
standardized_active: Optional[bool] = Field(
default=None, description="Whether the company is active or not."
)
first_fundamental_date: Optional[dateType] = Field(
default=None, description="Date of the company's first fundamental."
)
last_fundamental_date: Optional[dateType] = Field(
default=None, description="Date of the company's last fundamental."
)
first_stock_price_date: Optional[dateType] = Field(
default=None, description="Date of the company's first stock price."
)
last_stock_price_date: Optional[dateType] = Field(
default=None, description="Date of the company's last stock price."
)
@field_validator("symbol", mode="before", check_fields=False)

View File

@@ -45,7 +45,7 @@ class EquityOwnershipData(Data):
"""Equity Ownership Data."""
date: dateType = Field(description=DATA_DESCRIPTIONS.get("date", ""))
cik: int = Field(description="Cik of the stock ownership.")
cik: int = Field(description=DATA_DESCRIPTIONS.get("cik", ""))
filing_date: dateType = Field(description="Filing date of the stock ownership.")
investor_name: str = Field(
...,

View File

@@ -19,6 +19,7 @@ class ESGRiskRatingQueryParams(QueryParams):
symbol: str = Field(description=QUERY_DESCRIPTIONS.get("symbol", ""))
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
def upper_symbol(cls, v: Union[str, List[str], Set[str]]):
"""Convert symbol to uppercase."""
if isinstance(v, str):
@@ -30,7 +31,7 @@ class ESGRiskRatingData(Data):
"""ESG Risk Rating Data."""
symbol: str = Field(description=DATA_DESCRIPTIONS.get("symbol", ""))
cik: str = Field(description="CIK of the company.")
cik: str = Field(description=DATA_DESCRIPTIONS.get("cik", ""))
company_name: str = Field(description="Company name of the company.")
industry: str = Field(description="Industry of the company.")
year: int = Field(description="Year of the ESG risk rating.")
@@ -40,6 +41,7 @@ class ESGRiskRatingData(Data):
industry_rank: str = Field(description="Industry rank of the company.")
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
def upper_symbol(cls, v: Union[str, List[str], Set[str]]):
"""Convert symbol to uppercase."""
if isinstance(v, str):

View File

@@ -23,6 +23,7 @@ class ESGScoreQueryParams(QueryParams):
symbol: str = Field(description=QUERY_DESCRIPTIONS.get("symbol", ""))
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
def upper_symbol(cls, v: Union[str, List[str], Set[str]]):
"""Convert symbol to uppercase."""
if isinstance(v, str):
@@ -34,7 +35,7 @@ class ESGScoreData(Data):
"""ESG Score Data."""
symbol: str = Field(description=DATA_DESCRIPTIONS.get("symbol", ""))
cik: str = Field(description="CIK of the company.")
cik: str = Field(description=DATA_DESCRIPTIONS.get("cik", ""))
company_name: str = Field(description="Company name of the company.")
form_type: str = Field(description="Form type of the company.")
accepted_date: datetime = Field(description="Accepted date of the company.")
@@ -48,6 +49,7 @@ class ESGScoreData(Data):
url: str = Field(description="URL of the company.")
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
def upper_symbol(cls, v: Union[str, List[str], Set[str]]):
"""Convert symbol to uppercase."""
if isinstance(v, str):

View File

@@ -23,6 +23,7 @@ class ExecutiveCompensationQueryParams(QueryParams):
symbol: str = Field(description=QUERY_DESCRIPTIONS.get("symbol", ""))
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
def upper_symbol(cls, v: Union[str, List[str], Set[str]]):
"""Convert symbol to uppercase."""
if isinstance(v, str):
@@ -35,7 +36,8 @@ class ExecutiveCompensationData(Data):
symbol: str = Field(description=DATA_DESCRIPTIONS.get("symbol", ""))
cik: Optional[str] = Field(
default=None, description="Central Index Key (CIK) of the company."
default=None,
description=DATA_DESCRIPTIONS.get("cik", ""),
)
filing_date: dateType = Field(description="Date of the filing.")
accepted_date: datetime = Field(description="Date the filing was accepted.")
@@ -54,6 +56,7 @@ class ExecutiveCompensationData(Data):
url: str = Field(description="URL of the filing data.")
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
def upper_symbol(cls, v: Union[str, List[str], Set[str]]):
"""Convert symbol to uppercase."""
if isinstance(v, str):

View File

@@ -0,0 +1,45 @@
"""Historical Attributes Standard Model."""
from datetime import date as dateType
from typing import Literal, Optional
from pydantic import Field
from openbb_core.provider.abstract.data import Data
from openbb_core.provider.abstract.query_params import QueryParams
from openbb_core.provider.utils.descriptions import (
DATA_DESCRIPTIONS,
QUERY_DESCRIPTIONS,
)
class HistoricalAttributesQueryParams(QueryParams):
"""Historical Attributes Query."""
symbol: str = Field(description=QUERY_DESCRIPTIONS.get("symbol"))
tag: str = Field(description="Intrinio data tag ID or code.")
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")
)
frequency: Optional[
Literal["daily", "weekly", "monthly", "quarterly", "yearly"]
] = Field(default="yearly", description=QUERY_DESCRIPTIONS.get("frequency"))
limit: Optional[int] = Field(
default=1000, description=QUERY_DESCRIPTIONS.get("limit")
)
type: Optional[str] = Field(
default=None, description="Filter by type, when applicable."
)
sort: Optional[Literal["asc", "desc"]] = Field(
default="desc", description="Sort order."
)
class HistoricalAttributesData(Data):
"""Historical Attributes Data."""
date: dateType = Field(description=DATA_DESCRIPTIONS.get("date"))
value: Optional[float] = Field(default=None, description="The value of the data.")

View File

@@ -19,6 +19,7 @@ class HistoricalEmployeesQueryParams(QueryParams):
symbol: str = Field(description=QUERY_DESCRIPTIONS.get("symbol", ""))
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
def upper_symbol(cls, v: Union[str, List[str], Set[str]]):
"""Convert symbol to uppercase."""
if isinstance(v, str):
@@ -30,9 +31,7 @@ class HistoricalEmployeesData(Data):
"""Historical Employees Data."""
symbol: str = Field(description=DATA_DESCRIPTIONS.get("symbol", ""))
cik: int = Field(
description="CIK of the company to retrieve the historical employees of."
)
cik: int = Field(description=DATA_DESCRIPTIONS.get("cik", ""))
acceptance_time: datetime = Field(
description="Time of acceptance of the company employee."
)
@@ -68,6 +67,7 @@ class HistoricalEmployeesData(Data):
return datetime.strptime(v, "%Y-%m-%d")
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
def upper_symbol(cls, v: Union[str, List[str], Set[str]]):
"""Convert symbol to uppercase."""
if isinstance(v, str):

View File

@@ -60,7 +60,10 @@ class IncomeStatementData(Data):
period: Optional[str] = Field(
default=None, description="Period of the income statement."
)
cik: Optional[str] = Field(default=None, description="Central Index Key.")
cik: Optional[str] = Field(
default=None,
description=DATA_DESCRIPTIONS.get("cik", ""),
)
revenue: Optional[StrictFloat] = Field(default=None, description="Revenue.")
cost_of_revenue: Optional[StrictFloat] = Field(

View File

@@ -40,7 +40,7 @@ class IndexConstituentsData(Data):
default=None, description="Date the constituent company was added to the index."
)
cik: int = Field(
description="Central Index Key of the constituent company in the index."
description=DATA_DESCRIPTIONS.get("cik", ""),
)
founded: Optional[Union[date, str]] = Field(
default=None,
@@ -48,6 +48,7 @@ class IndexConstituentsData(Data):
)
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
def upper_symbol(cls, v: Union[str, List[str], Set[str]]):
"""Convert symbol to uppercase."""
if isinstance(v, str):

View File

@@ -1,60 +1,28 @@
"""Insider Trading Standard Model."""
from datetime import date, datetime, time
from typing import List, Literal, Optional, Set, Union
from typing import List, Optional, Set, Union
from dateutil import parser
from pydantic import Field, field_validator, model_validator
from pydantic import Field, StrictInt, field_validator
from openbb_core.provider.abstract.data import Data, ForceInt
from openbb_core.provider.abstract.data import Data
from openbb_core.provider.abstract.query_params import QueryParams
from openbb_core.provider.utils.descriptions import (
DATA_DESCRIPTIONS,
QUERY_DESCRIPTIONS,
)
TRANSACTION_TYPES = Literal[
"A-Award",
"C-Conversion",
"D-Return",
"E-ExpireShort",
"F-InKind",
"G-Gift",
"H-ExpireLong",
"I-Discretionary",
"J-Other",
"L-Small",
"M-Exempt",
"O-OutOfTheMoney",
"P-Purchase",
"S-Sale",
"U-Tender",
"W-Will",
"X-InTheMoney",
"Z-Trust",
]
class InsiderTradingQueryParams(QueryParams):
"""Insider Trading Query."""
symbol: str = Field(description=QUERY_DESCRIPTIONS.get("symbol", ""))
transaction_type: Union[List[TRANSACTION_TYPES], str, None] = Field(
default=None, description="Type of the transaction."
)
limit: ForceInt = Field(
limit: StrictInt = Field(
default=100,
description=QUERY_DESCRIPTIONS.get("limit", ""),
)
@model_validator(mode="after")
@classmethod
def validate_transaction_type(cls, values: "InsiderTradingQueryParams"):
"""Validate the transaction type."""
if isinstance(values.transaction_type, list):
values.transaction_type = ",".join(values.transaction_type)
return values
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
def upper_symbol(cls, v: Union[str, List[str], Set[str]]):
@@ -68,44 +36,35 @@ class InsiderTradingData(Data):
"""Insider Trading Data."""
symbol: str = Field(description=DATA_DESCRIPTIONS.get("symbol", ""))
company_cik: int = Field(description="Company CIK of the insider trading.")
filing_date: datetime = Field(description="Filing date of the insider trading.")
transaction_date: date = Field(
description="Transaction date of the insider trading."
transaction_date: Optional[date] = Field(
default=None, description="Transaction date of the insider trading."
)
reporting_cik: Optional[int] = Field(
default=None, description="Reporting CIK of the insider trading."
owner_cik: int = Field(description="Reporting CIK of the insider trading.")
owner_name: str = Field(description="Reporting name of the insider trading.")
owner_title: Optional[str] = Field(
default=None, description="Designation of owner of the insider trading."
)
transaction_type: str = Field(
description="Transaction type of the insider trading."
)
securities_owned: Optional[ForceInt] = Field(
default=None, description="Securities owned of the insider trading."
)
company_cik: Optional[int] = Field(
default=None, description="Company CIK of the insider trading."
)
reporting_name: Optional[str] = Field(
default=None, description="Reporting name of the insider trading."
)
type_of_owner: Optional[str] = Field(
default=None, description="Type of owner of the insider trading."
)
acquisition_or_disposition: Optional[str] = Field(
default=None,
description="Acquisition or disposition of the insider trading.",
)
form_type: str = Field(description="Form type of the insider trading.")
security_type: str = Field(description="Security type of the insider trading.")
securities_owned: Optional[float] = Field(
default=None, description="Number of securities owned in the insider trading."
)
securities_transacted: Optional[float] = Field(
default=None, description="Securities transacted of the insider trading."
)
price: Optional[float] = Field(
transaction_price: Optional[float] = Field(
default=None,
description="Price of the insider trading.",
)
security_name: Optional[str] = Field(
default=None, description="Security name of the insider trading."
)
link: str = Field(description="Link of the insider trading.")
filing_url: str = Field(description="Link of the insider trading.")
@field_validator("symbol", mode="before", check_fields=False)
@classmethod

View File

@@ -18,14 +18,9 @@ class InstitutionalOwnershipQueryParams(QueryParams):
"""Institutional Ownership Query."""
symbol: str = Field(description=QUERY_DESCRIPTIONS.get("symbol", ""))
include_current_quarter: Optional[bool] = Field(
default=False, description="Include current quarter data."
)
date: Optional[dateType] = Field(
default=None, description=QUERY_DESCRIPTIONS.get("date", "")
)
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
def upper_symbol(cls, v: Union[str, List[str], Set[str]]):
"""Convert symbol to uppercase."""
if isinstance(v, str):
@@ -37,96 +32,14 @@ class InstitutionalOwnershipData(Data):
"""Institutional Ownership Data."""
symbol: str = Field(description=DATA_DESCRIPTIONS.get("symbol", ""))
cik: Optional[str] = Field(default=None, description="CIK of the company.")
cik: Optional[str] = Field(
default=None,
description=DATA_DESCRIPTIONS.get("cik", ""),
)
date: dateType = Field(description=DATA_DESCRIPTIONS.get("date", ""))
investors_holding: int = Field(description="Number of investors holding the stock.")
last_investors_holding: int = Field(
description="Number of investors holding the stock in the last quarter."
)
investors_holding_change: int = Field(
description="Change in the number of investors holding the stock."
)
number_of_13f_shares: int = Field(default=None, description="Number of 13F shares.")
last_number_of_13f_shares: int = Field(
default=None, description="Number of 13F shares in the last quarter."
)
number_of_13f_shares_change: int = Field(
default=None, description="Change in the number of 13F shares."
)
total_invested: float = Field(description="Total amount invested.")
last_total_invested: float = Field(
description="Total amount invested in the last quarter."
)
total_invested_change: float = Field(
description="Change in the total amount invested."
)
ownership_percent: float = Field(description="Ownership percent.")
last_ownership_percent: float = Field(
description="Ownership percent in the last quarter."
)
ownership_percent_change: float = Field(
description="Change in the ownership percent."
)
new_positions: int = Field(description="Number of new positions.")
last_new_positions: int = Field(
description="Number of new positions in the last quarter."
)
new_positions_change: int = Field(
description="Change in the number of new positions."
)
increased_positions: int = Field(description="Number of increased positions.")
last_increased_positions: int = Field(
description="Number of increased positions in the last quarter."
)
increased_positions_change: int = Field(
description="Change in the number of increased positions."
)
closed_positions: int = Field(description="Number of closed positions.")
last_closed_positions: int = Field(
description="Number of closed positions in the last quarter."
)
closed_positions_change: int = Field(
description="Change in the number of closed positions."
)
reduced_positions: int = Field(description="Number of reduced positions.")
last_reduced_positions: int = Field(
description="Number of reduced positions in the last quarter."
)
reduced_positions_change: int = Field(
description="Change in the number of reduced positions."
)
total_calls: int = Field(
description="Total number of call options contracts traded for Apple Inc. on the specified date."
)
last_total_calls: int = Field(
description="Total number of call options contracts traded for Apple Inc. on the previous reporting date."
)
total_calls_change: int = Field(
description="Change in the total number of call options contracts traded between "
"the current and previous reporting dates."
)
total_puts: int = Field(
description="Total number of put options contracts traded for Apple Inc. on the specified date."
)
last_total_puts: int = Field(
description="Total number of put options contracts traded for Apple Inc. on the previous reporting date."
)
total_puts_change: int = Field(
description="Change in the total number of put "
"options contracts traded between the current and previous reporting dates."
)
put_call_ratio: float = Field(
description="Put-call ratio, which is the ratio of the total number of "
"put options to call options traded on the specified date."
)
last_put_call_ratio: float = Field(
description="Put-call ratio on the previous reporting date."
)
put_call_ratio_change: float = Field(
description="Change in the put-call ratio between the current and previous reporting dates."
)
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
def upper_symbol(cls, v: Union[str, List[str], Set[str]]):
"""Convert symbol to uppercase."""
if isinstance(v, str):

View File

@@ -1,7 +1,5 @@
"""Key Metrics Standard Model."""
from datetime import date as dateType
from typing import List, Literal, Optional, Set, Union
from pydantic import Field, field_validator
@@ -26,6 +24,7 @@ class KeyMetricsQueryParams(QueryParams):
)
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
def upper_symbol(cls, v: Union[str, List[str], Set[str]]):
"""Convert symbol to uppercase."""
if isinstance(v, str):
@@ -39,164 +38,15 @@ class KeyMetricsData(Data):
symbol: Optional[str] = Field(
default=None, description=DATA_DESCRIPTIONS.get("symbol", "")
)
date: dateType = Field(description=DATA_DESCRIPTIONS.get("date", ""))
period: str = Field(description="Period of the data.")
revenue_per_share: Optional[float] = Field(
default=None, description="Revenue per share"
)
net_income_per_share: Optional[float] = Field(
default=None, description="Net income per share"
)
operating_cash_flow_per_share: Optional[float] = Field(
default=None, description="Operating cash flow per share"
)
free_cash_flow_per_share: Optional[float] = Field(
default=None, description="Free cash flow per share"
)
cash_per_share: Optional[float] = Field(default=None, description="Cash per share")
book_value_per_share: Optional[float] = Field(
default=None, description="Book value per share"
)
tangible_book_value_per_share: Optional[float] = Field(
default=None, description="Tangible book value per share"
)
shareholders_equity_per_share: Optional[float] = Field(
default=None, description="Shareholders equity per share"
)
interest_debt_per_share: Optional[float] = Field(
default=None, description="Interest debt per share"
)
market_cap: Optional[float] = Field(
default=None, description="Market capitalization"
)
enterprise_value: Optional[float] = Field(
default=None, description="Enterprise value"
)
pe_ratio: Optional[float] = Field(
default=None, description="Price-to-earnings ratio (P/E ratio)"
)
price_to_sales_ratio: Optional[float] = Field(
default=None, description="Price-to-sales ratio"
)
pocf_ratio: Optional[float] = Field(
default=None, description="Price-to-operating cash flow ratio"
)
pfcf_ratio: Optional[float] = Field(
default=None, description="Price-to-free cash flow ratio"
)
pb_ratio: Optional[float] = Field(default=None, description="Price-to-book ratio")
ptb_ratio: Optional[float] = Field(
default=None, description="Price-to-tangible book ratio"
)
ev_to_sales: Optional[float] = Field(
default=None, description="Enterprise value-to-sales ratio"
)
enterprise_value_over_ebitda: Optional[float] = Field(
default=None, description="Enterprise value-to-EBITDA ratio"
)
ev_to_operating_cash_flow: Optional[float] = Field(
default=None, description="Enterprise value-to-operating cash flow ratio"
)
ev_to_free_cash_flow: Optional[float] = Field(
default=None, description="Enterprise value-to-free cash flow ratio"
)
earnings_yield: Optional[float] = Field(default=None, description="Earnings yield")
free_cash_flow_yield: Optional[float] = Field(
default=None, description="Free cash flow yield"
)
debt_to_equity: Optional[float] = Field(
default=None, description="Debt-to-equity ratio"
)
debt_to_assets: Optional[float] = Field(
default=None, description="Debt-to-assets ratio"
)
net_debt_to_ebitda: Optional[float] = Field(
default=None, description="Net debt-to-EBITDA ratio"
)
current_ratio: Optional[float] = Field(default=None, description="Current ratio")
interest_coverage: Optional[float] = Field(
default=None, description="Interest coverage"
)
income_quality: Optional[float] = Field(default=None, description="Income quality")
dividend_yield: Optional[float] = Field(default=None, description="Dividend yield")
payout_ratio: Optional[float] = Field(default=None, description="Payout ratio")
sales_general_and_administrative_to_revenue: Optional[float] = Field(
default=None,
description="Sales general and administrative expenses-to-revenue ratio",
)
research_and_development_to_revenue: Optional[float] = Field(
default=None, description="Research and development expenses-to-revenue ratio"
)
intangibles_to_total_assets: Optional[float] = Field(
default=None, description="Intangibles-to-total assets ratio"
)
capex_to_operating_cash_flow: Optional[float] = Field(
default=None, description="Capital expenditures-to-operating cash flow ratio"
)
capex_to_revenue: Optional[float] = Field(
default=None, description="Capital expenditures-to-revenue ratio"
)
capex_to_depreciation: Optional[float] = Field(
default=None, description="Capital expenditures-to-depreciation ratio"
)
stock_based_compensation_to_revenue: Optional[float] = Field(
default=None, description="Stock-based compensation-to-revenue ratio"
)
graham_number: Optional[float] = Field(default=None, description="Graham number")
roic: Optional[float] = Field(
default=None, description="Return on invested capital"
)
return_on_tangible_assets: Optional[float] = Field(
default=None, description="Return on tangible assets"
)
graham_net_net: Optional[float] = Field(
default=None, description="Graham net-net working capital"
)
working_capital: Optional[float] = Field(
default=None, description="Working capital"
)
tangible_asset_value: Optional[float] = Field(
default=None, description="Tangible asset value"
)
net_current_asset_value: Optional[float] = Field(
default=None, description="Net current asset value"
)
invested_capital: Optional[float] = Field(
default=None, description="Invested capital"
)
average_receivables: Optional[float] = Field(
default=None, description="Average receivables"
)
average_payables: Optional[float] = Field(
default=None, description="Average payables"
)
average_inventory: Optional[float] = Field(
default=None, description="Average inventory"
)
days_sales_outstanding: Optional[float] = Field(
default=None, description="Days sales outstanding"
)
days_payables_outstanding: Optional[float] = Field(
default=None, description="Days payables outstanding"
)
days_of_inventory_on_hand: Optional[float] = Field(
default=None, description="Days of inventory on hand"
)
receivables_turnover: Optional[float] = Field(
default=None, description="Receivables turnover"
)
payables_turnover: Optional[float] = Field(
default=None, description="Payables turnover"
)
inventory_turnover: Optional[float] = Field(
default=None, description="Inventory turnover"
)
roe: Optional[float] = Field(default=None, description="Return on equity")
capex_per_share: Optional[float] = Field(
default=None, description="Capital expenditures per share"
)
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
def upper_symbol(cls, v: Union[str, List[str], Set[str]]):
"""Convert symbol to uppercase."""
if isinstance(v, str):

View File

@@ -0,0 +1,24 @@
"""Latest Attributes Standard Model."""
from typing import Optional, Union
from pydantic import Field
from openbb_core.provider.abstract.data import Data
from openbb_core.provider.abstract.query_params import QueryParams
from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS
class LatestAttributesQueryParams(QueryParams):
"""Latest Attributes Query."""
symbol: str = Field(description=QUERY_DESCRIPTIONS.get("symbol"))
tag: str = Field(description="Intrinio data tag ID or code.")
class LatestAttributesData(Data):
"""Latest Attributes Data."""
value: Optional[Union[str, float]] = Field(
default=None, description="The value of the data."
)

View File

@@ -29,6 +29,7 @@ class MarketIndicesQueryParams(QueryParams):
)
@field_validator("symbol", mode="before", check_fields=False)
@classmethod
def upper_symbol(cls, v: Union[str, List[str], Set[str]]):
"""Convert symbol to uppercase."""
if isinstance(v, str):
@@ -40,10 +41,18 @@ class MarketIndicesData(Data):
"""Market Indices Data."""
date: datetime = Field(description=DATA_DESCRIPTIONS.get("date", ""))
open: StrictFloat = Field(description=DATA_DESCRIPTIONS.get("open", ""))
high: StrictFloat = Field(description=DATA_DESCRIPTIONS.get("high", ""))
low: StrictFloat = Field(description=DATA_DESCRIPTIONS.get("low", ""))
close: StrictFloat = Field(description=DATA_DESCRIPTIONS.get("close", ""))
open: Optional[StrictFloat] = Field(
default=None, description=DATA_DESCRIPTIONS.get("open", "")
)
high: Optional[StrictFloat] = Field(
default=None, description=DATA_DESCRIPTIONS.get("high", "")
)
low: Optional[StrictFloat] = Field(
default=None, description=DATA_DESCRIPTIONS.get("low", "")
)
close: Optional[StrictFloat] = Field(
default=None, description=DATA_DESCRIPTIONS.get("close", "")
)
volume: Optional[ForceInt] = Field(
default=None, description=DATA_DESCRIPTIONS.get("volume", "")
)

View File

@@ -0,0 +1,49 @@
"""Search Attributes Standard Model."""
from typing import Optional
from pydantic import Field
from openbb_core.provider.abstract.data import Data
from openbb_core.provider.abstract.query_params import QueryParams
from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS
class SearchAttributesQueryParams(QueryParams):
"""Search Attributes Query."""
query: str = Field(description="Query to search for.")
limit: Optional[int] = Field(
default=1000, description=QUERY_DESCRIPTIONS.get("limit")
)
class SearchAttributesData(Data):
"""Search 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."
)

View File

@@ -15,6 +15,7 @@ QUERY_DESCRIPTIONS = {
DATA_DESCRIPTIONS = {
"symbol": "Symbol representing the entity requested in the data.",
"cik": "Central Index Key (CIK) for the requested entity.",
"date": "The date of the data.",
"open": "The open price.",
"high": "The high price.",

View File

@@ -63,7 +63,7 @@ def test_crypto_search(params, headers):
"provider": "fmp",
"symbol": "BTCUSD",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
"end_date": "2023-01-03",
"timeseries": 1,
}
),
@@ -93,16 +93,6 @@ def test_crypto_search(params, headers):
"end_date": "2023-06-06",
}
),
(
{
"interval": "5m",
"period": "max",
"provider": "yfinance",
"symbol": "BTCUSD",
"start_date": "2023-01-01",
"end_date": "2023-01-08",
}
),
(
{
"interval": "1d",
@@ -130,7 +120,7 @@ def test_crypto_search(params, headers):
"exchanges": ["POLONIEX", "GDAX"],
"symbol": "BTCUSD",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
"end_date": "2023-01-02",
}
),
],

View File

@@ -85,7 +85,7 @@ def test_crypto_search(params, obb):
"provider": "polygon",
"symbol": "BTCUSD",
"start_date": "2023-01-01",
"end_date": "2023-01-02",
"end_date": "2023-06-06",
}
),
(
@@ -115,7 +115,7 @@ def test_crypto_search(params, obb):
"exchanges": ["POLONIEX", "GDAX"],
"symbol": "BTCUSD",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
"end_date": "2023-01-02",
}
),
],

View File

@@ -96,6 +96,15 @@ def test_equity_fundamental_balance_growth(params, headers):
[
({"start_date": "2023-11-05", "end_date": "2023-11-10", "provider": "fmp"}),
({"start_date": "2023-11-05", "end_date": "2023-11-10", "provider": "nasdaq"}),
(
{
"start_date": "2023-11-05",
"end_date": "2023-11-10",
"symbol": "AAPL",
"limit": 100,
"provider": "intrinio",
}
),
],
)
@pytest.mark.integration
@@ -380,12 +389,23 @@ def test_equity_fundamental_income_growth(params, headers):
[
(
{
"symbol": "AAPL",
"transaction_type": "P-Purchase",
"limit": 10,
"provider": "fmp",
"symbol": "AAPL",
"limit": 10,
"transaction_type": ["P-Purchase"],
}
)
),
(
{
"provider": "intrinio",
"symbol": "AAPL",
"limit": 10,
"start_date": "2021-01-01",
"end_date": "2023-06-06",
"ownership_type": None,
"sort_by": "updated_on",
}
),
],
)
@pytest.mark.integration
@@ -409,7 +429,14 @@ def test_equity_ownership_insider_trading(params, headers):
"date": "2021-09-30",
"provider": "fmp",
}
)
),
(
{
"provider": "intrinio",
"symbol": "AAPL",
"limit": 100,
}
),
],
)
@pytest.mark.integration
@@ -459,7 +486,18 @@ def test_equity_calendar_ipo(params, headers):
@pytest.mark.parametrize(
"params",
[({"symbol": "AAPL", "period": "annual", "limit": 100})],
[
(
{
"provider": "fmp",
"symbol": "AAPL",
"period": "annual",
"limit": 100,
"with_ttm": False,
}
),
({"provider": "intrinio", "symbol": "AAPL", "period": "annual", "limit": 100}),
],
)
@pytest.mark.integration
def test_equity_fundamental_metrics(params, headers):
@@ -597,15 +635,36 @@ def test_equity_fundamental_revenue_per_segment(params, headers):
@pytest.mark.parametrize(
"params",
[
({"symbol": "AAPL", "type": "1", "page": 1, "limit": 100, "provider": "fmp"}),
({"symbol": "AAPL", "form_type": "1", "limit": 100, "provider": "fmp"}),
(
{
"provider": "intrinio",
"symbol": "AAPL",
"start_date": "2021-01-01",
"end_date": "2023-11-01",
"form_type": None,
"limit": 100,
"thea_enabled": None,
}
),
(
{
"symbol": "AAPL",
"type": "10-K",
"limit": 100,
"limit": 3,
"type": "8-K",
"cik": None,
"use_cache": False,
"provider": "sec",
"use_cache": False,
}
),
(
{
"cik": "0001067983",
"limit": 3,
"type": "10-Q",
"symbol": None,
"provider": "sec",
"use_cache": False,
}
),
],
@@ -623,7 +682,10 @@ def test_equity_fundamental_filings(params, headers):
@pytest.mark.parametrize(
"params",
[({"symbol": "AAPL"})],
[
({"symbol": "AAPL", "provider": "fmp"}),
({"symbol": "AAPL", "provider": "intrinio"}),
],
)
@pytest.mark.integration
def test_equity_ownership_share_statistics(params, headers):
@@ -875,11 +937,11 @@ def test_equity_fundamental_multiples(params, headers):
],
)
@pytest.mark.integration
def test_equity_fundamental_search_financial_attributes(params, headers):
def test_equity_fundamental_search_attributes(params, headers):
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}"
url = f"http://0.0.0.0:8000/api/v1/equity/fundamental/search_attributes?{query_str}"
result = requests.get(url, headers=headers, timeout=10)
assert isinstance(result, requests.Response)
assert result.status_code == 200
@@ -893,7 +955,7 @@ def test_equity_fundamental_search_financial_attributes(params, headers):
"provider": "intrinio",
"symbol": "AAPL",
"tag": "ebit",
"period": "annual",
"frequency": "yearly",
"limit": 1000,
"type": None,
"start_date": "2013-01-01",
@@ -904,11 +966,41 @@ def test_equity_fundamental_search_financial_attributes(params, headers):
],
)
@pytest.mark.integration
def test_equity_fundamental_financial_attributes(params, headers):
def test_equity_fundamental_historical_attributes(params, headers):
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}"
url = f"http://0.0.0.0:8000/api/v1/equity/fundamental/historical_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": "ceo",
}
),
(
{
"provider": "intrinio",
"symbol": "MSFT",
"tag": "ebitda",
}
),
],
)
@pytest.mark.integration
def test_equity_fundamental_latest_attributes(params, headers):
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/latest_attributes?{query_str}"
result = requests.get(url, headers=headers, timeout=10)
assert isinstance(result, requests.Response)
assert result.status_code == 200
@@ -990,7 +1082,10 @@ def test_equity_price_quote(params, headers):
@pytest.mark.parametrize(
"params",
[({"symbol": "AAPL", "provider": "cboe"})],
[
({"symbol": "AAPL", "provider": "cboe"}),
({"provider": "intrinio", "symbol": "AAPL"}),
],
)
@pytest.mark.integration
def test_equity_profile(params, headers):
@@ -1180,6 +1275,17 @@ def test_equity_discovery_upcoming_release_days(params, headers):
"provider": "fmp",
}
),
(
{
"provider": "intrinio",
"symbol": "AAPL",
"thea_enabled": None,
"start_date": "2023-11-06",
"end_date": "2023-11-07",
"limit": 50,
"form_type": "10-Q",
}
),
],
)
@pytest.mark.integration

View File

@@ -96,6 +96,15 @@ def test_equity_fundamental_balance_growth(params, obb):
[
({"start_date": "2023-11-05", "end_date": "2023-11-10", "provider": "fmp"}),
({"start_date": "2023-11-05", "end_date": "2023-11-10", "provider": "nasdaq"}),
(
{
"start_date": "2023-11-05",
"end_date": "2023-11-10",
"symbol": "AAPL",
"limit": 100,
"provider": "intrinio",
}
),
],
)
@pytest.mark.integration
@@ -357,10 +366,28 @@ def test_equity_fundamental_income_growth(params, obb):
(
{
"symbol": "AAPL",
"transaction_type": ["P-Purchase"],
"limit": 10,
}
),
(
{
"provider": "fmp",
"symbol": "AAPL",
"limit": 10,
"transaction_type": ["P-Purchase"],
}
),
(
{
"provider": "intrinio",
"symbol": "AAPL",
"limit": 10,
"start_date": "2021-01-01",
"end_date": "2023-06-06",
"ownership_type": None,
"sort_by": "updated_on",
}
),
],
)
@pytest.mark.integration
@@ -379,6 +406,14 @@ def test_equity_ownership_insider_trading(params, obb):
"symbol": "AAPL",
"include_current_quarter": True,
"date": "2021-09-30",
"provider": "fmp",
}
),
(
{
"provider": "intrinio",
"symbol": "AAPL",
"limit": 100,
}
),
],
@@ -426,6 +461,16 @@ def test_equity_calendar_ipo(params, obb):
"params",
[
({"symbol": "AAPL", "period": "annual", "limit": 100}),
(
{
"provider": "fmp",
"symbol": "AAPL",
"period": "annual",
"limit": 100,
"with_ttm": False,
}
),
({"provider": "intrinio", "symbol": "AAPL", "period": "annual", "limit": 100}),
],
)
@pytest.mark.integration
@@ -546,15 +591,36 @@ def test_equity_fundamental_revenue_per_segment(params, obb):
@pytest.mark.parametrize(
"params",
[
({"symbol": "AAPL", "type": "1", "page": 1, "limit": 100, "provider": "fmp"}),
({"symbol": "AAPL", "form_type": "1", "limit": 100, "provider": "fmp"}),
(
{
"provider": "intrinio",
"symbol": "AAPL",
"start_date": "2021-01-01",
"end_date": "2023-11-01",
"form_type": None,
"limit": 100,
"thea_enabled": None,
}
),
(
{
"symbol": "AAPL",
"type": "10-K",
"limit": 100,
"limit": 3,
"type": "8-K",
"cik": None,
"use_cache": False,
"provider": "sec",
"use_cache": False,
}
),
(
{
"cik": "0001067983",
"limit": 3,
"type": "10-Q",
"symbol": None,
"provider": "sec",
"use_cache": False,
}
),
],
@@ -571,6 +637,8 @@ def test_equity_fundamental_filings(params, obb):
"params",
[
({"symbol": "AAPL"}),
({"symbol": "AAPL", "provider": "fmp"}),
({"symbol": "AAPL", "provider": "intrinio"}),
],
)
@pytest.mark.integration
@@ -813,8 +881,8 @@ def test_equity_fundamental_multiples(params, obb):
],
)
@pytest.mark.integration
def test_equity_fundamental_search_financial_attributes(params, obb):
result = obb.equity.fundamental.search_financial_attributes(**params)
def test_equity_fundamental_search_attributes(params, obb):
result = obb.equity.fundamental.search_attributes(**params)
assert result
assert isinstance(result, OBBject)
assert len(result.results) > 0
@@ -828,7 +896,7 @@ def test_equity_fundamental_search_financial_attributes(params, obb):
"provider": "intrinio",
"symbol": "AAPL",
"tag": "ebit",
"period": "annual",
"frequency": "yearly",
"limit": 1000,
"type": None,
"start_date": "2013-01-01",
@@ -839,8 +907,41 @@ def test_equity_fundamental_search_financial_attributes(params, obb):
],
)
@pytest.mark.integration
def test_equity_fundamental_financial_attributes(params, obb):
result = obb.equity.fundamental.financial_attributes(**params)
def test_equity_fundamental_historical_attributes(params, obb):
result = obb.equity.fundamental.historical_attributes(**params)
assert result
assert isinstance(result, OBBject)
assert len(result.results) > 0
@pytest.mark.parametrize(
"params",
[
(
{
"symbol": "AAPL",
"tag": "ceo",
}
),
(
{
"provider": "intrinio",
"symbol": "AAPL",
"tag": "ceo",
}
),
(
{
"provider": "intrinio",
"symbol": "MSFT",
"tag": "ebitda",
}
),
],
)
@pytest.mark.integration
def test_equity_fundamental_latest_attributes(params, obb):
result = obb.equity.fundamental.latest_attributes(**params)
assert result
assert isinstance(result, OBBject)
assert len(result.results) > 0
@@ -915,6 +1016,7 @@ def test_equity_price_quote(params, obb):
"params",
[
({"symbol": "AAPL", "provider": "cboe"}),
({"provider": "intrinio", "symbol": "AAPL"}),
],
)
@pytest.mark.integration
@@ -1088,6 +1190,17 @@ def test_equity_discovery_upcoming_release_days(params, obb):
"provider": "fmp",
}
),
(
{
"provider": "intrinio",
"symbol": "AAPL",
"thea_enabled": None,
"start_date": "2023-11-06",
"end_date": "2023-11-07",
"limit": 50,
"form_type": "10-Q",
}
),
],
)
@pytest.mark.integration

View File

@@ -103,25 +103,36 @@ async def employee_count(
return await OBBject.from_query(Query(**locals()))
@router.command(model="SearchFinancialAttributes")
async def search_financial_attributes(
@router.command(model="SearchAttributes")
async def search_attributes(
cc: CommandContext,
provider_choices: ProviderChoices,
standard_params: StandardParams,
extra_params: ExtraParams,
) -> OBBject[BaseModel]:
"""Search financial attributes for financial statements."""
"""Search Intrinio data tags."""
return await OBBject.from_query(Query(**locals()))
@router.command(model="FinancialAttributes")
async def financial_attributes(
@router.command(model="LatestAttributes")
async def latest_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."""
"""Fetch the latest value of a data tag from Intrinio."""
return await OBBject.from_query(Query(**locals()))
@router.command(model="HistoricalAttributes")
async def historical_attributes(
cc: CommandContext,
provider_choices: ProviderChoices,
standard_params: StandardParams,
extra_params: ExtraParams,
) -> OBBject[BaseModel]:
"""Fetch the historical values of a data tag from Intrinio."""
return await OBBject.from_query(Query(**locals()))

View File

@@ -120,6 +120,18 @@ def test_index_constituents(params, headers):
"end_date": "2023-06-06",
}
),
(
{
"provider": "intrinio",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
"symbol": "$DJI",
"tag": "level",
"sort": "desc",
"limit": 100,
"type": None,
}
),
],
)
@pytest.mark.integration

View File

@@ -117,6 +117,18 @@ def test_index_constituents(params, obb):
"end_date": "2023-06-06",
}
),
(
{
"provider": "intrinio",
"start_date": "2023-01-01",
"end_date": "2023-06-06",
"symbol": "$DJI",
"tag": "level",
"sort": "desc",
"limit": 100,
"type": None,
}
),
],
)
@pytest.mark.integration

View File

@@ -53,42 +53,6 @@ def test_regulators_sec_institutions_search(params, headers):
assert result.status_code == 200
@pytest.mark.parametrize(
"params",
[
(
{
"symbol": "AAPL",
"limit": 3,
"type": "8-K",
"cik": None,
"provider": "sec",
"use_cache": False,
}
),
(
{
"cik": "0001067983",
"limit": 3,
"type": "10-Q",
"symbol": None,
"provider": "sec",
"use_cache": False,
}
),
],
)
@pytest.mark.integration
def test_regulators_sec_filings(params, headers):
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/regulators/sec/filings?{query_str}"
result = requests.get(url, headers=headers, timeout=10)
assert isinstance(result, requests.Response)
assert result.status_code == 200
@pytest.mark.parametrize(
"params",
[

View File

@@ -47,39 +47,6 @@ def test_regulators_sec_institutions_search(params, obb):
assert len(result.results) > 0
@pytest.mark.parametrize(
"params",
[
(
{
"symbol": "AAPL",
"limit": 3,
"type": "8-K",
"cik": None,
"provider": "sec",
"use_cache": False,
}
),
(
{
"cik": "0001067983",
"limit": 3,
"type": "10-Q",
"symbol": None,
"provider": "sec",
"use_cache": False,
}
),
],
)
@pytest.mark.integration
def test_regulators_sec_filings(params, obb):
result = obb.regulators.sec.filings(**params)
assert result
assert isinstance(result, OBBject)
assert len(result.results) > 0
@pytest.mark.parametrize(
"params",
[

View File

@@ -15,17 +15,6 @@ from pydantic import BaseModel
router = Router(prefix="/sec")
@router.command(model="Filings")
async def filings(
cc: CommandContext,
provider_choices: ProviderChoices,
standard_params: StandardParams,
extra_params: ExtraParams,
) -> OBBject[BaseModel]:
"""Look up filings to the SEC by ticker symbol or CIK."""
return await OBBject.from_query(Query(**locals()))
@router.command(model="CikMap")
async def cik_map(
cc: CommandContext,

View File

@@ -130,10 +130,11 @@ def get_test_params(
"QueryParams"
]["fields"]
)
if test_params:
test_params["provider"] = provider
test_params.update(standard_params)
test_params_list.append(test_params)
# We add provider even if `test_params` is empty to test the provider itself
test_params["provider"] = provider
test_params.update(standard_params)
test_params_list.append(test_params)
return test_params_list

View File

@@ -277,7 +277,7 @@ def check_missing_integration_tests(test_type: Literal["api", "python"]) -> List
functions = get_module_functions(get_integration_tests(test_type="python"))
tested_functions = [
function.replace("test_", "")
function.replace("test_", "", 1)
for function in functions
if function.startswith("test_")
]

View File

@@ -1,10 +1,11 @@
"""FMP Provider module."""
"""FMP Provider Modules."""
from openbb_core.provider.abstract.provider import Provider
from openbb_fmp.models.analyst_estimates import FMPAnalystEstimatesFetcher
from openbb_fmp.models.available_indices import FMPAvailableIndicesFetcher
from openbb_fmp.models.balance_sheet import FMPBalanceSheetFetcher
from openbb_fmp.models.balance_sheet_growth import FMPBalanceSheetGrowthFetcher
from openbb_fmp.models.calendar_dividend import FMPDividendCalendarFetcher
from openbb_fmp.models.calendar_dividend import FMPCalendarDividendFetcher
from openbb_fmp.models.calendar_earnings import FMPCalendarEarningsFetcher
from openbb_fmp.models.calendar_splits import FMPCalendarSplitsFetcher
from openbb_fmp.models.cash_flow import FMPCashFlowStatementFetcher
@@ -16,7 +17,7 @@ from openbb_fmp.models.crypto_historical import FMPCryptoHistoricalFetcher
from openbb_fmp.models.crypto_search import FMPCryptoSearchFetcher
from openbb_fmp.models.currency_historical import FMPCurrencyHistoricalFetcher
from openbb_fmp.models.currency_pairs import FMPCurrencyPairsFetcher
from openbb_fmp.models.discovery_filings import FMPFilingsFetcher
from openbb_fmp.models.discovery_filings import FMPDiscoveryFilingsFetcher
from openbb_fmp.models.earnings_call_transcript import FMPEarningsCallTranscriptFetcher
from openbb_fmp.models.economic_calendar import FMPEconomicCalendarFetcher
from openbb_fmp.models.equity_historical import FMPEquityHistoricalFetcher
@@ -70,7 +71,7 @@ fmp_provider = Provider(
"AvailableIndices": FMPAvailableIndicesFetcher,
"BalanceSheet": FMPBalanceSheetFetcher,
"BalanceSheetGrowth": FMPBalanceSheetGrowthFetcher,
"CalendarDividend": FMPDividendCalendarFetcher,
"CalendarDividend": FMPCalendarDividendFetcher,
"CalendarEarnings": FMPCalendarEarningsFetcher,
"CalendarSplits": FMPCalendarSplitsFetcher,
"CashFlowStatement": FMPCashFlowStatementFetcher,
@@ -82,7 +83,7 @@ fmp_provider = Provider(
"CryptoSearch": FMPCryptoSearchFetcher,
"CurrencyHistorical": FMPCurrencyHistoricalFetcher,
"CurrencyPairs": FMPCurrencyPairsFetcher,
"DiscoveryFilings": FMPFilingsFetcher,
"DiscoveryFilings": FMPDiscoveryFilingsFetcher,
"EarningsCallTranscript": FMPEarningsCallTranscriptFetcher,
"EconomicCalendar": FMPEconomicCalendarFetcher,
"EquityHistorical": FMPEquityHistoricalFetcher,

View File

@@ -1,20 +1,20 @@
"""FMP Dividend Calendar Model."""
from datetime import datetime
from typing import Any, Dict, List, Optional
from dateutil.relativedelta import relativedelta
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.calendar_dividend import (
DividendCalendarData,
DividendCalendarQueryParams,
CalendarDividendData,
CalendarDividendQueryParams,
)
from openbb_fmp.utils.helpers import get_data_many, get_querystring
from openbb_core.provider.utils.helpers import get_querystring
from openbb_fmp.utils.helpers import get_data_many
from pydantic import Field, field_validator
class FMPDividendCalendarQueryParams(DividendCalendarQueryParams):
class FMPCalendarDividendQueryParams(CalendarDividendQueryParams):
"""FMP Dividend Calendar Query.
Source: https://site.financialmodelingprep.com/developer/docs/dividend-calendar-api/
@@ -23,7 +23,7 @@ class FMPDividendCalendarQueryParams(DividendCalendarQueryParams):
"""
class FMPDividendCalendarData(DividendCalendarData):
class FMPCalendarDividendData(CalendarDividendData):
"""FMP Dividend Calendar Data."""
__alias_dict__ = {
@@ -56,16 +56,16 @@ class FMPDividendCalendarData(DividendCalendarData):
return datetime.strptime(v, "%Y-%m-%d") if v else None
class FMPDividendCalendarFetcher(
class FMPCalendarDividendFetcher(
Fetcher[
FMPDividendCalendarQueryParams,
List[FMPDividendCalendarData],
FMPCalendarDividendQueryParams,
List[FMPCalendarDividendData],
]
):
"""Transform the query, extract and transform the data from the FMP endpoints."""
@staticmethod
def transform_query(params: Dict[str, Any]) -> FMPDividendCalendarQueryParams:
def transform_query(params: Dict[str, Any]) -> FMPCalendarDividendQueryParams:
"""Transform the query params."""
transformed_params = params
@@ -73,13 +73,13 @@ class FMPDividendCalendarFetcher(
if params.get("start_date") is None:
transformed_params["start_date"] = now
if params.get("end_date") is None:
transformed_params["end_date"] = now + relativedelta(days=3)
transformed_params["end_date"] = now + relativedelta(days=30)
return FMPDividendCalendarQueryParams(**transformed_params)
return FMPCalendarDividendQueryParams(**transformed_params)
@staticmethod
def extract_data(
query: FMPDividendCalendarQueryParams,
query: FMPCalendarDividendQueryParams,
credentials: Optional[Dict[str, str]],
**kwargs: Any,
) -> List[Dict]:
@@ -95,7 +95,7 @@ class FMPDividendCalendarFetcher(
@staticmethod
def transform_data(
query: FMPDividendCalendarQueryParams, data: List[Dict], **kwargs: Any
) -> List[FMPDividendCalendarData]:
query: FMPCalendarDividendQueryParams, data: List[Dict], **kwargs: Any
) -> List[FMPCalendarDividendData]:
"""Return the transformed data."""
return [FMPDividendCalendarData.model_validate(d) for d in data]
return [FMPCalendarDividendData.model_validate(d) for d in data]

View File

@@ -1,16 +1,15 @@
"""FMP Company Filings Model."""
from datetime import datetime
from typing import Any, Dict, List, Optional, Set, Union
import math
from typing import Any, Dict, List, Optional
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.company_filings import (
SEC_FORM_TYPES,
CompanyFilingsData,
CompanyFilingsQueryParams,
)
from openbb_fmp.utils.helpers import create_url, get_data_many
from pydantic import Field, field_validator
from openbb_core.provider.utils.helpers import get_querystring
from openbb_fmp.utils.helpers import get_data_many
class FMPCompanyFilingsQueryParams(CompanyFilingsQueryParams):
@@ -19,31 +18,20 @@ class FMPCompanyFilingsQueryParams(CompanyFilingsQueryParams):
Source: https://site.financialmodelingprep.com/developer/docs/sec-filings-api/
"""
type: Optional[SEC_FORM_TYPES] = Field(
default=None, description="Type of the SEC filing form."
)
page: Optional[int] = Field(default=0, description="Page number of the results.")
__alias_dict__ = {"form_type": "type"}
class FMPCompanyFilingsData(CompanyFilingsData):
"""FMP Company Filings Data."""
__alias_dict__ = {
"date": "fillingDate",
"filing_date": "fillingDate",
"accepted_date": "acceptedDate",
"report_type": "type",
"filing_url": "link",
"report_url": "finalLink",
}
symbol: str = Field(description="The ticker symbol of the company.")
cik: str = Field(description="CIK of the SEC filing.")
accepted_date: datetime = Field(description="Accepted date of the SEC filing.")
final_link: str = Field(description="Final link of the SEC filing.")
@field_validator("symbol", mode="before", check_fields=False)
def upper_symbol(cls, v: Union[str, List[str], Set[str]]):
"""Convert symbol to uppercase."""
if isinstance(v, str):
return v.upper()
return ",".join([symbol.upper() for symbol in list(v)])
class FMPCompanyFilingsFetcher(
Fetcher[
@@ -66,12 +54,20 @@ class FMPCompanyFilingsFetcher(
) -> List[Dict]:
"""Return the raw data from the FMP endpoint."""
api_key = credentials.get("fmp_api_key") if credentials else ""
data: List[Dict] = []
url = create_url(
3, f"sec_filings/{query.symbol}", api_key, query, exclude=["symbol"]
)
base_url = "https://financialmodelingprep.com/api/v3/sec_filings"
query_str = get_querystring(query.model_dump(by_alias=True), ["symbol"])
return get_data_many(url, **kwargs)
# FMP only allows 1000 results per page
pages = math.ceil(query.limit / 1000)
for page in range(pages):
query_str += f"&page={page}"
url = f"{base_url}/{query.symbol}?{query_str}&apikey={api_key}"
response = get_data_many(url, **kwargs)
data.extend(response)
return data[: query.limit]
@staticmethod
def transform_data(

View File

@@ -1,102 +1,82 @@
"""FMP Filings Model."""
"""FMP Discovery Filings Model."""
import datetime
from typing import Any, Dict, List, Literal, Optional
import math
from typing import Any, Dict, List, Optional
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.filings import (
FilingsData,
FilingsQueryParams,
from openbb_core.provider.standard_models.discovery_filings import (
DiscoveryFilingsData,
DiscoveryFilingsQueryParams,
)
from openbb_core.provider.utils.helpers import get_querystring
from openbb_fmp.utils.helpers import get_data_many
from pydantic import Field, field_validator
from pydantic import Field
class FMPFilingsQueryParams(FilingsQueryParams):
"""FMP Filings Query."""
class FMPDiscoveryFilingsQueryParams(DiscoveryFilingsQueryParams):
"""FMP Discovery Filings Query.
Source: https://site.financialmodelingprep.com/developer/docs/sec-rss-feeds-api/
"""
__alias_dict__ = {
"form_type": "type",
"is_done": "isDone",
"start_date": "from",
"end_date": "to",
"form_type": "type",
}
is_done: Optional[Literal["true", "false"]] = Field(
is_done: Optional[bool] = Field(
default=None,
description="Flag for whether or not the filing is done.",
alias="isDone",
)
class FMPFilingsData(FilingsData):
"""FMP Filings Data."""
class FMPDiscoveryFilingsData(DiscoveryFilingsData):
"""FMP Discovery Filings Data."""
__alias_dict__ = {
"timestamp": "date",
"symbol": "ticker",
"url": "link",
}
is_done: Optional[Literal["True", "False"]] = Field(
default=None, description="Whether or not the filing is done."
)
@field_validator("timestamp", mode="before")
def validate_timestamp(cls, v: Any) -> Any: # pylint: disable=no-self-argument
"""Validate the timestamp."""
return datetime.datetime.strptime(v, "%Y-%m-%d %H:%M:%S")
__alias_dict__ = {"symbol": "ticker"}
class FMPFilingsFetcher(
class FMPDiscoveryFilingsFetcher(
Fetcher[
FMPFilingsQueryParams,
List[FMPFilingsData],
FMPDiscoveryFilingsQueryParams,
List[FMPDiscoveryFilingsData],
]
):
"""Transform the query, extract and transform the data from the FMP endpoints."""
@staticmethod
def transform_query(params: Dict[str, Any]) -> FMPFilingsQueryParams:
def transform_query(params: Dict[str, Any]) -> FMPDiscoveryFilingsQueryParams:
"""Transform the query."""
transformed_params = params
if "start_date" not in transformed_params:
transformed_params["start_date"] = datetime.datetime.now().strftime(
"%Y-%m-%d"
)
if "end_date" not in transformed_params:
transformed_params["end_date"] = datetime.datetime.now().strftime(
"%Y-%m-%d"
)
return FMPFilingsQueryParams(**transformed_params)
return FMPDiscoveryFilingsQueryParams(**params)
@staticmethod
def extract_data(
query: FMPFilingsQueryParams,
query: FMPDiscoveryFilingsQueryParams,
credentials: Optional[Dict[str, str]],
**kwargs: Any,
) -> List[Dict]:
"""Return the raw data from the FMP endpoint."""
api_key = credentials.get("fmp_api_key") if credentials else ""
response: List[Dict] = [{}]
exclude = []
if query.form_type is None:
exclude.append("formType")
if not query.is_done:
exclude.append("isDone")
base_url = "https://financialmodelingprep.com/api/v4/rss_feed?"
query_string = get_querystring(query.model_dump(), exclude)
url = f"{base_url}{query_string}&apikey={api_key}"
data: List[Dict] = get_data_many(url, **kwargs)
data: List[Dict] = []
if len(data) > 0:
response = data
base_url = "https://financialmodelingprep.com/api/v4/rss_feed"
query_str = get_querystring(query.model_dump(by_alias=True), ["limit"])
return response
# FMP only allows 1000 results per page
pages = math.ceil(query.limit / 1000)
for page in range(pages):
query_str += f"&page={page}"
url = f"{base_url}?{query_str}&apikey={api_key}"
response = get_data_many(url, **kwargs)
data.extend(response)
return data[: query.limit]
@staticmethod
def transform_data(
query: FMPFilingsQueryParams, data: List[Dict], **kwargs: Any
) -> List[FMPFilingsData]:
query: FMPDiscoveryFilingsQueryParams, data: List[Dict], **kwargs: Any
) -> List[FMPDiscoveryFilingsData]:
"""Return the transformed data."""
return [FMPFilingsData.model_validate(d) for d in data]
return [FMPDiscoveryFilingsData.model_validate(d) for d in data]

View File

@@ -1,6 +1,6 @@
"""FMP Insider Trading Model."""
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Optional, Union
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.insider_trading import (
@@ -8,7 +8,9 @@ from openbb_core.provider.standard_models.insider_trading import (
InsiderTradingQueryParams,
)
from openbb_core.provider.utils.helpers import get_querystring
from openbb_fmp.utils.definitions import TRANSACTION_TYPES
from openbb_fmp.utils.helpers import get_data_many
from pydantic import Field, model_validator
class FMPInsiderTradingQueryParams(InsiderTradingQueryParams):
@@ -17,18 +19,36 @@ class FMPInsiderTradingQueryParams(InsiderTradingQueryParams):
Source: https://site.financialmodelingprep.com/developer/docs/#Stock-Insider-Trading
"""
__alias_dict__ = {"transaction_type": "transactionType"}
transaction_type: Optional[Union[List[TRANSACTION_TYPES], str]] = Field(
default=["P-Purchase"],
description="Type of the transaction.",
alias="transactionType",
)
@model_validator(mode="after")
@classmethod
def validate_transaction_type(cls, values: "InsiderTradingQueryParams"):
"""Validate the transaction type."""
if isinstance(values.transaction_type, list):
values.transaction_type = ",".join(values.transaction_type)
return values
class FMPInsiderTradingData(InsiderTradingData):
"""FMP Insider Trading Data."""
__alias_dict__ = {
"owner_cik": "reportingCik",
"owner_name": "reportingName",
"owner_title": "typeOfOwner",
"security_type": "securityName",
"transaction_price": "price",
"acquisition_or_disposition": "acquistionOrDisposition",
"last_number_of_13f_shares": "lastNumberOf13FShares",
"securities_owned": "securitiesOwned",
"filing_url": "link",
}
form_type: str = Field(description="Form type of the insider trading.")
class FMPInsiderTradingFetcher(
Fetcher[

View File

@@ -1,6 +1,6 @@
"""FMP Institutional Ownership Model."""
from datetime import datetime
from datetime import date as dateType
from typing import Any, Dict, List, Optional
from openbb_core.provider.abstract.fetcher import Fetcher
@@ -8,8 +8,9 @@ from openbb_core.provider.standard_models.institutional_ownership import (
InstitutionalOwnershipData,
InstitutionalOwnershipQueryParams,
)
from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS
from openbb_fmp.utils.helpers import create_url, get_data_many
from pydantic import field_validator
from pydantic import Field
class FMPInstitutionalOwnershipQueryParams(InstitutionalOwnershipQueryParams):
@@ -18,21 +19,111 @@ class FMPInstitutionalOwnershipQueryParams(InstitutionalOwnershipQueryParams):
Source: https://site.financialmodelingprep.com/developer/docs/institutional-stock-ownership-api/
"""
include_current_quarter: Optional[bool] = Field(
default=False, description="Include current quarter data."
)
date: Optional[dateType] = Field(
default=None, description=QUERY_DESCRIPTIONS.get("date", "")
)
class FMPInstitutionalOwnershipData(InstitutionalOwnershipData):
"""FMP Institutional Ownership Data."""
__alias_dict__ = {
"number_of_13f_shares": "numberOf13Fshares",
"last_number_of_13f_shares": "lastNumberOf13Fshares",
"number_of_13f_shares_change": "numberOf13FsharesChange",
}
@field_validator("date", mode="before", check_fields=False)
@classmethod
def time_validate(cls, v): # pylint: disable=no-self-argument
"""Return the date as a datetime object."""
return datetime.strptime(v, "%Y-%m-%d")
investors_holding: int = Field(description="Number of investors holding the stock.")
last_investors_holding: int = Field(
description="Number of investors holding the stock in the last quarter."
)
investors_holding_change: int = Field(
description="Change in the number of investors holding the stock."
)
number_of_13f_shares: int = Field(
default=None,
description="Number of 13F shares.",
alias="numberOf13Fshares",
)
last_number_of_13f_shares: int = Field(
default=None,
description="Number of 13F shares in the last quarter.",
alias="lastNumberOf13Fshares",
)
number_of_13f_shares_change: int = Field(
default=None,
description="Change in the number of 13F shares.",
alias="numberOf13FsharesChange",
)
total_invested: float = Field(description="Total amount invested.")
last_total_invested: float = Field(
description="Total amount invested in the last quarter."
)
total_invested_change: float = Field(
description="Change in the total amount invested."
)
ownership_percent: float = Field(description="Ownership percent.")
last_ownership_percent: float = Field(
description="Ownership percent in the last quarter."
)
ownership_percent_change: float = Field(
description="Change in the ownership percent."
)
new_positions: int = Field(description="Number of new positions.")
last_new_positions: int = Field(
description="Number of new positions in the last quarter."
)
new_positions_change: int = Field(
description="Change in the number of new positions."
)
increased_positions: int = Field(description="Number of increased positions.")
last_increased_positions: int = Field(
description="Number of increased positions in the last quarter."
)
increased_positions_change: int = Field(
description="Change in the number of increased positions."
)
closed_positions: int = Field(description="Number of closed positions.")
last_closed_positions: int = Field(
description="Number of closed positions in the last quarter."
)
closed_positions_change: int = Field(
description="Change in the number of closed positions."
)
reduced_positions: int = Field(description="Number of reduced positions.")
last_reduced_positions: int = Field(
description="Number of reduced positions in the last quarter."
)
reduced_positions_change: int = Field(
description="Change in the number of reduced positions."
)
total_calls: int = Field(
description="Total number of call options contracts traded for Apple Inc. on the specified date."
)
last_total_calls: int = Field(
description="Total number of call options contracts traded for Apple Inc. on the previous reporting date."
)
total_calls_change: int = Field(
description="Change in the total number of call options contracts traded between "
"the current and previous reporting dates."
)
total_puts: int = Field(
description="Total number of put options contracts traded for Apple Inc. on the specified date."
)
last_total_puts: int = Field(
description="Total number of put options contracts traded for Apple Inc. on the previous reporting date."
)
total_puts_change: int = Field(
description="Change in the total number of put "
"options contracts traded between the current and previous reporting dates."
)
put_call_ratio: float = Field(
description="Put-call ratio, which is the ratio of the total number of "
"put options to call options traded on the specified date."
)
last_put_call_ratio: float = Field(
description="Put-call ratio on the previous reporting date."
)
put_call_ratio_change: float = Field(
description="Change in the put-call ratio between the current and previous reporting dates."
)
class FMPInstitutionalOwnershipFetcher(
@@ -58,7 +149,6 @@ class FMPInstitutionalOwnershipFetcher(
api_key = credentials.get("fmp_api_key") if credentials else ""
url = create_url(4, "institutional-ownership/symbol-ownership", api_key, query)
return get_data_many(url, **kwargs)
@staticmethod

View File

@@ -1,7 +1,10 @@
"""FMP Key Metrics Model."""
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime
from datetime import (
date as dateType,
datetime,
)
from itertools import repeat
from typing import Any, Dict, List, Optional
@@ -11,6 +14,7 @@ from openbb_core.provider.standard_models.key_metrics import (
KeyMetricsData,
KeyMetricsQueryParams,
)
from openbb_core.provider.utils.descriptions import DATA_DESCRIPTIONS
from openbb_fmp.utils.helpers import get_data_many, get_data_one
from pydantic import Field
@@ -29,16 +33,167 @@ class FMPKeyMetricsQueryParams(KeyMetricsQueryParams):
class FMPKeyMetricsData(KeyMetricsData):
"""FMP Key Metrics Data."""
__alias_dict__ = {
"pocf_ratio": "pocfratio",
"research_and_development_to_revenue": "researchAndDdevelopementToRevenue",
"net_debt_to_ebitda": "netDebtToEBITDA",
"enterprise_value_over_ebitda": "enterpriseValueOverEBITDA",
}
date: dateType = Field(description=DATA_DESCRIPTIONS.get("date", ""))
period: str = Field(description="Period of the data.")
calendar_year: Optional[ForceInt] = Field(
default=None, description="Calendar year."
)
revenue_per_share: Optional[float] = Field(
default=None, description="Revenue per share"
)
net_income_per_share: Optional[float] = Field(
default=None, description="Net income per share"
)
operating_cash_flow_per_share: Optional[float] = Field(
default=None, description="Operating cash flow per share"
)
free_cash_flow_per_share: Optional[float] = Field(
default=None, description="Free cash flow per share"
)
cash_per_share: Optional[float] = Field(default=None, description="Cash per share")
book_value_per_share: Optional[float] = Field(
default=None, description="Book value per share"
)
tangible_book_value_per_share: Optional[float] = Field(
default=None, description="Tangible book value per share"
)
shareholders_equity_per_share: Optional[float] = Field(
default=None, description="Shareholders equity per share"
)
interest_debt_per_share: Optional[float] = Field(
default=None, description="Interest debt per share"
)
enterprise_value: Optional[float] = Field(
default=None, description="Enterprise value"
)
price_to_sales_ratio: Optional[float] = Field(
default=None, description="Price-to-sales ratio"
)
pocf_ratio: Optional[float] = Field(
default=None,
description="Price-to-operating cash flow ratio",
alias="pocfratio",
)
pfcf_ratio: Optional[float] = Field(
default=None, description="Price-to-free cash flow ratio"
)
pb_ratio: Optional[float] = Field(default=None, description="Price-to-book ratio")
ptb_ratio: Optional[float] = Field(
default=None, description="Price-to-tangible book ratio"
)
ev_to_sales: Optional[float] = Field(
default=None, description="Enterprise value-to-sales ratio"
)
enterprise_value_over_ebitda: Optional[float] = Field(
default=None,
description="Enterprise value-to-EBITDA ratio",
alias="enterpriseValueOverEBITDA",
)
ev_to_operating_cash_flow: Optional[float] = Field(
default=None, description="Enterprise value-to-operating cash flow ratio"
)
ev_to_free_cash_flow: Optional[float] = Field(
default=None, description="Enterprise value-to-free cash flow ratio"
)
earnings_yield: Optional[float] = Field(default=None, description="Earnings yield")
free_cash_flow_yield: Optional[float] = Field(
default=None, description="Free cash flow yield"
)
debt_to_equity: Optional[float] = Field(
default=None, description="Debt-to-equity ratio"
)
debt_to_assets: Optional[float] = Field(
default=None, description="Debt-to-assets ratio"
)
net_debt_to_ebitda: Optional[float] = Field(
default=None,
description="Net debt-to-EBITDA ratio",
alias="netDebtToEBITDA",
)
current_ratio: Optional[float] = Field(default=None, description="Current ratio")
interest_coverage: Optional[float] = Field(
default=None, description="Interest coverage"
)
income_quality: Optional[float] = Field(default=None, description="Income quality")
dividend_yield: Optional[float] = Field(default=None, description="Dividend yield")
payout_ratio: Optional[float] = Field(default=None, description="Payout ratio")
sales_general_and_administrative_to_revenue: Optional[float] = Field(
default=None,
description="Sales general and administrative expenses-to-revenue ratio",
)
research_and_development_to_revenue: Optional[float] = Field(
default=None,
description="Research and development expenses-to-revenue ratio",
alias="researchAndDdevelopementToRevenue",
)
intangibles_to_total_assets: Optional[float] = Field(
default=None, description="Intangibles-to-total assets ratio"
)
capex_to_operating_cash_flow: Optional[float] = Field(
default=None, description="Capital expenditures-to-operating cash flow ratio"
)
capex_to_revenue: Optional[float] = Field(
default=None, description="Capital expenditures-to-revenue ratio"
)
capex_to_depreciation: Optional[float] = Field(
default=None, description="Capital expenditures-to-depreciation ratio"
)
stock_based_compensation_to_revenue: Optional[float] = Field(
default=None, description="Stock-based compensation-to-revenue ratio"
)
graham_number: Optional[float] = Field(default=None, description="Graham number")
roic: Optional[float] = Field(
default=None, description="Return on invested capital"
)
return_on_tangible_assets: Optional[float] = Field(
default=None, description="Return on tangible assets"
)
graham_net_net: Optional[float] = Field(
default=None, description="Graham net-net working capital"
)
working_capital: Optional[float] = Field(
default=None, description="Working capital"
)
tangible_asset_value: Optional[float] = Field(
default=None, description="Tangible asset value"
)
net_current_asset_value: Optional[float] = Field(
default=None, description="Net current asset value"
)
invested_capital: Optional[float] = Field(
default=None, description="Invested capital"
)
average_receivables: Optional[float] = Field(
default=None, description="Average receivables"
)
average_payables: Optional[float] = Field(
default=None, description="Average payables"
)
average_inventory: Optional[float] = Field(
default=None, description="Average inventory"
)
days_sales_outstanding: Optional[float] = Field(
default=None, description="Days sales outstanding"
)
days_payables_outstanding: Optional[float] = Field(
default=None, description="Days payables outstanding"
)
days_of_inventory_on_hand: Optional[float] = Field(
default=None, description="Days of inventory on hand"
)
receivables_turnover: Optional[float] = Field(
default=None, description="Receivables turnover"
)
payables_turnover: Optional[float] = Field(
default=None, description="Payables turnover"
)
inventory_turnover: Optional[float] = Field(
default=None, description="Inventory turnover"
)
roe: Optional[float] = Field(default=None, description="Return on equity")
capex_per_share: Optional[float] = Field(
default=None, description="Capital expenditures per share"
)
class FMPKeyMetricsFetcher(

View File

@@ -7,7 +7,8 @@ from openbb_core.provider.standard_models.market_snapshots import (
MarketSnapshotsData,
MarketSnapshotsQueryParams,
)
from openbb_fmp.utils.helpers import MARKETS, get_data
from openbb_fmp.utils.definitions import MARKETS
from openbb_fmp.utils.helpers import get_data
from pydantic import Field

View File

@@ -1,4 +1,4 @@
"""FMP Definitions"""
"""FMP Literal Definitions."""
from typing import Literal
@@ -76,3 +76,85 @@ EXCHANGES = Literal[
"wse",
"xetra",
]
MARKETS = Literal[
"AMEX",
"AMS",
"ASE",
"ASX",
"ATH",
"BME",
"BRU",
"BUD",
"BUE",
"CAI",
"CNQ",
"CPH",
"DFM",
"DOH",
"DUS",
"ETF",
"EURONEXT",
"HEL",
"HKSE",
"ICE",
"IOB",
"IST",
"JKT",
"JNB",
"JPX",
"KLS",
"KOE",
"KSC",
"KUW",
"LSE",
"MEX",
"MIL",
"NASDAQ",
"NEO",
"NSE",
"NYSE",
"NZE",
"OSL",
"OTC",
"PNK",
"PRA",
"RIS",
"SAO",
"SAU",
"SES",
"SET",
"SGO",
"SHH",
"SHZ",
"SIX",
"STO",
"TAI",
"TLV",
"TSX",
"TWO",
"VIE",
"WSE",
"XETRA",
]
TRANSACTION_TYPES = Literal[
"A-Award",
"C-Conversion",
"D-Return",
"E-ExpireShort",
"F-InKind",
"G-Gift",
"H-ExpireLong",
"I-Discretionary",
"J-Other",
"L-Small",
"M-Exempt",
"O-OutOfTheMoney",
"P-Purchase",
"S-Sale",
"U-Tender",
"W-Will",
"X-InTheMoney",
"Z-Trust",
]

View File

@@ -3,7 +3,7 @@
import json
from datetime import date as dateType
from io import StringIO
from typing import Any, Dict, List, Literal, Optional, TypeVar, Union
from typing import Any, Dict, List, Optional, TypeVar, Union
import requests
from openbb_core.provider import helpers
@@ -201,68 +201,6 @@ def most_recent_quarter(base: dateType = dateType.today()) -> dateType:
return dateType(base.year, 9, 30)
MARKETS = Literal[
"AMEX",
"AMS",
"ASE",
"ASX",
"ATH",
"BME",
"BRU",
"BUD",
"BUE",
"CAI",
"CNQ",
"CPH",
"DFM",
"DOH",
"DUS",
"ETF",
"EURONEXT",
"HEL",
"HKSE",
"ICE",
"IOB",
"IST",
"JKT",
"JNB",
"JPX",
"KLS",
"KOE",
"KSC",
"KUW",
"LSE",
"MEX",
"MIL",
"NASDAQ",
"NEO",
"NSE",
"NYSE",
"NZE",
"OSL",
"OTC",
"PNK",
"PRA",
"RIS",
"SAO",
"SAU",
"SES",
"SET",
"SGO",
"SHH",
"SHZ",
"SIX",
"STO",
"TAI",
"TLV",
"TSX",
"TWO",
"VIE",
"WSE",
"XETRA",
]
def get_interval(value: str) -> str:
"""Get the intervals for the FMP API."""
intervals = {

View File

@@ -9,69 +9,32 @@ interactions:
Connection:
- keep-alive
method: GET
uri: https://financialmodelingprep.com/api/v3/sec_filings/AAPL?apikey=MOCK_API_KEY&limit=100&page=0
uri: https://financialmodelingprep.com/api/v3/sec_filings/AAPL?apikey=MOCK_API_KEY&limit=100&page=0&type=10-K
response:
body:
string: !!binary |
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
9Zs//gOVodQuXyoAAA==
headers:
Access-Control-Allow-Credentials:
- 'true'
@@ -91,9 +54,9 @@ interactions:
Content-Type:
- application/json; charset=utf-8
Date:
- Mon, 16 Oct 2023 12:22:28 GMT
- Fri, 24 Nov 2023 14:19:55 GMT
ETag:
- W/"977c-sLYrvzws+K6VGEtPezzXukIfaXM"
- W/"2a5f-zN/RtuH6u8nPt7fQ7N1lE3xuQcI"
Server:
- nginx/1.18.0 (Ubuntu)
Transfer-Encoding:

View File

@@ -0,0 +1,662 @@
interactions:
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
method: GET
uri: https://financialmodelingprep.com/api/v4/rss_feed?apikey=MOCK_API_KEY&page=0&type=8-K
response:
body:
string: !!binary |
H4sIAAAAAAAAA+y9W3PjSJKo+b6/AjYPdWbMupq4X/gGkiCFTBJkAZCUqtNmaywJlcktitQhqazK
Prb/fSPCA0RApACPQE8fWxPGenq6Y9Dh4tdx8Vu4/8//S9P+N/lfTfuP0+a0Lf5jqP2H//Nn7Wct
2X/fa/HuVHw9rE/Fk5Y9bordY3H8Gxl8/Lv2n7quG4blB57/X9p/Tjfb4vBf//E3mOmJ/AfoRKZu
Wj8bxs+mrenB0NSHhlN+st3s/qCffDudXo7DweDPP//8+7F4/PvX/fdBeHj8tvleHAfF09f1YUBm
Ww+4qAGVageW4Zimpdumq+vC0M9EHAz+vNk9FX/9/dvpuRT4uGHyqr+6/H/8vj88/9+nHy/lTy/H
T5vHP4oDHUzultn5p+139MPT4bUgA//v397FN15lWh6Nb5LlfDmLo0wbL9PVYBINGDndN2zXc5Hk
DH9omarkQBJQsjzPDgg4ywxcUxhi4NhgEzj+R+PBEQY30uBmaRTmWra8zW+iNNFGYULJkVWXjP/O
2Tm24+pIdro71D1ldkwSA2XotusQdoapB5YtDFF2MNjMjk2FZzfLRmNpduFpuzlqi/1pf9Duim+b
x21xpNsVdqtnkv+GAxQ3f+hYQ0t9t4IoRsk0AJxuOA4HB0Ns0bHBxt0KU+HBhYs7aW75+vC1OGmz
7f639VYLH//X6+a4OW32Oy3WxvvDCz/ufNuzHAcJ0CZrT3nTclGUlh44ukE2JwFIoHnCEACkg40A
YSo8wHwWhj3BrgTve4RdEd5KI3zYH79tvq0PryXF8b6kFviubiOpWc7QVD/7QNRbTcUJ3EtNhQw2
U2NT4ak9LBUu3E+vL5tTcdDui+12VxxrCp7n6oFuYbERBU/5quWiLrE517A5zVcGmwqP7dPtSn67
9tgoNnluizDN44T+nzjK85D8izxK43CeUQ0PFLzAcHWkcuwTm2JoBqr4QNL5OHM8Qo8odq4tDDF6
bLBRwYOp8PQW84U0u3Cz29cWGvlv2HOxC013hoarSoqLYqvKdezAsellYHu2KQzBZUAHGxcaTIVH
FcaLSc8Kz0pyUxrmiPCab34rDqcf5c35suVWg+Ppjo/TNzxybQ5NdX0DRA0qqXQ36jY3+2EIdiMd
bMQGH7+HjfziS3LzUf4gqfj25M7kRj05RXKf5cgRbNFzQUyF3UkbbfbZfvtKjYSjcNJZrufaOGXX
I7rH0FbHB6IGlVSGz+DqBwxxfEaL+gEf40+6aCTvlbvNJtpqfTjtisNRm68Al2vorm+icLlDxx3q
hjIuEAXeI6KamWS1mb7pOIEwRHHBYCMumAqPi/z0lTQvbpPe7I9E2V1vN2SvCqaoZ5jEREGCs8g6
s5TBgahBJZWtM53vXBji64wMNqu57GM8uFzepOqxUWz3CtbBL6/r3en1WZtudnnx+K3mBqEIuTVv
6V6Aux/coWENLWUDgYtiuEzDCnQyte4HnuELQ4wgG2y25tlUeIK/TPPw59ukZ9iRYb8KuxLsAXYE
+PN9Ls1weRelWb4cf/77eLn429k7YhhEvoGzYwk1Y2joytRA1KCSyq8PTxg6Xx/NDmD4GE9tmeXy
inES3UOglZsRru3puPA00erMoeErkwJRg0oqI+XwGBgMcVJksNmMYB/jSSX3mfwG/aikpEGFxddt
saaWVv6tOKxfCmJtPdb8Sq7lmhjzwdRMfWg7Q13drwSi3poPtmlfmg9ksNl8YFPh2YWzeSQN7zaJ
82iiZXmYR5kWJnm8WCYP1bqj7i3HQqw7xo4uPXV2TNKFS87Ur7jkzMb8Ef5H49HdhosHaXSjME3u
o/mcnPuT2yxPaQrJ2UFO/gTdRmEzAnL+d8m7oYJgddm65RqEmmM7ujjEqLHBRmrwN+OpjVJ5zXd0
2Gy3G6J6vKNweL7nGFhuujE0bWVuIOqtwuHWdBCucLhtcRk2lQy5edyjU0aX9uyU2cmH7Ht2JTt5
M/UmCtN8HiYTLfqySqNMuCG8IDAthJFAsHlDR++Q6wWSBpVQsBEccai0ERyr8YqAj/HUbvK5fFyQ
Osx3a+1mv33a7L6CJsdvVde2MQFCxoxG7ZXd5kwQW1VizN4S01vLmL3VcquyqfDIsnlyI+8G7plJ
I5uQv1n7vNt8/XY6Xhxt51SuwPIRiazd4XFRl/i8a/jaUrnoVHh8k8+Tsfy90PMT+cnfDT0/kZ+8
g2S23uy090x+3whsw0CCM8k/1M1WEAXmVpWCTkaFIQoOBpvBsanw4GZh8kWa22L/9LpdH7RF8bR5
XG9rCTie7XtoasTYV9ZJuKgLVc61haFSlWvO6+JT4aktlhN5peSuOJyKv7RoVxy+/qiQkT9Pt20P
i8wZVm4JWWQgieEJgsDyDMiDCAJhqMyDCBrXGf+j8cTu8kTe5loukvgX4TDTTc83XEQwoURlKrvF
QdLlWWZfO8uakwZhKjyq5eIXeRdmdiq229ejNl6zaDRZXuQHnZ5pAkllZzmOEejozWl3SVkFUZdH
mnPtSGu2s2AqPL9svJDGF0aLKI+zvwnGleWbBiK/pmSl7rUESbCuhMdbpjjElhobbDau2FR4VOEi
lz/9w8dv68OGHGe70+uhOJ4tBr5R41LvsB0dfapZQ135VOOiYGFVHnPLEoe4x9xq1TvoVBIA724U
/CE9wTrBHmBHgPKmAwFVwHPf0+b3zSOocIPTX2XQPrBs9EVhdXh9yUVdknOvkWt5ScOmwpMbL9Nf
e3Kq5OTADUJqry6TcD7JlgmBKAS6zACVyQrIzKGtbDIwQYNKJuSxmq4wxNVfMtjsXmIfvwdsEF4i
m4zlj7kR/YHamJx1X8npVj10s83Ac7HEDGdoqCezgqhLPfjKk3wy2LjIYCr8IhvNk8/y27NnJo3s
Vsu02Xw5Cmkg+i7K8mUqRhkc20f7QQg49VsUJF0owtwNUleEm90g/I/Gc5ulS/mllk+n2oostOf1
I3Mfrbf1B4IWOVOxbnPD7hCe4aIuXCHONVeI0/IUn02FJ0cgKMQaluM4yh+0VQjxrGW6WqZhHk3O
vjfPwa85Y+h0SBpkoi7RGdfQGS2+NzoVHl22XMn7LMP5Kk4izdZulvNJnMwyoWqG4equG+ACqR7N
GdHVs85BFINEk4x8ys0KfP4CGoZgs9LB5jQlNhWeG0Egv+T+ddzok3v1rfoeN/sat5b0rn8Lt0WU
xuOQFmnJ8yh9qArePFAtDvxKnut72JOOvoZWj9+DqIu71bwWIjTbnnLRqSTwjRZzaXzj5SLbfy8O
xebrrjRTxfAMzSIwsTaq7g0ddXsBRF2cdLZ75aSzm3csTIVHRynI3689u5Kd/Lbt2ZXspNFNwntq
os6i5ermISNn35zQPGfc4OMPuksMfFVqIAksecF/XvqYav5zvdGhxP9oPLTJfSaflTn+8depOKw1
+hJkt9/uv27qVeS8wA50NDmn03s4JmpQSYVkpcAQhspkpaBZo4OP8ejGD1/k34CQWzW8v51PBX1E
9y0LHaihtNSD9iBqUEnlqV2BMHRO7WqOPcPHeFr0R0vTorXitOVUI9syni7TJA5FRc5wA8/DZjvo
+tBQNxxAFGzHKm3a9mxhiIFjg83HGpsKD44ykAaXEaWX/ENbhOMbqgmTM60qxmJaLn696R2ehoCk
S/1NVOnO+ltzJiFMhae2iMfyl8FivVufg6rvpdh45C/HupYoPXX1F0TB1iTnuscSMcnprgtDsFvp
YLO1yqaSwBeGvyjocOvD02avTTbrr7v9kebY1DMMPxTC8SRWcDP1S1BcgvLR1p6fyE8+C6ffwm+2
sDTBfgWKK1AaX/b6/Lw5aYvN0/F0KNbP5+oufzuXd3HsIDBR7zMNl9iyXRzGIGpQSQWFmT8qgaFS
YW55ZQ4f4+lli7m8JyDfbA/rH9rosN49vYlPGBbygSFj1iFUDaJAxRMjO2Kw5xzZaTHJ2FR4Zvk8
lX+YSRNLfvxFHwQfr1RE9x3DCHBbFcApp4ZxUWBRCDF+/UotITLYvFXZVHhwd/mDfG7Y6tvr7s/1
oai9nXYdskOwuDqlBIMoxkZ0rvNaBnXnekstA5gKj2t1c5vI63Yfm5c0rvT1+bdtsdyJuJzADVyc
7Upw2V0uThDF2IieTOuaJ7Ml4wamwuNKFyP54ENaEH1jtt8/aVP6T+P988t696OutBnE6MbCM4eW
ckI1F3V5lF2pa0AGW7QOOpUEvJm8n2l8m90wdwlRMX7S4mSahlme3o7z2zTS8mUezrU0Iv8m0aa3
yYTnmNC8eJxTndA0hmaHHBMmiqEje9NiuUwm9cAJQ2wpssHmHBM2FZ5mlsoX3w+/r7dE36U1IPfE
nihEzdelfzlWd6PFztU3MIi63MDXgv1Wsx4CU+GphXdjecOhxxbOR/Ie9WkexoTdZs2MrPnp6XxV
6BYyB4wgc7u8hQNRcLQJ3mH3SlENMthyVdCp8Mjor5dfaj0zedOqZ9Yjk0Ymr/XekZ9AYN0Qbe50
WWav1ORMy3ewuoftDC31ACuIAtNdiOEY1975Gi2PHdhUeIB3Nwr+o55fjd/PqfyN2iOsI1SwXXuE
dYQK1TLv4yysNdyr5XDSp49YU5ag61AvDUTBdVEl53g897WWnOO15b6yqfDoKAJpblk4or7M03uV
DiwL+9SVkevgCAZRFBNrvOJRS8J1/MARhtiiY4PNi45NhSdHICjkIPboODppcl/qnVloYYoAlyNM
SNlDvYOniYkaVFJZhMZ0XWGIR2jIYDMp9jGe1JdlplAQ/aOikgaVhg/5TbRMLruuQh6TQe8i9B1g
Dh11nwiTdGlEONeMiMbdyP9oPLhU4c3+ZP3H/rTWZvvtU03dcEzLQa+0Lk8vuShGR3fNgK0007A4
MBjiDkurGRifCg9sMlZRNj44Mmle92m4elc5I7dVYKLvSbNLPXMQBUqsEGvmGcH1WLPeUuGRTYWn
RhFIc8s2X3c0OX+y/qFlL/vDqX4XBL7tocEZXZ6tgihGSfTy8urJdS9vc/VkPhUeXDaRvwuS4k9t
sX/dnWgRr+lmt949FkKtGztw9QB9Fehd+g6AqEtz4EoHaa/lSRdMhQeXLKbyOzU/rHfH/WOxFh1w
tmM4yAR9d2j5XZqngqhBJZUpHUb5PpoNcaXDaHsfzT7G80rjmTSu0Zr8h7Wb16/fiuP5yZtHn9+h
cXldnryBKFhLVeDUDMTSDzxwara9Z2BT4XGNPsunC44O6+97bfG6PZE9Sf0brBpVeZ7Ztu1b2G1p
uUO9w7ZkouCuDIh6Ck0IylZJMFQuM785dwamkuCW3slHYNLin7TPWy0XxNEDE53cYDlDW92+BFEX
p5h/pQih14ILpsLjSn+dy+tn9Ee8W7zRsTwHz83qom+AqEt941pum968PWEqPLcwVehbFo6idJwu
F6M40n7SpnE+vqFvQQaTaADsyG7wbWz+kWUMHfWbAEQNKqmQS+m7whDfomSwkR18LMEukc8JSdan
1wNNqTmQ2/Nw1H77od1tTuvnDfXhnk7rr/W9S1QgB33UGeSWUOYIot6uQT+4UjnDby7syKfCc0xm
d/IaSHgXZ9rodjKLcm2WLm9Xgq2geyY1p/Hc1N0fIOniZvWuVFAig41GPEyFxzYO5W/W6eZwPGlT
ovM+QfhUuFYNy7ewcQNLHzrq4VMQdXlPiFfH+Z5oPu9gKjy06fR+IU1tsT/tD0dqWGmz9XMh9lD1
TcPw0EqcPrSVVxoXBdtRiLbo4lAZbWmxSmEqPLVFNpOn9svrerv5WrxfHtl2dWIe/1vYgahLdtdq
PjQ3qOFT4dn9Mp/JR/qS/fe1Nt/8XhxPP7a1K8H2LMvCWvMdoTFRl9CuhfeaH5DzqfDQkrupfMp9
tPteHDaP7+Tc03rDZN3/O8iBpEtw7jVwzZcCTIUHFyV3ki22CbjlfKKtlnGSE10uCZNxzOoVlB5x
z9YDZD1uxk3dDQKS4BIQPOLOtffQzTWo+B+N57ZcLeWVueVqFiVVx0UzsHxkDxoApa6tgShQcYX3
VzyiXH9/ZTdrazCVDCmF82wxW5bd26ujLNBND/0IhvJS1zhAFIMjtnUzxCHGiw028oKp8LzIT5dP
u4+Tu/huqeU3URquots8HmfnqlNsa/69XHKmY6ANBIJQ3bcLoi6UNs+/orR5zVcoTIVHSGFIIxzv
nwpaxJFYVtTPO97vTpsdLWY+Pz3xdA+bZrn/W+CBKEZKdIzXCu6VjnGjOdcepsLDG48VKj2sDuu/
Nkfyf4rHzZHaCaxPA+FXb2npk20jAbDDYwUQdbn6ruXLNNtZfCo8wFUaykdLyWUaJVmcP9T2sBjP
clzPRKerWp3qjICoy9VnXlt9ZrNtz6bCwyMcJOMyrMxvkn5prItpBLbuSrBTLjbCRV0uvCtVlMhg
s+ucTfUeu2sVfwkGyWhgT69OTz6TJvx62Pz+QwgD+rruyjhGjA5vdZmoy4167ZWM0VJJlE2F36jh
TMHiGk+zkTZa7x4JLL7CBouQO4F9LzB0bJoD4aZe9ZeLopBqiW3gBH6T2NbiBIap8NwoAmlu4dP6
5cTTeIXEEM90TAfrTDKDLqm7IOpyY14LOLeUnoKp8MTCG4VnbGSR/Vjv3mn+6Tumi+xi6dKOUR3C
DSDqgpt7TQtuefkBU+G5jR7k7a4eG8WmkrvVk2PkFF4prNZ/QVe3ekKq4ZpoYnaX6iog6pLYNZ3D
bXmVwKbCE1t9Wcg3dRuny/uksu4rd5IRYCvqMWQdKjiCqEElFYKoliMMlUFUqzm0AB/jkY3Hn6WJ
ZT/lrIgjdVFWTQaMwDSxMWfT6pBVCZIGlVAecnaEoXPIuTlxFz7G08rykfzbjV833/eCU1zcloZu
6CbWFDCNoaOuo4GoSxecec0F12yEwlR4ar/GCrk0PTVCTd58sifaYr8tHll3z/cif66ju2jHkal3
AQii4OgXAvTOlfJHZktxWpgKD3A6WcjrHatwfB9l1fnGF5xpoPMZzE6+IhB1cXs613K4WnjBVHhe
9KdLujt6YNK8lt83u9Pmu+gPCkwH2d/ZJaSGurqiAaIud6N/bTc2a7MwFR7W8k4hhlB2ldGW02k8
jrRVulxFaU6fDOXpLVl1EFl2dcfG3geGP7SUPUQg6XKxXbMGmvnxP1qCn0Ka+GTzlRWsvN8ftk8N
7cVdC+v1oJUr1fOgQdTl8nOuLb+WFBo2FR7f5D7s+XXkJ1+4twdYAyh/W4yX+YPYVcC0LfSbBYpK
PeIHoi5RXSn2ZrY0JIOp8Kjoj5YmNdt85fV5nZrjg/ZiRRNzurzyAFEXxOxrl6vd4ipiU+GJzeJQ
xVX00Zn1wCSBqXhyw+2xOInqrkNfvuJhmcraGhfFyAiJgHaZVC8mAtotSfUwFR5WGMXSpKLv+z8K
HjmmwICXrVuejjXWadtT5bxJLoq50WpxPEMYOsfxWhLD2VR4XtHdUsER+bI5bE5auDls3+bG2IHv
GWijyu5gE3BRg0oqvFI7rzI6xP2RRssqg4/x1LLwLpKntrylSTGJ0IBHiyYxb+rMLCo9MPGxY8Ps
0IWSCRpUMsGXazrCUOnLNZt9ufCxBLtx9PNKYc319M70PvX0OtCTT0vt6VX05J9N9vQqevJv6Mff
iuf966F6P++anokOKdMOz+p6CYi60Eu84Ipe4rUEEdhUeFpjea/R5qZYH040Bl8LwbMi3dh4MuHV
IbsIRF0aCdY1IwFRWlwinhzfKNSm/PDE5FPYoidihR7PeeFvo3vozGajy0MrLmpQSeXHmCsMnY+x
lrRw9jEeGzm55RdaFk7pk/lZGkXJ1QcdhE2AfhND2Jnqb4hAFAMlJJs6/pWscDLY4mCjU+HZZbOR
fEr9p9eXzak4aPfFdkuMrXpKs6sH6HtTd7sElEHUpU0vPpQ82/QtmZNsKjy2T7cK+cw9NopNnlu2
2X4vDl8JinPftSoBkBzHho59d0XBqR9xIOqsaDgee+pnuLowBEccHWwEB1PhwWXyLqSeGqH280re
sxtufysOp+N+d+RNdt50v3ZtN/CwGQ06LXCvzA5EDSqpcKnyRxswVF6qLY824GM8u3Asv+TG691+
/8a/iy6vSEmpx6hAFKhoQjie16Oph+Pt5lUGU+FJzZaRQj7Dh0Ulf/7P41U0n4fa6iZMF+GYPeAL
50J+rmV7po3ekc7QVr88QdQAfrwVmC47zRxePRaG+GlGBptPMzYVHh7BIJ87Gf2TMNmee7xWsQSf
GMo6Vr0lzNRrCXBRlwqHWAXkrHA0q7cwFZ5Z9OtU3rc2Lk6vx7YWr4Hl4kumUH7qtwCIuuDnX4td
tRRzg6nw/MaR7KNRCF6Fm0NtzZWnHDGIkX1xu0IDUZfQrtSZIYPNpxybCg8tulvKuyRvk+hL7enB
h0JGf700smgejfM0XETjcZiQK0G7i27i8TyqmfGu5XkWNsZM+Sk/RuCiLi7W2lB5sbZ0lISp8Pyy
5Vzhbuj5ifzkFZNkmU6X889COKEq1aObhovNA+mEDSQxRI5jej68GLL5IysY4jaC3fzIiv/ReGpJ
Ju8SD7ebr/vju+8RvCCw0e8RKLcuGdBUFKwtwdFbLreao7dlucFUeHDhfCZ/rX7e7HbUmh9t9i/n
xJAzNs9zJbB1yaRhooCRUBrKutZxzWpLHKdT4bF9TnL5HIewOGTrrVhN3HNsckFJwFIPJoAovhN9
i4Wr6MoyhCEGiw222Fp0KjysMJvLw4onYe0tn6E7nu9hSy5QVup2KYgaVFLB2cHXGgyVzo6W97Xw
MZ4V+dnSqJL9b+WqetmWRlXgEIsQS8vu0rIERIEfSOj2zYsX17t9uy0FKthUeFqJ/Lr6wLBG81S+
nt2H5pX3vKR43cvzGi2zfJlo2TiOkjyexmNBdfV9xzPRqqvd4WUBSBpUQuHEdwxhqDzxnUZs/GM8
tlEmb2/20LIvKgGVaDW/Fby1umna6Cxm3eri0QBRg0oq5OMajjDEWZHBlsA6/RjPiv5maVLzeBSl
+YOW5WGq3abEKr9daD9piyin/m7BKjc80/AkGJrq3lsQdekVEiPIZ69Qcz4MTIVnOB9lkoXVWdG1
+Pn5dbd/Lmq1mk0d/QpZN7tUDgNRl16MayXVrZbMeTbVe7yuFVqLF8lCjhi1kB6368Nmv6sFOS1H
x+v9Rhc7HETBTelbtImqRb2HvFUVDMEmpYMtbh86FX6BEWNH/qnUx8Yl7yXLil1xzIvHb/WCpbqF
P8CMLh2qQNSlcqZfU85agLGp8MCyJJK/BG72h80/9zste1k/FrXwU1wLQNmuji6irutdCIIohisw
g4AR1HWTBwZgiF+jZktgAKbCE7zJFIKe2U/3ZNUVT+cEZdvzTGRZAGfoeENT3RQAUUwXExOUnWsF
EJ1mryJMhWeVhQq7k2zJ4oWtrtlh/+ebZubnAs2+Sw4W3HYlAI1OrZGZKLayxGRI91oypNsSLWZT
4QHGs1yhk0tPsE6wB9gRoHxxhZ5gnaD8MXibxHk0qdWyGyxvBjyYR3QqZHKWw5QVZXAg6cJ4KJ95
14yH5s5C/I/Gc7sdjRVKPqXRdJkuCLl5nLCiPJPbcU5NVSCn+7RMIYocdcB1iEv5ZZHE2rVrmVeu
XatxwfG/GQ9uNV/Jh0FXxemOlrUrk2RqveZoWwacsWrTfeoo+5K4qAvj3r3SH8duaRkPU0lgi3L5
tMkeG8Umf7yNyZ+9/XE8vSkRDteCTewenO1KNqjXJRccRA0qqeC75AmVMFT6Lu2Wa4F9jOc2nmeS
j4SYL2m6XCy1+2U6n9zHk0jsbOjTTDrcbWoNnaCDCw4kXaw255oHzmn0wPE/+j1q1zxKBID86Xa3
uA9TkZZhkI2ALMJgdesDyUXBhVnldeiBJwzxvA4y2OywZFPhFxn53dKslrun9fFasqRLZPu4BWYO
HafT4z0m6q26ZgdXusWTwWYXCZsKj2yZKLSLH9Nmt1dbpAWGZdk4r4g5tL0OnYRA0qASyuMwvjB0
jsM0FwCEj/HMxtNpLM0sMvcvxU5brQ+09dK1C5TqPTbuIiDs3KGhnhcDot6qa16gC0NcXfNaFhxM
hYcX5fdLleJFPb+KnzS8UboMJ/fhw9s9+49/TKJ//IPHBWmACLt1adtbZSuLi4L7oMphs/h5V8th
s1rwwVR4fKMHheqdN8Xzy/rxca+N93+vrbjAtJH1Yk1WQKvDimOiQOPwvMBiT9F8jxcBhCEedvBa
igDCVHhkNyuFNiXZIs5vtEU8mYfJRLwgTNtD9ukz6UMrdUUEJDE8QsFwt9b8kBFjg80XBJsKT4z8
eMnuB6Dshjk14T9F1IQPx7/cxlmcx0shr9nwXUe3cQa9SaMOXbpHMFFgGZiBAU+tPNcyhSFYcXSw
xYdEp3qP31W1N8xlX3j3CC8R9gS7EpR3Bt+mozDRlrf5NM7zKK0yb3T6xxo4bsbQDjpYFCDpQj9x
r+knzTFX/kfjzz7y8xVi+pO7KMm1PBrfJMv5ckbLipePsUQz1rNtG1kUhBA0OhDkoi5XnnFt5bWk
jrOp8AjDSaJQpqFn+IahNMEkpAcd0Y3T25T8820ez2NW4X68pMXuyf/3LuIadATH4WAy5jEKGg9A
vkelkZ1h5QWSpcoEDSqZ3IdnC0NnH16zWgMf45km6a287jwmCH+NkkwwPbIovYvHkXAyelZgI5Mp
qG+KmB/K9JikQSUU8Pm2MFTi81s6q7OP8fjGv07lXS3TMF3QS4RmIabjmzAht3KtwYfuO4GBC47R
JJShoZ76BKIuzLayn1jNbGvpJwZT4dlNF2P5ylxxksUzWlswe8jyaMHW22CRcP+x51jIFpP6UCeX
sbLtBpIuDBFfHCoNkZYuWTAVHhvR3RTqCy6TbDmPJyGNyPKyjFX2sO35yKx+ne1U5YgiFwVr6+xI
NgL70pFMB5uXG5sKzy2SNN+oH3lFTrbpa7E91mI7pumhrF2ywqxOMTEQNaikwsEWiEPlwdZS+xg+
xtOiP73nJcdLXr8b74/P+6uRCtuzDVwEkVLr1H0eRF26ocxrbqhmewymwlMbL7OFPLRw/vMipLkR
0+VyImgc5OA1XJTrDph1cBa7vNiW4bim54K+ZnMdGIb4OrObdWD+N0sQC+fyxEaH/frpcf8svm62
aOotkpVhdyqOxERdqBhlr5OaitHi5oSp8LTCu9lS0mHy0XFJ07qL0pjYqII+xsPTrmvhYq0UmTU0
1JGBqEElle1Hw7WEIb4fyWBzeJp9jEd2lybyca9F+JBpn7R74ehybMPHHvf0Tbiyxs8EMS6m7noe
qylLlC5xCFDRweaji02FR0V/tTyqzRctL7bF85pMdKwSLx3PDQyUx4MC04eOus4KokCxFx47WOLj
VIaMDTZnJrGpJJDFX+RXV/jysi2ITnFkxbY2px9aGsV5rfSz4Rs40xzgqZvmXBQzxEWvpSXWGy+9
llZL6Wc2FR5euFKo1tB1axJNTBnWO1vTvbY1GzPg/j1bM/y6fim0MF+JdUAMy8TlJAGsDioYiAId
VUjhMsWhMoWr5dEWTIXHFUqHYWjA+fU3mkr+KiYK6oFN/oEyvYGXrhx2AUmXG/FKNq/Xks0LU+Fx
3dyO5D0Wt6d/EkVsvXuq1zKyAsdFvTrSfWpMdlAqQNSgksqVClMYOisVzcc+fIwHdpv/Ks0r2X9f
195P2q5nOLgb0mcWpPpWBFGDSiqgMnxhqERlND9mg4/xqJLlnXxQYPIwXi7ICT+5zfI0Fp3W5JA1
ceE8As10O3TLZIIGlUxwVYAdzodKV0VLiTH4GI9sIt/+cRTN5/H4dvG2XuzfhIKxjm/jqq1zcurL
DURdKmTXXp9aLeWv2VR4dqP5WN78diItfCK787F40haEx2Gz3taz3HzfRW9Vs1MrDhB1eQ1ciyK3
sIOp8OymUSjPLnvcv57Ga9EWd4gehEtVILSMLgoZF3WhYxjXHiUYLY8S2FR4Wtk4lw/MRbvTYf20
frcUoOsHlodD5w0tZ2ir5wOCKDDAq2dXDjc0a8+unGZDk0+FR5enCrXH5htaov6HNtvuf1tvq5JH
jqc7PhYZOdc6pFCCqEElFdyKHBkMlW7FFmTwMR7ZfJQ/9MzkmckHMHtmn6WZwUvSmyic5zfaLF3e
VtUU6ZtMD2kReKxMiDI3kDSohILKBoccHypVNq/1IaknZRAk8tWvw92PP8k1UGhpQdZZdDwRIsJV
QHSjAOnWoL1KOnDjogaVVG5JiUNnS6p5vcHHeHA3S4WSUeH34vBDmxS73ebIK9Rz60D3fQPn0nbp
S5guFcnAeOIiYaVB5jMfKlea0RjH5B/jgYV3CtZBGsZJli/ThTYmhoJGCzeHK2YjiGXXDcvzfZyF
wAps6crpVFzUW+XDDq68+SaDzQEBNhUe4Gg8j6UJLsIsXizFlHqi8Di4U40V11JviAOSLlGZ11C1
+IXYVHhU9FdLo5rEszgP5xq/Dd5mM5cWlWs7Fs7DDcXJ1G0EEHUBsIxH1QC2VPKBqfAAJzfhWD7p
oCdYJ9gD7AhQPp9+dtj8/vtmx1QTogvnh9fjqWan6rproo8/o8Pxx0UBKCHcblypvW61+C5hKjy7
WapQ2WcSRSntslmWA7EMx/Bx6VVQ9U05cgySwEaoCq+b/KaoFV43W24KmApPaiIfyBt/o//pnZbt
X180IkE7fSvov9lSJwmxwNab3XOxq686L9BdhIJiaNQHZw8NdZUYRF3sWM+4smO95ix6mArPcpxl
kXz0qsfZgFM+AtHjbMApn3ja42zAKU0zTMc32jhcMSUH3C3zfHKOWHu2j4hVGJrh0/JTlc9blhtI
GlRCwW1g+sJQ6TYwW6pBsI/x2MLxbC6/qXtulJt8BmHPjXKTxjY7FOuTNl//URy1yYH8kIKohZP9
4x/Cs17LI4AQ+TiMnmUNHWV6XBSwqiKLbolPjCy6zfj4VHh8s/lE/nFHGj4slslE+xQuIvElW+VZ
Nsk/ITzyjJ3hDC1lfxVIGlRCwd8HHiw+VPr7muvB84/x6NJPU5X+Az28Mzz5EFAPj8OTJjdfTGt1
WJcvL/vD6XVH/l0hNuIlljomW5/x08mdoZxVx0VdnnpX8lvJYLPrhU2FBzhfhPJ3bVass9P6oC2K
p83j+tzFWEjh/Bjw4vGtguu0X39v1l+PsDNCee9pv4fPe1ia3af1bv+9ONRKzDkOxtcMrKwO5Q25
KGb1O57je+zdoMFDvXwIbls62MyKTYVn9SlJ5UsD34VzoqjkVzNlDd30TITfmWDzWMkRdWxM0qAS
ylNlTWGo1FGaE7H5x3hqdwv5AGXy+ttmDanrZJWdisNuTQ86sk+FTiO+bzgOIjjE6Fne0OmS8UlF
sRUmFCK1rCt14622cktsKjy+5HYUy98QPcAaQGl+02gRZg/CS0yL/oU4c8Jj76KVQ0RcFAMjRNNM
T6wwwqNpZLDZCcCmwrMiP1s+daX7S4ASmnpnh3deAhjC0Pl4a3lM/t//EoBuyK8HAuFJuy+2211x
PNbUOUEH8TzHQSSYdQfIRcESExzsluhzLx3sLY8MYSo8wvtorlL5tsd4ifFW3uPeY7zAKM1wuUji
X4SrlahIvuFiTz7THxrK3ieQxLAIzyssX2wDwY4+Nth49MFUeFLLxS/yiaFJ8eep+EMbvR43bKll
xeH75pGYrYJiQnR4VF4t0OtkeoGoQSWV3RwmX2gwxG8Os2Whwcd4fEl0r/JGsecn8JPfqj0/kZ88
vvSLtvq2PjyvH9nTqDev8DwjsHWcI4CQs7qQA1EXV4R5LWGvJVYGU0mQS7/IZ//05Dg5BWs2us+E
gKzj2h6mUzeg0ru8wANRg0oq254Of3YBQ3x7Oi3PLuBjCVT3mXxM8aOSkgaVFj92++0TPfZ3x9fn
4qCtDvun18eTWEDO810bk35c4uvg1gRRsAGroo5OmX4sFnV0WtKPYSo8vjR6kLcabsaL96wE27GQ
fnMGrfJPSEMDUReektpQ6SlxW3zBbCo8NPL75XdnD22h0AOzp7ZQ6L26IqgKcqzVOusNFhN+srmB
i8vc5OTU3ZkgCm4BIeW9VHprKe9+88UAU+HJrUYKlaXzw2ZHC36ttrTP+WVZUd0zbQ9Tb4ixM4IO
rmCQdKmoXfOCmG2ZJXQqPLp8dSOPLn24e5jXaqQFJqZUApDyutVIo6KAi+8Y8B6bblJhiIcFzZaa
HDAVHlVKfrQ0qtmhKHaj/V/aapl9JFiz0fKLNKx/fTTLcIa2ctmh96JZutiqsIxm6S2XgUI0S/4K
7QF2CweOf5z2kx874WgzPMfFtDj7F8ACUZcXqHPtAm15iM2mwsMaPyiEt2aH9Y/j43pbaKPN6XG/
2cHbRO0/R/n4v0oHm29jHtyV9NSTM7moS8NKtLXOhlXzwxGYCk9vNlIoP5QUp8c1qzpau0htD1PJ
HHjZXVYbiGJwxHiCd6Wrr9USfYap8LyS8Uqh/vsHBybNazq/zW7iZHa9Ty3RMC30xrQ6ZCSBpEEl
FEL2ZfdQNlSG7Ju7WPCP8dim01h+X2bECnjdrg/UNJi+nl4PhZYXj992++3+6w9aU7lceabpB2jr
wBwa6s+uQdTlLepcu0WbLwaYCo8wm83lL4ZwHE7i8G39Q6GHiqejOnABOmPoqG9aEHVxK3jiUHkr
eM0F4mEqPDpKQRrdv9CaJ+DUm2y9a82LQ2dlpPm0+7dY8+EfBVV93yvm59BkW+xuZfX8lMmBqEEl
lR94tjB0PvCalxx8jCcXfh7JOyunm6eCVT5PSlthutmR5bdZb0WClmX4aLNVd7uUyAVRg0oqz/Ky
haFzllczQfgYT3CayD+0uS+O1NLSbnfMa7nkvFzHsNC8nA43LBcF92mVVu54V9LKnbZEQjYVnte9
vMMyntZPt3KD2p6NPdp0e2h22aBUFLCxLQtqx+u2YwhDfHmRwZYNSqfC44rT5S/SwPLD+qn4s/hN
W6wPfxT1wJXjexbWOqWdHZRdlFzUxU3qXrOvmlMv+VR4arm8sZARxZfqvkT/uKHdj8iurNrQmpar
Y/0ftKODMjSQdGEyuNdMhuZeK/yPxjNbxGP5SzRbjlItW/9e1Gqjm7RJEx6XetEcLgrgCN21bEMY
gp1JB1sUXToVnhf96dK8kuLPmx9Ph/3XolZN3vIMw8SGq2hjB3U1DURdrrBrWYEthfdhKjyxJLqX
L7Q5W78+fttDb4dzzKV2DzhBEMiwU7cNQNQluytNMazmJtF8Kjy7uziRf446XpO/YPuuiuvbhoVO
ZuvWTgREXSgcrjhUKhwtOSAwFZ4cMarkXSH57WoVpfdhGtH6m8lETJ0hZwmqnnyJTVe340HUoJIK
ei2vtAlDpV7bUmkTPsZjIwSkqc2LZ2IOPNUuA9qSCdMNidByh04n0x1EDSqpJpQYEYc4LTLYSAs+
xtOaLxJ50z3eHV82h+KpoYyS4SDLKHF46jsURF2cbY5z5WxzmpcaTIWHFycKZZRm+/1T+tfVG8En
e8HAuSoZterWl6YGogaVVG66O8LQ2XRvpgYf46nNJql8vHR1IKbnXgtfT/tnZryL2EzTwHR+AGyd
TAMQNYCNePZPmoElDHH/JBlsxsamwmNbpZmCddBzo9ykseXpbZYTS2oUJp/hBk20B7CpLNoBBXsv
2G6HFrwgCYxOIUGrfDFUS9BqTqTnfzQeWq4C7fPm+bf1dqtF2+LxdNjv3nYZ0V0P+daKgHO6OCZB
1KCSagptlPjQ2a3W0mWEfYwn91n+Rpjsty/fNrv3b1OTrHkTp7gRcnYXSwFEgZbmBYFnsTwa19aF
IZ5HQwYbycFUeHKT+Uo+aZeZV/t6h7NAN330BjWGtrpNCqIGlVRQ3CxxqFTcmjs+84/xtGapAq15
sf59++Ot7jGYRNzH5hPrDhc6IOT0Ls1GQNSFXVWGT2t2ldXckx2mwpObT+cP8hdpj46jkybHW9qs
1ofTrjhc5j3HMVdHLCPwsNqvFXQJ+YGoC3XEF/O3SnWkJYMGpsIjnK0USsv3CN8ilN/APcO3DBUC
WkSrS4qabuJ5XmDjHvkRXH4XSx9EgfIrxGV4xZV6XKal4gpMhccVp0muVNDhIwOTpnWbhWQvPr8Q
m/VI9yXfq+SynZev/0zTQ0ZoGLwO5blB1KCSCjYEDwzCUGlDtGSUw8d4eISD/B0xmt8SQzX8m9Ba
SXd9bCID0FI38EEUQxMEgeWBJmzzVBAYKiPNLakgMBWe1oj8bGla98X9/vCH6AwxLA/TWglYUfeb
MisQNaik1qxTGEJap/AxntV9pHKKfVhY0qTG39aHrwVY8i+HzbGoeUBMzzMdXA4Dw2Z1sOOZKDBD
Lc+zqX/XIoOeMMSwscFmO55Nhcc2TmcK4JZ3URImuTZfzuKMtYhjJd+F40wnSpDr4AKmLmtTq6yZ
gSRweei0oTFbdCa/DmCILzoy2JLNQKeSoHc3n0nTW0xutTTKlrfpOMretsIk28Iz0JuVYFO+BZig
QSUTvCFlf0I2VHpD2voTso/x1AgAaWjJMp1kecoKvZWoyCVvYcNWBJV6pjMTNKhkAirPE4ZKVM3b
k3+MR/XpXt5vtPxzq6WshQB/llA9WHYdh+wGCWTq6iyIulBnTXGoVGdbinzAVHhoy3Qsr5GFiyiN
x2FC1lcepUlIW8GVrSzEc43WV/ewviPLHjrKdVKonEElEq5SRxwqr1Knua4WfIznF8YzlX4CPUGR
oDS++fpQPO21VXE67LfF63Mt+4h6q9FuDruLGQqiBpVUuE11Wxgqb1O9JU2cfYynNl/F8tSK/W59
INwmaVaLzlsySq/VKVxqnZVe1/RNnzqGrMDjFfBgCLQ3OtjiGJJTesmPlicWhanw4sq3TfI34kGp
62kgaVAJhf3o6cJQuR+9Rhudf4znRH6zNCd2immrMJ3eLoSasr5pYt+6MF7qaacgiS0iz7ECk2lo
ZFM5whCP7pHBZl5sKjyvmPxsaWAZ+XD3tK9F93Qv8NCwzC6BFhDFlpIYaDHF9wdloKWZFp8KTytb
JvI5MvNZrN3sn+s2p+MTXRptcxpDW70GA4gaVFJBqeV12WCoVGp1t/mcZx/jeZGfLp+hm71sDpuT
FhaHffbjeCqej1ezsizX9n20F83okPDBRYE6W1VHsQJdGKIEYbCRIEyFJ5it5Ldnvv5tv9+u//64
fxaei/q27ujo49/oVsKIigI4QoZMrXFvmSFjNq84mArPKx/N5S+Ajw5MIWr3SlQxGgu4oT2hv2nZ
46bYPdaPOJc+Q0dvUL3L+zMQBT40IdnUvpZI35IeA1Ph+c1uMnldlkZQfmhj+uaRHm2Pe/FmCEwd
707Tu+RjgahLy/3ai6qWBGeYCo9tlebyKQtZHk+j+dVyAp5Jy+lLUFP24YKkS2j6NWjNyi1MhYeW
TX9eyfs7emo9Mmlk8n0Se2Yq3SV7aj+v5F+6jDanhEC5ZhfofuCSC18Cm7LaAZIYI9MAs4BchLYT
CENM62CDzWY7mwqPLYnnUQ9OEZw0tZskvUhMOxsJpoP3Puqd3gYxURxRlZbmXXus0VJkAabCUyMA
QpUMhR4cASdNLZzPszzMI9F3GwQ6smJiR1ogaVAJBd+t7wpDpe/WbzRF+cd4WOR3/7ySD6d8YFw9
KzwrFe3sQ+OS99x+aFzyXqBPP33SsiQcf9amy6VYEkD3fCeQuhzVVX8mCdxkYkKaJQydE9JaCn+z
qfDUPn1SsMyn8+V9lGac2Lmgn0GLCko4zToEA0AUW1S18NO1WldmszYBU+GJkR8vDWw5Dt9VwUwa
XMRDU0+r4qIuVbBrTyq8licVbCo8NAJAPmbXUyPU5HP3emwEm0I0pdgVBygDEB43ay2uQZyfnjhC
x9RdibvUUD/kQNTFIWdcq69jtKR3s6nwCGfhLyqmZs/wDUOFBq09wzpDaYA3RBNchOlnwZ+bReld
PBZ7VftGEBgSfjZdGR9IYqderYSuIQyBn63tISP/o/H0blSs02mmff6cXslmtk3T8LHITH+oq2dF
gigGSPSFG54wVPrCW1YcTIVnNs0+SyNL4vFyHuWsIkp2E6aRqBR7to9+12J6HawILmpQSQXjq2wp
xIZK48tuzoyEj/HUEoUS64s1Oea+kTOtXgd2sJgM+BFnORY6ucgk/1DOXOai4DwTi8HqwhBHRwab
jzg2FR7dYjSSZ5eGD4tlMtE+hQuy2KqD7ny+eSb5JzQ7u0MrSJA0qIRyk98Qhs4mf/P5Bh/j0aWf
piqZ8z28Mzx5b1wPj8OTJpfcxbQfglBz0nZ8A301mENdPf0URA0qqfDMgCc3wxBHZbak68LHeFTJ
3URyiw5CjT56nCXxr/TVYx6Nb5LlfDl70LLl/Ja+c6lV7nR8M/g3UWSiBpVURrEspwhDnGJbOUX4
+D2Kg/CS4zjPJJ3BjKO1KGvR67rrejaalDG0lO9TJmhQyeTJzo4wxDkZzZ1K+McynBaLhRwm6jL5
67fD5ulr8a7fxKXvJ7DkutQK56LYghKzKC2x5RBjxwabNRE2FX6nLr+o1I7p2ZXsFFx1PTvOThrd
dLM7FY/ftOhxf2QPFLRJ8b3Y7l9ogcAaQ8e0dWxZFMLQUD71uCgGTPR5OmIZo9Ln2ZxSw6fCM5xG
XxSeefcQLyD2DLszlH8t00O8gCh/E0PYP8vTKMqFOLYeWB46+m/4HV65MUGXXrtrLxmMZnsM/mY8
sCzPVbKWPjaxHpcULpVU3xux1oztmQb26CKYdGVPMBM0qGSCXyQwhKHSLxK0YGIf4zHdrOTjNely
/Pk+ms+18DZfLlgdi78JXSJN2/awSSW0T7pyoIaLulhe+rUukS21GGAqPLd0KR91mN1GmRDRCgzT
Rr+Upy2q1XchkzSohML6KkOEbKhcX809wPjHeE6zSL4CwyJM418fFlG1pmxa7AxbM5z2o1beiVzU
pVl1rfdXc516PhWe1SL9Vd4TQhSxr8VhsaeWaL1APdFgsCVRCDJTPXMERDE+jqkbls+Wl6N7whBf
XmSwERlMhUc2TWbyuuto+1rMN7uqbYn4Pp7WkUaf+Z2wgSg4qoT38bxdZv19fFu7TDYVHtvoi7z9
TnSJZBKmE20UL/PlEhoDc2qGaxrIzsBATf31LYi6pGZdo9aSSMimwlObh/LRl9X6cfP75lFbrv/Q
stOBAPlK/93Ly/5wet3Rp8xpFOe1miCOGVjYLFbD7PIAHEQNKqlc77CEobPe0XLWsY/xLFdjhQs0
p/UoT2TbHlmqA6UnWJoeMfF89IlndineBqIGlVQeyLKEoXMgq6XLMvsYDy6XjCn02Di2+3t5A30a
p1n+9pnpIE4GPHJKlj26NpRhDC1ldCBpUAnlmSKOMHTOFGkO0MDHMuSm8qHTTzQ/P060/CbSRssv
QhqX7vk+Gpk+tJU1OZAEt4JDe1FTZIZXXhRsCJDRweY0LjYVHhn99dLIVusftJJKtv/99Of6UOve
5wR64GAj9ARah7waEPXWpLKDKxa73WyK8qnw1Fbhg7xWclP8eNVGm/12T+7V9VbLi8dvO/rvfgiJ
cLrvWjra0urUnBREMViiD9IW3ZKlD9Ju1oVhKjy/m4eRgodo/7yeU3b14oCebUhsUvVcSy7qQp0r
66vU1DmnuQwqTIXnlc1nCtHTDw5Mmte82Fd2Fqgd5+Ykpm042G2pB0O9Q7CUibo41krzq3as+S3B
UjYVntr8ZqySVd5zu+kXmwo0lWcgPbebcSx5F7BkLXYTLNaHP2rqmuv6JtaC172h3UFdY6Iu1Q1x
6KxutFSpZFO9h+xa4tZ8tpBXc7M8TLXol9s4f9BulvNJnMwysfq1p3sWugS73iV8AJIGlVBIeuMV
UWGoTHprqR8LH+OXW5anqXxj7x4dRydNLt79vqGPtASbwLCIeo11e+jdnnkwUYNKKuTylt1w2RCn
RQabXZbsYzytOJnKqx438ezmnr0UX6XLVZTmcVRbZ4FJbBOstqu7HXpwgKRBJRTcHk4gDJVuj5aS
R/AxntyN7H1AwIWvX5+Lp81ftQ5pbuDrWNc4gWUp1w/noi4vg2sVe+wW9xqbCk8rvJ3JPxfPijV1
g5fNDMWUSqJ91LIqAx/dMlin/1BmCKIuzSvR4jqbVy3F6tlUeIbZLFZYcz3EC4g9w+4M5VNTx/Fd
TDQU+qZyDG8qa3eGbXs21oWpO0Nb2VUOki6tiCt9Iu3mdhP8j8aDIwjkI4STw/rrfkfbMUdERfn6
460hBgvP9gIfbVI4XYKCIIrBEhIgzEAsFM0TIMhg88JjU+H5TaZzhROQtql7Wu+0pPiLen9r29f8
eBDHSf5LD/FfALFn2J2h/DXSH4e141CaH71642RWVdCYzZcjGro+W76ubdt4N0EneCDqEt6VN0pk
sDkzjE2Fh5fdpPLp5eembGmUh/H8HfvXc2hDEgmAyombIOlSl7kWjW17MM2mwvNLEnnHQTgeE3CE
IPlXv9zGWcz+NU2aOD8Gof+DJ6de15CLutCf7eCK/my3PQah/4NHFyVRKn/w9fDO8OQdfD28Mzx5
3a+Hd4YnzW5UbAmS/Qsti5N/Kw7rl+KVTFlv6UP+10L7Sztlx4KoS3zXsgPslq6ybCo8vtFcofZt
dpunS5pRvCJqyyIUblmaRm7o2EdKhJqhTg1EUURivTS79DyL9dJaXAZ8Kjw12rNZmtqk+H2zI3bF
94J3knpcH4o3z9C9QGLDdmAHoi7Z6dfYtRRiYlPh2U3k19u5B28UzuaRtrzNp3Ge09K4wrOcQEfn
8nRiB5Iu0HlXyvTZLX2zYSo8ujCSX3Xh62n/VBzFULfuuYEVYJ9L6HaHhzkgaVAJhZCjLw6VIcfm
5AD+sQSsiUKJucVPUF5OfHZpuSa6e6VuDh1l7zETdGE2eKJXtDQbvJZyLmwqPKtFLu8B/bioVMrF
R1keRdo8vJ1EtE/xYhUmsVi/UKchdwl0unr2CYgaVFIhsM3fysFQGdhueSsHH+PhRfJ1MvNwRdil
D6KSYRiuhGpmdGmDDaJgWQndKnVHGGKw2GBzFgCbCg8rV2iHOl4f11rZSZYc+FwpsyzfQisWRofz
nou63JlXHveSwWaljE2F5zUOM8kKaPR63BW/vW732urb+vC8fmRmwHpbMwR8wwk8tOPIGJrquQAg
6tLzJhYMOnveWsKHbCo8vTCJ5O+AcRreT5fpRLtN4jyaVO5K+qLdQhcQodCUrwImCPgITR1scYgh
Y4PNVwGbCo+M/nr5FbfabzfHb8UT68ArrjLy16IvAH3oqF8AIIrxETNOrGsZJy0XAEyFR7Zayt8A
Hx2YSmrwOFzFOY0f0KxDpqXN0uWtUCXDM10Xb6d36VUDkuD49wxTB3XDdgNhiKsbZLDZBGBT4eGN
xyPJaAJLD873r1+//fa62Z7Icnt6PZ4OG97tmXuGXFpMXIKdsq3JRTFSjuf4HkulI4vIFoYAHh1s
9gyxqd6Ddy1TOB/Nc0m/ZM/vDT85fKKv4xzVqpc7L01423AMXCzVGRrB0FHfv0zSxQXLU2PrF6zV
sn/ZVPj9Gy4SeZ0k27+evhWHHdGFtxsiY7dZl6XPtX/8g5yMsAi9wCEqEhYg+Ye6DQGiLrQ6W1T0
Sq3Obl6EMBWe4Ggcyl+4swN70fq4WW+1aHcqDi+HzfFNv3vXdJA6sUO3cAeLAkQxVGQHkv+lW9i3
gkAYgi1MB1u2MJ0KT4/cmQvJA7DHV8cnTe9u/bSvFTogf5iNy/i3h5bfIQWRiwK1xHdoNUF6WZTV
u2GIXxZt1bthKjyqu3Ai/7JksT78+LrfPhFczy/r3aa+xnTdwUZibHbAqevHIOrigCuviNoB13xF
8Knw4BazuXy5uM+jTEsLuj2PJ4KDQCO/6MRqYOYHoq9ocRzXirv4pm3hFBZrSGh26LgHogaVVP52
whaGuLZMBhtRwsd4lISKgsMpztM4iXi+0t/E+LMd+LqLcwFYtM6Go67mgaiLBWheS/dq2bkwFZ7a
OJ/J37DT4vX0uiu0+ebrt9NFVWrAFximg3yvYzEnp/rBB6Le2reGJfaV4/YtGWzEB1Ph8U3n07se
Xyd88pu25yfyk9fwen4iP/mkuWzzz39u36/GbwYWshq/OTSDLhofiLogZ1wjZ7SkCbOp8OSyX3+d
y6+8nhyQ69ecKjlpcNNoEqW0uWM8S87F1CYRL6Zmep6DLFhq0uZxjnJwDCQNKqGgGbuuMFRqxm5z
z3P4GM9tmsmXw8022zUzMk4/wKio15rwAhPnfDKp965DMyUQNaikitj4EA4b/xiPbXy3GMvHxz7v
6K2a/bnevXOz+r5j2zjnicnKCiu7PrkotsBoYpfN3Memb5rCEMCjg817lU2Fh/c5ye5VKhD1+M74
enbq7FSCjqzuJs3Hnj2IjeMswwxwkQqTZjZZ6mcdiBpUUvlZZwtD57OupbI8+xjPbBpJ44pG4YOQ
AOY6OvJ9FzEW9C72AoiC898xPd/gEVlTGDpHZJuXFkyFx0R/szSoT+F4OcqEroRVGEx3zABNjda5
UnakM0EMkJDXZJZNf8W8JrO56S//m/HMPkkDy57Xh1N22r9oWbH9XSP/6rD+WlzUrHZ8x0LWECbr
ze9Q1ZWLOh9dPGnassT0fbbk2GCzFsKmwuPLFpn8UdYTFAnmmUIx4en68FwcjtpP2qIgP2i9Ox1Z
HPb4bX14Ew0LfB1pfNFElKGt7igGUWzPisFsXvu1HszWmzN5YCo8xeliKpmMQiFuDseTNntdHwi/
H+/ws3XyXyhOK9FpK2BD2QbjogaVVLhh+aULQ+UNGzRbE/CxBL/ZKJaPkfUAawCl+U1oCsokoy8L
90KPJaIbIfVgnep06tljTNCgkmlCyTpxiOMig83XLvsYj2sylqcVnrZkmW0eCaz18fSmKJHobqKP
DtD8yJpTTz4BUaClVG8LjdprTUoQBpvtCDYVnmA4Dm/kjbCe4VuGPcLOCOX9xXf30zJtTLwqAsPy
LZTBQawNx+xSGBBEcZuM5h6Ss88ITJ44C0Ps7GODLWEdOhUeGfnx8ol3D8v0s3Yf5uyBE9wThu4H
PsrPSWhZnRorM0mDSihcFLovDJUXhd74hJp/jIdFfrf8KQc9SvL0lvzz5CEJF/FYi25p4ZKyViy5
bJeLSJveJhOuJtuugXO1E5hES3HUs1BAFKgktuPYzJHiBZYhDAFNOtisJrOp8DSnk0h+s87226di
py3Wp8PmL5qA91rbtESvR7rbCbluqVAgCjB5Hg3P0HQ7zxCHeLqd1xLdganw5GaLmbzGslg/bY7k
Vsj+1yt9jT5bHyhIlrZ4Wm92b/sCe4GDfKZISZpdAhcg6q3HxXDFUuPc42K0tAyDqfAkF9lM3p23
Omy+b9b8af/lGvScwMVV42Buqi7dYEDUoJLKzkKTJ3HDED8LyWAzOfYxntwqvZOP+Kxet8eCtoM5
Pm6K3eMb54CJTWOk3KwuKw5EvXUOEDv/0jlABptPPVMujXE1z+RXHC21dtq/bNbiDrVsF+lMYWX/
h7bywzsuisHRXTMIaK8m0zC5zgJDjBcbbOQFU+F5RUkq79Qj6tyP90M7julaqI4JlJve6ZUnE3Wh
C/OefnVd2GrenzAVntvoQT6i2GOj2FQCYj05Rk42is0ekWV5GuZ5NNayaHybUoW4qjMRWORgRtmt
uj903A5RH5AEmKrn/4YZCEPsiGODjWYFTPUeuWvvxwgCyTXHyE02X2nDSO1+f9g+vWv624GLKwnA
EapbZiDqQpGzrilyba9n2VQyDCf3oWRDop7hNYaSNlkP8RpEeTfB+TUoewA60W6WGTzsPheKZQei
Z9s+0sAlEOlrC1WIIOlSRxYfiJ515OYsPJgKf5WEC/ksvPnynjC6rLej667u4h6nEGK2MzSUIxhM
0KCSCY4p3gwFhkrHVEszFPgYD4z8eGlgSfG8fj2stUXxxDpz1lrT6x7O8+nTh3hd3E9MFFgRXhB4
0ATW5foKDAEzOthsiLGp8MySRSr5nowddbM0iqd/E5aXbQUmzk9CYFkdQtpM0KCSaYq9dmCojCe2
LS/2scyZNoumP0v6iT82LTlW1D13N3rbyJptRtPzAh1n5RNiRpdmdSBqUEmFAyywhaHyAAuak+rg
Y/xmJL9eHlm2/1Ob7fdPov83IAcBzocEtNR1DBB1eT+KV+b5fmx7iUinwtPKlvfyCSbLv8jET6X7
d7xmveZF/cx0AtvERR0YO6vDSmOiYFmdX0b4gSMOMXZssHmlsanw7JZfsl96eB3g/drDU4fXs1Nn
J/+g6aZYH05aXhxP2nz92/6wPu0Pb2on2C4x8NDXayffHIhi+oeY16lfKSdMBpsvDDYVHt9NNpa3
qHp6Aj0F+2q9299tfjvsd/WGk0Qd8HTUKwlg1qEcEYgCfUQollDmwNaKJbTkwMJUeGZJuJR/7b/Y
PB72v+1PlzYpOVKswMP6PTpRA0mX0K7VcNKbExFhKjy0xWgp6QYm0D7PbydRnGjxRXcI7nYzXddD
q8T6UFffoyDq8oLwrl0QbQ3C6VR4dHHyOZT0XPbsBHY9OmV08ldDx670BJjpdCmic7UrPWFzWeWU
DjYCa+5KfwlMqSf9fL172h9p3sgLK95cy7ahqfHYi4F6eNWdlSDqUgVxr6kgLdk2bCoJbOFI/kVE
skyny/lnLVve5jdRKuxO3aP5QGhqdofrFCSxhVV7gmgIQ9ypRAabwwhsKjy0JJMMCAIzYJVH45tk
OV/OIJiQk7GyTGeF0fccZOkXitEc2sqHHEhizAzP8G3mKPdMIxCGACMdbNZK2FQSGPNYnmMWhfNo
ooVxynANJhFsV3q86FgHMH1PohzQ56IYH9f0TZ9uVytwfUsYAgcdHWzcrjAVHlkWyed4rdaPm983
j9rsUBQ7jTYo3e235KIojuIlQY4NXLd6is/oEMzioi5XnHttxbWUk2BT4fGtZrn8JfHftnO9oRN0
qEz83s51rnFs7mDyb9m5nze70/qwfrdbmh34Fp5cp8oIIOrywrWuXbjNOximwqP7nKfyOvGS/Ij1
4amGTnvZ8mLYnu0HuEYJFJzRKS+YiYIFVuU/6L44xPMf9OZmTHwqPDiyC79IgxuTyyKtBe0N20Oz
srxOefwe9yGBTB4ndIWhc5ywOcsBPsaTUinYTFfW835W7Irj5nhuvSx2/PINcldhtyd1lChXlwBJ
4BURA2Di0DkA1lwoAabCo8tvFvL90e4fkslNuNBulnk0z7SfNLLiyGoVWwcbnmm6Pu5+9ej7EV09
pxpEwYYUEw5dYYjhY4PNe5RNhcd3L//Q8CYejaI8F6qY0JMBp8h5NFtfvRwYFzWopJrC02A+VMal
m58G84/xpOjPlmY1icef/0emZSuyumir79lyOamtMp0cqS4uIdhjSrCy9cVFDSqpwM4Wh0p2dosS
zD7Gs5t8lo85hERLW860SQQpb9V9oBN9HedNYsTUvUlUzqASyXCZui4McVy0mUnjdcA+xuMKJ7E0
rs/LG3KSVb2YfN9xLVww0GPV5pUxgaQLDcMVh0oNo6XXC0yFB/VZpUbfC7GqriSmGtr89MQdlr5h
u9iLs+MTaSbqwv92pYwrHWx2WLKp8PCym1DekdTTE+ipPA/pAYoAZV+JEICjKMu10S19GiJ0tnJt
G5mnCsiUXb8gaVAJBSsBWkjwodJKaO56zj/GExuN5MuozcMkT5dJ/KVS14hCiX0YzVh16DoKosDJ
JvQh8a71IfGa00RgKjyseZ7KG59JdK99itIsemAWwW06ZonkpafIIrcT8jG+S/0d6s8sQdKlRWBd
swgadTX+R+PBJZ9SaW6L4ulEsxseKxeHa1iGjlNsXfo6oYP5BKIGlVSu2NrC0Fmxben3xT7Gw1pM
5G+A0euB/MSvJ/oA/7R/U5XK8QIzwB1k7tDuVlyOibp0qV0rLqe3FJdjU+GxjW5TeYfHv6QiJKVm
D01lT8e5IiSXCac/90zCUHn6+/+nK0JG34vt/t0n9oHtIsN+DJmu7PXmogaVVNifvisMlfvTb3nZ
wT7GQ4vu5vLeoej5t+LxVHtg73umh6v5SGgZxtBW93SDqEElFZaYKQ6VS6z59Sn/WILWYiQfJJg8
jJcLshcnt1mevnluRcMYSGidqji4ZRCFy+SeW18YOntum+sAwcd4ZBN5lYw+74vE/Bj636OB85+x
NvCWsvLKRYESUT0K18sHkuKjcL1F34ep8KRChUBo+Fwcip0Wb8kP3W+OtBwhjwn4roPzzXYlRgX9
K3jxv1mCVzyP5Z2zH5zYXN6e/ODEYvn78YMTU9AoPjoxeXf2BycW9+eYJLH5QprYKF7Ow3S2FOJx
uu/rpo3zvbo0q0rdkgRJjI0YMy/fG9Ri5l5LMhCbCo9rNJ/Jn2HjmygLV1H4OdKI/T2P87jmG9ON
wMMk3+qayVpZqCv7TBCsqXO4SQ/K/DQh3EQHW5YZnQrPbbz6LIcNKqIUj+vDb/vd5p9r5u9fbV+P
l+Wh7rih6dgGpvMxw2g7Q0t5/XFRb0H6wWXcjg42G5psqvdAXq2MMk7lzYFsu36ulzL2DUyghMGi
Mc4OsJioC1gQIH4Dy292lsFU+FWXzUOFhuQfl5X825+Py0oaFaFUaBn1J542v28ewaM4OP1Vggss
G5GbQcDp1KnY4eEKiLoA513WdaKDLeDoVHhw5NL7VX5D9uSAnDS4+VpL98f1OcWx7pcNbEyUiWEz
vKGjHmUCUYyR4zm+x0qiGD6P0sEQOBnpYItflk6FxzZPb+T9siH5D+9qTmzPcQIHC0t3usSWQBQj
I+QV+IGYasDzCvzmhFA+FR7W5yhN5HfnR6YlDStP/wonkXYThfP8pirpZFi+6WBaS+iaEVDltUPi
CogCU6l6mm3w3NDa02wy2IgMpsIjW0QT+ayz8LA5/vPdcog+rsoa40b76XbQN/xzlTVxqXlXCnf4
LRkZMBWeW5jGv8qnFvTgGDiFE60HR8HJWwM9OAZOmts4SnLaM3wWppMo0X7SVlF+bgpD/kQPU/ga
uBkd3giAJNBliVZmQh1Jz9WFIa6nkcEWvxqdCo+NEuixKWGTf2GXRPeC/9FwXNvTsRtTN4eOeoYG
iBpUUlm6gVM+bWdDPN2ADDYnTrGP8aiS+6wnhSUlDWr8jf7Hd1q2f33RiAjt9K2g/2Zb741TS6Py
cO93CExaHXdoq7u9QRRsRyG+wpuX1OMrLfl6MBUe5jjLIvm3Aj3OBpzyVlePswGnfDppj7MBpzTN
6TKdaItlvqz6AOqWFwSIKkaElkdTmS1lWkzQoJLJk3I9YYjfMi1P3PnHeFbTn1eSlcM/MKseFBLU
zyv5l8Yfl5W8a3z+Sk/21+cqpCCc8ZYVIA18As3qUMeJi2IHuliViOfQ1KsSOS3vVthUeGzzOJc/
4/PD+v8pHk/7ww8t3L58W7/f3cX1MP0iGULaALZDxhETdeEjsUW3SekjsZu70cFUeIT5eBmqvF/s
Kb6l2CPsjFDpJfL663q7WT+/26rOsRxURJrAM/2hqe4mAFGMlBjMqcV3ymCO13wQwlR4eFk4k19/
PTlOTt653qPj6OQjYeNormXjWCzdaToW0h71aJ1YQ1lXAUmglzh24NgEFn3u7wtDDBYbbNTwYCo8
rPGdfJbS7Qt0ZK5Vh/XIH4deWgRWh3g+E3Wh2PFky7pi11wjgE+Fp3W7kn8z9KFpqdyc3YHZXbzq
AjAhgcu7ljPoteQM9sD++4FJ08qjT8tES8NkfFPZ8oHru6i8cErL6JBezwRdsPIu60XSwWZbnk2F
Z5WnCqb85vs62X9fa6v5uDTgAzfArivDGVrqmZQgCsCUTlojCLwLJy0bbDHg6VR4VvP4TtL0ZG8R
kuh+sUxy0LgGk2gAzAzX9kxEjU1gZncp5AqiBpVU8BWVLzrYUOkrsptDgvDxe8yuvTpIInldghVh
3jx9LTRaa/MAbziuOI582w4wXQ6AoTW01G9MEHV5Y15zHLUUPIGp8OtulkYKsZae4VuG0gjDh3Qp
Xp+67yLfW1FURhc/JYi6sJSgvMIbS6m5vAKfCo+K/mppVP/SFtSMH+tCrczvagtq3eP86ldqC7/m
FtSX/OQ7off83vKTd3L0AGsA5XNXR8XpVBzerTvvW6aNKSxTolPvfM5FUU5ikSzDF4fY2ccGm9Gx
qfDoRrlC+fRPUT6a30a028Z9+CCmfZGj2UY7i2jLiA5l/5ioQSUVdDxeCRCGSh3PbNHx2Md4aJ/I
r5eGNtquH//QFvvXHc36eH/DEiUBG02gFdXVb1wQdblhnWsbtmXVsanwAEeLcKxi4vcM6wx7gB0B
qsVW95s/1qxn5JoYHfvXl5rKbBo6Oh+CFvVXty5A1IV1YYnR/tK6sFpUZjYVnl2eKrTw68EBOAVV
JZrPU1raf5SGSb22v+eZ5E/GYrOGtvqNC6IGlVSegeMLQ+cMnGZs8DEe2ygdyceyFmEaL6KJAItY
lZ6LdUERWB1cUCBqUEk1oYlEIAxxWEZLhUX4GA9rkS7k8wVZupImtgETu7yQ0zXANIs8g+vwIiko
W5II94F9zYpoTnHgfzMe2/x2IX8Z3O42p4KYXxtabre43iLH0HXHkFh1XU42KglQCb5i1xWGSl+x
29xdCKbC07tV6C7EPHbb9a64hs3wbAvvfKLNDtWxgahBJRVONs8VhsqTzWvpjcM+xnObJbLlBMHH
XvwVPz+/7opaXWfLNzE1PoGY0SEXk4u63KfmtX3a/JocpnqP2HUP+5dYjhkhNp0RzePwlVj8gqYb
6K6BfPrGiHVoKA+iGB7b8wKLlXbweTMwPgRrjA42EoOp8GtsOlso+Ec+ODB5C/+DA5PmlT1k46VY
3S1wiTqNRaV3KE/DBA0qmSZ0XfKFIX7ck8G28LNuSoRlsgf5Ws7TzZH8p6GUT9nXlujPQYA+6vUu
ywpEDSqpXJM1haGzJttSOIR9jKc1zeS1/iwc0XL0p3fdupZp4MqHMHDqnai5KLamRLeuJ7ZDKN26
XotHnE2FB0cgZPInfo+Oo5MmFyd3S6EJQq1/reFhTSeXdv611amBKDjthRiqfS3btCXcDFPhqVEA
0tRG8+VyES9r/gzD8l3kw3GCiyr+yrhA1OV9aV+7L5txwVR4XKP5Qn6R2dPDfnfS7uizokNRK1Hm
+Zbv4WwlQs0dWurUQBRDZHqB47LWEZSWMATU6GDzdcCmwlObTpNc/oFfSNSy/eH0df21INuS/JwT
fZil5YfXY+3hrWPYqJ6PwNDp0KKbixpUUuFK5V4OGCqv1JaHHPAxnuEiznOV52w9xbcUe4SdEaq8
Qe0pXlKUj8XcbLbb0/7lWn6cSXQqCw3O6vDgg4sCl5Hgr3REF2bpr3Sa7xKYCg/uJpfPmQ5Tmiwd
ruKcllJKl7crbZ5PuJc3sD0b08obsOkd1htIGlRCYbmB45cPlcvNaM6eho/x1MLxTNZd2XPj3OT1
454b5SaNLU/jMbHGwmSc3YRplGmQgE6bVBrIxF93aHvdOmsakPfLhUI8AUpS8aEyntBckop/jCeW
j0efpYmFxWH/nWB4NxnOC3wP1zGAonM7xWKYqEEllbMzhaEzuxZ3E/sYzy68UyhYEH4lSPJv+9ev
3051ZOTPw5pkFJl6MAZEwZ1ZNXLVyziq2MiVDLYgo1NJIJvF8hu0Rybvm6NvGo6bI82b+fP0jQXq
303fcokJjos3u6y9h7oCB6LA8q8qEni1iqIMIxts9tOxqfAYZ7NQ4Wl4j/EKxp7iv4Ki/J5O4vHN
ch5m2jROiLYSEy3vXOacXm/I7AfGTV2946JAmROc7bzbfN3Z3vI2GqbCcyME5LWVu+KwOa2P2nR9
eK63G3YDW8cFxRizDpYriLpca961tdZsucJUeGZ303QhzYw+B0tGaRzRyl2LVZjUmuhC4T8sOLtD
wWmQBJeskDdiXMvvMppDrzAVnttsJP/SYVLs1tuNNl6/sLc275xxgWE5HvqMs4emunYMoi7XnXVt
3TX3VYGp8Pwm0Vjhwu0J1gnK3xI9wTpBaYDhz4v14Q9tdSgeN/vXo7YoCMfaxeEEhuNjg462NbSU
PQNc1OVla127bJvhwVR4eOEilb9sp2G6iNJM+0kj/2d8EyZ5xlwrVWNxn/wduAQnxs7ooKgwUZdG
m33NaGsO2MJUeHbTxVg+VDGfxdrN/rmorzVfdz30XWt2eVcIogaVVHDc6eJQ6bjTm/vXwMd4XuSn
y/vXZ+vXx297eBpyNSXYcYIgQO9TvUuYG0QxUGIuhXntPXpzMSo+FZ7dXZzI+4qX3ze70+Z7zZti
kv/W8LB09Y0Joi60Ol1M5C+1Or3Nm0KnwsNa3t1Js7qd8QJng1XIfcO+b7vI3CaX2KgdHkqDpEEl
FPybYLjyodK/GTR70+FjPCvyu+UD/x8YljQrqmc0PCnXaUc+7PFleUNb/egHUbD9hMiqfe0liN28
I2EqPDiiZchnbYbzeRyx2tLxMtHyaHyTLOfLWc1KJX+0YWKDEJbT4XkDEwSXpONb7N40ArOM57Mh
turYYOOqg78ZDy8kBKThzTe/FYfTD22+PtFHvc/FYfO41uanp/ImIEo5OlRIwKnX9eKiBpVUtmHN
MqDDhviGNdvq7LGP8ejm8VzeNujRlegU1NueHWcnb1al0YwcdOGc97fUVulyFaW03f3fRB+w7Zlo
gMSmV3bLcVGX6q5Ykems7ja/w4ep8ADTm0glg6knKBKUxvev3bzm0Onwrvydzetd27wtGcUKm7dH
p4zu//Sd8f9ndvJ3xnh92P7YFlpWPL4eiidtXuyoh6TmILFtU8c+NrGMLjY/iHpr87v+lcotbkvR
KpgKj288G8kXOEijRZh+1m6W80mczGpvTizXt1xsrJVSU7fLQNSgksoN2kAYOhu0zYoKfIynRn++
NLXZ7XxK0GXL23QcidCIPe3YromFpncoPAKSymXl64yZ4RiuMATM6GCLE4BOhWc2u03lr9ZFnMQL
cX3Zrue52MRDgqpDbAZEXWoh5jUtpDl7DqbCsyI/Wz4ufS5FNT4Ux9OV4FZ8x/NIHMtDtn3vyhBE
MWC+GRimS9eb53BvMAzBeqODzXkkbCoJhuNQ/oFTz/CCoXycuod4AVHei/frvTYhfzmhx8CJ1YQE
JcXyXAv5zp8R7BAxBFFwT3hERWNFIj0jMIUhTpAMNl+3bCo8wXESyXvyqAOZTP2VaHc8/iX2rTFc
Fx1ppdzU/SkgasC0ECFKbV5LCWupXgVT4bktPk9nPTc1bio1NW+S9P06kKbjoK2JbuCYKLYrxZwS
51ruZmv1YDoVHhwBoNTarAdHwElTy/avp28v+8PlFcvREZMIncNpBl1KzoMouAmEGFktbFbGyFrK
QMJUeHSrZZqrrLme3pmeQuXWnl5JTxrd8rD5SpTjBUFx2LzJnvN103awqYemP3Q6aMRM1KCSyjOa
AmHonNHUcr+yj/HYlulMfsX12Cg2+ZzX1X73WGjTzW69eyTcLqsF+56to99yEnh6h63KRF3qdOLQ
WadrbsAEU+HhrSZzeRd7skwnWZ4uF0LmBPkvHFmEjtCiRUhVaTFBg0omLDQwYvlQudCa+13yj/Gs
Pt3Lb8/5j2K71Vghzf3Lt/XheV3foG7gYD2cptulFBGIgrNfdKaLOXVnZ3pzpiZMhec2f1DotRQ+
vklzErOc3MBCV3Dqhg1EXWDzgivYvJaXS2wqPLZwHMn7R7I8TEe3489CywzdNwMTHaI27aGpjAsk
DSqhEHtwdWGojD24jYuMf4ynlY1u5R/ffM+/v7fGXNuwfWwqmGl1eSkMogaVVKDmiUMltZY1Bh/j
qd3ld/Ka2n1xJMrGTguPx+JUlcopn49U2i5RMX0fG/UyzS4vM0EUABMK7htiDX7GkA22RCXoVHiG
9wqFScdRkkfpahknuRYlUTp7qDJKDPKXI2ssE2pdipRyUZen27VLwWu+FGAqPLVxspKmNkrj5HOU
ElQEXhLmLDunysXRPd3Ctt1m5NTXG0gaVEJhyzq2MFRuWadZC4GP8eCiUH7HrmjEYa+Fr6f9M3Qt
FHQQ0zTQz4CNYOioH3Qg6sKTxKtH1j1Jbc0K2VR4aqs067GpYVOxrY6n669rLGrNYKF5Q0s5os9F
DSqpbIdaPMoPQ3yHWi1FXuBjCWhLxbW2edrsyb/Yv7AEpvxtvTmdnMJoc572T1ZfciCKoRKD/GUt
01qQv7l+NZ8KTy/7ZdrT60RPYcP2+Cp88npJcdkW3rIs8odicblDR9nm4qIGlVTwiPDMdBgqPSIt
menwMR5XpFLjcDX7KRKMBF23A/QjQvKPDjn8IOpC3XXtK+qu2/K6l02FJ7Uaz35eyUedPy4r+aS4
j8tKPlX147KSt9U/Liv5yMDHZRX3rJCs5DMnPyqpn1e9doVnJZ8p9FFZSYNKw0kcJrxA9Nnd6gc6
vu+M4XRILwBJl7isa7gaSxrxPxqPK53IB33vNoevm91mrSXMa7jeaqP17o/jt3XZU4V7w2j8GRuX
I/w6FGsAUZRWLcMA8oPeZBi01elhU+EB3oUK5aJHC+2dRrGGZzq+h42ZU2rquxREsTUmOl6ta45X
q3mXwlR4aqNF/kUlfa8HR8BJU7vfHJ/2z/mhEBt+1Drs+uRkQbesIPAs5b0KkjgoyNujLuiyIZJR
5u3BYPNZx6bCs7uPFJofVew+Hi/5/Tlbb9d//SD/57n+rpb8qZaBTujuBIwJensV6MGVxnd685Mf
/jfjec3mX36VJpZsvn47Tff7p+sRpYC+1MRjM9WxgajLQ828dqi1RJTYVHhwyUxhY84puB19qFIs
9qf94W1qKL7dgmF2uQxA1KCSCv5p3kcWhkr/tNVcjxI+xnP7NZIv3dZjo9hUdI+7OLsJHyA741xf
q0psIaaK5Vlo+8rs0t2TSRpUQiE9g1dFhaEyPcNvPuPgYzy7u0zeZs9+PP+2J1OIC83yTBvNyuj0
UICJGlRSYaG5vjBULjS3rRMq/RgPK3tYSMOaLee3o3PPotFkLO5P23M9dJ0FQx866thA1KCSyteY
Lgyd11izSQUf47HNyK+W5pZG2SKaCBXZbcs30EeZ3kHhAEkX1rtzpXYnGWzcjTAVnlS6kI+NfExQ
2WIir2LEu6e9Ntvuf1tvteivx2/r3dfiP4//pa1OxBioSu7Ytuk72GC4HnQpuQOiYAtWuQO8+UQ9
d4AMNu9KNhUeYTyL5C3PeZhMojEka//jH+PwH/8ofZGO6WPNAYKsQ5kdEDWopML5zwt6wFB5/rcU
9ICP8cjmicJBNo+nEeEVaaN4STZqPOY5oH8nACfRBwM4Hcu/EUjC0TKlVU8nt1me0gKo83wC1AyX
aDvYs073u6gdIApONuF9Yllzp/Y+sSVjG6bCU0tG6b38YddjG8mvtXSRVmGD89MK2/Kwz3cILUv5
rRgXdUnLuUar+TqAqfC0yC+XpkW7ScbJTMui9C4ekxU2my9HRMutYgbkr7DRfSR0r0ttfxB1cZUG
Yu2n8ioNml8lwlR4dtlNKh+kWkRZqKXLh3CeP2h5SjbpIP8CKpxFLGQXG2kh1NTfcoKkywVnXltw
jXcB/6Px0Ba5/IL7XPw4Un/Hu0EDV6d/swQ55RgfFzWopJpi4ToYKg2qlsJ18DEe3efoIZNmR7ss
RkleKyUu1mTTAx0fb9HdLloviBpUUgEd73kFQyW65p5X/GM8Ovmoe/b6Uhy0xebxsCcH2/PL66k4
1FJnPQdfK5Fy85S5gahBJRW42ZYwVHJraZUDH+O5ZYuxfMoQXWqrNBrHGa1hX3XIIWaiF0gstQ67
FERdHnD6tQOu2e0BU+GR5aux/C6FSIv2kzYvNsfXQ1GmuL855xzPCWQWnfr7dS7q/2vv7H4cRbJE
/75/BU+re6XpNd8f+YYx6aQTYzdgZ2VJ98GdSXd722XX2s7qrv3rb0ScwAQJCSdgdrRSeaTRjEIo
TvpXEHG+T9PKF4uzr1Z+TxyBbYUnOI8H5Kl9Ph6/KJvda8E+1y9vh90LS2t4N3PIstAd19RxuQxM
1KSSqkNXXVtY4h+t3jN4Ah7G8/ss7670337/Urzu/q5F4m3PVdHqiHVnDD/hQBQjIwattLYpkt2T
1flWeFj+ei7vD7mPw0/CCHrXJjaKDKnBBxtImlRCeRG2KiyVV0F3KSx/GA+K/mZpUEslDaM4flb8
db6kQ002oZiZ5WoO2uFm3emD9V2Q1LgO1DZztHsuE/+j8diWafwsjS1M/CT3lRUxtBZ+EK5z6j6q
JsBomuPYJtq+su60wS8cF8VeLzEQ7zrCUhmId7s/TdgKj46orfJ6m/85el5SpxtVdYOaquuorqVj
i8NU/c4ccaAxUXDUi5NzDGEJ0j3oYveBxrbCU/M/P8tPTfvnhatU7X8gXOXYwtL1fOtpkPCvCFdl
F/Lk9LR7/b34uAeiSY4MPD1zuIsXRDWPOa3tmOvp0MS2wtPzV/6N3ih68vWwN3wCPvl6jYDQ+u/i
cC7thct3mgD9Qp2YgplvO66F9pkTgMNvWhA1qaTyCllXWCoDNN3T1vjDeIDB5yd5iyst9rstbU/H
Q6vN3nSa7uDvW22EfsdFwSUheIF5vKvuBXa77w3YCs8uDeMbuxHs5M+9pz+KgkAhEGlA/3whTISE
6GZTAKI+WabEITjC7wSiJpVU/g2bwtL1G+5OKYeH8SyfHmJ5z/oN5Uco5fsr3Fh+xFK+r+KN5Ucs
5TPFHo+HbV7sa3UPpoVPFqO4BvuRQRKg0SzP4bQscamkZXWGy/gfjaf1mA9o6JnuvpGLGZppHS/k
13BsE2UW8pvZMTV8XoB6Zw1mx0VNKqlgDfOCQlgqreHugkL+MB5eGm3kKwrX592x3o3HVdGlg+NQ
gSjQWOiEF6o8G7rNXz1YYqjYYndskW2FR7XOInmHC6sXedj+9acy3R3PL7uCKIO1UIXp2J6Ndo6q
d8Zwyw1ENZ1VWpuzqttyg63w7JKHp0dpdlM/Ce6jNMv5PPblI5/H7ti0yhjFzCIv2wilGSRRPnWv
aEulak9aNf+j8cjIr7+XV05uzKSRLVN/HiZRUPO7mx6y3sG6M627qoRbFhYX1bDKHDFruLTKnO6z
H7bC01qSny1NK50H1XxEYfCw7VnIjuvWneGOSEvnoppXgLh0vQK6icFWeGLkx8tfAb+8nahamxaX
7U7UzjTDslQPl81k0TjFiGpBEDWppIKGUbqh2FKpYXSrZ/xhPLNf0jyUP8du0EL5fkc3aKF8IHG1
itcZWJpKVpzo6L55FvykAzndM11kc3+TYLvTBl+bXBSo/IbnuvQmMDSVl47DEpxrdLGTHGyFJ7d6
lq+vJNgWfsYUjmvml2YT4w0XQTRZ/fPwyCuIen8LWKU3U7wFrB5vJmwlQSueDoiB/dcbbbjydftS
1NR/D63+mzTCP2IUM4hieISm9BZPn6s1pbd6ZkPCVnhi2S+Z/BUQ+PlSmBtB20Wj3y113BxSnb9Z
lq07NqSvmrzjDyzxE4wsduqv8DfjOdGfLM9pmWTrRZgqdNDV/TKOlteMfSErgli7nq3ifEEGm+M6
+CQDSfBWuQb5LwXoGrzqDZYAIF3s9gWxrSQArjL5pEyfwAvFBFZV1T3kzAODjg8aEQwEUQyMEE11
TTGJiUdT3Z4EEtgKz8oP5buaBsfDofh7q2R0qtxZ+WDgre2YJtJ8MujkgxHRaBDVMJ/0tqBW98fK
t8LzC5JP8trGcp3fp8skVxbF625bS/n1XAcXCNTvTOPOHG5ygqhJJRV8tXw8HyyVvtre9lv0YTwz
8vOlkaXhZOF/au+34mqqhwsH6KzAfviLBqIaXg1V7F1TejW6Uwv5VnhoKfn50tRWfvqoBFH+/L5P
nmqpJnKSnM6yMQf3vWCCGhqa1aah9dSPw9+MJ7YKnuUrtrLty59nZUW0tAMxoubH/W/iq+YZpo6c
sKHdGd4YBweIapxpWptLqMfBAVvhwWWrubxq+0iLBK8+IVHnIP/EpoaFpjkjAgMgqaFzGG06h9Gt
c8BWeGaP6YAB3lk0X/iKP5tFLFc6W8ZrOkCoTs91bQ33pZKjzRsR7ARJjTeOK3H1N657Xib/o/H0
Mj+TT6t5jGe788vxW3H6LhYyWLqJTM9XqfU5vK6Si3p3G5ie17wN6GLnJwpb4YGR3y4PLCneTsdF
cTnVSz9013NxE3BUcqIZY4iBqIb1yS+IuvXpdAefYCs8sWSdyltV97OA1rfV8sp13cF1EaS01DHV
ziCq+UG2qbU92gZshadFf7g0rXD+cWaq4eGKPzi0ET3KmCgwnKpSNqMcOy2Wshk9ei1shYcWzuf3
QyZ037gRbgOau924zeUDnWEWLGv14UKJlm07KipAQKjp1ohRaCDpvcPDsVvSx8lit3OIbSUBLZN3
eCyO++LljfxImr9xKV5qY5XIn2prKJudUKOR4uE2O4hqGFNGS94LWez2c7Ct8NgW01Te/IyjzXUE
a/NLjaIyx8rScWO8aE8k904bDhBEXdUzlc6l0mzTNoUlSH6hiz05VnQrPEDCYiqf/XwjWCcoX/hx
I1gnKA0w2+6Ph21Zu0A9li/kf2fFt91LIXYBogWfKCufdTa70wdfHlwUO/NUW/fYdDldK5MnYYkR
ZIvdxyDbSoJgMvssf+dGiTuNljUrwlR1BxWHobTMMY1YQNR7BUV3WwZVkMVuK4JthacVJf7/kvdN
v9MHe5U+fN/stvetJ7zwL3nfHpcPcVaxUl3XsnG94GgjOG1EfBkkNV42p2WIDFnsifrRrfCoHgcE
/ejM83SdlfPOheCfbam4SkDaDI7odcNzGEDUpJIKuTKOJSyVuTI9GR/wMB5ZHMrfpz+/kbu0OClP
xX5/KM71cR626qk4s4tQGzfOg4l65xvRPM9s+EboYvepxrbCU/t5vZJX5G7YKDZ5brNoE6ZZdB+F
M+Uh9OP8IfDTEHrp8U+VltXgLgKX2vrm4FAWF9UwW82WziJOT5sf2ApPb/YgH5K5sbuyS+SLg270
KnrysYbssv3tN9rlzLBVJTvu3xrtuUzPc5B+E0ZvTM0LE9U89fS2U68v6Y1uhaeX5b68/rY57l+O
h1qFuO45HiqqBbBGRB1AFCMjBlF5Umo9iNoz6hy2wsPaxIG899yfbgLaAgj6J4lGluYYhoa+V0dB
A1GMkNDTzNJbpouRxU5osBUeGv390tCW9/dRECqrdLkK05x7gZeL2vFmWraDaxZK8OnUIzwYH4hq
Hm8tPZGdvhb5bCs8vuWA9oM3egK9AVdruD1flLQ4H99OL8X5nVuOH3iaqbvY20F3RySNcFGTSioY
YLwVCyyVBlhPKxZ4GI8vTMNMXie+4RPwyVuwN3wCPnl6h9/ezrt32ontehr6rBvVaRVENc+6llRp
pydVGraSwJXcy2snSTrn3iVRM1E1w8WlX1Jg5qixzkxUE1hbqLWn3hu2wgNLBnTMv/ezPEzofIEl
911qloUbQknDMvqIexQkTSqhvGhNE5bKL9HqCUqzh/Gk6K+WRpWftsRKUFZ/bE9fti8FsbBetvua
iWV4poHr/EHYqaMcSyCKkRJz4SDK8C4XrifKAFvh4eXBUv67XPkBMe0DosOFiyhMrxVsVS2zTk4H
VM0fZaePyLvkoiaVVEjF5226YIm/eGSx+xNlD+PZrVZTefV3M42UzfblZXeAgo/JlCeQODYxoLEO
EVqdNdhrDpIaFr3b5sd0u+MMsBUeGfn18hM8VwXRNb6CjsGD0e8C0Kap4aZ4AroxAWgmqmGrauIc
2dJW1Xp7N2oyUzxXwYC0whu7Kzt5ZfcG7wpP3tCa7t8KZbo7vQpxZ8v1LB2njzh3lj7GNABRoKcJ
wVRIHnkXTLW67wXYCs9rGk/le49NyYM0Q46WAr6cBWb0gLVw3kvnzrTH1M+DqObF0ObqdXtcvWwr
CWbTeD7gHbtBk2b2GC6Yt/cflbpmGqZt4ix2586w76zhxxiIanyWZkvGLx1T3EmLbYWn9Uh+tzSt
xfayO2zP9CUDW+GDKkod/7qNqgznohqvm+O2vG49+VuwFR7gIk/kM5L0c/GtOFy+K7/WO965tuq4
OLeHwyqch5sIIGpSSQUTgY9YhKXSROgZsQgP45Hl2UZeZ3sKgzKphlWe1ivaDEPHWQkOTZgenmYO
kpgFqhGxGgwY4GN5+BKn1jOWh//ReGoEgDS0wFTyP4rT9iuz5esz72xyw+O0NGdsdiUTxd4rsUbL
busQYndfBrAVHlpA/iNNbXU6LqIMitvK3oqiD8QxDWTuGyOnDScHohquNr0tSN/ThgC2wpNbLQa4
QFjLo4cw9Vds6My7SYEWcpYAxaaN8lAyUY37tPTy1u7T7vmUfCsJbFkub8sn4VMmpApa5AbHNYdi
gwHv1MFODy5qUkll55lVtvNkS/w8s3paxMLDeFLJUybf5+JHJSUNKvy0Spe8R8PDMp5FyZycaP+x
ubYFIXoOGhw5w4YnGoEoUMTEEiNVWILTny52g2Nb4cFRBtLkcmIKrFMlC/34mRxfeZgmPq2hvw5g
ZwB11dZxw3YJQHNUjRaImlRSQefQxKVS59C6M0HgYTzAfCVfouWvZ35Qj03ZRNfBWVL2SEMARL0/
+TVP9Hnwk1/rKaCHrfCsyO+WZrUhutnheLWe6gWnrqnhvEL2yFQjENWwnuxmVThd7PGi0a3w0DZp
spTPZlueX7Yn5aHY7i+1wknLdm0Lp2JQ78YYFQNENV40t6UsQesOF/Ct8MyWWSA/qSM8FKffiy+C
N8h1PANZ0WyzporDYYGo5gvW5g3qMQBgKzysMAlW8j7uH5mWvMPxR6YlDWuxTlcPz8oyioUmlI5G
R7FieRkjMpdBEvNW1GpFdWGJ4WKL3Q4MthUeF/nl0rSIAvcYzZIQKqnKq9FSdQfXXobQIpbkYFpc
VBOX1oarJ8DEtsLjoj9dmldC1InscqJp8u+bKHKvNpsIimRHa/eGswNRk0oq6K2WuFTqrVa3UxYe
xrNLslw+BhBkEZ3l9/VUnM8vRyXmNqZpeqYrQUwdbiqBqIZSYbdprz2KGGyFJxYEsfxZFpyK4k8l
PW5flcWOXAI1ByN17OGag1NuzpiCZBDVuAOsNvd/z5sGW0lwSx/lTzXQXP8q9q0NBEr9nxwu/VeC
5ii6Rru0Dc9S46LAmqyi6KatCks8ik4We/R/uhUe38NTGMsrHDd+Ir8bvlH45K2DT8Xp+HfdaAdo
jqOaiL53AM3wxigmIOr9VUGUsuZVQRY7ocFWeGifZPv2THwC7T4JpuWAXPGaUA3Lc/qvCc7MvTOG
u3BBFNwJgieShwXqnsi+sADb6iNmE79Jjf5+OWw0laPYfz3uaN/YVRqu/qHEMU9OcFXyj4ajpjKD
Ybj7EUQ1LldTbCNYXq496d6wFf5NW36WJuZ/Jfrby45waM1JcHVarYrhprl3ujEiUsxFNY612klX
Hms97XZhKzy37F5eJ6GlZ7SJmxIlmzDLF2GSc5vrmtf3Q7Bb5dGAWeo3ehU9+Qv1Rq+iJw3vduLB
iTegFUji5+vUj5W5n11nm7zra6+prukhZgFQfg5ThIfftCBqUknVa1UbbKlMyeqr2mAP4/klc/kQ
c/6W7b583VevXe2tIwoSynwg1ExvlH7CRDFEYnYRn+Zazy4yerrfsa3w1PIB44c2u9P25bL9MC9L
tTVV7w+WAjlDHdM7G0RRTLUKK01sQM7IscWeYCndCk9uE8kaEQRdfLxcitP3/3g5fqlNhTFMRF4R
AKOVtcMDpSCqoQqXUyNrqrDRfcDBVnhgcZ4+yx9wPzoxaWDkMt3vildlvandoZqmIWZccWDaGBsV
RMHRRawlPiXM0jVhqUwz0nusLbYVHpi/3kQ3YJLA5C+AaewHj9MwTZ+V+PLKD3169uKUNIcmLpjD
eYGoSSUVlAzeHQCWSiWjpzsAPIznNZV3gPhfitPuZXtQnras193x9OeZBWm2h++11k/kBjJQ/hAK
cMxEGC5qUknlAFVh6Qqwp1iDPYwH6A+Ydn4l6O9Oe1ZfOz8d375W7Mh/bF3FXgd0WvzgogMqh1G6
uiwdoqU22sayxc7QM/zJEuB8eS95sv31eDpDcsN3Whp/aJkKYJmm1R8brNANv0lBFKCqUsHt0uEr
poLbPQ5f2ApPLwnzYMgUihtBkeAN3yh8Q8Z53B//FqMzlmngzznrzhh8znFRzaOuUYDAFrujM2wr
PKv75YDxkT8oKDlOLIy1WMdxmCj+Ol8ulmzGWpXnbHqeZ0twGxEzBVHNj7GlQRtZ7FZC2FYfcWsL
ZREG8pfBfJqJDYp1+q+FRzUiuxlENUxQra2xR4+XA7bCv2LkN0tzohUHcRzNqQ98SmculyP8ahPD
fgR6UTIAX0w+yVWYZsukcuFqKu2nj8WljxnnCqImlVQwDq41fNfG/myxOxOJPYzHFa8y+a9yufJj
5f6t2Nect6aOGSXBeWkjmu5wUZNKKuOleo6wxHmRxZ7cD11qlAT96TdecrzkPZCrdZ74i2tTrEZX
U9XVHQeRKv5PgAeSJgyN7roaM9w1C3RavgTs6GKn/Qlb4dmtVvLKq3++nOhE9O1LUXetmYaOfdnG
pVWCqPfKmdsspmKLPa41uhUemJ/l8ikM+e7Lr8Wpq32dRa4pGxtcoU1QBr9sXBTcklWykem1JBuZ
PR3CYSs8vHwxlYcXnY+nrehTUx3dNRAtS6+wqtENsrBAUpOV1saqu08ibIVnFWXyqPiZtqCNJVMo
EV2Es8jPw7bzzaA+aSxFe0TCPUhqnm922/nW3ZsCtsJTXEULaYqzgh1wi7f9ZfeTv78Up8P2Qn4T
uV4P3C3OkhrRZ501RnEDUXCL6sTUZPCcsqkMLAE8utj9ubKt8PAW2X0kaYre8FX4Zgt5f8eKGFQh
+XbLBJDrlPmySaymGq6BGN93ZTei6gNENa/ZRikpW+yOyLCt8OxWn+Rrlme736Bf8/uLVkxisAwb
nTSjGiNCClxU4+5wxY4D5d3h9sSz2FZ4erP7Ae2bl0Ho03cvSub17gLV1FzVMRxEg2JOT7/TB797
TNCkkimGsvgSLpTFH8azW0bysebAzx/8Z2VO20ARXmWDXbhmbWrQ4JkNT5sBSc0Xzml74TpNMP5H
46ERAPIpDXHRaDBQZVWamoW1vVRtlC3BRDUOuVq1W3nI9XymsBWeWvwwKHp14zZgQNgN2sOgSNWN
20MQyXuXpse/vmxpQPRwpnrw4Xee3BBfXq++OcfysK5yQm94ZT0X1aTX5i5x+9qo0K3w9KZPA7q1
+fv99rS7fP8wFdW1VRuvA49jx0QxUELquFHLJmeXKlvsZse2wrPz41jeBRBM07DRh1LTDFdDdPi/
AhsRjQdRzZetJZnG7ZmOAFvhgdHfLg1sTT1zYkuCf1Q9CSzT0BBtezg2dUyKPYhijCxLd1xIsTdr
S1zjNbsncvCt8NjW01D+PQv/LvbbJrcykVdXNXTggYIb3mYFRMHLVQWcrfJwEwPOVs/hBlvhwX0K
Y3kL/wYOwMnnp2Z/HN/2r8VpvarwfW8pFIcrwrVMZMqDTatjRqSSg6jmFdHS658sdl8RbCs8yGzt
DzIhbijbUQ5QkG8oW1HKcyyKk3gSOrptmrjMc4LKHXMSgqgJnGRVoZYq1m6B44Qu9qSS0K0kUIUD
7t57Yk8Up8Xx8m42nU2b8OMuDzoV4M4cbpCBKNBNdDosDYobeMYcLHF1hSx2Xx5sKzyy+2Quj8x/
Ox1PW9QXajiIpkqcoTVqRgwTBc45IYOJY61nMKndKTmwFZ6hn/uPN4bjGco7Bm4QGxDly/JvEBsQ
5Y/E1fa0/XJ8O1yU+f7463bPwxX0LJcgNtxTAJIoHdUzHddhFq+meqawxLMC1O7oIv+j8cBWfipv
uP3gvOTttR8cmPwblmpKGixAoZvMQjjCPE11kT4omw5hG9HPHUS914NNnlZX04PNnrQ62AoPjPxu
aVz+IkyjwE+UMA6DPKUTTpdPYUozJaqItWp6an83/Cs9ffDbRuVMKpE8vVpcKgPW3b0e+MN4dH64
Wg1oR3WDx+FJo5sVL9vTr8fD7r+3TN9Y7d/ej1Y/fVU2XAOxUDMnrwxHJJyAKPDeCZ532xKWSs+7
3ROqsORmTs6CAZ734I83+jP2+9q4TmVzDS+6Oq5noUZN2VEjx5gouBaIuWqyUn1y2LnCEryAdLEn
vEi3wpMLgk00xO10g1fCG+D+vMHj8KTJRYdvhFch1GRaNE6HvSRMd0xNJoiaVFLhntBUYam8J7Se
gT3sYTysZJPL63P3fk47yP1MLthM8YNf1lEW0ZQ6YU4UOYhVtJfTdO7MwRl1XNQEXhUhE1YVloAe
XeyJZNOt8PQIiAHTG274BHw3emPoybuYprtLstu3di5UXc82kWWbbMSWMZgbSGKMdA1itOTsMnhP
HFhi2NhitwnLtsJjS6I4/Gkln4V9A0fASVPz/34p9vst74AuxnQMQ0WMFLkiG1FRDaLAWBASAmxD
WCoTAnoGCcJWeGb+J9lMCtbzYBWusqhmsTqOievZTWkZI4ojmKBJJZOpIqrtCUtcFSGL3SYre/gj
Vm1tDlYDjNbVfnsoLopmtDbFJLqQhctRJ9jUO3P4Swai2BslJqnzRJR6krrT4yZnW+FfslWcPMiP
dAtSP3gMU2Xqp2kYK8t4Rm7QdZKnz0qWL9NQGMyu2o7uoVVhdcz8XRDV+FY52fq3avV0jGdb4TEG
03RAeTqhGIeC7mGY5B8b6xcxvDElYCCq4RepTWMs/SJGd2k6bIVntUwDeVb+fewHdS+cg0t2ZaSG
x7ConEklEqwrzxSWSuuqx10OD+MxkV8sTWm13R8Vf385Kklx+Yu137vmbZJ/XwtZTU2Q0cFlQ5Fx
UZNKKlwEvJEhLJUXgdHtdIOH8dRWfiJvVCXLjb/xPwmHFr2gsIcWYTViNAiIahxaZlvGYfeMer4V
nlVCfrM0q8X2r/PxQF6r307bM3n85fJ2Kt53KSTKpGu7WKXWsMZcniAKaDmaa7ImLo7GB9LAErei
tJ6BNLAVHuAimkt6i5iG5n/bvRKT4HguTt92L7VcVzpyw7KxWgcBN3z6LkhikMiPJv+l3IjRKS4B
N7rYebTBVh9xa9PWgtlCftZz/P1I/oj9fro902lI0eH1jbyBdGBeNSrbdD0DbU0RftpgflzUpJIK
pxy36GGpPOXMnq5e7GH8ixc/yzdnXT0swyT6pCyWREMTyiBccsOb2NizMS4dhIlqnnUtfdCsHmKw
FZ7YKtxIE5tnoZI9Z3m4yCrNwzNN08XangTX8NgVSJpUQuH9ch1hqXy/ekpX4WE8rflG3ph6ek5m
D/5CeVjmYZwp/87K9NM8E0tuHF23XfS9ao6pjgNR8GKZqmHTRAfLcF1VWGL02GLnuwZb4ek9PUjD
+yekhRjGiNxVIS0E/i/X2Gxh6aqx9XQjGZAWcuMlx0v+6/zBgcm/Yf6ftEeLmDfpWJaHLO0FWIP9
aFwUO6nEOgXeoKVep9DToAW2wtN6DNMBFtSPTEse1v9Ipu44hu2Zuq7XkqlLFrtVswGZujeG4xkO
+GpvEN9DlA+K3iA2IMrn3WfBw5M/VYIHP41ZX6+yU5Ch2Q66fMtQ76pG9bLQQBJYB4JHXBd7RJQe
cb27vRJshWdGf/1Pq59v1KSp3ZBJIxuSv3C/O7z8Ues8Ah7fWm2qYejI/iMAcLBjhItqEtTaCHYX
WsJWeIL3SSBv3Yef/HmYCH4Q3TEdQ4LViF4tIGpSSYUIFp89AEtlBKtn9gA8jGf1aS6vGz/4cbzw
00flPkr8JIj8uJr1efXBuRqdOYTHN7wMGiQxVJZFbkJqWmie5rnCEqTM0MUes5VuhadHSUjjC46v
hRL8sd0dlKT4i7ZauuwOBbH6r8PLbJPYQ2gtZAw7Lqppl6ltdlm3Cw62wsMLgkS+2t5P6Y3gr6Kc
vHVVVF53HQed+UyIacPbUIMoOMUEB7nRlm3UM4ARtsIT89NAntgjzZn563h8/VDnpf/k2Nwjym74
QQeimuzMNna9k2XJVnh2j08Dejvc2F3Zyatxi+O37eFY0z5MuY90uHEFohrHmum2HGs9MxlhKzys
xXIzII0ye4j9ZCaoH7Zjuvgg8yhYIIqSEacWu5YhLDFYbLH7DmBb4WGR3y3NKlsmSyUPHwXDwFVt
zfmXwAJJ8Mk5tF8IU9WMKpOBLHFVzejOZOB/NJ5VtszlfXDPURjPNJUOPsqCKEyCsNZeT6f/2khu
ujPCoOKiJpVUCC44hrBUBhecnvZ67GE8uOc8lFdyk2WaPyjhOl2uQj9RllGspMtnP86fxVkEqqPb
BlbLJQCHj+VlgiaVTMCniUslvr5Rn+xhPL4kla/ym1Pt9sMumJpnatjKcJ3OcBgKjYtqXppt6aZG
d7opbIXHNvcT+cyth2J7uih5cSbWAB1Wub0cT+867Zm2a3rob9YekwsCopq3aFsP0b7sGbYVnt5D
FsiPKrvRE+jJK2wx+QlKArXh4eFSnC7kK/5CLFMRoGFYOnI+KgFojsp/ZqImldSaWwSWkG4ReBgP
MH6WT0VaZzMawr8citNZiblhamuq7aLfN+POGv6+gahJJZXnujnC0jXXrTtdHB7G4yI/fUC6Q5jO
lkG4VMLZOvChxO9anUtPZhv9mhkjRiVxUXAhCJ5Kta3LtNqT3cu2wnNbEQLS3Pyvx/3+qCyKVzok
pK1eTVPJiWJizVJdHTECjotq3LB6yxBjsthNj22Fp+cvQnnrId4RQ/51s92/Fcqs+Fbsj1/pEVcb
aWbY+BOOwBv+6oEoRkqMAdbCgmUMsKc9PGyFhxdvZvI3RPCcLx+jJMyjIBMy8W3NQyeSa96YMw5E
NUxVmKn3zlTVu4OmsBUeGPnt8uZXlGTkmIvyZ9HqUmmxsAQvY3BGFxc1qaTCncDfOVgq74TuCY38
YTwv8sulccVR/hD5kP4svmDkTEGnwBFg6ghgTNSkkgo6B49kwVKpc1g9xxl7GA8s9uWDfguabd9a
s0zbarjo2kg2b3AwNBA1qaQCNNcUlkpobp9rkj6Mh7ZYJgPOsRs2gk2a2jRaxn4mFI6qrqbpJtaj
yxq/D4UFkppKWls4ubtvL/+j8azoz5ZmFR0u26+12LtFVUNsPFmz7qzBPkou6j0sx/OasJye1CLY
Cg8rSgb0m5mv4/tqYqBwVaqsgzf6a7TGJD8zSSUgV7Xox0i0Kk9Ygo+RLna/YGwrPLP5ekATsmmY
5HH43Ch+0VRDM1T0ZWmN6bsIopq6v+hwu+r+3TkesBWe2TSTn9uW7X4/0IlGs+13Jft6PF3qB7/n
mg627oVyG672g6iGZ8hoS4buC+WxrfDcspm8W+3z901xOm8/9uda5O+VONnGJE0yUU2DyWwzmHrI
sa3w5D5vMvmT7YauRCevpN3Y0cU4W8mn6t7Qlejkc8Vv7Ep28p/sKkwXvnIffVLCZBOly2RBtJT2
9EBPswwZxW4wRZDUhKi3QeyuoYet8BBXYRZJM/T350J0RzoW9VX9K0hxUU1URhuqnnkybCs8Kj+U
JxUezt/PLwVr0/CyKw71Ng2ao9meKfOhDjZOuagmOK0NXE+yM9sKDy5MsgG5WTd0HJ00uXgV01T7
7eFlJwRfxAApsanR2Q2E3fCUEC6qaee3RK6cnr73sBWeHcEgrw2n/izyE5jyKV4FqqdLHHDG4JcN
JAEcy6G0DHZUiUvcxqdz2ruvAroVnlc6k09BWhWnL7vt4dtuvy+U9Ph9u6ejZU9v5wv3KOmqiu4K
oo3qCgKimpa+mAd9tfR7OhizrfDkVht571uWp36e/Z9s8X95stb9MlVmy0WU0Cjze09TFgbrNMoj
ukA0FfK/CvkbrZ9s/kGbhumhP2hjjOMORDGmnucZMPlDNXj0AZa4R9joiT7AVnjM85/lwzW/rP0k
Xy8gEXMyCyfwQTvk08DHA407Y7AnBSQ1D0Cz7QDsbv0GW+F5/bII5EP3+QNr6zMjryR7za5noGOo
uo1Gpo9wpIOkJrKWyjay2I2MbYVHRn69NDH/69f9rnit17UJXWY9R8KM0O704d8miGocgVpb/r3W
bYzBVnhw/iqWT8HMnrNgKeRIq56t6uhjbMwobSao8Ya5bdEHt6fFLNsKDyp7HuAVfoiSMFiKAXra
nA49GkBTx4wGAFEN9ddpqwLvaSsLW0nAigZYp9OCNnhT/F1t5qltaraBfrfUEf2xuagmsLaUGac7
owG2wgP75Efyrri8OPz+n28HamldqvYDQgmR6ZHPANvMnrDTB4e4uCgGSow78FytetyhJ1cLtsKz
y3+eyudq+c/5mkadVw9+uvCFhsWGa7kaWvMdlaIFohigWnWpKSwxZmyx+wNlW+GZ0Z8vzWx5KJ4I
hpOy2J52h0I06h3dw18Ao2rVQNSkksqLPRxhqUw7crpfNHgYD22ZhPLukGC79798Fb5KpttYEl+l
NvgKAEkTdtsJKW2auFSmtGmdWc78j8azCvyFvNIPtpWSLQTT6smPf1r4aQ6dxfvsKrO0q1QP3c1B
dcc0IwBRTbvKbLOrej5ithWe8fznj5Tef/t//x+W/a8E7MIEAA==
headers:
Access-Control-Allow-Credentials:
- 'true'
Access-Control-Allow-Headers:
- X-Requested-With, content-type, auth-token, Authorization, stripe-signature,
APPS
Access-Control-Allow-Methods:
- GET, POST, OPTIONS
Access-Control-Allow-Origin:
- '*'
Access-Control-Max-Age:
- '3600'
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json; charset=utf-8
Date:
- Fri, 24 Nov 2023 14:22:43 GMT
ETag:
- W/"4c2ec-WidRiW0E+CVM6xd3bF36zv5Jhm8"
Server:
- nginx/1.18.0 (Ubuntu)
Transfer-Encoding:
- chunked
Vary:
- Accept-Encoding
X-Frame-Options:
- SAMEORIGIN
X-Powered-By:
- Express
status:
code: 200
message: OK
version: 1

View File

@@ -9,67 +9,27 @@ interactions:
Connection:
- keep-alive
method: GET
uri: https://financialmodelingprep.com/api/v4/insider-trading?apikey=MOCK_API_KEY&limit=100&page=0&symbol=AAPL
uri: https://financialmodelingprep.com/api/v4/insider-trading?apikey=MOCK_API_KEY&limit=100&page=0&symbol=AAPL&transactionType=P-Purchase
response:
body:
string: !!binary |
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
/j909E4f4OYAAA==
H4sIAAAAAAAAA+3ZT1PbOBQA8Hs/hSbnWtGTrH++pSHbzUyADKH00OnBmxjwlNhZ25TNdPrdKzk4
BOymJbs2JGsGDlGeZUnvN8+S+fQGoW/mD6FOupz/Fd90PNTp9cajzttV62V4E0ZXR34W2G8oAe4Q
6VCFQHkMPCKKwCzxo9SfZmEclaLdIigJFnGSmQ774RcbQQgBwjVlvKKb8+Ui72bsjG+T6bWfBkVQ
GkxvkzALg/T0LgpmJogJV9x/OY3nCz9aPtyBMDMQzUpjOPHnef8fe+9PBmdo8mHSO1kPw9z79NL2
ntiQWZgE0yxOiq/96d+3YWoHeZochekiTkP7IV+89crFybyYglse+fn9RPPhUzPK+4hFEk7tNUAp
1o+vWhYj7sfzeRyhSRZPvxQ9mzTlE77OskXqdbt3d3fYXIev4q/dnlm+8GuQdoPZlZ90Z37md1eL
0rXro7SmLgNukqEI2WhybP7yRieMZsE/+Dqbd8ztvr89EDZ2znvMZmP4D2xcDLR1U7Ob/WZTqYax
w1FDHQCHklwN93hVup9Gg96mhhLJtFvRzXPUuIbermyG7w2as9N3g7NztE56027UUzdccEwUbabe
AFApCDW5YCA532hyTApXjYcqx13X9L2UI0oVhwsXU/H4qtbNf+2Gqf1241a4YZhwyls5Ncs5wIpD
MTSzx2nEDVgJzLjRHlEel9vd5NG0fjdUyJ23xq/BDbASHCY5ZnS9dauDzsPirKwoMDtkMGcVRRjf
aLJJXDUeKB9g+85Hlvm42FW8zr1O83zMMRuIQwUiwgPqueusVPIhwjGlj6ptfEDZn3/Jh+rdD+b9
3vH43WA0Qh+Ho9Gwd4wuXsgQKz+7JGDeeP0hkgimXLUJyOR91dgwIHNbU3/Wb39aQNsAbUyiAKQ4
1nW+TX71gEwF0mbz1QL6LUAVFYhiqQ4LUE6CSHvwMoB+8Wr5PpqXkre5A3KpK6ocPgcQABewq6BJ
/8/j4dE5GpwN+2jwUnpISY/QWMjG9QjCQT7eANk05o2tnleqh1XqkXUe3Fs9B6MHqvU0fvhq9eyj
nuonl2yfXK2e3fRIgkmd/6p4MT1gZutR8Nj294Y22vzKUuI29DBOOdEV3TxHD5XFMj/bzh/+LLi5
Qb0ou46j5U/sxJeXJqOJhyZBFMYJujCf0DgJ0nAWRFldokCXypEBRV8CFNESSqBsY/OglANby1EL
6uegikmtPQkM8Pia/52ntkDt7Ek/9cQ1Vq/a05vPPwC7Xhcf8ywAAA==
headers:
Access-Control-Allow-Credentials:
- 'true'
@@ -89,9 +49,9 @@ interactions:
Content-Type:
- application/json; charset=utf-8
Date:
- Mon, 20 Nov 2023 16:20:05 GMT
- Thu, 23 Nov 2023 15:37:00 GMT
ETag:
- W/"e6e0-WqxggactfSAHm3q18AwDkFUqhiI"
- W/"2cf3-POGuhFFFDsLUP2Mt0xcWd1R6lJk"
Server:
- nginx/1.18.0 (Ubuntu)
Transfer-Encoding:
@@ -115,65 +75,65 @@ interactions:
Connection:
- keep-alive
method: GET
uri: https://financialmodelingprep.com/api/v4/insider-trading?apikey=MOCK_API_KEY&limit=100&page=1&symbol=AAPL
uri: https://financialmodelingprep.com/api/v4/insider-trading?apikey=MOCK_API_KEY&limit=100&page=1&symbol=AAPL&transactionType=P-Purchase
response:
body:
string: !!binary |
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
tM9dl3+0EUS//fv/bAb3jEvnAAA=
headers:
Access-Control-Allow-Credentials:
- 'true'
@@ -193,9 +153,9 @@ interactions:
Content-Type:
- application/json; charset=utf-8
Date:
- Mon, 20 Nov 2023 16:20:05 GMT
- Thu, 23 Nov 2023 15:37:01 GMT
ETag:
- W/"e59a-qcQrGVt0VXwnUpk8Sd48LPNxD9o"
- W/"e74b-0FzVtfs2zRuL/i2ZB39MetZzpn0"
Server:
- nginx/1.18.0 (Ubuntu)
Transfer-Encoding:

View File

@@ -1,3 +1,5 @@
"""Unit tests for FMP provider modules."""
from datetime import date
import pytest
@@ -6,7 +8,7 @@ from openbb_fmp.models.analyst_estimates import FMPAnalystEstimatesFetcher
from openbb_fmp.models.available_indices import FMPAvailableIndicesFetcher
from openbb_fmp.models.balance_sheet import FMPBalanceSheetFetcher
from openbb_fmp.models.balance_sheet_growth import FMPBalanceSheetGrowthFetcher
from openbb_fmp.models.calendar_dividend import FMPDividendCalendarFetcher
from openbb_fmp.models.calendar_dividend import FMPCalendarDividendFetcher
from openbb_fmp.models.calendar_earnings import FMPCalendarEarningsFetcher
from openbb_fmp.models.calendar_splits import FMPCalendarSplitsFetcher
from openbb_fmp.models.cash_flow import FMPCashFlowStatementFetcher
@@ -18,7 +20,7 @@ from openbb_fmp.models.crypto_historical import FMPCryptoHistoricalFetcher
from openbb_fmp.models.crypto_search import FMPCryptoSearchFetcher
from openbb_fmp.models.currency_historical import FMPCurrencyHistoricalFetcher
from openbb_fmp.models.currency_pairs import FMPCurrencyPairsFetcher
from openbb_fmp.models.discovery_filings import FMPFilingsFetcher
from openbb_fmp.models.discovery_filings import FMPDiscoveryFilingsFetcher
from openbb_fmp.models.earnings_call_transcript import FMPEarningsCallTranscriptFetcher
from openbb_fmp.models.economic_calendar import FMPEconomicCalendarFetcher
from openbb_fmp.models.equity_historical import FMPEquityHistoricalFetcher
@@ -80,7 +82,7 @@ def vcr_config():
@pytest.mark.record_http
def test_fmp_company_filings_fetcher(credentials=test_credentials):
params = {"symbol": "AAPL", "limit": 100}
params = {"symbol": "AAPL", "form_type": "10-K", "limit": 100}
fetcher = FMPCompanyFilingsFetcher()
result = fetcher.test(params, credentials)
@@ -450,7 +452,7 @@ def test_fmp_index_constituents_fetcher(credentials=test_credentials):
def test_fmp_calendar_dividend_fetcher(credentials=test_credentials):
params = {"start_date": date(2023, 11, 6), "end_date": date(2023, 11, 10)}
fetcher = FMPDividendCalendarFetcher()
fetcher = FMPCalendarDividendFetcher()
result = fetcher.test(params, credentials)
assert result is None
@@ -573,16 +575,16 @@ def test_fmp_etf_holdings_performance_fetcher(credentials=test_credentials):
@pytest.mark.record_http
def test_fmp_filings_fetcher(credentials=test_credentials):
def test_fmp_discovery_filings_fetcher(credentials=test_credentials):
params = {
"form_type": "8-K",
"limit": 10,
"start_date": None,
"end_date": None,
"form_type": "8-K",
"limit": 100,
"is_done": None,
}
fetcher = FMPFilingsFetcher()
fetcher = FMPDiscoveryFilingsFetcher()
result = fetcher.test(params, credentials)
assert result is None

View File

@@ -1,23 +1,34 @@
"""Intrinio provider module."""
"""Intrinio Provider Modules."""
from openbb_core.provider.abstract.provider import Provider
from openbb_intrinio.models.balance_sheet import IntrinioBalanceSheetFetcher
from openbb_intrinio.models.calendar_dividend import IntrinioCalendarDividendFetcher
from openbb_intrinio.models.calendar_ipo import IntrinioCalendarIpoFetcher
from openbb_intrinio.models.cash_flow import IntrinioCashFlowStatementFetcher
from openbb_intrinio.models.company_filings import IntrinioCompanyFilingsFetcher
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_info import IntrinioEquityInfoFetcher
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.historical_attributes import (
IntrinioHistoricalAttributesFetcher,
)
from openbb_intrinio.models.income_statement import IntrinioIncomeStatementFetcher
from openbb_intrinio.models.insider_trading import IntrinioInsiderTradingFetcher
from openbb_intrinio.models.institutional_ownership import (
IntrinioInstitutionalOwnershipFetcher,
)
from openbb_intrinio.models.key_metrics import IntrinioKeyMetricsFetcher
from openbb_intrinio.models.latest_attributes import IntrinioLatestAttributesFetcher
from openbb_intrinio.models.market_indices import IntrinioMarketIndicesFetcher
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_intrinio.models.search_attributes import (
IntrinioSearchAttributesFetcher,
)
from openbb_intrinio.models.share_statistics import IntrinioShareStatisticsFetcher
from openbb_intrinio.models.world_news import IntrinioWorldNewsFetcher
intrinio_provider = Provider(
@@ -30,16 +41,25 @@ intrinio_provider = Provider(
"BalanceSheet": IntrinioBalanceSheetFetcher,
"CalendarIpo": IntrinioCalendarIpoFetcher,
"CashFlowStatement": IntrinioCashFlowStatementFetcher,
"CompanyFilings": IntrinioCompanyFilingsFetcher,
"CompanyNews": IntrinioCompanyNewsFetcher,
"CurrencyPairs": IntrinioCurrencyPairsFetcher,
"CalendarDividend": IntrinioCalendarDividendFetcher,
"EquityHistorical": IntrinioEquityHistoricalFetcher,
"EquityInfo": IntrinioEquityInfoFetcher,
"EquityQuote": IntrinioEquityQuoteFetcher,
"FinancialAttributes": IntrinioFinancialAttributesFetcher,
"FredIndices": IntrinioFredIndicesFetcher,
"WorldNews": IntrinioWorldNewsFetcher,
"HistoricalAttributes": IntrinioHistoricalAttributesFetcher,
"IncomeStatement": IntrinioIncomeStatementFetcher,
"InsiderTrading": IntrinioInsiderTradingFetcher,
"InstitutionalOwnership": IntrinioInstitutionalOwnershipFetcher,
"KeyMetrics": IntrinioKeyMetricsFetcher,
"LatestAttributes": IntrinioLatestAttributesFetcher,
"MarketIndices": IntrinioMarketIndicesFetcher,
"OptionsChains": IntrinioOptionsChainsFetcher,
"OptionsUnusual": IntrinioOptionsUnusualFetcher,
"SearchFinancialAttributes": IntrinioSearchFinancialAttributesFetcher,
"SearchAttributes": IntrinioSearchAttributesFetcher,
"ShareStatistics": IntrinioShareStatisticsFetcher,
"WorldNews": IntrinioWorldNewsFetcher,
},
)

View File

@@ -0,0 +1,99 @@
"""Intrinio Dividend Calendar Model."""
from datetime import datetime
from typing import Any, Dict, List, Optional
from dateutil.relativedelta import relativedelta
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.calendar_dividend import (
CalendarDividendData,
CalendarDividendQueryParams,
)
from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS
from openbb_core.provider.utils.helpers import get_querystring
from openbb_intrinio.utils.helpers import get_data_many
from pydantic import Field
class IntrinioCalendarDividendQueryParams(CalendarDividendQueryParams):
"""Intrinio Dividend Calendar Query.
Source: https://docs.intrinio.com/documentation/web_api/get_security_stock_price_adjustments_v2
"""
symbol: str = Field(description=QUERY_DESCRIPTIONS.get("symbol", ""))
limit: Optional[int] = Field(
default=100,
description=QUERY_DESCRIPTIONS.get("limit", ""),
alias="page_size",
)
class IntrinioCalendarDividendData(CalendarDividendData):
"""Intrinio Dividend Calendar Data."""
__alias_dict__ = {
"amount": "dividend",
}
factor: float = Field(
description=(
"factor by which to multiply stock prices before this date, "
"in order to calculate historically-adjusted stock prices."
),
)
dividend_currency: Optional[str] = Field(
default=None, description="The currency of the dividend."
)
split_ratio: float = Field(
description="The ratio of the stock split, if a stock split occurred.",
)
class IntrinioCalendarDividendFetcher(
Fetcher[
IntrinioCalendarDividendQueryParams,
List[IntrinioCalendarDividendData],
]
):
"""Transform the query, extract and transform the data from the Intrinio endpoints."""
@staticmethod
def transform_query(params: Dict[str, Any]) -> IntrinioCalendarDividendQueryParams:
"""Transform the query params."""
transformed_params = params
now = datetime.now().date()
if params.get("start_date") is None:
transformed_params["start_date"] = now - relativedelta(year=1)
if params.get("end_date") is None:
transformed_params["end_date"] = now
return IntrinioCalendarDividendQueryParams(**transformed_params)
@staticmethod
def extract_data(
query: IntrinioCalendarDividendQueryParams,
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 ""
base_url = "https://api-v2.intrinio.com/securities"
query_str = get_querystring(query.model_dump(), ["symbol"])
url = f"{base_url}/{query.symbol}/prices/adjustments?{query_str}&api_key={api_key}"
return get_data_many(url, "stock_price_adjustments", **kwargs)
@staticmethod
def transform_data(
query: IntrinioCalendarDividendQueryParams, data: List[Dict], **kwargs: Any
) -> List[IntrinioCalendarDividendData]:
"""Return the transformed data."""
transformed_data: List[Dict] = [
{"symbol": query.symbol, **item} for item in data
]
return [
IntrinioCalendarDividendData.model_validate(d) for d in transformed_data
]

View File

@@ -0,0 +1,103 @@
"""Intrinio Company Filings Model."""
from datetime import (
date as dateType,
datetime,
)
from typing import Any, Dict, List, Optional
from dateutil.relativedelta import relativedelta
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.company_filings import (
CompanyFilingsData,
CompanyFilingsQueryParams,
)
from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS
from openbb_core.provider.utils.helpers import get_querystring
from openbb_intrinio.utils.helpers import get_data_many
from pydantic import Field
class IntrinioCompanyFilingsQueryParams(CompanyFilingsQueryParams):
"""Intrinio Company Filings Query.
Source: https://docs.intrinio.com/documentation/web_api/get_company_filings_v2
"""
__alias_dict__ = {
"form_type": "report_type",
"limit": "page_size",
}
start_date: Optional[dateType] = Field(
default=None,
description=QUERY_DESCRIPTIONS["start_date"],
)
end_date: Optional[dateType] = Field(
default=None,
description=QUERY_DESCRIPTIONS["end_date"],
)
thea_enabled: Optional[bool] = Field(
default=None,
description="Return filings that have been read by Intrinio's Thea NLP.",
)
class IntrinioCompanyFilingsData(CompanyFilingsData):
"""Intrinio Company Filings Data."""
id: str = Field(description="Intrinio ID of the filing.")
period_end_date: Optional[dateType] = Field(
default=None,
description="Ending date of the fiscal period for the filing.",
)
sec_unique_id: str = Field(description="SEC unique ID of the filing.")
instance_url: Optional[str] = Field(
default=None,
description="URL for the XBRL filing for the report.",
)
industry_group: str = Field(description="Industry group of the company.")
industry_category: str = Field(description="Industry category of the company.")
class IntrinioCompanyFilingsFetcher(
Fetcher[
IntrinioCompanyFilingsQueryParams,
List[IntrinioCompanyFilingsData],
]
):
"""Transform the query, extract and transform the data from the Intrinio endpoints."""
@staticmethod
def transform_query(params: Dict[str, Any]) -> IntrinioCompanyFilingsQueryParams:
"""Transform the query."""
transformed_params = params
now = datetime.now().date()
if params.get("start_date") is None:
transformed_params["start_date"] = now - relativedelta(years=1)
if params.get("end_date") is None:
transformed_params["end_date"] = now
return IntrinioCompanyFilingsQueryParams(**transformed_params)
@staticmethod
def extract_data(
query: IntrinioCompanyFilingsQueryParams,
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 ""
base_url = "https://api-v2.intrinio.com/companies"
query_str = get_querystring(query.model_dump(by_alias=True), ["symbol"])
url = f"{base_url}/{query.symbol}/filings?{query_str}&api_key={api_key}"
return get_data_many(url, "filings", **kwargs)
@staticmethod
def transform_data(
query: IntrinioCompanyFilingsQueryParams, data: List[Dict], **kwargs: Any
) -> List[IntrinioCompanyFilingsData]:
"""Return the transformed data."""
return [IntrinioCompanyFilingsData.model_validate(d) for d in data]

View File

@@ -0,0 +1,66 @@
"""Intrinio Equity Info Model."""
from typing import Any, Dict, List, Optional
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.equity_info import (
EquityInfoData,
EquityInfoQueryParams,
)
from openbb_intrinio.utils.helpers import get_data_one
from pydantic import Field
class IntrinioEquityInfoQueryParams(EquityInfoQueryParams):
"""Intrinio Equity Info Query.
Source: https://docs.intrinio.com/documentation/web_api/get_company_v2
"""
class IntrinioEquityInfoData(EquityInfoData):
"""Intrinio Equity Info Data."""
__alias_dict__ = {
"symbol": "ticker",
}
id: str = Field(default=None, description="Intrinio ID for the company.")
thea_enabled: Optional[bool] = Field(
default=None, description="Whether the company has been enabled for Thea."
)
class IntrinioEquityInfoFetcher(
Fetcher[
IntrinioEquityInfoQueryParams,
IntrinioEquityInfoData,
]
):
"""Transform the query, extract and transform the data from the Intrinio endpoints."""
@staticmethod
def transform_query(params: Dict[str, Any]) -> IntrinioEquityInfoQueryParams:
"""Transform the query."""
return IntrinioEquityInfoQueryParams(**params)
@staticmethod
def extract_data(
query: IntrinioEquityInfoQueryParams, # pylint: disable=unused-argument
credentials: Optional[Dict[str, str]],
**kwargs: Any,
) -> Dict:
"""Return the raw data from the Intrinio endpoint."""
api_key = credentials.get("intrinio_api_key") if credentials else ""
base_url = "https://api-v2.intrinio.com"
url = f"{base_url}/companies/{query.symbol}?api_key={api_key}"
return get_data_one(url, **kwargs)
@staticmethod
def transform_data(
query: IntrinioEquityInfoQueryParams,
data: List[Dict],
**kwargs: Any, # pylint: disable=unused-argument
) -> IntrinioEquityInfoData:
"""Transforms the data."""
return IntrinioEquityInfoData.model_validate(data)

View File

@@ -0,0 +1,75 @@
"""Intrinio Historical Attributes Model."""
from datetime import datetime
from typing import Any, Dict, List, Optional
from dateutil.relativedelta import relativedelta
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.historical_attributes import (
HistoricalAttributesData,
HistoricalAttributesQueryParams,
)
from openbb_core.provider.utils.helpers import get_querystring
from openbb_intrinio.utils.helpers import get_data_many
class IntrinioHistoricalAttributesQueryParams(HistoricalAttributesQueryParams):
"""Intrinio Historical Attributes Query.
Source: https://docs.intrinio.com/documentation/web_api/get_historical_data_v2
"""
__alias_dict__ = {"sort": "sort_order", "limit": "page_size"}
class IntrinioHistoricalAttributesData(HistoricalAttributesData):
"""Intrinio Historical Attributes Data."""
class IntrinioHistoricalAttributesFetcher(
Fetcher[
IntrinioHistoricalAttributesQueryParams,
List[IntrinioHistoricalAttributesData],
]
):
"""Transform the query, extract and transform the data from the Intrinio endpoints."""
@staticmethod
def transform_query(
params: Dict[str, Any]
) -> IntrinioHistoricalAttributesQueryParams:
"""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 IntrinioHistoricalAttributesQueryParams(**transformed_params)
@staticmethod
def extract_data(
query: IntrinioHistoricalAttributesQueryParams, # pylint: disable=unused-argument
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 ""
base_url = "https://api-v2.intrinio.com"
query_str = get_querystring(query.model_dump(by_alias=True), ["symbol", "tag"])
url = f"{base_url}/historical_data/{query.symbol}/{query.tag}?{query_str}&api_key={api_key}"
return get_data_many(url, "historical_data")
@staticmethod
def transform_data(
query: IntrinioHistoricalAttributesQueryParams, # pylint: disable=unused-argument
data: List[Dict],
**kwargs: Any,
) -> List[IntrinioHistoricalAttributesData]:
"""Return the transformed data."""
return [IntrinioHistoricalAttributesData.model_validate(d) for d in data]

View File

@@ -0,0 +1,162 @@
"""Intrinio Insider Trading Model."""
from datetime import (
date as dateType,
datetime,
)
from typing import Any, Dict, List, Literal, Optional
from dateutil.relativedelta import relativedelta
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.insider_trading import (
InsiderTradingData,
InsiderTradingQueryParams,
)
from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS
from openbb_core.provider.utils.helpers import get_querystring
from openbb_intrinio.utils.helpers import get_data_many
from pydantic import Field
class IntrinioInsiderTradingQueryParams(InsiderTradingQueryParams):
"""Intrinio Insider Trading Query.
Source: https://docs.intrinio.com/documentation/web_api/insider_transaction_filings_by_company_v2
"""
__alias_dict__ = {"limit": "page_size"}
start_date: Optional[dateType] = Field(
description=QUERY_DESCRIPTIONS.get("start_date", "")
)
end_date: Optional[dateType] = Field(
description=QUERY_DESCRIPTIONS.get("end_date", "")
)
ownership_type: Optional[Literal["D", "I"]] = Field(
default=None,
description="Type of ownership.",
)
sort_by: Optional[Literal["filing_date", "updated_on"]] = Field(
default="updated_on",
description="Field to sort by.",
)
class IntrinioInsiderTradingData(InsiderTradingData):
"""Intrinio Insider Trading Data."""
__alias_dict__ = {
"owner_title": "officer_title",
"transaction_type": "transaction_type_code",
"acquisition_or_disposition": "acquisition_disposition_code",
"security_type": "security_title",
"securities_owned": "total_shares_owned",
"securities_transacted": "amount_of_shares",
}
company_name: str = Field(description="Name of the company.")
conversion_exercise_price: Optional[float] = Field(
default=None,
description="Conversion/Exercise price of the insider trading.",
)
deemed_execution_date: Optional[dateType] = Field(
default=None,
description="Deemed execution date of the insider trading.",
)
exercise_date: Optional[dateType] = Field(
default=None,
description="Exercise date of the insider trading.",
)
expiration_date: Optional[dateType] = Field(
default=None,
description="Expiration date of the insider trading.",
)
underlying_security_title: Optional[str] = Field(
default=None,
description="Name of the underlying non-derivative security related to this derivative transaction.",
)
underlying_shares: Optional[int] = Field(
default=None,
description="Number of underlying shares related to this derivative transaction.",
)
nature_of_ownership: Optional[str] = Field(
default=None,
description="Nature of ownership of the insider trading.",
)
director: bool = Field(description="Whether the owner is a director.")
officer: bool = Field(description="Whether the owner is an officer.")
ten_percent_owner: bool = Field(description="Whether the owner is a 10% owner.")
other_relation: bool = Field(
description="Whether the owner is having another relation."
)
derivative_transaction: bool = Field(
description="Whether the owner is having a derivative transaction."
)
report_line_number: Optional[int] = Field(
default=None,
description="Report line number of the insider trading.",
)
filing_url: str = Field(description="URL of the filing.")
class IntrinioInsiderTradingFetcher(
Fetcher[
IntrinioInsiderTradingQueryParams,
List[IntrinioInsiderTradingData],
]
):
"""Transform the query, extract and transform the data from the Intrinio endpoints."""
@staticmethod
def transform_query(params: Dict[str, Any]) -> IntrinioInsiderTradingQueryParams:
"""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 IntrinioInsiderTradingQueryParams(**transformed_params)
@staticmethod
def extract_data(
query: IntrinioInsiderTradingQueryParams,
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 ""
base_url = "https://api-v2.intrinio.com/companies"
query_str = get_querystring(query.model_dump(by_alias=True), ["symbol"])
url = f"{base_url}/{query.symbol}/insider_transaction_filings?{query_str}&api_key={api_key}"
return get_data_many(url, "transaction_filings", **kwargs)
@staticmethod
def transform_data(
query: IntrinioInsiderTradingQueryParams, data: List[Dict], **kwargs: Any
) -> List[IntrinioInsiderTradingData]:
"""Return the transformed data."""
transformed_data: List[Dict] = []
for item in data:
transformed_data.extend(
[
{
**sub_item,
"filing_date": item["filing_date"],
"filing_url": item["filing_url"],
"symbol": item["issuer_ticker"],
"company_cik": item["issuer_cik"],
"company_name": item["issuer_company"],
"owner_cik": item["owner"]["owner_cik"],
"owner_name": item["owner"]["owner_name"],
}
for sub_item in item["transactions"]
]
)
return [IntrinioInsiderTradingData.model_validate(d) for d in transformed_data]

View File

@@ -0,0 +1,116 @@
"""Intrinio Institutional Ownership Model."""
from concurrent.futures import ThreadPoolExecutor
from typing import Any, Dict, List, Optional
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.institutional_ownership import (
InstitutionalOwnershipData,
InstitutionalOwnershipQueryParams,
)
from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS
from openbb_core.provider.utils.helpers import get_querystring
from openbb_intrinio.utils.helpers import get_data_many, get_data_one
from pydantic import Field
class IntrinioInstitutionalOwnershipQueryParams(InstitutionalOwnershipQueryParams):
"""Intrinio Institutional Ownership Query.
Source: https://docs.intrinio.com/documentation/web_api/get_security_insider_ownership_v2
https://docs.intrinio.com/documentation/web_api/get_owner_by_id_v2
"""
limit: Optional[int] = Field(
default=100, description=QUERY_DESCRIPTIONS.get("limit", ""), alias="page_size"
)
class IntrinioInstitutionalOwnershipData(InstitutionalOwnershipData):
"""Intrinio Institutional Ownership Data."""
__alias_dict__ = {
"cik": "owner_cik",
"date": "period_ended",
}
name: str = Field(
description="Name of the institutional owner.", alias="owner_name"
)
value: float = Field(description="Value of the institutional owner.")
amount: float = Field(description="Amount of the institutional owner.")
sole_voting_authority: float = Field(
description="Sole voting authority of the institutional owner."
)
shared_voting_authority: float = Field(
description="Shared voting authority of the institutional owner."
)
no_voting_authority: float = Field(
description="No voting authority of the institutional owner."
)
previous_amount: float = Field(
description="Previous amount of the institutional owner."
)
amount_change: float = Field(
description="Amount change of the institutional owner."
)
amount_percent_change: float = Field(
description="Amount percent change of the institutional owner."
)
class IntrinioInstitutionalOwnershipFetcher(
Fetcher[
IntrinioInstitutionalOwnershipQueryParams,
List[IntrinioInstitutionalOwnershipData],
]
):
"""Transform the query, extract and transform the data from the Intrinio endpoints."""
@staticmethod
def transform_query(
params: Dict[str, Any]
) -> IntrinioInstitutionalOwnershipQueryParams:
"""Transform the query params."""
return IntrinioInstitutionalOwnershipQueryParams(**params)
@staticmethod
def extract_data(
query: IntrinioInstitutionalOwnershipQueryParams,
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), ["symbol"])
url = (
f"{base_url}/securities/{query.symbol}/institutional_ownership?"
f"{query_str}&api_key={api_key}"
)
data = get_data_many(url, "ownership", **kwargs)
def get_owner_name(item: Dict) -> Dict:
cik = item["owner_cik"]
cik_url = f"{base_url}/owners/{cik}?api_key={api_key}"
cik_data = get_data_one(cik_url, **kwargs)
owner_name = cik_data["owner_name"]
item["symbol"] = query.symbol
item["owner_name"] = owner_name
return item
with ThreadPoolExecutor() as executor:
data = list(executor.map(get_owner_name, data))
return data
@staticmethod
def transform_data(
query: IntrinioInstitutionalOwnershipQueryParams,
data: List[Dict],
**kwargs: Any,
) -> List[IntrinioInstitutionalOwnershipData]:
"""Return the transformed data."""
return [IntrinioInstitutionalOwnershipData.model_validate(d) for d in data]

View File

@@ -0,0 +1,85 @@
"""Intrinio Key Metrics Model."""
from typing import Any, Dict, List, Optional
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.key_metrics import (
KeyMetricsData,
KeyMetricsQueryParams,
)
from openbb_intrinio.utils.helpers import get_data_one
from pydantic import Field
class IntrinioKeyMetricsQueryParams(KeyMetricsQueryParams):
"""Intrinio Key Metrics Query.
Source: https://data.intrinio.com/data-tag/beta
https://data.intrinio.com/data-tag/volume
https://data.intrinio.com/data-tag/52_week_high
https://data.intrinio.com/data-tag/52_week_low
https://data.intrinio.com/data-tag/dividendyield
https://data.intrinio.com/data-tag/pricetoearnings
"""
class IntrinioKeyMetricsData(KeyMetricsData):
"""Intrinio Key Metrics Data."""
__alias_dict__ = {
"market_cap": "marketcap",
"pe_ratio": "pricetoearnings",
}
beta: float = Field(description="Beta")
volume: float = Field(description="Volume")
fifty_two_week_high: float = Field(description="52 week high", alias="52_week_high")
fifty_two_week_low: float = Field(description="52 week low", alias="52_week_low")
dividend_yield: float = Field(description="Dividend yield", alias="dividendyield")
class IntrinioKeyMetricsFetcher(
Fetcher[
IntrinioKeyMetricsQueryParams,
IntrinioKeyMetricsData,
]
):
"""Transform the query, extract and transform the data from the Intrinio endpoints."""
@staticmethod
def transform_query(params: Dict[str, Any]) -> IntrinioKeyMetricsQueryParams:
"""Transform the query params."""
return IntrinioKeyMetricsQueryParams(**params)
@staticmethod
def extract_data(
query: IntrinioKeyMetricsQueryParams, # pylint: disable=unused-argument
credentials: Optional[Dict[str, str]],
**kwargs: Any,
) -> Dict:
"""Return the raw data from the Intrinio endpoint."""
api_key = credentials.get("intrinio_api_key") if credentials else ""
data: Dict = {}
tags = [
"beta",
"volume",
"marketcap",
"52_week_high",
"52_week_low",
"dividendyield",
"pricetoearnings",
]
data["symbol"] = query.symbol
for tag in tags:
url = f"https://api-v2.intrinio.com/companies/{query.symbol}/data_point/{tag}?api_key={api_key}"
data[tag] = get_data_one(url).get("value")
return data
@staticmethod
def transform_data(
query: IntrinioKeyMetricsQueryParams, data: List[Dict], **kwargs: Any
) -> IntrinioKeyMetricsData:
"""Return the transformed data."""
return IntrinioKeyMetricsData.model_validate(data)

View File

@@ -0,0 +1,56 @@
"""Intrinio Latest Attributes Model."""
from typing import Any, Dict, Optional
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.latest_attributes import (
LatestAttributesData,
LatestAttributesQueryParams,
)
from openbb_intrinio.utils.helpers import get_data
class IntrinioLatestAttributesQueryParams(LatestAttributesQueryParams):
"""Intrinio Latest Attributes Query.
Source: https://docs.intrinio.com/documentation/web_api/get_data_point_number_v2
https://docs.intrinio.com/documentation/web_api/get_data_point_text_v2
"""
class IntrinioLatestAttributesData(LatestAttributesData):
"""Intrinio Latest Attributes Data."""
class IntrinioLatestAttributesFetcher(
Fetcher[
IntrinioLatestAttributesQueryParams,
IntrinioLatestAttributesData,
]
):
"""Transform the query, extract and transform the data from the Intrinio endpoints."""
@staticmethod
def transform_query(params: Dict[str, Any]) -> IntrinioLatestAttributesQueryParams:
"""Transform the query params."""
return IntrinioLatestAttributesQueryParams(**params)
@staticmethod
def extract_data(
query: IntrinioLatestAttributesQueryParams, # pylint: disable=unused-argument
credentials: Optional[Dict[str, str]],
**kwargs: Any,
) -> Dict:
"""Return the raw data from the Intrinio endpoint."""
api_key = credentials.get("intrinio_api_key") if credentials else ""
url = f"https://api-v2.intrinio.com/companies/{query.symbol}/data_point/{query.tag}?api_key={api_key}"
return get_data(url)
@staticmethod
def transform_data(
query: IntrinioLatestAttributesQueryParams, # pylint: disable=unused-argument
data: Dict,
**kwargs: Any,
) -> IntrinioLatestAttributesData:
"""Return the transformed data."""
return IntrinioLatestAttributesData.model_validate(data)

View File

@@ -0,0 +1,91 @@
"""Intrinio Market Indices Model."""
from datetime import datetime
from typing import Any, Dict, List, Literal, Optional
from dateutil.relativedelta import relativedelta
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.market_indices import (
MarketIndicesData,
MarketIndicesQueryParams,
)
from openbb_core.provider.utils.descriptions import QUERY_DESCRIPTIONS
from openbb_core.provider.utils.helpers import get_querystring
from openbb_intrinio.utils.helpers import get_data_many
from pydantic import Field, NonNegativeInt
class IntrinioMarketIndicesQueryParams(MarketIndicesQueryParams):
"""Intrinio Market Indices Query.
Source: https://docs.intrinio.com/documentation/web_api/get_stock_market_index_historical_data_v2
"""
tag: str = Field(description="Index tag.")
type: Optional[str] = Field(
default=None,
description="Index type.",
)
sort: Literal["asc", "desc"] = Field(
default="desc",
description="Sort order.",
alias="sort_order",
)
limit: NonNegativeInt = Field(
default=100,
description=QUERY_DESCRIPTIONS.get("limit", ""),
alias="page_size",
)
class IntrinioMarketIndicesData(MarketIndicesData):
"""Intrinio Market Indices Data."""
value: Optional[float] = Field(default=None, description="Index value.")
class IntrinioMarketIndicesFetcher(
Fetcher[
IntrinioMarketIndicesQueryParams,
List[IntrinioMarketIndicesData],
]
):
"""Transform the query, extract and transform the data from the Intrinio endpoints."""
@staticmethod
def transform_query(params: Dict[str, Any]) -> IntrinioMarketIndicesQueryParams:
"""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=1)
if params.get("end_date") is None:
transformed_params["end_date"] = now
return IntrinioMarketIndicesQueryParams(**transformed_params)
@staticmethod
def extract_data(
query: IntrinioMarketIndicesQueryParams,
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 ""
base_url = "https://api-v2.intrinio.com/indices/stock_market"
query_str = get_querystring(query.model_dump(by_alias=True), ["symbol", "tag"])
url = f"{base_url}/{query.symbol}/historical_data/{query.tag}?{query_str}&api_key={api_key}"
return get_data_many(url, "historical_data", **kwargs)
@staticmethod
def transform_data(
query: IntrinioMarketIndicesQueryParams, # pylint: disable=unused-argument
data: List[Dict],
**kwargs: Any,
) -> List[IntrinioMarketIndicesData]:
"""Return the transformed data."""
return [IntrinioMarketIndicesData.model_validate(d) for d in data]

View File

@@ -1,26 +1,27 @@
"""Intrinio Search Financial Attributes Model."""
"""Intrinio Search Attributes Model."""
from typing import Any, Dict, List, Optional
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.search_financial_attributes import (
SearchFinancialAttributesData,
SearchFinancialAttributesQueryParams,
from openbb_core.provider.standard_models.search_attributes import (
SearchAttributesData,
SearchAttributesQueryParams,
)
from openbb_core.provider.utils.helpers import get_querystring
from openbb_intrinio.utils.helpers import get_data_one
class IntrinioSearchFinancialAttributesQueryParams(
SearchFinancialAttributesQueryParams
):
"""Intrinio Search Financial Attributes Query."""
class IntrinioSearchAttributesQueryParams(SearchAttributesQueryParams):
"""Intrinio Search Attributes Query.
Source: https://docs.intrinio.com/documentation/web_api/search_data_tags_v2
"""
__alias_dict__ = {"limit": "page_size"}
class IntrinioSearchFinancialAttributesData(SearchFinancialAttributesData):
"""Intrinio Search Financial Attributes Data."""
class IntrinioSearchAttributesData(SearchAttributesData):
"""Intrinio Search Attributes Data."""
__alias_dict__ = {
"parent_name": "parent",
@@ -28,24 +29,22 @@ class IntrinioSearchFinancialAttributesData(SearchFinancialAttributesData):
}
class IntrinioSearchFinancialAttributesFetcher(
class IntrinioSearchAttributesFetcher(
Fetcher[
IntrinioSearchFinancialAttributesQueryParams,
List[IntrinioSearchFinancialAttributesData],
IntrinioSearchAttributesQueryParams,
List[IntrinioSearchAttributesData],
]
):
"""Transform the query, extract and transform the data from the Intrinio endpoints."""
@staticmethod
def transform_query(
params: Dict[str, Any]
) -> IntrinioSearchFinancialAttributesQueryParams:
def transform_query(params: Dict[str, Any]) -> IntrinioSearchAttributesQueryParams:
"""Transform the query params."""
return IntrinioSearchFinancialAttributesQueryParams(**params)
return IntrinioSearchAttributesQueryParams(**params)
@staticmethod
def extract_data(
query: IntrinioSearchFinancialAttributesQueryParams,
query: IntrinioSearchAttributesQueryParams,
credentials: Optional[Dict[str, str]],
**kwargs: Any,
) -> List[Dict]:
@@ -66,11 +65,9 @@ class IntrinioSearchFinancialAttributesFetcher(
@staticmethod
def transform_data(
query: IntrinioSearchFinancialAttributesQueryParams,
query: IntrinioSearchAttributesQueryParams,
data: List[Dict],
**kwargs: Any,
) -> List[IntrinioSearchFinancialAttributesData]:
) -> List[IntrinioSearchAttributesData]:
"""Return the transformed data."""
return [
IntrinioSearchFinancialAttributesData.model_validate(item) for item in data
]
return [IntrinioSearchAttributesData.model_validate(item) for item in data]

View File

@@ -0,0 +1,83 @@
"""Intrinio Share Statistics Model."""
from datetime import datetime
from typing import Any, Dict, List, Optional
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.share_statistics import (
ShareStatisticsData,
ShareStatisticsQueryParams,
)
from openbb_intrinio.utils.helpers import get_data_one
from pydantic import Field
class IntrinioShareStatisticsQueryParams(ShareStatisticsQueryParams):
"""Intrinio Share Statistics Query.
Source: https://data.intrinio.com/data-tag/adjweightedavebasicdilutedsharesos
https://data.intrinio.com/data-tag/weightedavebasicdilutedsharesos
https://data.intrinio.com/data-tag/public_float
"""
class IntrinioShareStatisticsData(ShareStatisticsData):
"""Intrinio Share Statistics Data."""
__alias_dict__ = {
"outstanding_shares": "weightedavebasicdilutedsharesos",
}
adjusted_outstanding_shares: Optional[float] = Field(
default=None,
description="Total number of shares of a publicly-traded company, adjusted for splits.",
alias="adjweightedavebasicdilutedsharesos",
)
public_float: Optional[float] = Field(
default=None,
description="Aggregate market value of the shares of a publicly-traded company.",
)
class IntrinioShareStatisticsFetcher(
Fetcher[
IntrinioShareStatisticsQueryParams,
List[IntrinioShareStatisticsData],
]
):
"""Transform the query, extract and transform the data from the Intrinio endpoints."""
@staticmethod
def transform_query(params: Dict[str, Any]) -> IntrinioShareStatisticsQueryParams:
"""Transform the query params."""
return IntrinioShareStatisticsQueryParams(**params)
@staticmethod
def extract_data(
query: IntrinioShareStatisticsQueryParams,
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: Dict = {}
tags = [
"public_float",
"weightedavebasicdilutedsharesos",
"adjweightedavebasicdilutedsharesos",
]
for tag in tags:
url = f"https://api-v2.intrinio.com/companies/{query.symbol}/data_point/{tag}/number?api_key={api_key}"
data[tag] = get_data_one(url, **kwargs).get("value")
data["symbol"] = query.symbol
data["date"] = datetime.now().date()
return [data]
@staticmethod
def transform_data(
query: IntrinioShareStatisticsQueryParams, data: List[Dict], **kwargs: Any
) -> List[IntrinioShareStatisticsData]:
"""Return the transformed data."""
return [IntrinioShareStatisticsData.model_validate(d) for d in data]

View File

@@ -63,7 +63,7 @@ def get_data(url: str, **kwargs: Any) -> Union[list, dict]:
except SSLError:
r = request(url)
if r.status_code == 404:
raise RuntimeError("Intrinio endpoint doesn't exist")
raise RuntimeError("Intrinio endpoint doesn't exist.")
data = r.json()
if r.status_code != 200:
@@ -72,8 +72,8 @@ def get_data(url: str, **kwargs: Any) -> Union[list, dict]:
value = error or message
raise RuntimeError(f"Error in Intrinio request -> {value}")
if "Error Message" in data:
raise RuntimeError("Intrinio Error Message -> " + data["Error Message"])
if isinstance(data, (str, float)):
data = {"value": data}
if len(data) == 0:
raise EmptyDataError()

View File

@@ -0,0 +1,38 @@
interactions:
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
method: GET
uri: https://api-v2.intrinio.com/securities/AAPL/prices/adjustments?api_key=MOCK_API_KEY&end_date=2023-06-06&limit=100&start_date=2023-01-01
response:
body:
string: !!binary |
H4sIAONzX2UAA5RRW2vCMBT+KyPPKklr7OVN2ZDB5i7icIwRQhpjtE1Lkg6r+N934gbDvu3xfNdz
khNyvhZ71lgtJOPFrnW+ksY7lH+cUMG9RDmKcBQPMR2SCA3QhgtfW5TjUZalE5ImmOKYZgkZoEJ/
6UKaIpDR+G9morVWGtFB1mp5CyGuKbVnlntdo5yM8HnQK4uGBPfLxgmlOE3IhJC4Vxb/q+wTMAky
7UF0QhoyAsC42lm1A4eoq4abjl0YGNhiPZkfn0LW5bXkQWy5UfJH4A6K1cdHm3UgMLwKV0ybppQ3
90Zc0ooA3b0sw9DfzmuxlzZYps8Pv921016yKyZfBfdGKw3AbDbHGM+yd7qOriw9fv36Pg5Lb7mV
TJTcuSsFWdJF+paCAr6/4rZjpXZeG4Vyb1t5hmvkwbOGK9jftGV5/gYAAP//AwCKhFgOLwIAAA==
headers:
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Thu, 23 Nov 2023 15:46:43 GMT
Transfer-Encoding:
- chunked
Vary:
- Origin,Accept-Encoding
status:
code: 200
message: OK
version: 1

View File

@@ -0,0 +1,84 @@
interactions:
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
method: GET
uri: https://api-v2.intrinio.com/companies/AAPL/filings?api_key=MOCK_API_KEY&end_date=2023-06-06&page_size=100&start_date=2023-01-01
response:
body:
string: !!binary |
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
/7TA5cf/AQAA//8DAAmaTBKVcgAA
headers:
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Fri, 24 Nov 2023 14:25:53 GMT
Transfer-Encoding:
- chunked
Vary:
- Origin,Accept-Encoding
status:
code: 200
message: OK
version: 1

View File

@@ -0,0 +1,57 @@
interactions:
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
method: GET
uri: https://api-v2.intrinio.com/companies/AAPL?api_key=MOCK_API_KEY
response:
body:
string: !!binary |
H4sIAMZ7X2UAA8RW224bNxD9FUJPLSIZKzmxI+tJcZzESGypjpykQIEFTVJawhS54cWKUvTfe2ap
1SVx0aBoUcM2lhxyeObMzCF/72jZOesItyyvP528/jrpdDtRi3vlMTseT99hbPlS0aiujWKXVmDK
KI2ZNx9vp29viqvp5Pb5q9efXsyOh08b44Kb8ttdR7CE6MR9qb6IitsFWa/H71+OfyGLFp2z42en
fXxWzsdSqiC8rqN29sALg0EvbOiyJbdpzkVMXmHErcSMv1cxsICPWFfOkqFWPjjLDUOMdYoYdVnk
dwYLu2yluKfvjQMuhArBea3CEbuMjJvgWFDGBPbAvXYpMK8Mj0pi1j9oQRtDEhXjgWWMYy+4VHDH
FiAAxrttHO2e0WbpVULU3c3gnHvZfk/5OuPZmY7YrFJNCNyuG0cqYGhDWjYRIWRjMgla9oL+Coh3
8A8KggqjxhLhQckkOIHpMmVBRu11UPmwhXtQ3i4x3RJJKTPOLv73ZLCV80autFQ/mhass4xLqXOs
cY88N5/jXKanhIkSZcASZveRjtgVF/u274GP4IHL/TXLZKLu1cnXLqg2LCRb+6mTAR6/UGCMaO4h
VLbSQI0QWaW4bM7NedrjoXJL9T0bhAF503bROm8rZ/ah/frIo6i67IXiyEHtHfJOHL+BQ2zIPjW+
WHSo3yP2KnmQ5LtMR1r+AK43JY3yozqua7Tllt8RE8alHd8MrU3Ltmby78AasrLLUo0kzZ1fAoi2
wiRJIVBucBB733iIFacEG7diIsEplTcwMqmDIOYax9KtrHEcrACgzgUdskUvdGzShOq2cdedd87d
U53mnqPwXLdp0U211U4KHlDzbLypGoBYP1o5bTT/ngSsKg0HG/cp0H+O8H1T0kYHhEJMqS8gVCsr
FGonYoPtSbUk9J5LjZaImYnW+7VahSeE4wCCxeymQxf8K9XuN6BmH558AwmKbYD0AWXuQbDdETw6
kC+Adr07D+fALbwCkUzAMtpTsyxumA5VU/w1XzeaswHRJR5XisQsIHShLPQLRYk/HGiMgsbgeJQo
uIjrv9NFOve/0UYSIhRl9PouUY3HSnvZqyEh68OyRME3+NsmxErv0qI6LPzDQPYU7tGtNOlV5DrH
5WyjQU0PboKWEBcBVjlIJghCtYHuYAockAyUyKoIhb2nXKG+iLdV5bARm2mQT9ryiTMoEZ56ZXcV
rDilSDioX65bbVl/eHqalSaL3OeEc5XPxvNEGdQWfXjOjQZGqzldOUI53DIzvXSxWrOXR+wcvUvz
mZwyeQM7cayOMAdLm80Sig90AebJ9QUbT6fvLth0fPOWfRz/imNupxc3s8vrCT7HbPis6NNzZYnY
0F3/aO/24Ea+S0vAf3paPP+ZDYcnvX7RL7Co+tz67sPcB11zbXVU7J1z9YF90DmzyZj9qVLouMa2
LVsHG0pcNZC7UjhJz6kWFuoUu8r8ECPBhe3y+nxyM53cjGcXL4lMfY/JAj/Hg6I/PKarnrQ6lvNM
h8QIKwbF4LjX7/eKQT6YNIbmdynL88IlGz0hvaXYJKq6kX7ci2O0IroB61Afjzsgww95UMvauLVS
SFP/5CnQb4Mlv4myNz6fXX64oDclGsDRO/Zq+zhBYM1pEleLX5doUbVwzZnnm3udvYFerXDl7a9b
oOvq/UW/paIYnLDJfA6FYhefk65JHOjxTAhzhLS9efSiA+CUdKcECsho5yz6pLqdufZEeIKdtiNZ
W9aL014x7A2GTV7+ahFSg0XHVGTZVX5gQ7iEalf1h8+LXn+A39bVI4vaLA9oEXSJl8rSE0RmpH/8
CQAA//8DABJQ3fonDAAA
headers:
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Thu, 23 Nov 2023 16:20:22 GMT
Transfer-Encoding:
- chunked
Vary:
- Origin,Accept-Encoding
status:
code: 200
message: OK
version: 1

View File

@@ -0,0 +1,84 @@
interactions:
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
method: GET
uri: https://api-v2.intrinio.com/companies/AAPL/filings?api_key=MOCK_API_KEY&end_date=2023-06-06&page_size=100&start_date=2023-01-01
response:
body:
string: !!binary |
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
/7TA5cf/AQAA//8DAAmaTBKVcgAA
headers:
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Thu, 23 Nov 2023 15:59:16 GMT
Transfer-Encoding:
- chunked
Vary:
- Origin,Accept-Encoding
status:
code: 200
message: OK
version: 1

View File

@@ -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&provider=intrinio&sort_order=desc&start_date=2013-01-01
response:
body:
string: !!binary |
H4sIADIQXmUAA3TQ2wrCMAwG4Hfp9ZQkPaTdq4iMokMHZYpuIoy9u7U3LaPrVWg+/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:
- Wed, 22 Nov 2023 14:29:06 GMT
Transfer-Encoding:
- chunked
Vary:
- Origin,Accept-Encoding
status:
code: 200
message: OK
version: 1

View File

@@ -0,0 +1,206 @@
interactions:
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
method: GET
uri: https://api-v2.intrinio.com/companies/AAPL/insider_transaction_filings?api_key=MOCK_API_KEY&end_date=2023-11-27&page_size=100&sort_by=updated_on&start_date=2018-11-27
response:
body:
string: !!binary |
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
H3oDAA==
headers:
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Mon, 27 Nov 2023 10:01:15 GMT
Transfer-Encoding:
- chunked
Vary:
- Origin,Accept-Encoding
status:
code: 200
message: OK
version: 1

View File

@@ -0,0 +1,219 @@
interactions:
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
method: GET
uri: https://api-v2.intrinio.com/companies/AAPL/data_point/beta?api_key=MOCK_API_KEY
response:
body:
string: !!binary |
H4sIAKN4X2UAAzLUMzI2MAYAAAD//wMAyNiMHAYAAAA=
headers:
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Thu, 23 Nov 2023 16:06:59 GMT
Transfer-Encoding:
- chunked
Vary:
- Origin,Accept-Encoding
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
method: GET
uri: https://api-v2.intrinio.com/companies/AAPL/data_point/volume?api_key=MOCK_API_KEY
response:
body:
string: !!binary |
H4sIAKR4X2UAAzK2NDM2MDA01DMAAAAA//8DADlUTt0KAAAA
headers:
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Thu, 23 Nov 2023 16:07:00 GMT
Transfer-Encoding:
- chunked
Vary:
- Origin,Accept-Encoding
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
method: GET
uri: https://api-v2.intrinio.com/companies/AAPL/data_point/marketcap?api_key=MOCK_API_KEY
response:
body:
string: !!binary |
H4sIAKZ4X2UAAzI2MDI1sLQwNjMzNTfQMwAAAAD//wMAFx5PfA8AAAA=
headers:
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Thu, 23 Nov 2023 16:07:02 GMT
Transfer-Encoding:
- chunked
Vary:
- Origin,Accept-Encoding
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
method: GET
uri: https://api-v2.intrinio.com/companies/AAPL/data_point/52_week_high?api_key=MOCK_API_KEY
response:
body:
string: !!binary |
H4sIAKd4X2UAAzK0NNczBwAAAP//AwA2jt5FBQAAAA==
headers:
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Thu, 23 Nov 2023 16:07:03 GMT
Transfer-Encoding:
- chunked
Vary:
- Origin,Accept-Encoding
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
method: GET
uri: https://api-v2.intrinio.com/companies/AAPL/data_point/52_week_low?api_key=MOCK_API_KEY
response:
body:
string: !!binary |
H4sIAKh4X2UAAzI0MtYzsQAAAAD//wMAgelg5gYAAAA=
headers:
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Thu, 23 Nov 2023 16:07:04 GMT
Transfer-Encoding:
- chunked
Vary:
- Origin,Accept-Encoding
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
method: GET
uri: https://api-v2.intrinio.com/companies/AAPL/data_point/dividendyield?api_key=MOCK_API_KEY
response:
body:
string: !!binary |
H4sIAKl4X2UAAzLQMzAwtTA2BwAAAP//AwB6DW17CAAAAA==
headers:
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Thu, 23 Nov 2023 16:07:05 GMT
Transfer-Encoding:
- chunked
Vary:
- Origin,Accept-Encoding
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
method: GET
uri: https://api-v2.intrinio.com/companies/AAPL/data_point/pricetoearnings?api_key=MOCK_API_KEY
response:
body:
string: !!binary |
H4sIAKp4X2UAAzI21DO0sDACAAAA//8DAEvjlwgHAAAA
headers:
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Thu, 23 Nov 2023 16:07:06 GMT
Transfer-Encoding:
- chunked
Vary:
- Origin,Accept-Encoding
status:
code: 200
message: OK
version: 1

View File

@@ -0,0 +1,33 @@
interactions:
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
method: GET
uri: https://api-v2.intrinio.com/companies/AAPL/data_point/ceo?api_key=MOCK_API_KEY
response:
body:
string: !!binary |
H4sIAIQPXmUAA1IKyczNL8moVHDRU3DOz89WAgAAAP//AwBIpQsVEQAAAA==
headers:
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Wed, 22 Nov 2023 14:26:12 GMT
Transfer-Encoding:
- chunked
Vary:
- Origin,Accept-Encoding
status:
code: 200
message: OK
version: 1

View File

@@ -0,0 +1,51 @@
interactions:
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
method: GET
uri: https://api-v2.intrinio.com/indices/stock_market/$DJI/historical_data/level?api_key=MOCK_API_KEY&end_date=2023-06-06&page_size=100&sort_order=desc&start_date=2023-01-01
response:
body:
string: !!binary |
H4sIAIByX2UAA3RXTW8bRwz9K8KiR3vAj/kUkIOBXBLA6SHtpUUhbKxNo0BeJdLKsWP4v5ebIOhy
hgJ0kLQP5PANHx/3ufu0O02H4+6u32+2/dR367+fO/kydOuOgPgaony6q+6h35/lT+aQ2FF+uWph
QcEiuRwtGC1hSWDJhOESBhFdSA0sXPMSRgWyIxMGKpqXpG0J4ZrK/7DxvN9bEEUGFHbsLdiSDErR
uxgsmFewUlwhC8YqaQguoAVT1FKOLlhVYlnCPEUX2YJldZ8cXLGSYtLRwCXrBlDzhuTQ4g1VE7HP
rm0OQalCGcBFizZEDSs2bQi6UHRs8QFF9ze6bEUDRVtEqaBYMFVoTN6xdVewbBBGSi5ZtIFqEI/e
kQlTvMXsXTArXfLmIaBrb9RfU1aokh22dyUw1R9Zus2GRX1Xwq6ZVA8ZBpfbCrzWFecUXEuHoFR/
ZBkexTya6o8ksjK6zVeyyiU5aPvDV7IqyVSfr2RVcnLY9ofAdKFyNm/RhqzuiorVlL7SVfQyQCwU
1l2UzGBaVvM0smiDRaHmzPWVAfkcHFkJtaR8liFvJlSUCRmW8nwlqSgNiS0ZrA1IRri/AFuSQTkU
B+2tszIgCYbJGH+slUdcRO1WTqU88kwO2kK58iDiZA0F1h5ECOFC0mULEYhAsVUBa+lRiGAXqlgj
7y1DY60VzDKZiwmLOpo0pFWBsiDMcqPG5sFaeYTBGjGshYcZi2V7rLWCRZwqWjBlQUTBWysWawuS
rSKbR4OkW1JGhxlMi0/c0ZvRlFy4gCsWa9qBANgmVzkQxdlr26RUCSFKCW0wqnSQs2ivHR9U6SBj
tHYxqnYxDGytRVTtYuDDhaRqfKAM5mDCFv1hDkmqDGP2WWPEU7WHxRJdbkVAWgQeKTswYUvOPAi1
xvpNlf2QkFHaEUOVY+R4IZpexKIszbmVO1WLWPHF8mPSMvAYLvCmZZALOrC6Q8ugyC3YSUnxNjeR
1d/VIlbIWlBQe5DcgiRto2H1EjSbi9GSWC1sJWXLNrBa2GZ6fVspVgtb8mwpGauFLTHblWrxxVku
JkxVyvP6Z1WqNzaY7cWKpjc2EsnI2f656nbjdnjs1s/dbito+bWh7w/vvt8I+vR0/+Gwl39/e/32
jfwe+/s54uvDt9XbwzicVm/G7fk0HXf9fnXzMBz7fwdB3R3GaTcO4yTQd4fj9Gl1cz/M7+Y/np3H
6fgkT/4cd9OwXb2f5JSn1eHjAnT+Mh998/E4fD0P492M3va7/ZM82venafPz+fZXbTgT+gfgGnkt
rwwA8JcgDx9Ow/Ghn3aHcXOa+uP0A44/4aECDKOKRt2LFDs8Tpsvc0nr7vbzzdPtE3y7fQ+Pv9+8
etW9/AcAAP//AAAA//8DAF+LRxtzEAAA
headers:
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Thu, 23 Nov 2023 15:40:48 GMT
Transfer-Encoding:
- chunked
Vary:
- Origin,Accept-Encoding
status:
code: 200
message: OK
version: 1

View File

@@ -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

View File

@@ -0,0 +1,95 @@
interactions:
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
method: GET
uri: https://api-v2.intrinio.com/companies/AAPL/data_point/public_float/number?api_key=MOCK_API_KEY
response:
body:
string: !!binary |
H4sIAL94X2UAAzIytTQ0NDM1AAM9AwAAAAD//wMAWSojCg8AAAA=
headers:
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Thu, 23 Nov 2023 16:07:27 GMT
Transfer-Encoding:
- chunked
Vary:
- Origin,Accept-Encoding
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
method: GET
uri: https://api-v2.intrinio.com/companies/AAPL/data_point/weightedavebasicdilutedsharesos/number?api_key=MOCK_API_KEY
response:
body:
string: !!binary |
H4sIAMB4X2UAAzI0NTU1Mjc1MjAw0DMAAAAA//8DAEqfb+oNAAAA
headers:
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Thu, 23 Nov 2023 16:07:28 GMT
Transfer-Encoding:
- chunked
Vary:
- Origin,Accept-Encoding
status:
code: 200
message: OK
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate, br
Connection:
- keep-alive
method: GET
uri: https://api-v2.intrinio.com/companies/AAPL/data_point/adjweightedavebasicdilutedsharesos/number?api_key=MOCK_API_KEY
response:
body:
string: !!binary |
H4sIAMJ4X2UAAzI0NTU1Mjc1MjAw0DMAAAAA//8DAEqfb+oNAAAA
headers:
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Thu, 23 Nov 2023 16:07:30 GMT
Transfer-Encoding:
- chunked
Vary:
- Origin,Accept-Encoding
status:
code: 200
message: OK
version: 1

View File

@@ -4,22 +4,33 @@ from unittest import mock
import pytest
from openbb_core.app.service.user_service import UserService
from openbb_intrinio.models.balance_sheet import IntrinioBalanceSheetFetcher
from openbb_intrinio.models.calendar_dividend import IntrinioCalendarDividendFetcher
from openbb_intrinio.models.calendar_ipo import IntrinioCalendarIpoFetcher
from openbb_intrinio.models.cash_flow import IntrinioCashFlowStatementFetcher
from openbb_intrinio.models.company_filings import IntrinioCompanyFilingsFetcher
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_info import IntrinioEquityInfoFetcher
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.historical_attributes import (
IntrinioHistoricalAttributesFetcher,
)
from openbb_intrinio.models.income_statement import IntrinioIncomeStatementFetcher
from openbb_intrinio.models.insider_trading import IntrinioInsiderTradingFetcher
from openbb_intrinio.models.institutional_ownership import (
IntrinioInstitutionalOwnershipFetcher,
)
from openbb_intrinio.models.key_metrics import IntrinioKeyMetricsFetcher
from openbb_intrinio.models.latest_attributes import IntrinioLatestAttributesFetcher
from openbb_intrinio.models.market_indices import IntrinioMarketIndicesFetcher
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_intrinio.models.search_attributes import (
IntrinioSearchAttributesFetcher,
)
from openbb_intrinio.models.share_statistics import IntrinioShareStatisticsFetcher
from openbb_intrinio.models.world_news import IntrinioWorldNewsFetcher
test_credentials = UserService().default_user_settings.credentials.model_dump(
@@ -163,21 +174,21 @@ def test_intrinio_calendar_ipo_fetcher(credentials=test_credentials):
@pytest.mark.record_http
def test_intrinio_search_financial_attributes(credentials=test_credentials):
def test_intrinio_search_attributes(credentials=test_credentials):
params = {"query": "ebit"}
fetcher = IntrinioSearchFinancialAttributesFetcher()
fetcher = IntrinioSearchAttributesFetcher()
result = fetcher.test(params, credentials)
assert result is None
@pytest.mark.record_http
def test_intrinio_financial_attributes(credentials=test_credentials):
def test_intrinio_historical_attributes(credentials=test_credentials):
params = {
"provider": "intrinio",
"symbol": "AAPL",
"tag": "ebit",
"period": "annual",
"frequency": "yearly",
"limit": 1000,
"type": None,
"start_date": date(2013, 1, 1),
@@ -185,6 +196,106 @@ def test_intrinio_financial_attributes(credentials=test_credentials):
"sort": "desc",
}
fetcher = IntrinioFinancialAttributesFetcher()
fetcher = IntrinioHistoricalAttributesFetcher()
result = fetcher.test(params, credentials)
assert result is None
@pytest.mark.record_http
def test_intrinio_latest_attributes(credentials=test_credentials):
params = {
"provider": "intrinio",
"symbol": "AAPL",
"tag": "ceo",
}
fetcher = IntrinioLatestAttributesFetcher()
result = fetcher.test(params, credentials)
assert result is None
@pytest.mark.record_http
def test_intrinio_equity_info_fetcher(credentials=test_credentials):
params = {"symbol": "AAPL"}
fetcher = IntrinioEquityInfoFetcher()
result = fetcher.test(params, credentials)
assert result is None
@pytest.mark.record_http
def test_intrinio_market_indices_fetcher(credentials=test_credentials):
params = {
"symbol": "$DJI",
"tag": "level",
"start_date": date(2023, 1, 1),
"end_date": date(2023, 6, 6),
}
fetcher = IntrinioMarketIndicesFetcher()
result = fetcher.test(params, credentials)
assert result is None
@pytest.mark.record_http
def test_intrinio_calendar_dividend_fetcher(credentials=test_credentials):
params = {
"symbol": "AAPL",
"start_date": date(2023, 1, 1),
"end_date": date(2023, 6, 6),
}
fetcher = IntrinioCalendarDividendFetcher()
result = fetcher.test(params, credentials)
assert result is None
@pytest.mark.record_http
def test_intrinio_company_filings_fetcher(credentials=test_credentials):
params = {
"symbol": "AAPL",
"form_type": None,
"limit": 100,
"start_date": date(2023, 1, 1),
"end_date": date(2023, 6, 6),
}
fetcher = IntrinioCompanyFilingsFetcher()
result = fetcher.test(params, credentials)
assert result is None
@pytest.mark.record_http
def test_intrinio_insider_trading_fetcher(credentials=test_credentials):
params = {"symbol": "AAPL"}
fetcher = IntrinioInsiderTradingFetcher()
result = fetcher.test(params, credentials)
assert result is None
@pytest.mark.record_http
def test_intrinio_institutional_ownership_fetcher(credentials=test_credentials):
params = {"symbol": "AAPL"}
fetcher = IntrinioInstitutionalOwnershipFetcher()
result = fetcher.test(params, credentials)
assert result is None
@pytest.mark.record_http
def test_intrinio_key_metrics_fetcher(credentials=test_credentials):
params = {"symbol": "AAPL"}
fetcher = IntrinioKeyMetricsFetcher()
result = fetcher.test(params, credentials)
assert result is None
@pytest.mark.record_http
def test_intrinio_share_statistics_fetcher(credentials=test_credentials):
params = {"symbol": "AAPL"}
fetcher = IntrinioShareStatisticsFetcher()
result = fetcher.test(params, credentials)
assert result is None

View File

@@ -1,6 +1,6 @@
"""Nasdaq provider module."""
from openbb_core.provider.abstract.provider import Provider
from openbb_nasdaq.models.calendar_dividend import NasdaqDividendCalendarFetcher
from openbb_nasdaq.models.calendar_dividend import NasdaqCalendarDividendFetcher
from openbb_nasdaq.models.calendar_earnings import NasdaqCalendarEarningsFetcher
from openbb_nasdaq.models.calendar_ipo import NasdaqCalendarIpoFetcher
from openbb_nasdaq.models.cot import NasdaqCotFetcher
@@ -17,7 +17,7 @@ provides premier platforms and services for global capital markets and beyond wi
unmatched technology, insights and markets expertise.""",
credentials=["api_key"],
fetcher_dict={
"CalendarDividend": NasdaqDividendCalendarFetcher,
"CalendarDividend": NasdaqCalendarDividendFetcher,
"CalendarEarnings": NasdaqCalendarEarningsFetcher,
"CalendarIpo": NasdaqCalendarIpoFetcher,
"COT": NasdaqCotFetcher,

View File

@@ -7,21 +7,21 @@ from typing import Any, Dict, List, Optional
import requests
from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.calendar_dividend import (
DividendCalendarData,
DividendCalendarQueryParams,
CalendarDividendData,
CalendarDividendQueryParams,
)
from openbb_nasdaq.utils.helpers import IPO_HEADERS, date_range
from pydantic import Field, field_validator
class NasdaqDividendCalendarQueryParams(DividendCalendarQueryParams):
class NasdaqCalendarDividendQueryParams(CalendarDividendQueryParams):
"""Nasdaq Dividend Calendar Query.
Source: https://www.nasdaq.com/market-activity/dividends
"""
class NasdaqDividendCalendarData(DividendCalendarData):
class NasdaqCalendarDividendData(CalendarDividendData):
"""Nasdaq Dividend Calendar Data."""
__alias_dict__ = {
@@ -52,10 +52,10 @@ class NasdaqDividendCalendarData(DividendCalendarData):
return datetime.strptime(v, "%m/%d/%Y").date() if v else None
class NasdaqDividendCalendarFetcher(
class NasdaqCalendarDividendFetcher(
Fetcher[
NasdaqDividendCalendarQueryParams,
List[NasdaqDividendCalendarData],
NasdaqCalendarDividendQueryParams,
List[NasdaqCalendarDividendData],
]
):
"""Transform the query, extract and transform the data from the Nasdaq endpoints."""
@@ -63,7 +63,7 @@ class NasdaqDividendCalendarFetcher(
require_credentials = False
@staticmethod
def transform_query(params: Dict[str, Any]) -> NasdaqDividendCalendarQueryParams:
def transform_query(params: Dict[str, Any]) -> NasdaqCalendarDividendQueryParams:
"""Transform the query params."""
now = datetime.today().date()
transformed_params = params
@@ -74,11 +74,11 @@ class NasdaqDividendCalendarFetcher(
if params.get("end_date") is None:
transformed_params["end_date"] = now + timedelta(days=3)
return NasdaqDividendCalendarQueryParams(**transformed_params)
return NasdaqCalendarDividendQueryParams(**transformed_params)
@staticmethod
def extract_data(
query: NasdaqDividendCalendarQueryParams,
query: NasdaqCalendarDividendQueryParams,
credentials: Optional[Dict[str, str]], # pylint: disable=unused-argument
**kwargs: Any,
) -> List[Dict]:
@@ -110,9 +110,9 @@ class NasdaqDividendCalendarFetcher(
@staticmethod
def transform_data(
query: NasdaqDividendCalendarQueryParams, # pylint: disable=unused-argument
query: NasdaqCalendarDividendQueryParams, # pylint: disable=unused-argument
data: List[Dict],
**kwargs: Any, # pylint: disable=unused-argument
) -> List[NasdaqDividendCalendarData]:
) -> List[NasdaqCalendarDividendData]:
"""Return the transformed data."""
return [NasdaqDividendCalendarData.model_validate(d) for d in data]
return [NasdaqCalendarDividendData.model_validate(d) for d in data]

View File

@@ -2,7 +2,7 @@ import datetime
import pytest
from openbb_core.app.service.user_service import UserService
from openbb_nasdaq.models.calendar_dividend import NasdaqDividendCalendarFetcher
from openbb_nasdaq.models.calendar_dividend import NasdaqCalendarDividendFetcher
from openbb_nasdaq.models.calendar_earnings import NasdaqCalendarEarningsFetcher
from openbb_nasdaq.models.calendar_ipo import NasdaqCalendarIpoFetcher
from openbb_nasdaq.models.cot import NasdaqCotFetcher
@@ -43,7 +43,7 @@ def test_nasdaq_calendar_dividend_fetcher(credentials=test_credentials):
"end_date": datetime.date(2023, 11, 6),
}
fetcher = NasdaqDividendCalendarFetcher()
fetcher = NasdaqCalendarDividendFetcher()
result = fetcher.test(params, credentials)
assert result is None

View File

@@ -2,7 +2,6 @@
from datetime import (
date as dateType,
datetime,
timedelta,
)
from typing import Any, Dict, List, Optional, Union
@@ -40,7 +39,9 @@ class SecCompanyFilingsQueryParams(CompanyFilingsQueryParams):
default=None,
)
type: Optional[FORM_TYPES] = Field(
description="Type of the SEC filing form.", default=None
description="Type of the SEC filing form.",
default=None,
alias="form_type",
)
use_cache: bool = Field(
description="Whether or not to use cache. If True, cache will store for one day.",
@@ -52,9 +53,11 @@ class SecCompanyFilingsData(CompanyFilingsData):
"""SEC Company Filings Data."""
__alias_dict__ = {
"date": "filingDate",
"link": "primaryDocumentUrl",
"type": "form",
"filing_date": "filingDate",
"accepted_date": "acceptanceDateTime",
"filing_url": "filingDetailUrl",
"report_url": "primaryDocumentUrl",
"report_type": "form",
}
report_date: Optional[dateType] = Field(
@@ -62,11 +65,6 @@ class SecCompanyFilingsData(CompanyFilingsData):
default=None,
alias="reportDate",
)
accepted_date: Optional[datetime] = Field(
description="Accepted date of the SEC filing.",
default=None,
alias="acceptanceDateTime",
)
act: Optional[Union[str, int]] = Field(
description="The SEC Act number.", default=None
)
@@ -116,11 +114,6 @@ class SecCompanyFilingsData(CompanyFilingsData):
default=None,
alias="completeSubmissionUrl",
)
filing_detail_url: Optional[str] = Field(
description="The URL to the filing details.",
default=None,
alias="filingDetailUrl",
)
xml: Optional[str] = Field(
description="The URL to the primary XML document.", default=None
)