Fix tests

This commit is contained in:
ValueRaider
2026-05-23 14:46:39 +01:00
parent 0721ed0d9a
commit 54bef21e98
5 changed files with 86 additions and 51 deletions
+21 -21
View File
@@ -67,27 +67,27 @@ class TestPriceRepairAssumptions(unittest.TestCase):
if vol_match.all():
# print(" - volume match 100%")
pass
elif vol_match_ndiff == 1 and not vol_match[0]:
# print(" - volume almost-perfect match, only first different")
# debug = True
elif vol_match_ndiff == 1 and (not vol_match[-1]):
# Almost perfect, only last row different. Not my fault.
pass
else:
# print(f" - volume match {vol_match_nmatch}/{len(vol_match)} {vol_match.to_numpy()}")
print(f" - volume significantly different in first row: vol_diff_pct={vol_diff_pct*100}%")
print(f" - volume significantly different in first or last row: vol_diff_pct={vol_diff_pct*100}%")
debug = True
if debug:
print("- investigate:")
print(f" - tkr = {tkr}")
print(f" - interval = {interval}")
print(f" - period = {period}")
print("- df_truth:")
print(df_truth)#[['Open', 'Close', 'Volume']])
print(df_truth[['Open', 'Close', 'Volume']])
df_1d = dat.history(interval='1d', period=period)
print("- df_1d:")
print(df_1d)#[['Open', 'Close', 'Volume']])
print(df_1d[['Open', 'Close', 'Volume']])
print("- dfr:")
print(dfr)#[['Open', 'Close', 'Volume']])
return
print(dfr[['Open', 'Close', 'Volume']])
self.assertFalse(True)
@@ -356,7 +356,6 @@ class TestPriceRepair(unittest.TestCase):
self.assertTrue("Repaired?" in df_repaired.columns)
self.assertFalse(df_repaired["Repaired?"].isna().any())
@unittest.skip("Currently failing - need to investigate")
def test_repair_zeroes_daily(self):
tkr = "BBIL.L"
dat = yf.Ticker(tkr, session=self.session)
@@ -388,15 +387,13 @@ class TestPriceRepair(unittest.TestCase):
# Test that 'Adj Close' is reconstructed correctly,
# particularly when a dividend occurred within 1 day.
self.skipTest("Currently failing because Yahoo returning slightly different data for interval 1d vs 1h on day Aug 6 2024")
tkr = "INTC"
df = _pd.DataFrame(data={"Open": [2.020000e+01, 2.032000e+01, 1.992000e+01, 1.910000e+01, 2.008000e+01],
"High": [2.039000e+01, 2.063000e+01, 2.025000e+01, 2.055000e+01, 2.015000e+01],
"Low": [1.929000e+01, 1.975000e+01, 1.895000e+01, 1.884000e+01, 1.950000e+01],
"Close": [2.011000e+01, 1.983000e+01, 1.899000e+01, 2.049000e+01, 1.971000e+01],
"Adj Close": [1.998323e+01, 1.970500e+01, 1.899000e+01, 2.049000e+01, 1.971000e+01],
"Volume": [1.473857e+08, 1.066704e+08, 9.797230e+07, 9.683680e+07, 7.639450e+07],
df = _pd.DataFrame(data={"Open": [2.008000e+01, 1.910000e+01, 1.992000e+01, 2.032000e+01, 2.020000e+01],
"High": [2.015000e+01, 2.055000e+01, 2.025000e+01, 2.063000e+01, 2.039000e+01],
"Low": [1.950000e+01, 1.884000e+01, 1.895000e+01, 1.975000e+01, 1.929000e+01],
"Close": [1.971000e+01, 2.049000e+01, 1.899000e+01, 1.983000e+01, 2.011000e+01],
"Adj Close": [1.971000e+01, 2.049000e+01, 1.899000e+01, 1.970500e+01, 1.998323e+01],
"Volume": [7.639450e+07, 9.683680e+07, 9.797230e+07, 1.066704e+08, 1.473857e+08],
"Dividends": [0.000000e+00, 0.000000e+00, 1.250000e-01, 0.000000e+00, 0.000000e+00]},
index=_pd.to_datetime([_dt.datetime(2024, 8, 9),
_dt.datetime(2024, 8, 8),
@@ -422,9 +419,13 @@ class TestPriceRepair(unittest.TestCase):
try:
self.assertTrue(_np.isclose(df_slice_bad_repaired[c], df_slice[c], rtol=rtol).all())
except Exception:
print(f"# column = {c}")
print("# correct:") ; print(df_slice[c])
print("# repaired:") ; print(df_slice_bad_repaired[c])
df_slice_bad['Adj'] = df_slice_bad['Adj Close'] / df_slice_bad['Close']
df_slice_bad_repaired['Adj'] = df_slice_bad_repaired['Adj Close'] / df_slice_bad_repaired['Close']
df_slice['Adj'] = df_slice['Adj Close'] / df_slice['Close']
print(f"# column={c}, i={i}, j={j}")
print("# bad:") ; print(df_slice_bad[['Close', 'Adj Close', 'Adj', 'Dividends']])
print("# repaired:") ; print(df_slice_bad_repaired[['Close', 'Adj Close', 'Adj', 'Dividends']])
print("# correct:") ; print(df_slice[['Close', 'Adj Close', 'Adj', 'Dividends']])
raise
self.assertTrue("Repaired?" in df_slice_bad_repaired.columns)
self.assertFalse(df_slice_bad_repaired["Repaired?"].isna().any())
@@ -581,7 +582,6 @@ class TestPriceRepair(unittest.TestCase):
bad_tkrs.append('4063.T') # Div with same-day split not split adjusted
# Adj too small
bad_tkrs += ['ADIG.L']
bad_tkrs += ['CLC.L']
bad_tkrs += ['RGL.L']
bad_tkrs += ['SERE.L']
+1 -1
View File
@@ -48,7 +48,7 @@ class TestPriceHistory(unittest.TestCase):
def test_download_multi_small_interval(self):
use_tkrs = ["AAPL", "0Q3.DE", "ATVI"]
df = yf.download(use_tkrs, period="1d", interval="5m", auto_adjust=True)
self.assertEqual(df.index.tz, _dt.timezone.utc)
self.assertEqual(df.index.tz, _tz.timezone("America/New_York"))
def test_download_with_invalid_ticker(self):
#Checks if using an invalid symbol gives the same output as not using an invalid symbol in combination with a valid symbol (AAPL)
+56 -28
View File
@@ -20,7 +20,7 @@ from yfinance.config import YfConfig
import unittest
# import requests_cache
from unittest.mock import patch, MagicMock
from typing import Union, Any, get_args, _GenericAlias
from typing import Union, Any, get_args, get_origin
# from urllib.parse import urlparse, parse_qs, urlencode, urlunparse
ticker_attributes = (
@@ -62,17 +62,18 @@ ticker_attributes = (
def assert_attribute_type(testClass: unittest.TestCase, instance, attribute_name, expected_type):
try:
attribute = getattr(instance, attribute_name)
if attribute is not None and expected_type is not Any:
err_msg = f'{attribute_name} type is {type(attribute)} not {expected_type}'
if isinstance(expected_type, _GenericAlias) and expected_type.__origin__ is Union:
allowed_types = get_args(expected_type)
testClass.assertTrue(isinstance(attribute, allowed_types), err_msg)
else:
testClass.assertEqual(type(attribute), expected_type, err_msg)
except Exception:
testClass.assertRaises(
YFNotImplementedError, lambda: getattr(instance, attribute_name)
)
except YFNotImplementedError:
# Some attributes legitimately raise on missing/bad tickers.
return
if attribute is not None and expected_type is not Any:
err_msg = f'{attribute_name} type is {type(attribute)} not {expected_type}'
if get_origin(expected_type) is Union:
allowed_types = get_args(expected_type)
testClass.assertTrue(isinstance(attribute, allowed_types), err_msg)
else:
testClass.assertEqual(type(attribute), expected_type, err_msg)
class TestTicker(unittest.TestCase):
session = None
@@ -308,23 +309,50 @@ class TestTickerHistory(unittest.TestCase):
def test_download(self):
tomorrow = pd.Timestamp.now().date() + pd.Timedelta(days=1) # helps with caching
for t in [False, True]:
for i in [False, True]:
for m in [False, True]:
for threads in [False, True]:
for ignore_tz in [False, True]:
for mli in [False, True]:
for n in [1, 'all']:
symbols = self.symbols[0] if n == 1 else self.symbols
data = yf.download(symbols, end=tomorrow, session=self.session,
threads=t, ignore_tz=i, multi_level_index=m)
self.assertIsInstance(data, pd.DataFrame, "data has wrong type")
self.assertFalse(data.empty, "data is empty")
if i:
self.assertIsNone(data.index.tz)
else:
self.assertIsNotNone(data.index.tz)
if (not m) and n == 1:
self.assertFalse(isinstance(data.columns, pd.MultiIndex))
else:
self.assertIsInstance(data.columns, pd.MultiIndex)
for interval in ['1d', '1h']:
if n == 1:
symbols = self.symbols[0]
else:
# Add some other countries
symbols = self.symbols + ['BATS.L', '7974.T']
data = yf.download(symbols, end=tomorrow, session=self.session,
threads=threads, ignore_tz=ignore_tz, multi_level_index=mli,
interval=interval, progress=False)
self.assertIsInstance(data, pd.DataFrame, "data has wrong type")
self.assertFalse(data.empty, "data is empty")
if ignore_tz:
self.assertIsNone(data.index.tz)
else:
self.assertIsNotNone(data.index.tz)
self.assertEqual(str(data.index.tz), "America/New_York")
if (not mli) and n == 1:
self.assertFalse(isinstance(data.columns, pd.MultiIndex))
else:
self.assertIsInstance(data.columns, pd.MultiIndex)
if interval == '1d':
if ignore_tz:
self.assertTrue((data.index.hour == 0).all())
self.assertTrue((data.index.minute == 0).all())
else:
self.assertTrue((data.index.minute == 0).all())
if n == 1:
self.assertTrue((data.index.hour == 0).all())
else:
self.assertTrue((data.index.hour != 0).any())
elif interval == '1h':
hours = pd.Index(data.index.hour)
if ignore_tz:
self.assertTrue(((hours >= 7) & (hours <= 19)).all())
else:
if n == 1:
self.assertTrue(((hours >= 7) & (hours <= 19)).all())
else:
self.assertTrue((~((hours >= 7) & (hours <= 19))).any())
# Hopefully one day we find an equivalent "requests_cache" that works with "curl_cffi"
# def test_no_expensive_calls_introduced(self):
+1 -1
View File
@@ -424,7 +424,7 @@ class Calendars:
limit=limit,
offset=offset,
force=force,
)
).sort_values('Event Start Date', ascending=False)
@log_indent_decorator
def get_ipo_info_calendar(
+7
View File
@@ -654,6 +654,11 @@ class PriceHistory:
else:
df2 = df.resample(resample_period, label='left', closed='left', offset=offset).agg(resample_map)
df2.loc[df2['Stock Splits']==1.0, 'Stock Splits'] = 0.0
# Handle NaNs from very long holidays.
prev_close = df2['Close'].shift(1).ffill()
for c in ['Open', 'High', 'Low', 'Close']:
df2[c] = df2[c].fillna(prev_close)
return df2
@utils.log_indent_decorator
@@ -2944,6 +2949,8 @@ class PriceHistory:
def _calc_volume_zscore(volume, block):
# print(f"_calc_volume_zscore(volume={volume})")
values = block['Volume'].to_numpy()
if len(values) == 0 or (values == 0).all():
return 0
std = np.std(values, ddof=1)
if std == 0.0:
return 0