Skip to content

Commit

Permalink
Merge pull request #1157 from fredrik-corneliusson/fix-fundamentals-r…
Browse files Browse the repository at this point in the history
…egression-bug

Fix fundamentals regression bug
  • Loading branch information
ValueRaider committed Nov 11, 2022
2 parents 43aae83 + fff8e91 commit 2a0e149
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 22 deletions.
48 changes: 34 additions & 14 deletions tests/ticker.py
@@ -1,28 +1,40 @@
"""
Tests for Ticker
To run all tests in suite from commandline:
python -m unittest tests.ticker
Specific test class:
python -m unittest tests.ticker.TestTicker
"""
import pandas as pd

from .context import yfinance as yf

import unittest
import requests_cache

log_requests = False
# Set this to see the exact requests that are made during tests
DEBUG_LOG_REQUESTS = True

if log_requests:
if DEBUG_LOG_REQUESTS:
import logging

logging.basicConfig(level=logging.DEBUG)

# Create temp session
import requests_cache, tempfile
td = tempfile.TemporaryDirectory()

class TestTicker(unittest.TestCase):
def setUp(self):
global td
self.td = td
self.session = requests_cache.CachedSession(self.td.name + '/' + "yfinance.cache")
session = None

def tearDown(self):
self.session.close()
@classmethod
def setUpClass(cls):
cls.session = requests_cache.CachedSession()

@classmethod
def tearDownClass(cls):
if cls.session is not None:
cls.session.close()

def test_getTz(self):
tkrs = ["IMP.JO", "BHG.JO", "SSW.JO", "BP.L", "INTC"]
Expand Down Expand Up @@ -74,6 +86,7 @@ def test_badTicker(self):
dat.earnings_dates
dat.earnings_forecasts


class TestTickerEarnings(unittest.TestCase):

def setUp(self):
Expand Down Expand Up @@ -173,33 +186,41 @@ def tearDown(self):
self.ticker = None

def test_balance_sheet(self):
expected_row = "TotalAssets"
data = self.ticker.balance_sheet
self.assertIsInstance(data, pd.DataFrame, "data has wrong type")
self.assertFalse(data.empty, "data is empty")
self.assertIn(expected_row, data.index, "Did not find expected row in index")

data_cached = self.ticker.balance_sheet
self.assertIs(data, data_cached, "data not cached")

def test_quarterly_balance_sheet(self):
expected_row = "TotalAssets"
data = self.ticker.quarterly_balance_sheet
self.assertIsInstance(data, pd.DataFrame, "data has wrong type")
self.assertFalse(data.empty, "data is empty")
self.assertIn(expected_row, data.index, "Did not find expected row in index")

data_cached = self.ticker.quarterly_balance_sheet
self.assertIs(data, data_cached, "data not cached")

def test_cashflow(self):
expected_row = "OperatingCashFlow"
data = self.ticker.cashflow
self.assertIsInstance(data, pd.DataFrame, "data has wrong type")
self.assertFalse(data.empty, "data is empty")
self.assertIn(expected_row, data.index, "Did not find expected row in index")

data_cached = self.ticker.cashflow
self.assertIs(data, data_cached, "data not cached")

def test_quarterly_cashflow(self):
expected_row = "OperatingCashFlow"
data = self.ticker.quarterly_cashflow
self.assertIsInstance(data, pd.DataFrame, "data has wrong type")
self.assertFalse(data.empty, "data is empty")
self.assertIn(expected_row, data.index, "Did not find expected row in index")

data_cached = self.ticker.quarterly_cashflow
self.assertIs(data, data_cached, "data not cached")
Expand Down Expand Up @@ -271,10 +292,9 @@ def suite():
suite.addTest(TestTicker('Test ticker'))
suite.addTest(TestTickerEarnings('Test earnings'))
suite.addTest(TestTickerHolders('Test holders'))
suite.addTest(TestTickerMiscFinancials('Test balance sheet'))
suite.addTest(TestTickerMiscFinancials('Test misc financials'))
return suite


if __name__ == '__main__':
runner = unittest.TextTestRunner()
runner.run(suite())
unittest.main()
10 changes: 6 additions & 4 deletions yfinance/base.py
Expand Up @@ -877,7 +877,7 @@ def cleanup(data):

# generic patterns
for name in ["income", "balance-sheet", "cash-flow"]:
annual, qtr = self._create_financials_table(name, financials_data, proxy)
annual, qtr = self._create_financials_table(name, proxy)
if annual is not None:
self._financials[name]["yearly"] = annual
if qtr is not None:
Expand Down Expand Up @@ -985,7 +985,7 @@ def cleanup(data):

self._fundamentals = True

def _create_financials_table(self, name, financials_data, proxy):
def _create_financials_table(self, name, proxy):
acceptable_names = ["income", "balance-sheet", "cash-flow"]
if not name in acceptable_names:
raise Exception("name '{}' must be one of: {}".format(name, acceptable_names))
Expand All @@ -994,15 +994,17 @@ def _create_financials_table(self, name, financials_data, proxy):
# Yahoo stores the 'income' table internally under 'financials' key
name = "financials"

ticker_url = "{}/{}".format(self._scrape_url, self.ticker)
data_stores = self._data.get_json_data_stores(ticker_url + '/' + name, proxy)
_stmt_annual = None
_stmt_qtr = None
try:
# Developers note: TTM and template stuff allows for reproducing the nested structure
# visible on Yahoo website. But more work needed to make it user-friendly! Ideally
# return a tree data structure instead of Pandas MultiIndex
# So until this is implemented, just return simple tables
_stmt_annual = self._data.get_financials_time_series(name, "annual", financials_data, proxy)
_stmt_qtr = self._data.get_financials_time_series(name, "quarterly", financials_data, proxy)
_stmt_annual = self._data.get_financials_time_series("annual", data_stores, proxy)
_stmt_qtr = self._data.get_financials_time_series("quarterly", data_stores, proxy)

# template_ttm_order, template_annual_order, template_order, level_detail = utils.build_template(data_store["FinancialTemplateStore"])
# TTM_dicts, Annual_dicts = utils.retreive_financial_details(data_store['QuoteTimeSeriesStore'])
Expand Down
5 changes: 1 addition & 4 deletions yfinance/data.py
Expand Up @@ -86,11 +86,8 @@ def get_json_data_stores(self, url, proxy=None):
return json.loads(new_data)

# Note cant use lru_cache as financials_data is a nested dict (freezeargs only handle flat dicts)
def get_financials_time_series(self, name, timescale, financials_data, proxy=None):
def get_financials_time_series(self, timescale, financials_data, proxy=None):

acceptable_names = ["financials", "balance-sheet", "cash-flow"]
if name not in acceptable_names:
raise Exception("name '{}' must be one of: {}".format(name, acceptable_names))
acceptable_timestamps = ["annual", "quarterly"]
if timescale not in acceptable_timestamps:
raise Exception("timescale '{}' must be one of: {}".format(timescale, acceptable_timestamps))
Expand Down

0 comments on commit 2a0e149

Please sign in to comment.