Repository: ranaroussi/yfinance

Issue:
AttributeError: 'float' object has no attribute 'upper'
Any ideas on the error below?

My code works fine on windows locally on my laptop, it basically pulls data for a group of 5000 stocks 1 by 1. When i try to run it onlinus most of the symbols work fine, but i seem to get this error a handfull of times during the loop.

Any ideas what might be causing it, i already have it wrapped in a try/except ststement but the error weirdly still shows (and the code continues if i use ctrl+c)

Any help appreciated. 

Exception in thread Thread-1337:
Traceback (most recent call last):
  File "/usr/lib/python3.8/threading.py", line 932, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.8/threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
  File "/usr/local/lib/python3.8/dist-packages/multitasking/__init__.py", line 104, in _run_via_pool
    return callee(*args, **kwargs)
  File "/usr/local/lib/python3.8/dist-packages/yfinance/multi.py", line 191, in _download_one_threaded
    data = _download_one(ticker, start, end, auto_adjust, back_adjust,
  File "/usr/local/lib/python3.8/dist-packages/yfinance/multi.py", line 205, in _download_one
    return Ticker(ticker).history(period=period, interval=interval,
  File "/usr/local/lib/python3.8/dist-packages/yfinance/base.py", line 168, in history
    end = utils._parse_user_dt(end, tz)
  File "/usr/local/lib/python3.8/dist-packages/yfinance/utils.py", line 154, in _parse_user_dt
    dt = _tz.timezone(exchange_tz).localize(dt)
  File "/usr/local/lib/python3.8/dist-packages/pytz/__init__.py", line 170, in timezone
    if zone.upper() == 'UTC':
AttributeError: 'float' object has no attribute 'upper'


Discussion:
This will be related to my recent timezone changes, but I need your help to debug. I figure it's related to either the spam, or the threading.

1. I've added some debug code to branch 'debug/tz-is-float'. Can you switch to it and tell me if you see any error messages like this?
> Exception: Ticker('AMZN').info['exchangeTimezoneName'] = '0' (<class 'int'>) but should be str

2. I've also added a potential fix - In yfinance/utils.py bottom uncomment the two lines containing 'cache_mutex' - does that fix problem?
i dont see any commented lines in utils.py
Exception in thread Thread-1130:
Traceback (most recent call last):
  File "/usr/lib/python3.8/threading.py", line 932, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.8/threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
  File "/usr/local/lib/python3.8/dist-packages/multitasking/__init__.py", line 104, in _run_via_pool
    return callee(*args, **kwargs)
  File "/usr/local/lib/python3.8/dist-packages/yfinance/multi.py", line 199, in _download_one_threaded
    data = _download_one(ticker, start, end, auto_adjust, back_adjust,
  File "/usr/local/lib/python3.8/dist-packages/yfinance/multi.py", line 213, in _download_one
    return Ticker(ticker).history(period=period, interval=interval,
  File "/usr/local/lib/python3.8/dist-packages/yfinance/base.py", line 168, in history
    end = utils._parse_user_dt(end, tz)
  File "/usr/local/lib/python3.8/dist-packages/yfinance/utils.py", line 154, in _parse_user_dt
    dt = _tz.timezone(exchange_tz).localize(dt)
  File "/usr/local/lib/python3.8/dist-packages/pytz/__init__.py", line 170, in timezone
    if zone.upper() == 'UTC':
AttributeError: 'float' object has no attribute 'upper'
i unistalled and re installed... looks like same error
not sure how i switch to branch 'debug/tz-is-float'

I meant a GitHub branch, not PIP. Read this: https://github.com/ranaroussi/yfinance/discussions/1080
ok loaded new files from git and found those cache lines to uncomment. re running now
Exception in thread Thread-1130:
Traceback (most recent call last):
  File "/usr/lib/python3.8/threading.py", line 932, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.8/threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
  File "/usr/local/lib/python3.8/dist-packages/multitasking/__init__.py", line 104, in _run_via_pool
    return callee(*args, **kwargs)
  File "/usr/local/lib/python3.8/dist-packages/yfinance/multi.py", line 199, in _download_one_threaded
    data = _download_one(ticker, start, end, auto_adjust, back_adjust,
  File "/usr/local/lib/python3.8/dist-packages/yfinance/multi.py", line 213, in _download_one
    return Ticker(ticker).history(period=period, interval=interval,
  File "/usr/local/lib/python3.8/dist-packages/yfinance/base.py", line 154, in history
    tz = self._get_ticker_tz()
  File "/usr/local/lib/python3.8/dist-packages/yfinance/base.py", line 345, in _get_ticker_tz
    raise Exception("Ticker('{}') cached tz = '{}' ({}) but should be str".format(self.ticker, tkr_tz, type(tkr_tz)))
Exception: Ticker('BRK.B') cached tz = 'nan' (<class 'float'>) but should be str
so it looks like its the symbols

even on my laptop BRK.B fails, but my code handles it, whereas on linux it throws an error still, not sure understand that...

try:
        yf.pdr_override()
        data = pdr.get_data_yahoo(x, start=start_date, end=end_date)
        adj = data['Adj Close']
        adj14 = adj.tail(n=14)
        bb_low(adj14,1)
except:

ive run that command and re running now.

really appreciate the help and support, cant thank you enough for your work on this.
Ticker 'BRK.B' doesn't actually exist, so this is about yfinance handling problems in nice way. I've pushed an update that should solve.

yfinance has a local cache now for storing timezones. If cache different between machines then different behaviour.
as in a new version via pip?
No to the GitHub branch. I can't push to PIP until you confirm fix.
ok will download again and re run.

background is.... i download all the symbols from my broker then check some metrics on them using yfinance
Exception in thread Thread-320:
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/urllib3/response.py", line 685, in _update_chunk_length
    self.chunk_left = int(line, 16)
ValueError: invalid literal for int() with base 16: b''

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/urllib3/response.py", line 425, in _error_catcher
    yield
  File "/usr/lib/python3/dist-packages/urllib3/response.py", line 752, in read_chunked
    self._update_chunk_length()
  File "/usr/lib/python3/dist-packages/urllib3/response.py", line 689, in _update_chunk_length
    raise httplib.IncompleteRead(line)
http.client.IncompleteRead: IncompleteRead(0 bytes read)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.8/dist-packages/requests/models.py", line 816, in generate
    yield from self.raw.stream(chunk_size, decode_content=True)
  File "/usr/lib/python3/dist-packages/urllib3/response.py", line 560, in stream
    for line in self.read_chunked(amt, decode_content=decode_content):
  File "/usr/lib/python3/dist-packages/urllib3/response.py", line 781, in read_chunked
    self._original_response.close()
  File "/usr/lib/python3.8/contextlib.py", line 131, in __exit__
    self.gen.throw(type, value, traceback)
  File "/usr/lib/python3/dist-packages/urllib3/response.py", line 443, in _error_catcher
    raise ProtocolError("Connection broken: %r" % e, e)
urllib3.exceptions.ProtocolError: ('Connection broken: IncompleteRead(0 bytes read)', IncompleteRead(0 bytes read))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.8/threading.py", line 932, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.8/threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
  File "/usr/local/lib/python3.8/dist-packages/multitasking/__init__.py", line 104, in _run_via_pool
    return callee(*args, **kwargs)
  File "/usr/local/lib/python3.8/dist-packages/yfinance/multi.py", line 199, in _download_one_threaded
    data = _download_one(ticker, start, end, auto_adjust, back_adjust,
  File "/usr/local/lib/python3.8/dist-packages/yfinance/multi.py", line 213, in _download_one
    return Ticker(ticker).history(period=period, interval=interval,
  File "/usr/local/lib/python3.8/dist-packages/yfinance/base.py", line 153, in history
    tz = self._get_ticker_tz()
  File "/usr/local/lib/python3.8/dist-packages/yfinance/base.py", line 345, in _get_ticker_tz
    if not 'exchangeTimezoneName' in self.info:
  File "/usr/local/lib/python3.8/dist-packages/yfinance/ticker.py", line 147, in info
    return self.get_info()
  File "/usr/local/lib/python3.8/dist-packages/yfinance/base.py", line 711, in get_info
    self._get_info(proxy)
  File "/usr/local/lib/python3.8/dist-packages/yfinance/base.py", line 377, in _get_info
    data = utils.get_json(ticker_url, proxy, self.session)
  File "/usr/local/lib/python3.8/dist-packages/yfinance/utils.py", line 113, in get_json
    html = session.get(url=url, proxies=proxy, headers=user_agent_headers).text
  File "/usr/local/lib/python3.8/dist-packages/requests/api.py", line 73, in get
    return request("get", url, params=params, **kwargs)
  File "/usr/local/lib/python3.8/dist-packages/requests/api.py", line 59, in request
    return session.request(method=method, url=url, **kwargs)
  File "/usr/local/lib/python3.8/dist-packages/requests/sessions.py", line 587, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/local/lib/python3.8/dist-packages/requests/sessions.py", line 745, in send
    r.content
  File "/usr/local/lib/python3.8/dist-packages/requests/models.py", line 899, in content
    self._content = b"".join(self.iter_content(CONTENT_CHUNK_SIZE)) or b""
  File "/usr/local/lib/python3.8/dist-packages/requests/models.py", line 818, in generate
    raise ChunkedEncodingError(e)
requests.exceptions.ChunkedEncodingError: ('Connection broken: IncompleteRead(0 bytes read)', IncompleteRead(0 bytes read))
downloaded the branch again and now im getting a different error as above

BRK.B is now working fine though
[*********************100%***********************]  1 of 1 completed

1 Failed download:
- BRK.B: No data found for this date range, symbol may be delisted
error getting or processing data

Well of course you get an error, BRK.B is an illegal value. It's not on Yahoo as I've already said.
sorry to be clear BRK.B is now working as expected and returns "no data found for this date range, symbol might be delisted"

however a different symbol is still not being handled properly and is returning that longer error that i posted above.
I can't debug your errors remotely. I need to know ticker to reproduce here.
it was COO, but ive re run a second time and its processed it fine.

i do appreciate you looking at this and apologies if it turns out to be my stupidity.

on a positive note, whatever you did in the branch has fixed the handling of the errors i was seeing before. i will continue to run this code and let you know if i get any other errors like above.
this time recieved the same error as above on HCSG
I cannot reproduce your errors so you may have to debug yourself. They look like your network connection is bad, nothing to do with yfinance.
thanks for your help on the original error.
I've moved the fixes into git branch 'feature/improve-tz-cache'. Eventually I'll send it to PIP, maybe 1-2 weeks - I have some other fixes to do.
Hello,

I can't find the branch named  'feature/improve-tz-cache' on github.

I am suffering the same problem (AttributeError: 'float' object has no attribute 'upper') with the PIP version (0.1.84).

I have also downloaded the version from github as you mentioned previously. I get another error :

################## cut
(...)
self.conn.executemany('replace into "kv" (key, value) values (?,?)', records)
sqlite3.IntegrityError: NOT NULL constraint failed: kv.key
################## cut

It is located in "yfinance/yfinance/utils.py", line 558, in bulk_set"

Any idea or branch that I can use to get a working version?
Thanks.

Ignore that sqlite error. I've just release 0.1.85, that should fix problems.
I have installed the new 0.1.85.
Well it works for a good ticker like MMM. But if I try SDLP as a ticker then it does not work.

I have just tried with bpython3 with 2 lines of code : 
import yfinance as yf
mydf = yf.download('SDLP', start='2022-09-12', end='2022-11-02')

I never get back to the prompt ! I need to break with ctrl-C.
When I hit the Ctrl-C I get those lines :

########################### cut
Got error from yahoo api for ticker SDLP, Error: {'code': 'Not Found', 'description': 'No data found, symbol may be
 delisted'}Exception in thread Exception in threading.excepthook:Exception ignored in thread started byException ig
nored in sys.unraisablehookTraceback (most recent call last):
  File "<input>", line 1, in <module>
    mydf = yf.download('SDLP', start='2022-09-12', end='2022-11-02')
  File "/home/richard/.local/lib/python3.8/site-packages/yfinance/multi.py", line 118, in download
    _time.sleep(0.01)
  File "/usr/lib/python3/dist-packages/bpython/curtsiesfrontend/coderunner.py", line 176, in sigint_handler
    raise KeyboardInterrupt()
KeyboardInterrupt
########################### cut

But with 'MMM' for example (a good ticker) it works.

Maybe this feedback can help.


Resolving patch:
diff --git a/yfinance/base.py b/yfinance/base.py
--- a/yfinance/base.py
+++ b/yfinance/base.py
@@ -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
@@ -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' \
@@ -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


Test patch:
diff --git a/test_yfinance.py b/test_yfinance.py
--- a/test_yfinance.py
+++ b/test_yfinance.py
@@ -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
@@ -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 != {})