Skip to content

Commit b837c1e

Browse files
authored
Merge pull request #1834 from ranaroussi/dev
sync dev -> main
2 parents 176c3d6 + 2630c66 commit b837c1e

File tree

8 files changed

+66
-30
lines changed

8 files changed

+66
-30
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ $ pip install yfinance --upgrade --no-cache-dir
5555
To install with optional dependencies, replace `optional` with: `nospam` for [caching-requests](#smarter-scraping), `repair` for [price repair](https://github.com/ranaroussi/yfinance/wiki/Price-repair), or `nospam,repair` for both:
5656

5757
``` {.sourceCode .bash}
58-
$ pip install yfinance[optional]
58+
$ pip install "yfinance[optional]"
5959
```
6060

6161
[Required dependencies](./requirements.txt) , [all dependencies](./setup.py#L62).

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ pytz>=2022.5
88
frozendict>=2.3.4
99
beautifulsoup4>=4.11.1
1010
html5lib>=1.1
11-
peewee>=3.16.2
11+
peewee>=3.16.2

tests/ticker.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,24 @@ def test_badTicker(self):
112112
dat.fast_info[k]
113113

114114
for attribute_name, attribute_type in ticker_attributes:
115-
assert_attribute_type(self, dat, attribute_name, attribute_type)
116-
115+
assert_attribute_type(self, dat, attribute_name, attribute_type)
116+
117+
with self.assertRaises(YFNotImplementedError):
118+
assert isinstance(dat.earnings, pd.Series)
119+
assert dat.earnings.empty
120+
assert isinstance(dat.dividends, pd.Series)
121+
assert dat.dividends.empty
122+
assert isinstance(dat.splits, pd.Series)
123+
assert dat.splits.empty
124+
assert isinstance(dat.capital_gains, pd.Series)
125+
assert dat.capital_gains.empty
126+
with self.assertRaises(YFNotImplementedError):
127+
assert isinstance(dat.shares, pd.DataFrame)
128+
assert dat.shares.empty
129+
assert isinstance(dat.actions, pd.DataFrame)
130+
assert dat.actions.empty
131+
132+
117133
def test_goodTicker(self):
118134
# that yfinance works when full api is called on same instance of ticker
119135

yfinance/base.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import logging
2828
import time as _time
2929
import warnings
30-
from typing import Optional
30+
from typing import Optional, Union
3131
from urllib.parse import quote as urlencode
3232

3333
import dateutil as _dateutil
@@ -42,7 +42,10 @@
4242
from .scrapers.holders import Holders
4343
from .scrapers.quote import Quote, FastInfo
4444

45-
from .const import _BASE_URL_, _ROOT_URL_
45+
from .const import _BASE_URL_, _ROOT_URL_, price_colnames
46+
47+
48+
_empty_series = pd.Series()
4649

4750

4851
class TickerBase:
@@ -426,7 +429,9 @@ def history(self, period="1mo", interval="1d",
426429
if not actions:
427430
df = df.drop(columns=["Dividends", "Stock Splits", "Capital Gains"], errors='ignore')
428431
if not keepna:
429-
mask_nan_or_zero = (df.isna() | (df == 0)).all(axis=1)
432+
data_colnames = price_colnames + ['Volume'] + ['Dividends', 'Stock Splits', 'Capital Gains']
433+
data_colnames = [c for c in data_colnames if c in df.columns]
434+
mask_nan_or_zero = (df[data_colnames].isna() | (df[data_colnames] == 0)).all(axis=1)
430435
df = df.drop(mask_nan_or_zero.index[mask_nan_or_zero])
431436

432437
logger.debug(f'{self.ticker}: yfinance returning OHLC: {df.index[0]} -> {df.index[-1]}')
@@ -455,7 +460,7 @@ def _reconstruct_intervals_batch(self, df, interval, prepost, tag=-1):
455460
else:
456461
intraday = True
457462

458-
price_cols = [c for c in ["Open", "High", "Low", "Close", "Adj Close"] if c in df]
463+
price_cols = [c for c in price_colnames if c in df]
459464
data_cols = price_cols + ["Volume"]
460465

461466
# If interval is weekly then can construct with daily. But if smaller intervals then
@@ -1011,7 +1016,7 @@ def _fix_zeroes(self, df, interval, tz_exchange, prepost):
10111016
elif df2.index.tz != tz_exchange:
10121017
df2.index = df2.index.tz_convert(tz_exchange)
10131018

1014-
price_cols = [c for c in ["Open", "High", "Low", "Close", "Adj Close"] if c in df2.columns]
1019+
price_cols = [c for c in price_colnames if c in df2.columns]
10151020
f_prices_bad = (df2[price_cols] == 0.0) | df2[price_cols].isna()
10161021
df2_reserve = None
10171022
if intraday:
@@ -1916,7 +1921,7 @@ def get_balance_sheet(self, proxy=None, as_dict=False, pretty=False, freq="yearl
19161921
def get_balancesheet(self, proxy=None, as_dict=False, pretty=False, freq="yearly"):
19171922
return self.get_balance_sheet(proxy, as_dict, pretty, freq)
19181923

1919-
def get_cash_flow(self, proxy=None, as_dict=False, pretty=False, freq="yearly"):
1924+
def get_cash_flow(self, proxy=None, as_dict=False, pretty=False, freq="yearly") -> Union[pd.DataFrame, dict]:
19201925
"""
19211926
:Parameters:
19221927
as_dict: bool
@@ -1946,31 +1951,31 @@ def get_cash_flow(self, proxy=None, as_dict=False, pretty=False, freq="yearly"):
19461951
def get_cashflow(self, proxy=None, as_dict=False, pretty=False, freq="yearly"):
19471952
return self.get_cash_flow(proxy, as_dict, pretty, freq)
19481953

1949-
def get_dividends(self, proxy=None):
1954+
def get_dividends(self, proxy=None) -> pd.Series:
19501955
if self._history is None:
19511956
self.history(period="max", proxy=proxy)
19521957
if self._history is not None and "Dividends" in self._history:
19531958
dividends = self._history["Dividends"]
19541959
return dividends[dividends != 0]
1955-
return []
1960+
return pd.Series()
19561961

1957-
def get_capital_gains(self, proxy=None):
1962+
def get_capital_gains(self, proxy=None) -> pd.Series:
19581963
if self._history is None:
19591964
self.history(period="max", proxy=proxy)
19601965
if self._history is not None and "Capital Gains" in self._history:
19611966
capital_gains = self._history["Capital Gains"]
19621967
return capital_gains[capital_gains != 0]
1963-
return []
1968+
return _empty_series
19641969

1965-
def get_splits(self, proxy=None):
1970+
def get_splits(self, proxy=None) -> pd.Series:
19661971
if self._history is None:
19671972
self.history(period="max", proxy=proxy)
19681973
if self._history is not None and "Stock Splits" in self._history:
19691974
splits = self._history["Stock Splits"]
19701975
return splits[splits != 0]
1971-
return []
1976+
return pd.Series()
19721977

1973-
def get_actions(self, proxy=None):
1978+
def get_actions(self, proxy=None) -> pd.Series:
19741979
if self._history is None:
19751980
self.history(period="max", proxy=proxy)
19761981
if self._history is not None and "Dividends" in self._history and "Stock Splits" in self._history:
@@ -1979,9 +1984,9 @@ def get_actions(self, proxy=None):
19791984
action_columns.append("Capital Gains")
19801985
actions = self._history[action_columns]
19811986
return actions[actions != 0].dropna(how='all').fillna(0)
1982-
return []
1987+
return _empty_series
19831988

1984-
def get_shares(self, proxy=None, as_dict=False):
1989+
def get_shares(self, proxy=None, as_dict=False) -> Union[pd.DataFrame, dict]:
19851990
self._fundamentals.proxy = proxy or self.proxy
19861991
data = self._fundamentals.shares
19871992
if as_dict:
@@ -2078,7 +2083,7 @@ def get_isin(self, proxy=None) -> Optional[str]:
20782083
self._isin = data.split(search_str)[1].split('"')[0].split('|')[0]
20792084
return self._isin
20802085

2081-
def get_news(self, proxy=None):
2086+
def get_news(self, proxy=None) -> list:
20822087
if self._news:
20832088
return self._news
20842089

yfinance/cache.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,14 @@ def initialise(self):
145145

146146
db.connect()
147147
tz_db_proxy.initialize(db)
148-
db.create_tables([_KV])
148+
try:
149+
db.create_tables([_KV])
150+
except _peewee.OperationalError as e:
151+
if 'WITHOUT' in str(e):
152+
_KV._meta.without_rowid = False
153+
db.create_tables([_KV])
154+
else:
155+
raise
149156
self.initialised = 1 # success
150157

151158
def lookup(self, key):
@@ -344,7 +351,14 @@ def initialise(self):
344351

345352
db.connect()
346353
Cookie_db_proxy.initialize(db)
347-
db.create_tables([_CookieSchema])
354+
try:
355+
db.create_tables([_CookieSchema])
356+
except _peewee.OperationalError as e:
357+
if 'WITHOUT' in str(e):
358+
_CookieSchema._meta.without_rowid = False
359+
db.create_tables([_CookieSchema])
360+
else:
361+
raise
348362
self.initialised = 1 # success
349363

350364
def lookup(self, strategy):

yfinance/multi.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,11 +217,11 @@ def download(tickers, start=None, end=None, actions=False, threads=True, ignore_
217217

218218
try:
219219
data = _pd.concat(shared._DFS.values(), axis=1, sort=True,
220-
keys=shared._DFS.keys())
220+
keys=shared._DFS.keys(), names=['Ticker', 'Price'])
221221
except Exception:
222222
_realign_dfs()
223223
data = _pd.concat(shared._DFS.values(), axis=1, sort=True,
224-
keys=shared._DFS.keys())
224+
keys=shared._DFS.keys(), names=['Ticker', 'Price'])
225225
data.index = _pd.to_datetime(data.index)
226226
# switch names back to isins if applicable
227227
data.rename(columns=shared._ISINS, inplace=True)

yfinance/scrapers/quote.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -733,10 +733,11 @@ def _fetch_complementary(self, proxy):
733733

734734
json_str = self._data.cache_get(url=url, proxy=proxy).text
735735
json_data = json.loads(json_str)
736-
if json_data["timeseries"]["error"] is not None:
737-
raise YFinanceException("Failed to parse json response from Yahoo Finance: " + json_data["error"])
736+
json_result = json_data.get("timeseries") or json_data.get("finance")
737+
if json_result["error"] is not None:
738+
raise YFinanceException("Failed to parse json response from Yahoo Finance: " + str(json_result["error"]))
738739
for k in keys:
739-
keydict = json_data["timeseries"]["result"][0]
740+
keydict = json_result["result"][0]
740741
if k in keydict:
741742
self._info[k] = keydict[k][-1]["reportedValue"]["raw"]
742743
else:

yfinance/ticker.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def dividends(self) -> _pd.Series:
134134
return self.get_dividends()
135135

136136
@property
137-
def capital_gains(self):
137+
def capital_gains(self) -> _pd.Series:
138138
return self.get_capital_gains()
139139

140140
@property
@@ -146,7 +146,7 @@ def actions(self) -> _pd.DataFrame:
146146
return self.get_actions()
147147

148148
@property
149-
def shares(self) -> _pd.DataFrame :
149+
def shares(self) -> _pd.DataFrame:
150150
return self.get_shares()
151151

152152
@property
@@ -259,7 +259,7 @@ def options(self) -> tuple:
259259
return tuple(self._expirations.keys())
260260

261261
@property
262-
def news(self):
262+
def news(self) -> list:
263263
return self.get_news()
264264

265265
@property

0 commit comments

Comments
 (0)