Merge pull request #1773 from bot-unit/feature/upgrades_downgrades

add upgrades downgrades
This commit is contained in:
ValueRaider
2023-12-13 20:59:58 +00:00
committed by GitHub
4 changed files with 62 additions and 9 deletions

View File

@@ -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")

View File

@@ -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

View File

@@ -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:

View File

@@ -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()