diff --git a/test_yfinance.py b/test_yfinance.py index 098eaa45a..b1e099d8c 100644 --- a/test_yfinance.py +++ b/test_yfinance.py @@ -56,7 +56,6 @@ def test_attributes(self): ticker.options ticker.news ticker.earnings_trend - ticker.earnings_history ticker.earnings_dates ticker.earnings_forecasts diff --git a/tests/ticker.py b/tests/ticker.py index e3de47619..e64920917 100644 --- a/tests/ticker.py +++ b/tests/ticker.py @@ -2,13 +2,18 @@ import unittest +# Create temp session +import requests_cache, tempfile +td = tempfile.TemporaryDirectory() class TestTicker(unittest.TestCase): def setUp(self): - pass + global td + self.td = td + self.session = requests_cache.CachedSession(self.td.name + '/' + "yfinance.cache") def tearDown(self): - pass + self.session.close() def test_getTz(self): tkrs = ["IMP.JO", "BHG.JO", "SSW.JO", "BP.L", "INTC"] @@ -17,11 +22,48 @@ def test_getTz(self): yf.utils.get_tz_cache().store(tkr, None) # Test: - dat = yf.Ticker(tkr) + dat = yf.Ticker(tkr, session=self.session) tz = dat._get_ticker_tz(debug_mode=False, proxy=None, timeout=None) self.assertIsNotNone(tz) + def test_badTicker(self): + # Check yfinance doesn't die when ticker delisted + + tkr = "AM2Z.TA" + dat = yf.Ticker(tkr, session=self.session) + dat.history(period="1wk") + dat.history(start="2022-01-01") + dat.history(start="2022-01-01", end="2022-03-01") + yf.download([tkr], period="1wk") + dat.isin + dat.major_holders + dat.institutional_holders + dat.mutualfund_holders + dat.dividends + dat.splits + dat.actions + dat.shares + dat.info + dat.calendar + dat.recommendations + dat.earnings + dat.quarterly_earnings + dat.income_stmt + dat.quarterly_income_stmt + dat.balance_sheet + dat.quarterly_balance_sheet + dat.cashflow + dat.quarterly_cashflow + dat.recommendations_summary + dat.analyst_price_target + dat.revenue_forecasts + dat.sustainability + dat.options + dat.news + dat.earnings_trend + dat.earnings_dates + dat.earnings_forecasts if __name__ == '__main__': unittest.main() diff --git a/yfinance/base.py b/yfinance/base.py index 77e2ae0f4..dc0564d47 100644 --- a/yfinance/base.py +++ b/yfinance/base.py @@ -154,7 +154,7 @@ def history(self, period="1mo", interval="1d", tz = self._get_ticker_tz(debug, proxy, timeout) if tz is None: # Every valid ticker has a timezone. Missing = problem - err_msg = "No timezone found, symbol certainly delisted" + err_msg = "No timezone found, symbol may be delisted" shared._DFS[self.ticker] = utils.empty_df() shared._ERRORS[self.ticker] = err_msg if debug: @@ -609,6 +609,11 @@ def _get_info(self, proxy=None): ticker_url = "{}/{}".format(self._scrape_url, self.ticker) # get info and sustainability + json_data = utils.get_json_data_stores(ticker_url, proxy, self.session) + if 'QuoteSummaryStore' not in json_data: + err_msg = "No summary info found, symbol may be delisted" + print('- %s: %s' % (self.ticker, err_msg)) + return None data = utils.get_json_data_stores(ticker_url, proxy, self.session)['QuoteSummaryStore'] # sustainability @@ -822,14 +827,19 @@ def cleanup(data): self._get_info(proxy) # get fundamentals - fin_data = utils.get_json_data_stores(ticker_url + '/financials', proxy, self.session) - fin_data_quote = fin_data['QuoteSummaryStore'] - - # generic patterns self._earnings = {"yearly": utils._pd.DataFrame(), "quarterly": utils._pd.DataFrame()} self._financials = {} for name in ["income", "balance-sheet", "cash-flow"]: self._financials[name] = {"yearly":utils._pd.DataFrame(), "quarterly":utils._pd.DataFrame()} + + fin_data = utils.get_json_data_stores(ticker_url + '/financials', proxy, self.session) + if not "QuoteSummaryStore" in fin_data: + err_msg = "No financials data found, symbol may be delisted" + print('- %s: %s' % (self.ticker, err_msg)) + return None + fin_data_quote = fin_data['QuoteSummaryStore'] + + # generic patterns for name in ["income", "balance-sheet", "cash-flow"]: annual, qtr = self._create_financials_table(name, proxy) if annual is not None: @@ -1142,6 +1152,9 @@ def get_isin(self, proxy=None): q = ticker self.get_info(proxy=proxy) + if self._info is None: + # Don't print error message cause _get_info() will print one + return None if "shortName" in self._info: q = self._info['shortName'] @@ -1242,8 +1255,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 @@ -1277,40 +1292,3 @@ def get_earnings_dates(self, proxy=None): self._earnings_dates = dates return dates - - def get_earnings_history(self, proxy=None): - if self._earnings_history: - return self._earnings_history - - # setup proxy in requests format - if proxy is not None: - if isinstance(proxy, dict) and "https" in proxy: - proxy = proxy["https"] - proxy = {"https": proxy} - - url = "{}/calendar/earnings?symbol={}".format(_ROOT_URL_, self.ticker) - session = self.session or _requests - data = session.get( - url=url, - proxies=proxy, - headers=utils.user_agent_headers - ).text - - if "Will be right back" in data: - raise RuntimeError("*** YAHOO! FINANCE IS CURRENTLY DOWN! ***\n" - "Our engineers are working quickly to resolve " - "the issue. Thank you for your patience.") - - try: - # read_html returns a list of pandas Dataframes of all the tables in `data` - data = _pd.read_html(data)[0] - data.replace("-", _np.nan, inplace=True) - - data['EPS Estimate'] = _pd.to_numeric(data['EPS Estimate']) - data['Reported EPS'] = _pd.to_numeric(data['Reported EPS']) - self._earnings_history = data - # if no tables are found a ValueError is thrown - except ValueError: - print("Could not find earnings history data for {}.".format(self.ticker)) - return - return data