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

Improve handling delisted tickers #1142

Merged
merged 1 commit into from Nov 3, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
66 changes: 62 additions & 4 deletions test_yfinance.py
Expand Up @@ -15,22 +15,52 @@

import yfinance as yf
import unittest
import datetime

session = None
import requests_cache ; session = requests_cache.CachedSession("yfinance.cache", expire_after=24*60*60)

symbols = ['MSFT', 'IWO', 'VFINX', '^GSPC', 'BTC-USD']
tickers = [yf.Ticker(symbol) for symbol in symbols]
tickers = [yf.Ticker(symbol, session=session) for symbol in symbols]
delisted_symbols = ["BRK.B", "SDLP"]
delisted_tickers = [yf.Ticker(symbol, session=session) for symbol in delisted_symbols]


class TestTicker(unittest.TestCase):
def setUp(self):
d_today = datetime.date.today()
d_today -= datetime.timedelta(days=30)
self.start_d = datetime.date(d_today.year, d_today.month, 1)

def test_info_history(self):
# always should have info and history for valid symbols
for ticker in tickers:
# always should have info and history for valid symbols
assert(ticker.info is not None and ticker.info != {})
history = ticker.history(period="max")
history = ticker.history(period="1mo")
assert(history.empty is False and history is not None)
histories = yf.download(symbols, period="1mo", session=session)
assert(histories.empty is False and histories is not None)

histories = yf.download(symbols, period="1yr")
for ticker in tickers:
assert(ticker.info is not None and ticker.info != {})
history = ticker.history(start=self.start_d)
assert(history.empty is False and history is not None)
histories = yf.download(symbols, start=self.start_d, session=session)
assert(histories.empty is False and histories is not None)

def test_info_history_nofail(self):
# should not throw Exception for delisted tickers, just print a message
for ticker in delisted_tickers:
history = ticker.history(period="1mo")
histories = yf.download(delisted_symbols, period="1mo", session=session)
histories = yf.download(delisted_symbols[0], period="1mo", session=session)
histories = yf.download(delisted_symbols[1], period="1mo")#, session=session)
for ticker in delisted_tickers:
history = ticker.history(start=self.start_d)
histories = yf.download(delisted_symbols, start=self.start_d, session=session)
histories = yf.download(delisted_symbols[0], start=self.start_d, session=session)
histories = yf.download(delisted_symbols[1], start=self.start_d, session=session)

def test_attributes(self):
for ticker in tickers:
ticker.isin
Expand Down Expand Up @@ -58,6 +88,34 @@ def test_attributes(self):
ticker.earnings_history
ticker.earnings_dates

def test_attributes_nofail(self):
# should not throw Exception for delisted tickers, just print a message
for ticker in delisted_tickers:
ticker.isin
ticker.major_holders
ticker.institutional_holders
ticker.mutualfund_holders
ticker.dividends
ticker.splits
ticker.actions
ticker.info
ticker.calendar
ticker.recommendations
ticker.earnings
ticker.quarterly_earnings
ticker.financials
ticker.quarterly_financials
ticker.balance_sheet
ticker.quarterly_balance_sheet
ticker.cashflow
ticker.quarterly_cashflow
ticker.sustainability
ticker.options
ticker.news
ticker.shares
ticker.earnings_history
ticker.earnings_dates

def test_holders(self):
for ticker in tickers:
assert(ticker.info is not None and ticker.info != {})
Expand Down
21 changes: 14 additions & 7 deletions yfinance/base.py
Expand Up @@ -349,11 +349,12 @@ def _get_ticker_tz(self, debug_mode, proxy, timeout):
if tkr_tz is None:
tkr_tz = self._fetch_ticker_tz(debug_mode, proxy, timeout)

try:
utils.cache_store_tkr_tz(self.ticker, tkr_tz)
except PermissionError:
# System probably read-only, so cannot cache
pass
if tkr_tz is not None:
try:
utils.cache_store_tkr_tz(self.ticker, tkr_tz)
except PermissionError:
# System probably read-only, so cannot cache
pass

self._tz = tkr_tz
return tkr_tz
Expand Down Expand Up @@ -858,6 +859,10 @@ def get_isin(self, proxy=None):
self.get_info(proxy=proxy)
if "shortName" in self._info:
q = self._info['shortName']
if q is None:
err_msg = "Cannot map to ISIN code, symbol may be delisted"
print('- %s: %s' % (self.ticker, err_msg))
return None

url = 'https://markets.businessinsider.com/ajax/' \
'SearchController_Suggest?max_results=25&query=%s' \
Expand Down Expand Up @@ -956,8 +961,10 @@ def get_earnings_dates(self, proxy=None):
dates = _pd.concat([dates, data], axis=0)
page_offset += page_size

if dates is None:
raise Exception("No data found, symbol may be delisted")
if (dates is None) or dates.shape[0]==0:
err_msg = "No earnings dates found, symbol may be delisted"
print('- %s: %s' % (self.ticker, err_msg))
return None
dates = dates.reset_index(drop=True)

# Drop redundant columns
Expand Down