Skip to content

Commit

Permalink
implement Sector and Industry
Browse files Browse the repository at this point in the history
  • Loading branch information
ericpien committed Sep 22, 2024
1 parent 6b7a511 commit 10fd843
Show file tree
Hide file tree
Showing 7 changed files with 460 additions and 1 deletion.
49 changes: 49 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,55 @@ data = yf.download("SPY AAPL", period="1mo")

#### `yf.download()` and `Ticker.history()` have many options for configuring fetching and processing. [Review the Wiki](https://github.com/ranaroussi/yfinance/wiki) for more options and detail.

### Sector and Industry

The `Sector` and `Industry` modules allow you to access the US market information.

To initialize, use the relevant sector or industry key as below. (Complete mapping of the keys is available in `const.py`.)

```python
import yfinance as yf

tech = yf.Sector('technology')
software = yf.Industry('software-infrastructure')

# Common information
tech.key
tech.name
tech.symbol
tech.ticker
tech.overview
tech.top_companies
tech.research_reports

# Sector information
tech.top_etfs
tech.top_mutual_funds
tech.industries

# Industry information
software.sector_key
software.sector_name
software.top_performing_companies
software.top_growth_companies
```

The modules can be chained with Ticker as below.
```python
import yfinance as yf

# Ticker to Sector and Industry
msft = yf.Ticker('MSFT')
tech = yf.Sector(msft.info.get('sectorKey'))
software = yf.Industry(msft.info.get('industryKey'))

# Sector and Industry to Ticker
tech_ticker = tech.ticker
tech_ticker.info
software_ticker = software.ticker
software_ticker.history()
```

### Logging

`yfinance` now uses the `logging` module to handle messages, default behaviour is only print errors. If debugging, use `yf.enable_debug_mode()` to switch logging to debug with custom formatting.
Expand Down
4 changes: 3 additions & 1 deletion yfinance/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@
from .multi import download
from .utils import enable_debug_mode
from .cache import set_tz_cache_location
from .domain.sector import Sector
from .domain.industry import Industry

__version__ = version.version
__author__ = "Ran Aroussi"

import warnings
warnings.filterwarnings('default', category=DeprecationWarning, module='^yfinance')

__all__ = ['download', 'Ticker', 'Tickers', 'enable_debug_mode', 'set_tz_cache_location']
__all__ = ['download', 'Ticker', 'Tickers', 'enable_debug_mode', 'set_tz_cache_location', 'Sector', 'Industry']
150 changes: 150 additions & 0 deletions yfinance/const.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
_QUERY1_URL_ = 'https://query1.finance.yahoo.com'
_BASE_URL_ = 'https://query2.finance.yahoo.com'
_ROOT_URL_ = 'https://finance.yahoo.com'

Expand Down Expand Up @@ -155,3 +156,152 @@
"recommendationTrend",
"futuresChain",
)

# map last updated as of 2024.09.18
SECTOR_INDUSTY_MAPPING = {
'basic-materials': {'specialty-chemicals',
'gold',
'building-materials',
'copper',
'steel',
'agricultural-inputs',
'chemicals',
'other-industrial-metals-mining',
'lumber-wood-production',
'aluminum',
'other-precious-metals-mining',
'coking-coal',
'paper-paper-products',
'silver'},
'communication-services': {'internet-content-information',
'telecom-services',
'entertainment',
'electronic-gaming-multimedia',
'advertising-agencies',
'broadcasting',
'publishing'},
'consumer-cyclical': {'internet-retail',
'auto-manufacturers',
'restaurants',
'home-improvement-retail',
'travel-services',
'specialty-retail',
'apparel-retail',
'residential-construction',
'footwear-accessories',
'packaging-containers',
'lodging',
'auto-parts',
'auto-truck-dealerships',
'gambling',
'resorts-casinos',
'leisure',
'apparel-manufacturing',
'personal-services',
'furnishings-fixtures-appliances',
'recreational-vehicles',
'luxury-goods',
'department-stores',
'textile-manufacturing'},
'consumer-defensive': {'discount-stores',
'beverages-non-alcoholic',
'household-personal-products',
'packaged-foods',
'tobacco',
'confectioners',
'farm-products',
'food-distribution',
'grocery-stores',
'beverages-brewers',
'education-training-services',
'beverages-wineries-distilleries'},
'energy': {'oil-gas-integrated',
'oil-gas-midstream',
'oil-gas-e-p',
'oil-gas-equipment-services',
'oil-gas-refining-marketing',
'uranium',
'oil-gas-drilling',
'thermal-coal'},
'financial-services': {'banks-diversified',
'credit-services',
'asset-management',
'insurance-diversified',
'banks-regional',
'capital-markets',
'financial-data-stock-exchanges',
'insurance-property-casualty',
'insurance-brokers',
'insurance-life',
'insurance-specialty',
'mortgage-finance',
'insurance-reinsurance',
'shell-companies',
'financial-conglomerates'},
'healthcare': {'drug-manufacturers-general',
'healthcare-plans',
'biotechnology',
'medical-devices',
'diagnostics-research',
'medical-instruments-supplies',
'medical-care-facilities',
'drug-manufacturers-specialty-generic',
'health-information-services',
'medical-distribution',
'pharmaceutical-retailers'},
'industrials': {'aerospace-defense',
'specialty-industrial-machinery',
'railroads',
'building-products-equipment',
'farm-heavy-construction-machinery',
'specialty-business-services',
'integrated-freight-logistics',
'waste-management',
'conglomerates',
'industrial-distribution',
'engineering-construction',
'rental-leasing-services',
'consulting-services',
'trucking',
'electrical-equipment-parts',
'airlines',
'tools-accessories',
'pollution-treatment-controls',
'security-protection-services',
'marine-shipping',
'metal-fabrication',
'infrastructure-operations',
'staffing-employment-services',
'airports-air-services',
'business-equipment-supplies'},
'real-estate': {'reit-specialty',
'reit-industrial',
'reit-retail',
'reit-residential',
'reit-healthcare-facilities',
'real-estate-services',
'reit-office',
'reit-diversified',
'reit-mortgage',
'reit-hotel-motel',
'real-estate-development',
'real-estate-diversified'},
'technology': {'software-infrastructure',
'semiconductors',
'consumer-electronics',
'software-application',
'information-technology-services',
'semiconductor-equipment-materials',
'communication-equipment',
'computer-hardware',
'electronic-components',
'scientific-technical-instruments',
'solar',
'electronics-computer-distribution'},
'utilities': {'utilities-regulated-electric',
'utilities-renewable',
'utilities-diversified',
'utilities-regulated-gas',
'utilities-independent-power-producers',
'utilities-regulated-water'}
}
5 changes: 5 additions & 0 deletions yfinance/domain/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# domain/__init__.py
from .sector import Sector
from .industry import Industry

__all__ = ['Sector', 'Industry']
97 changes: 97 additions & 0 deletions yfinance/domain/domain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
from ..ticker import Ticker
from ..const import _QUERY1_URL_
from ..data import YfData
from typing import Dict, List, Optional

import pandas as _pd

_QUERY_URL_ = f'{_QUERY1_URL_}/v1/finance'

class Domain:
def __init__(self, key: str, session=None, proxy=None):
self._key: str = key
self.proxy = proxy
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_report: Optional[List[Dict[str, str]]] = None

@property
def key(self) -> str:
return self._key

@property
def name(self) -> str:
self._ensure_fetched(self._name)
return self._name

@property
def symbol(self) -> str:
self._ensure_fetched(self._symbol)
return self._symbol

@property
def ticker(self) -> Ticker:
self._ensure_fetched(self._symbol)
return Ticker(self._symbol)

@property
def overview(self) -> Dict:
self._ensure_fetched(self._overview)
return self._overview

@property
def top_companies(self) -> Optional[_pd.DataFrame]:
self._ensure_fetched(self._top_companies)
return self._top_companies

@property
def research_reports(self) -> List[Dict[str, str]]:
self._ensure_fetched(self._research_reports)
return self._research_reports

def _fetch(self, query_url, proxy) -> Dict:
params_dict = {"formatted": "true", "withReturns": "true", "lang": "en-US", "region": "US"}
result = self._data.get_raw_json(query_url, user_agent_headers=self._data.user_agent_headers, params=params_dict, proxy=proxy)
return result

def _parse_and_assign_common(self, data) -> None:
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:
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]:
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')

def _fetch_and_parse(self) -> None:
raise NotImplementedError("_fetch_and_parse() needs to be implemented by children classes")

def _ensure_fetched(self, attribute) -> None:
if attribute is None:
self._fetch_and_parse()
Loading

0 comments on commit 10fd843

Please sign in to comment.