addisonlynch / iexfinance

Python SDK for IEX Cloud
https://addisonlynch.github.io/iexfinance
Apache License 2.0
649 stars 136 forks source link

KeyError exception unnecessarily raised #262

Open nima opened 3 years ago

nima commented 3 years ago

Format of data that raises the exception:

{'A': {'sharesOutstanding': 306849526}, 'AAPL': {'sharesOutstanding': 16788096000}, 'ABBV': {'sharesOutstanding': 1765473923}, 'ACB': {'sharesOutstanding': 197444621}, 'ALB': {'sharesOutstanding': 116632129}, 'ALK': {'sharesOutstanding': 123663778}, 'ALL': {'sharesOutstanding': 304068026}, 'ALXN': {'sharesOutstanding': 219847960}, 'AMC': {'sharesOutstanding': 287276558}, 'AMCX': {'sharesOutstanding': 29754780}, 'AMD': {'sharesOutstanding': 1211280009}, 'AMZN': {'sharesOutstanding': 503564743}, 'APPS': {'sharesOutstanding': 89413606}, 'ARKK': {'sharesOutstanding': 181000000}, 'ATVI': {'sharesOutstanding': 772857185}, 'AXP': {'sharesOutstanding': 805588980}, 'AZN': {'sharesOutstanding': 2625337448}, 'BA': {'sharesOutstanding': 582996860}, 'BABA': {'sharesOutstanding': 2705636600}, 'BAC': {'sharesOutstanding': 8650789694}, 'BAR': {'sharesOutstanding': 62500000}, 'BE': {'sharesOutstanding': 138307369}, 'BFST': {'sharesOutstanding': 20667237}, 'BIDU': {'sharesOutstanding': 269563100}, 'BILI': {'sharesOutstanding': 262070837}, 'BKI': {'sharesOutstanding': 157029311}, 'BLDP': {'sharesOutstanding': 282197366}, 'BND': {'sharesOutstanding': 803823018}, 'BRK.B': {'sharesOutstanding': 1370951744}, 'BWA': {'sharesOutstanding': 244518666}, 'C': {'sharesOutstanding': 2081959678}, 'CBOE': {'sharesOutstanding': 108041921}, 'CCF': {'sharesOutstanding': 9446391}, 'CMCSA': {'sharesOutstanding': 4565878780}, 'CNK': {'sharesOutstanding': 118158433}, 'CNQ': {'sharesOutstanding': 1185474913}, 'COST': {'sharesOutstanding': 442955229}, 'CRON': {'sharesOutstanding': 360253332}, 'CSCO': {'sharesOutstanding': 4221785547}, 'CSL': {'sharesOutstanding': 53291296}, 'CVX': {'sharesOutstanding': 1867323448}, 'CYD': {'sharesOutstanding': 40858290}, 'D': {'sharesOutstanding': 815819095}, 'DAL': {'sharesOutstanding': 638146665}, 'DAN': {'sharesOutstanding': 144670587}, 'DBX': {'sharesOutstanding': 315878882}, 'DELL': {'sharesOutstanding': 263653066}, 'DHT': {'sharesOutstanding': 147420931}, 'DIS': {'sharesOutstanding': 1815263899}, 'DLTR': {'sharesOutstanding': 235192152}, 'DVY': {'sharesOutstanding': 150500000}, 'EA': {'sharesOutstanding': 287626027}, 'EB': {'sharesOutstanding': 68281695}, 'EBAY': {'sharesOutstanding': 680445767}, 'EDV': {'sharesOutstanding': 8400000}, 'ENBL': {'sharesOutstanding': 435474966}, 'ESGC': {'sharesOutstanding': 185317843}, 'ETSY': {'sharesOutstanding': 126090966}, 'EXPE': {'sharesOutstanding': 138341099}, 'EXPI': {'sharesOutstanding': 69952115}, 'F': {'sharesOutstanding': 3907842941}, 'FB': {'sharesOutstanding': 2405448410}, 'FEYE': {'sharesOutstanding': 227741472}, 'FFIV': {'sharesOutstanding': 61650819}, 'FGD': {'sharesOutstanding': 17700002}, 'FHN': {'sharesOutstanding': 554787728}, 'FI': {'sharesOutstanding': 226318715}, 'FIT': {'sharesOutstanding': 244867751}, 'FLEX': {'sharesOutstanding': 499172368}, 'FLXS': {'sharesOutstanding': 7061152}, 'FNF': {'sharesOutstanding': 293673787}, 'FNKO': {'sharesOutstanding': 35611762}, 'FOX': {'sharesOutstanding': 256191870}, 'FOXA': {'sharesOutstanding': 334351352}, 'FOXF': {'sharesOutstanding': 41715243}, 'FPE': {'sharesOutstanding': 297705000}, 'FSI': {'sharesOutstanding': 12240545}, 'FTI': {'sharesOutstanding': 449466233}, 'GDX': {'sharesOutstanding': 430552500}, 'GE': {'sharesOutstanding': 8767942000}, 'GLMD': {'sharesOutstanding': 21113066}, 'GM': {'sharesOutstanding': 1440912820}, 'GME': {'sharesOutstanding': 69746960}, 'GOOG': {'sharesOutstanding': 327556472}, 'GOOGL': {'sharesOutstanding': 300737081}, 'GPRO': {'sharesOutstanding': 122634624}, 'GRMN': {'sharesOutstanding': 191571374}, 'GRPN': {'sharesOutstanding': 28812655}, 'GS': {'sharesOutstanding': 344067789}, 'GT': {'sharesOutstanding': 233220098}, 'GTXMQ': {'sharesOutstanding': 75788279}, 'GUNR': {'sharesOutstanding': 109050001}, 'HAS': {'sharesOutstanding': 137030619}, 'HNDL': {'sharesOutstanding': 11050000}, 'HOG': {'sharesOutstanding': 153278729}, 'HON': {'sharesOutstanding': 695501159}, 'HUYA': {'sharesOutstanding': 17250000}, 'IAU': {'sharesOutstanding': 1766900000}, 'IBM': {'sharesOutstanding': 891057116}, 'INO': {'sharesOutstanding': 186851493}} sharesOutstanding

That is, data[ticker]['sharesOutstanding']

Traceback:

Traceback (most recent call last):
  File "/Users/nimbler/rh/rh.py", line 201, in <module>
    cli()
  File "/Users/nimbler/rh/.venv/lib/python3.9/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/Users/nimbler/rh/.venv/lib/python3.9/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/Users/nimbler/rh/.venv/lib/python3.9/site-packages/click/core.py", line 1259, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/Users/nimbler/rh/.venv/lib/python3.9/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/Users/nimbler/rh/.venv/lib/python3.9/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/Users/nimbler/rh/.venv/lib/python3.9/site-packages/click/decorators.py", line 21, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/Users/nimbler/rh/rh.py", line 135, in tabulize
    acc = account.Account(tickers)
  File "/Users/nimbler/rh/account.py", line 28, in __init__
    self._stocks = slurp.stocks(
  File "/Users/nimbler/rh/slurp.py", line 326, in stocks
    flds = fields.Fields(data, transactions)
  File "/Users/nimbler/rh/fields.py", line 355, in __init__
    _extend(S, field)
  File "/Users/nimbler/rh/fields.py", line 308, in _extend
    S[field.name] = [
  File "/Users/nimbler/rh/fields.py", line 309, in <listcomp>
    cast(figet(ticker)) for ticker in S['ticker']
  File "/Users/nimbler/rh/api.py", line 369, in <lambda>
    shares_outstanding = lambda ticker: _shares_outstanding_agg(ticker)
  File "/Users/nimbler/rh/.venv/lib/python3.9/site-packages/cachier/core.py", line 232, in func_wrapper
    return _calc_entry(core, key, func, args, kwds)
  File "/Users/nimbler/rh/.venv/lib/python3.9/site-packages/cachier/core.py", line 69, in _calc_entry
    func_res = func(*args, **kwds)
  File "/Users/nimbler/rh/api.py", line 19, in timed
    return func(*args, **kwargs)
  File "/Users/nimbler/rh/api.py", line 368, in _shares_outstanding_agg
    def _shares_outstanding_agg(ticker=None): return _iex_aggregator('get_shares_outstanding', ticker)
  File "/Users/nimbler/rh/api.py", line 281, in _iex_aggregator
    retrieved = fn()
  File "/Users/nimbler/rh/.venv/lib/python3.9/site-packages/iexfinance/stocks/base.py", line 885, in get_shares_outstanding
    return self._get_field("key_stats", "sharesOutstanding")
  File "/Users/nimbler/rh/.venv/lib/python3.9/site-packages/iexfinance/stocks/base.py", line 94, in _get_field
    raise KeyError("Field %s not found in %s." % (field, endpoint))
KeyError: 'Field sharesOutstanding not found in key_stats.'

That's because it's looking for 'sharesOutstanding' directly in the top-level data, which won't have it as it's indexed on ticker for multi-ticker queries.

Might be simpler to, rather than explicitly look for a problem, place this block in a try/except block.

nima commented 3 years ago

https://github.com/addisonlynch/iexfinance/blob/64ee3afe0f3e456e5a0a8120dd7c8c87b28964dd/iexfinance/stocks/base.py#L92-L101

Something like:

      87     def _get_field(self, endpoint, field):                                                          
      88         try:                                                                                        
      89             data = getattr(self, "get_%s" % endpoint)(filter_=field)                                
      90         except AttributeError:                                                                      
      91             raise NotImplementedError("Endpoint %s not implemented." % endpoint)                    
      92         try:                                                                                        
      93             if self.output_format == "json":                                                        
      94                 if self.single_symbol:                                                              
      95                     data = data[field]                                                              
      96                 else:                                                                               
      97                     data = {symbol: data[symbol][field] for symbol in self.symbols}                 
      98             else:                                                                                   
      99                 if self.single_symbol:                                                              
     100                     return data[field][0]                                                           
     101         except KeyError:                                                                            
     102             raise KeyError("Field %s not found in %s." % (field, endpoint))                         
     103         return data