mirror of
https://github.com/ranaroussi/yfinance.git
synced 2024-01-29 09:38:56 +03:00
Merge pull request #1773 from bot-unit/feature/upgrades_downgrades
add upgrades downgrades
This commit is contained in:
@@ -30,9 +30,11 @@ ticker_attributes = (
|
||||
("info", dict),
|
||||
("calendar", pd.DataFrame),
|
||||
("recommendations", Union[pd.DataFrame, dict]),
|
||||
("recommendations_summary", Union[pd.DataFrame, dict]),
|
||||
("upgrades_downgrades", Union[pd.DataFrame, dict]),
|
||||
("recommendations_history", Union[pd.DataFrame, dict]),
|
||||
("earnings", pd.DataFrame),
|
||||
("quarterly_earnings", pd.DataFrame),
|
||||
("recommendations_summary", Union[pd.DataFrame, dict]),
|
||||
("quarterly_cashflow", pd.DataFrame),
|
||||
("cashflow", pd.DataFrame),
|
||||
("quarterly_balance_sheet", pd.DataFrame),
|
||||
@@ -645,7 +647,7 @@ class TestTickerMiscFinancials(unittest.TestCase):
|
||||
data_cached = self.ticker.recommendations
|
||||
self.assertIs(data, data_cached, "data not cached")
|
||||
|
||||
# def test_recommendations_summary(self):
|
||||
# def test_recommendations_summary(self): # currently alias for recommendations
|
||||
# data = self.ticker.recommendations_summary
|
||||
# self.assertIsInstance(data, pd.DataFrame, "data has wrong type")
|
||||
# self.assertFalse(data.empty, "data is empty")
|
||||
@@ -653,6 +655,19 @@ class TestTickerMiscFinancials(unittest.TestCase):
|
||||
# data_cached = self.ticker.recommendations_summary
|
||||
# self.assertIs(data, data_cached, "data not cached")
|
||||
|
||||
def test_recommendations_history(self): # alias for upgrades_downgrades
|
||||
data = self.ticker.upgrades_downgrades
|
||||
data_history = self.ticker.recommendations_history
|
||||
self.assertTrue(data.equals(data_history))
|
||||
self.assertIsInstance(data, pd.DataFrame, "data has wrong type")
|
||||
self.assertFalse(data.empty, "data is empty")
|
||||
self.assertTrue(len(data.columns) == 4, "data has wrong number of columns")
|
||||
self.assertEqual(data.columns.values.tolist(), ['Firm', 'ToGrade', 'FromGrade', 'Action'], "data has wrong column names")
|
||||
self.assertIsInstance(data.index, pd.DatetimeIndex, "data has wrong index type")
|
||||
|
||||
data_cached = self.ticker.upgrades_downgrades
|
||||
self.assertIs(data, data_cached, "data not cached")
|
||||
|
||||
# def test_analyst_price_target(self):
|
||||
# data = self.ticker.analyst_price_target
|
||||
# self.assertIsInstance(data, pd.DataFrame, "data has wrong type")
|
||||
|
||||
@@ -1718,6 +1718,21 @@ class TickerBase:
|
||||
return data.to_dict()
|
||||
return data
|
||||
|
||||
def get_recommendations_summary(self, proxy=None, as_dict=False):
|
||||
return self.get_recommendations(proxy=proxy, as_dict=as_dict)
|
||||
|
||||
def get_upgrades_downgrades(self, proxy=None, as_dict=False):
|
||||
"""
|
||||
Returns a DataFrame with the recommendations changes (upgrades/downgrades)
|
||||
Index: date of grade
|
||||
Columns: firm toGrade fromGrade action
|
||||
"""
|
||||
self._quote.proxy = proxy or self.proxy
|
||||
data = self._quote.upgrades_downgrades
|
||||
if as_dict:
|
||||
return data.to_dict()
|
||||
return data
|
||||
|
||||
def get_calendar(self, proxy=None, as_dict=False):
|
||||
self._quote.proxy = proxy or self.proxy
|
||||
data = self._quote.calendar
|
||||
@@ -1770,9 +1785,6 @@ class TickerBase:
|
||||
return data.to_dict()
|
||||
return data
|
||||
|
||||
def get_recommendations_summary(self, proxy=None, as_dict=False):
|
||||
return self.get_recommendations(proxy=proxy, as_dict=as_dict)
|
||||
|
||||
def get_analyst_price_target(self, proxy=None, as_dict=False):
|
||||
self._analysis.proxy = proxy or self.proxy
|
||||
data = self._analysis.analyst_price_target
|
||||
|
||||
@@ -560,6 +560,7 @@ class Quote:
|
||||
self._retired_info = None
|
||||
self._sustainability = None
|
||||
self._recommendations = None
|
||||
self._upgrades_downgrades = None
|
||||
self._calendar = None
|
||||
|
||||
self._already_scraped = False
|
||||
@@ -591,6 +592,23 @@ class Quote:
|
||||
self._recommendations = pd.DataFrame(data)
|
||||
return self._recommendations
|
||||
|
||||
@property
|
||||
def upgrades_downgrades(self) -> pd.DataFrame:
|
||||
if self._upgrades_downgrades is None:
|
||||
result = self._fetch(self.proxy, modules=['upgradeDowngradeHistory'])
|
||||
try:
|
||||
data = result["quoteSummary"]["result"][0]["upgradeDowngradeHistory"]["history"]
|
||||
if len(data) == 0:
|
||||
raise YFinanceDataException(f"No upgrade/downgrade history found for {self._symbol}")
|
||||
df = pd.DataFrame(data)
|
||||
df.rename(columns={"epochGradeDate": "GradeDate", 'firm': 'Firm', 'toGrade': 'ToGrade', 'fromGrade': 'FromGrade', 'action': 'Action'}, inplace=True)
|
||||
df.set_index('GradeDate', inplace=True)
|
||||
df.index = pd.to_datetime(df.index, unit='s')
|
||||
self._upgrades_downgrades = df
|
||||
except (KeyError, IndexError):
|
||||
raise YFinanceDataException(f"Failed to parse json response from Yahoo Finance: {result}")
|
||||
return self._upgrades_downgrades
|
||||
|
||||
@property
|
||||
def calendar(self) -> pd.DataFrame:
|
||||
if self._calendar is None:
|
||||
|
||||
@@ -153,6 +153,18 @@ class Ticker(TickerBase):
|
||||
def recommendations(self):
|
||||
return self.get_recommendations()
|
||||
|
||||
@property
|
||||
def recommendations_summary(self):
|
||||
return self.get_recommendations_summary()
|
||||
|
||||
@property
|
||||
def upgrades_downgrades(self):
|
||||
return self.get_upgrades_downgrades()
|
||||
|
||||
@property
|
||||
def recommendations_history(self):
|
||||
return self.get_upgrades_downgrades()
|
||||
|
||||
@property
|
||||
def earnings(self) -> _pd.DataFrame:
|
||||
return self.get_earnings()
|
||||
@@ -217,10 +229,6 @@ class Ticker(TickerBase):
|
||||
def quarterly_cashflow(self) -> _pd.DataFrame:
|
||||
return self.quarterly_cash_flow
|
||||
|
||||
@property
|
||||
def recommendations_summary(self):
|
||||
return self.get_recommendations_summary()
|
||||
|
||||
@property
|
||||
def analyst_price_target(self) -> _pd.DataFrame:
|
||||
return self.get_analyst_price_target()
|
||||
|
||||
Reference in New Issue
Block a user