From 543e4fe582447c58d251121e8f6386c90051d873 Mon Sep 17 00:00:00 2001 From: Stephan Deibel Date: Thu, 9 Feb 2023 13:46:52 -0500 Subject: [PATCH 1/2] Fix format_history_metadata for some symbols Fix format_history_metadata when firstTradeDate is None, as is the case for QCSTIX and probably others. --- yfinance/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yfinance/utils.py b/yfinance/utils.py index f7a1b0f4c..d0b3c2aee 100644 --- a/yfinance/utils.py +++ b/yfinance/utils.py @@ -699,7 +699,7 @@ def format_history_metadata(md): tz = md["exchangeTimezoneName"] for k in ["firstTradeDate", "regularMarketTime"]: - if k in md: + if k in md and md[k] is not None: md[k] = _pd.to_datetime(md[k], unit='s', utc=True).tz_convert(tz) if "currentTradingPeriod" in md: From c362d54b1a460fca63ba2011127be2277ad9446f Mon Sep 17 00:00:00 2001 From: ValueRaider Date: Thu, 9 Feb 2023 19:41:50 +0000 Subject: [PATCH 2/2] Fix other metadata accesses + tests --- tests/ticker.py | 83 +++++++++++++++++++++++++++--------------------- yfinance/base.py | 43 ++++++++++++++++--------- 2 files changed, 74 insertions(+), 52 deletions(-) diff --git a/tests/ticker.py b/tests/ticker.py index 181036490..55431b665 100644 --- a/tests/ticker.py +++ b/tests/ticker.py @@ -52,12 +52,16 @@ def test_getTz(self): def test_badTicker(self): # Check yfinance doesn't die when ticker delisted - tkr = "AM2Z.TA" + tkr = "DJI" # typo of "^DJI" 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") + + for k in dat.fast_info: + dat.fast_info[k] + dat.isin dat.major_holders dat.institutional_holders @@ -91,43 +95,48 @@ def test_badTicker(self): def test_goodTicker(self): # that yfinance works when full api is called on same instance of ticker - tkr = "IBM" - dat = yf.Ticker(tkr, session=self.session) - - dat.isin - dat.major_holders - dat.institutional_holders - dat.mutualfund_holders - dat.dividends - dat.splits - dat.actions - dat.shares - dat.get_shares_full() - 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 + tkrs = ["IBM"] + tkrs.append("QCSTIX") # weird ticker, no price history but has previous close + for tkr in tkrs: + 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.history(period="1wk") + dat.history(start="2022-01-01") + dat.history(start="2022-01-01", end="2022-03-01") + yf.download([tkr], period="1wk") + + for k in dat.fast_info: + dat.fast_info[k] + + dat.isin + dat.major_holders + dat.institutional_holders + dat.mutualfund_holders + dat.dividends + dat.splits + dat.actions + dat.shares + dat.get_shares_full() + 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 class TestTickerHistory(unittest.TestCase): diff --git a/yfinance/base.py b/yfinance/base.py index 02e927d83..49771ea4a 100644 --- a/yfinance/base.py +++ b/yfinance/base.py @@ -270,11 +270,15 @@ def last_price(self): return self._last_price prices = self._get_1y_prices() if prices.empty: - self._last_price = self._get_exchange_metadata()["regularMarketPrice"] + md = self._get_exchange_metadata() + if "regularMarketPrice" in md: + self._last_price = md["regularMarketPrice"] else: self._last_price = float(prices["Close"].iloc[-1]) if _np.isnan(self._last_price): - self._last_price = self._get_exchange_metadata()["regularMarketPrice"] + md = self._get_exchange_metadata() + if "regularMarketPrice" in md: + self._last_price = md["regularMarketPrice"] return self._last_price @property @@ -282,16 +286,23 @@ def previous_close(self): if self._prev_close is not None: return self._prev_close prices = self._get_1wk_1h_prepost_prices() - prices = prices[["Close"]].groupby(prices.index.date).last() - if prices.shape[0] < 2: - # Very few symbols have previousClose despite no - # no trading data. E.g. 'QCSTIX'. - # So fallback to original info[] if available. - self._tkr.info # trigger fetch - if "previousClose" in self._tkr._quote._retired_info: - self._prev_close = self._tkr._quote._retired_info["previousClose"] + fail = False + if prices.empty: + fail = True else: - self._prev_close = float(prices["Close"].iloc[-2]) + prices = prices[["Close"]].groupby(prices.index.date).last() + if prices.shape[0] < 2: + # Very few symbols have previousClose despite no + # no trading data e.g. 'QCSTIX'. + fail = True + else: + self._prev_close = float(prices["Close"].iloc[-2]) + if fail: + # Fallback to original info[] if available. + self._tkr.info # trigger fetch + k = "previousClose" + if self._tkr._quote._retired_info is not None and k in self._tkr._quote._retired_info: + self._prev_close = self._tkr._quote._retired_info[k] return self._prev_close @property @@ -309,8 +320,9 @@ def regular_market_previous_close(self): # no trading data. E.g. 'QCSTIX'. # So fallback to original info[] if available. self._tkr.info # trigger fetch - if "regularMarketPreviousClose" in self._tkr._quote._retired_info: - self._reg_prev_close = self._tkr._quote._retired_info["regularMarketPreviousClose"] + k = "regularMarketPreviousClose" + if self._tkr._quote._retired_info is not None and k in self._tkr._quote._retired_info: + self._reg_prev_close = self._tkr._quote._retired_info[k] else: self._reg_prev_close = float(prices["Close"].iloc[-2]) return self._reg_prev_close @@ -483,8 +495,9 @@ def market_cap(self): # E.g. 'BTC-USD' # So fallback to original info[] if available. self._tkr.info - if "marketCap" in self._tkr._quote._retired_info: - self._mcap = self._tkr._quote._retired_info["marketCap"] + k = "marketCap" + if self._tkr._quote._retired_info is not None and k in self._tkr._quote._retired_info: + self._mcap = self._tkr._quote._retired_info[k] else: self._mcap = float(shares * self.last_price) return self._mcap