Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/add-yfinance-functions: Add equity.profile for yFinance #5978

Merged
merged 4 commits into from
Jan 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1147,6 +1147,7 @@ def test_equity_screener(params, headers):
[
({"source": "iex", "provider": "intrinio", "symbol": "AAPL"}),
({"symbol": "AAPL", "provider": "fmp"}),
({"symbol": "AAPL", "provider": "yfinance"}),
],
)
@pytest.mark.integration
Expand All @@ -1167,6 +1168,7 @@ def test_equity_price_quote(params, headers):
({"symbol": "MSFT", "provider": "intrinio"}),
({"symbol": "AAPL,MSFT", "provider": "cboe"}),
({"symbol": "AAPL,MSFT", "provider": "intrinio"}),
({"symbol": "AAPL,MSFT", "provider": "yfinance"}),
],
)
@pytest.mark.integration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1092,6 +1092,7 @@ def test_equity_screener(params, obb):
({"symbol": "AAPL"}),
({"source": "iex", "provider": "intrinio", "symbol": "AAPL"}),
({"symbol": "AAPL", "provider": "fmp"}),
({"symbol": "AAPL", "provider": "yfinance"}),
],
)
@pytest.mark.integration
Expand All @@ -1109,6 +1110,7 @@ def test_equity_price_quote(params, obb):
({"symbol": "MSFT", "provider": "intrinio"}),
({"symbol": "AAPL,MSFT", "provider": "cboe"}),
({"symbol": "AAPL,MSFT", "provider": "intrinio"}),
({"symbol": "AAPL,MSFT", "provider": "yfinance"}),
],
)
@pytest.mark.integration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from openbb_yfinance.models.crypto_historical import YFinanceCryptoHistoricalFetcher
from openbb_yfinance.models.currency_historical import YFinanceCurrencyHistoricalFetcher
from openbb_yfinance.models.equity_historical import YFinanceEquityHistoricalFetcher
from openbb_yfinance.models.equity_profile import YFinanceEquityProfileFetcher
from openbb_yfinance.models.equity_quote import YFinanceEquityQuoteFetcher
from openbb_yfinance.models.etf_historical import YFinanceEtfHistoricalFetcher
from openbb_yfinance.models.futures_curve import YFinanceFuturesCurveFetcher
from openbb_yfinance.models.futures_historical import YFinanceFuturesHistoricalFetcher
Expand Down Expand Up @@ -46,7 +48,9 @@
"EquityAggressiveSmallCaps": YFAggressiveSmallCapsFetcher,
"EquityGainers": YFGainersFetcher,
"EquityHistorical": YFinanceEquityHistoricalFetcher,
"EquityInfo": YFinanceEquityProfileFetcher,
"EquityLosers": YFLosersFetcher,
"EquityQuote": YFinanceEquityQuoteFetcher,
"EquityUndervaluedGrowth": YFUndervaluedGrowthEquitiesFetcher,
"EquityUndervaluedLargeCaps": YFUndervaluedLargeCapsFetcher,
"EtfHistorical": YFinanceEtfHistoricalFetcher,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
"""YFinance Equity Profile fetcher"""
# pylint: disable=unused-argument
import asyncio
import warnings
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.equity_info import (
EquityInfoData,
EquityInfoQueryParams,
)
from pydantic import Field, field_validator
from yfinance import Ticker

_warn = warnings.warn


class YFinanceEquityProfileQueryParams(EquityInfoQueryParams):
"""YFinance Equity Profile query params."""


class YFinanceEquityProfileData(EquityInfoData):
"""YFinance Equity Profile Data."""

__alias_dict__ = {
"name": "longName",
"issue_type": "quoteType",
"stock_exchange": "exchange",
"first_stock_price_date": "firstTradeDateEpochUtc",
"exchange_timezone": "timeZoneFullName",
"industry_category": "industry",
"hq_country": "country",
"hq_address1": "address1",
"hq_address_city": "city",
"hq_address_postal_code": "zip",
"hq_state": "state",
"business_phone_no": "phone",
"company_url": "website",
"long_description": "longBusinessSummary",
"employees": "fullTimeEmployees",
"market_cap": "marketCap",
"dividend_yield": "dividendYield",
}

exchange_timezone: Optional[str] = Field(
description="The timezone of the exchange.",
default=None,
alias="timeZoneFullName",
)
issue_type: Optional[str] = Field(
description="The issuance type of the asset.", default=None, alias="issueType"
)
currency: Optional[str] = Field(
description="The currency in which the asset is traded.", default=None
)
market_cap: Optional[int] = Field(
description="The market capitalization of the asset.",
default=None,
)
shares_outstanding: Optional[int] = Field(
description="The number of listed shares outstanding.",
default=None,
alias="sharesOutstanding",
)
shares_float: Optional[int] = Field(
description="The number of shares in the public float.",
default=None,
alias="floatShares",
)
shares_implied_outstanding: Optional[int] = Field(
description="The implied total number of shares outstanding.",
default=None,
alias="impliedSharesOutstanding",
)
shares_short: Optional[int] = Field(
description="The reported number of shares short.",
default=None,
alias="sharesShort",
)
dividend_yield: Optional[float] = Field(
description="The dividend yield of the asset, as a normalized percent.",
default=None,
json_schema_extra={"unit_measurement": "percent", "frontend_multiply": 100},
alias="yield",
)
beta: Optional[float] = Field(
description="The beta of the asset relative to the broad market.",
default=None,
)

@field_validator("first_stock_price_date", mode="before", check_fields=False)
@classmethod
def validate_first_trade_date(cls, v):
"""Validate first stock price date."""
return datetime.utcfromtimestamp(v).date() if v else None


class YFinanceEquityProfileFetcher(
Fetcher[YFinanceEquityProfileQueryParams, List[YFinanceEquityProfileData]]
):
"""YFinance Equity Profile fetcher."""

@staticmethod
def transform_query(params: Dict[str, Any]) -> YFinanceEquityProfileQueryParams:
"""Transform the query."""
return YFinanceEquityProfileQueryParams(**params)

@staticmethod
async def aextract_data(
query: YFinanceEquityProfileQueryParams,
credentials: Optional[Dict[str, str]],
**kwargs: Any,
) -> List[Dict]:
"""Extract the raw data from YFinance"""

symbols = query.symbol.split(",")
results = []
fields = [
"symbol",
"longName",
"exchange",
"timeZoneFullName",
"quoteType",
"firstTradeDateEpochUtc",
"currency",
"sharesOutstanding",
"floatShares",
"impliedSharesOutstanding",
"sharesShort",
"sector",
"industry",
"address1",
"city",
"state",
"zip",
"country",
"phone",
"website",
"fullTimeEmployees",
"longBusinessSummary",
"marketCap",
"yield",
"dividendYield",
"beta",
]

async def get_one(symbol):
"""Get the data for one ticker symbol."""
result = {}
ticker = {}
try:
ticker = Ticker(symbol).get_info()
except Exception as e:
_warn(f"Error getting data for {symbol}: {e}")
if ticker:
for field in fields:
if field in ticker:
result[field] = ticker.get(field, None)
if result:
results.append(result)

tasks = [get_one(symbol) for symbol in symbols]

await asyncio.gather(*tasks)

return results

@staticmethod
def transform_data(
query: YFinanceEquityProfileQueryParams,
data: List[Dict],
**kwargs: Any,
) -> List[YFinanceEquityProfileData]:
"""Transform the data."""
return [YFinanceEquityProfileData.model_validate(d) for d in data]
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
"""YFinance Equity Quote fetcher"""
# pylint: disable=unused-argument
import asyncio
import warnings
from typing import Any, Dict, List, Optional

from openbb_core.provider.abstract.fetcher import Fetcher
from openbb_core.provider.standard_models.equity_quote import (
EquityQuoteData,
EquityQuoteQueryParams,
)
from pydantic import Field
from yfinance import Ticker

_warn = warnings.warn


class YFinanceEquityQuoteQueryParams(EquityQuoteQueryParams):
"""YFinance Equity Profile query params."""


class YFinanceEquityQuoteData(EquityQuoteData):
"""YFinance Equity Profile Data."""

__alias_dict__ = {
"name": "longName",
"asset_type": "quoteType",
"last_price": "currentPrice",
"high": "dayHigh",
"low": "dayLow",
"prev_close": "previousClose",
"year_high": "fiftyTwoWeekHigh",
"year_low": "fiftyTwoWeekLow",
}

ma_50d: Optional[float] = Field(
default=None,
description="50-day moving average price.",
alias="fiftyDayAverage",
)
ma_200d: Optional[float] = Field(
default=None,
description="200-day moving average price.",
alias="twoHundredDayAverage",
)
volume_average: Optional[float] = Field(
default=None,
description="Average daily trading volume.",
alias="averageVolume",
)
volume_average_10d: Optional[float] = Field(
default=None,
description="Average daily trading volume in the last 10 days.",
alias="averageDailyVolume10Day",
)
currency: Optional[str] = Field(
default=None,
description="Currency of the price.",
)


class YFinanceEquityQuoteFetcher(
Fetcher[YFinanceEquityQuoteQueryParams, List[YFinanceEquityQuoteData]]
):
"""YFinance Equity Profile fetcher."""

@staticmethod
def transform_query(params: Dict[str, Any]) -> YFinanceEquityQuoteQueryParams:
"""Transform the query."""
return YFinanceEquityQuoteQueryParams(**params)

@staticmethod
async def aextract_data(
query: YFinanceEquityQuoteQueryParams,
credentials: Optional[Dict[str, str]],
**kwargs: Any,
) -> List[Dict]:
"""Extract the raw data from YFinance"""

symbols = query.symbol.split(",")
results = []
fields = [
"symbol",
"longName",
"exchange",
"quoteType",
"bid",
"bidSize",
"ask",
"askSize",
"currentPrice",
"open",
"dayHigh",
"dayLow",
"previousClose",
"volume",
"averageVolume",
"averageDailyVolume10Day",
"fiftyTwoWeekHigh",
"fiftyTwoWeekLow",
"fiftyDayAverage",
"twoHundredDayAverage",
"currency",
]

async def get_one(symbol):
"""Get the data for one ticker symbol."""
result = {}
ticker = {}
try:
ticker = Ticker(symbol).get_info()
except Exception as e:
_warn(f"Error getting data for {symbol}: {e}")
if ticker:
for field in fields:
if field in ticker:
result[field] = ticker.get(field, None)
if result:
results.append(result)

tasks = [get_one(symbol) for symbol in symbols]

await asyncio.gather(*tasks)

return results

@staticmethod
def transform_data(
query: YFinanceEquityQuoteQueryParams,
data: List[Dict],
**kwargs: Any,
) -> List[YFinanceEquityQuoteData]:
"""Transform the data."""
return [YFinanceEquityQuoteData.model_validate(d) for d in data]
Loading
Loading