Import python venv for stability

This commit is contained in:
2026-02-15 21:24:16 -08:00
parent 1343e93a59
commit 7d784705c9
4997 changed files with 1628270 additions and 0 deletions
@@ -0,0 +1,5 @@
# domain/__init__.py
from .sector import Sector
from .industry import Industry
__all__ = ['Sector', 'Industry']
@@ -0,0 +1,195 @@
from abc import ABC, abstractmethod
import pandas as _pd
from typing import Dict, List, Optional
from ..const import _QUERY1_URL_
from ..data import YfData
from ..ticker import Ticker
_QUERY_URL_ = f'{_QUERY1_URL_}/v1/finance'
class Domain(ABC):
"""
Abstract base class representing a domain entity in financial data, with key attributes
and methods for fetching and parsing data. Derived classes must implement the `_fetch_and_parse()` method.
"""
def __init__(self, key: str, session=None):
"""
Initializes the Domain object with a key, session.
Args:
key (str): Unique key identifying the domain entity.
session (Optional[requests.Session]): Session object for HTTP requests. Defaults to None.
"""
self._key: str = key
self.session = session
self._data: YfData = YfData(session=session)
self._name: Optional[str] = None
self._symbol: Optional[str] = None
self._overview: Optional[Dict] = None
self._top_companies: Optional[_pd.DataFrame] = None
self._research_reports: Optional[List[Dict[str, str]]] = None
@property
def key(self) -> str:
"""
Retrieves the key of the domain entity.
Returns:
str: The unique key of the domain entity.
"""
return self._key
@property
def name(self) -> str:
"""
Retrieves the name of the domain entity.
Returns:
str: The name of the domain entity.
"""
self._ensure_fetched(self._name)
return self._name
@property
def symbol(self) -> str:
"""
Retrieves the symbol of the domain entity.
Returns:
str: The symbol representing the domain entity.
"""
self._ensure_fetched(self._symbol)
return self._symbol
@property
def ticker(self) -> Ticker:
"""
Retrieves a Ticker object based on the domain entity's symbol.
Returns:
Ticker: A Ticker object associated with the domain entity.
"""
self._ensure_fetched(self._symbol)
return Ticker(self._symbol)
@property
def overview(self) -> Dict:
"""
Retrieves the overview information of the domain entity.
Returns:
Dict: A dictionary containing an overview of the domain entity.
"""
self._ensure_fetched(self._overview)
return self._overview
@property
def top_companies(self) -> Optional[_pd.DataFrame]:
"""
Retrieves the top companies within the domain entity.
Returns:
pandas.DataFrame: A DataFrame containing the top companies in the domain.
"""
self._ensure_fetched(self._top_companies)
return self._top_companies
@property
def research_reports(self) -> List[Dict[str, str]]:
"""
Retrieves research reports related to the domain entity.
Returns:
List[Dict[str, str]]: A list of research reports, where each report is a dictionary with metadata.
"""
self._ensure_fetched(self._research_reports)
return self._research_reports
def _fetch(self, query_url) -> Dict:
"""
Fetches data from the given query URL.
Args:
query_url (str): The URL used for the data query.
Returns:
Dict: The JSON response data from the request.
"""
params_dict = {"formatted": "true", "withReturns": "true", "lang": "en-US", "region": "US"}
result = self._data.get_raw_json(query_url, params=params_dict)
return result
def _parse_and_assign_common(self, data) -> None:
"""
Parses and assigns common data fields such as name, symbol, overview, and top companies.
Args:
data (Dict): The raw data received from the API.
"""
self._name = data.get('name')
self._symbol = data.get('symbol')
self._overview = self._parse_overview(data.get('overview', {}))
self._top_companies = self._parse_top_companies(data.get('topCompanies', {}))
self._research_reports = data.get('researchReports')
def _parse_overview(self, overview) -> Dict:
"""
Parses the overview data for the domain entity.
Args:
overview (Dict): The raw overview data.
Returns:
Dict: A dictionary containing parsed overview information.
"""
return {
"companies_count": overview.get('companiesCount', None),
"market_cap": overview.get('marketCap', {}).get('raw', None),
"message_board_id": overview.get('messageBoardId', None),
"description": overview.get('description', None),
"industries_count": overview.get('industriesCount', None),
"market_weight": overview.get('marketWeight', {}).get('raw', None),
"employee_count": overview.get('employeeCount', {}).get('raw', None)
}
def _parse_top_companies(self, top_companies) -> Optional[_pd.DataFrame]:
"""
Parses the top companies data and converts it into a pandas DataFrame.
Args:
top_companies (Dict): The raw top companies data.
Returns:
Optional[pandas.DataFrame]: A DataFrame containing top company data, or None if no data is available.
"""
top_companies_column = ['symbol', 'name', 'rating', 'market weight']
top_companies_values = [(c.get('symbol'),
c.get('name'),
c.get('rating'),
c.get('marketWeight',{}).get('raw',None)) for c in top_companies]
if not top_companies_values:
return None
return _pd.DataFrame(top_companies_values, columns=top_companies_column).set_index('symbol')
@abstractmethod
def _fetch_and_parse(self) -> None:
"""
Abstract method for fetching and parsing domain-specific data.
Must be implemented by derived classes.
"""
raise NotImplementedError("_fetch_and_parse() needs to be implemented by children classes")
def _ensure_fetched(self, attribute) -> None:
"""
Ensures that the given attribute is fetched by calling `_fetch_and_parse()` if the attribute is None.
Args:
attribute: The attribute to check and potentially fetch.
"""
if attribute is None:
self._fetch_and_parse()
@@ -0,0 +1,153 @@
from __future__ import print_function
import pandas as _pd
from typing import Dict, Optional
from .. import utils
from ..config import YfConfig
from ..data import YfData
from .domain import Domain, _QUERY_URL_
class Industry(Domain):
"""
Represents an industry within a sector.
"""
def __init__(self, key, session=None):
"""
Args:
key (str): The key identifier for the industry.
session (optional): The session to use for requests.
"""
YfData(session=session)
super(Industry, self).__init__(key, session)
self._query_url = f'{_QUERY_URL_}/industries/{self._key}'
self._sector_key = None
self._sector_name = None
self._top_performing_companies = None
self._top_growth_companies = None
def __repr__(self):
"""
Returns a string representation of the Industry instance.
Returns:
str: String representation of the Industry instance.
"""
return f'yfinance.Industry object <{self._key}>'
@property
def sector_key(self) -> str:
"""
Returns the sector key of the industry.
Returns:
str: The sector key.
"""
self._ensure_fetched(self._sector_key)
return self._sector_key
@property
def sector_name(self) -> str:
"""
Returns the sector name of the industry.
Returns:
str: The sector name.
"""
self._ensure_fetched(self._sector_name)
return self._sector_name
@property
def top_performing_companies(self) -> Optional[_pd.DataFrame]:
"""
Returns the top performing companies in the industry.
Returns:
Optional[pd.DataFrame]: DataFrame containing top performing companies.
"""
self._ensure_fetched(self._top_performing_companies)
return self._top_performing_companies
@property
def top_growth_companies(self) -> Optional[_pd.DataFrame]:
"""
Returns the top growth companies in the industry.
Returns:
Optional[pd.DataFrame]: DataFrame containing top growth companies.
"""
self._ensure_fetched(self._top_growth_companies)
return self._top_growth_companies
def _parse_top_performing_companies(self, top_performing_companies: Dict) -> Optional[_pd.DataFrame]:
"""
Parses the top performing companies data.
Args:
top_performing_companies (Dict): Dictionary containing top performing companies data.
Returns:
Optional[pd.DataFrame]: DataFrame containing parsed top performing companies data.
"""
compnaies_column = ['symbol','name','ytd return','last price','target price']
compnaies_values = [(c.get('symbol', None),
c.get('name', None),
c.get('ytdReturn',{}).get('raw', None),
c.get('lastPrice',{}).get('raw', None),
c.get('targetPrice',{}).get('raw', None),) for c in top_performing_companies]
if not compnaies_values:
return None
return _pd.DataFrame(compnaies_values, columns = compnaies_column).set_index('symbol')
def _parse_top_growth_companies(self, top_growth_companies: Dict) -> Optional[_pd.DataFrame]:
"""
Parses the top growth companies data.
Args:
top_growth_companies (Dict): Dictionary containing top growth companies data.
Returns:
Optional[pd.DataFrame]: DataFrame containing parsed top growth companies data.
"""
compnaies_column = ['symbol','name','ytd return','growth estimate']
compnaies_values = [(c.get('symbol', None),
c.get('name', None),
c.get('ytdReturn',{}).get('raw', None),
c.get('growthEstimate',{}).get('raw', None),) for c in top_growth_companies]
if not compnaies_values:
return None
return _pd.DataFrame(compnaies_values, columns = compnaies_column).set_index('symbol')
def _fetch_and_parse(self) -> None:
"""
Fetches and parses the industry data.
"""
result = None
try:
result = self._fetch(self._query_url)
data = result['data']
self._parse_and_assign_common(data)
self._sector_key = data.get('sectorKey')
self._sector_name = data.get('sectorName')
self._top_performing_companies = self._parse_top_performing_companies(data.get('topPerformingCompanies'))
self._top_growth_companies = self._parse_top_growth_companies(data.get('topGrowthCompanies'))
return result
except Exception as e:
if not YfConfig.debug.hide_exceptions:
raise
logger = utils.get_yf_logger()
logger.error(f"Failed to get industry data for '{self._key}' reason: {e}")
logger.debug("Got response: ")
logger.debug("-------------")
logger.debug(f" {result}")
logger.debug("-------------")
@@ -0,0 +1,107 @@
import datetime as dt
import json as _json
from ..config import YfConfig
from ..const import _QUERY1_URL_
from ..data import utils, YfData
from ..exceptions import YFDataException
class Market:
def __init__(self, market:'str', session=None, timeout=30):
self.market = market
self.session = session
self.timeout = timeout
self._data = YfData(session=self.session)
self._logger = utils.get_yf_logger()
self._status = None
self._summary = None
def _fetch_json(self, url, params):
data = self._data.cache_get(url=url, params=params, timeout=self.timeout)
if data is None or "Will be right back" in data.text:
raise YFDataException("*** YAHOO! FINANCE IS CURRENTLY DOWN! ***")
try:
return data.json()
except _json.JSONDecodeError:
if not YfConfig.debug.hide_exceptions:
raise
self._logger.error(f"{self.market}: Failed to retrieve market data and recieved faulty data.")
return {}
def _parse_data(self):
# Fetch both to ensure they are at the same time
if (self._status is not None) and (self._summary is not None):
return
self._logger.debug(f"{self.market}: Parsing market data")
# Summary
summary_url = f"{_QUERY1_URL_}/v6/finance/quote/marketSummary"
summary_fields = ["shortName", "regularMarketPrice", "regularMarketChange", "regularMarketChangePercent"]
summary_params = {
"fields": ",".join(summary_fields),
"formatted": False,
"lang": "en-US",
"market": self.market
}
status_url = f"{_QUERY1_URL_}/v6/finance/markettime"
status_params = {
"formatted": True,
"key": "finance",
"lang": "en-US",
"market": self.market
}
self._summary = self._fetch_json(summary_url, summary_params)
self._status = self._fetch_json(status_url, status_params)
try:
self._summary = self._summary['marketSummaryResponse']['result']
self._summary = {x['exchange']:x for x in self._summary}
except Exception as e:
if not YfConfig.debug.hide_exceptions:
raise
self._logger.error(f"{self.market}: Failed to parse market summary")
self._logger.debug(f"{type(e)}: {e}")
try:
# Unpack
self._status = self._status['finance']['marketTimes'][0]['marketTime'][0]
self._status['timezone'] = self._status['timezone'][0]
del self._status['time'] # redundant
except Exception as e:
if not YfConfig.debug.hide_exceptions:
raise
self._logger.error(f"{self.market}: Failed to parse market status")
self._logger.debug(f"{type(e)}: {e}")
try:
self._status.update({
"open": dt.datetime.fromisoformat(self._status["open"]),
"close": dt.datetime.fromisoformat(self._status["close"]),
"tz": dt.timezone(dt.timedelta(hours=int(self._status["timezone"]["gmtoffset"]))/1000, self._status["timezone"]["short"])
})
except Exception as e:
if not YfConfig.debug.hide_exceptions:
raise
self._logger.error(f"{self.market}: Failed to update market status")
self._logger.debug(f"{type(e)}: {e}")
@property
def status(self):
self._parse_data()
return self._status
@property
def summary(self):
self._parse_data()
return self._summary
@@ -0,0 +1,152 @@
from __future__ import print_function
import pandas as _pd
from typing import Dict, Optional
from ..config import YfConfig
from ..const import SECTOR_INDUSTY_MAPPING_LC
from ..utils import dynamic_docstring, generate_list_table_from_dict, get_yf_logger
from .domain import Domain, _QUERY_URL_
class Sector(Domain):
"""
Represents a financial market sector and allows retrieval of sector-related data
such as top ETFs, top mutual funds, and industry data.
"""
def __init__(self, key, session=None):
"""
Args:
key (str): The key representing the sector.
session (requests.Session, optional): A session for making requests. Defaults to None.
.. seealso::
:attr:`Sector.industries <yfinance.Sector.industries>`
Map of sector and industry
"""
super(Sector, self).__init__(key, session)
self._query_url: str = f'{_QUERY_URL_}/sectors/{self._key}'
self._top_etfs: Optional[Dict] = None
self._top_mutual_funds: Optional[Dict] = None
self._industries: Optional[_pd.DataFrame] = None
def __repr__(self):
"""
Returns the string representation of the Sector object.
Returns:
str: A string representation of the object.
"""
return f'yfinance.Sector object <{self._key}>'
@property
def top_etfs(self) -> Dict[str, str]:
"""
Gets the top ETFs for the sector.
Returns:
Dict[str, str]: A dictionary of ETF symbols and names.
"""
self._ensure_fetched(self._top_etfs)
return self._top_etfs
@property
def top_mutual_funds(self) -> Dict[str, str]:
"""
Gets the top mutual funds for the sector.
Returns:
Dict[str, str]: A dictionary of mutual fund symbols and names.
"""
self._ensure_fetched(self._top_mutual_funds)
return self._top_mutual_funds
@dynamic_docstring({"sector_industry": generate_list_table_from_dict(SECTOR_INDUSTY_MAPPING_LC,bullets=True)})
@property
def industries(self) -> _pd.DataFrame:
"""
Gets the industries within the sector.
Returns:
pandas.DataFrame: A DataFrame with industries' key, name, symbol, and market weight.
{sector_industry}
"""
self._ensure_fetched(self._industries)
return self._industries
def _parse_top_etfs(self, top_etfs: Dict) -> Dict[str, str]:
"""
Parses top ETF data from the API response.
Args:
top_etfs (Dict): The raw ETF data from the API response.
Returns:
Dict[str, str]: A dictionary of ETF symbols and names.
"""
return {e.get('symbol'): e.get('name') for e in top_etfs}
def _parse_top_mutual_funds(self, top_mutual_funds: Dict) -> Dict[str, str]:
"""
Parses top mutual funds data from the API response.
Args:
top_mutual_funds (Dict): The raw mutual fund data from the API response.
Returns:
Dict[str, str]: A dictionary of mutual fund symbols and names.
"""
return {e.get('symbol'): e.get('name') for e in top_mutual_funds}
def _parse_industries(self, industries: Dict) -> _pd.DataFrame:
"""
Parses industry data from the API response into a DataFrame.
Args:
industries (Dict): The raw industry data from the API response.
Returns:
pandas.DataFrame: A DataFrame containing industry key, name, symbol, and market weight.
"""
industries_column = ['key','name','symbol','market weight']
industries_values = [(i.get('key'),
i.get('name'),
i.get('symbol'),
i.get('marketWeight',{}).get('raw', None)
) for i in industries if i.get('name') != 'All Industries']
return _pd.DataFrame(industries_values, columns=industries_column).set_index('key')
def _fetch_and_parse(self) -> None:
"""
Fetches and parses sector data from the API.
Fetches data for the sector and parses the top ETFs, top mutual funds,
and industries within the sector. Stores the parsed data in the corresponding
attributes `_top_etfs`, `_top_mutual_funds`, and `_industries`.
Raises:
Exception: If fetching or parsing the sector data fails.
"""
result = None
try:
result = self._fetch(self._query_url)
data = result['data']
self._parse_and_assign_common(data)
self._top_etfs = self._parse_top_etfs(data.get('topETFs', {}))
self._top_mutual_funds = self._parse_top_mutual_funds(data.get('topMutualFunds', {}))
self._industries = self._parse_industries(data.get('industries', {}))
except Exception as e:
if not YfConfig.debug.hide_exceptions:
raise
logger = get_yf_logger()
logger.error(f"Failed to get sector data for '{self._key}' reason: {e}")
logger.debug("Got response: ")
logger.debug("-------------")
logger.debug(f" {result}")
logger.debug("-------------")