fix: stabilize yfinance dividend TTM fixtures (#2214)

This commit is contained in:
zhulinsen
2026-08-14 22:40:59 +08:00
committed by GitHub
parent 294210883c
commit 5159bd72e8

View File

@@ -8,13 +8,11 @@ graceful degradation when yfinance is unavailable.
""" """
from __future__ import annotations from __future__ import annotations
from datetime import datetime, timezone
import unittest import unittest
from datetime import datetime, timedelta from datetime import datetime, timezone
from unittest.mock import patch, MagicMock from unittest.mock import patch, MagicMock
import pandas as pd import pandas as pd
import pytz
from data_provider.yfinance_fundamental_adapter import ( from data_provider.yfinance_fundamental_adapter import (
YfinanceFundamentalAdapter, YfinanceFundamentalAdapter,
@@ -22,6 +20,14 @@ from data_provider.yfinance_fundamental_adapter import (
) )
_DIVIDEND_EVENT_DATES = (
"2025-08-11",
"2025-11-10",
"2026-02-09",
"2026-05-11",
)
def _build_mock_ticker( def _build_mock_ticker(
info: dict, info: dict,
income_stmt: pd.DataFrame | None = None, income_stmt: pd.DataFrame | None = None,
@@ -82,14 +88,6 @@ class TestYfinanceFundamentalAdapter(unittest.TestCase):
"trailingAnnualDividendRate": 1.04, "trailingAnnualDividendRate": 1.04,
"dividendYield": 0.36, "dividendYield": 0.36,
} }
income_df = pd.DataFrame(
{
pd.Timestamp("2026-03-31"): {"Total Revenue": 1.11e11, "Net Income": 2.95e10},
pd.Timestamp("2025-12-31"): {"Total Revenue": 1.24e11, "Net Income": 3.62e10},
pd.Timestamp("2025-09-30"): {"Total Revenue": 9.49e10, "Net Income": 2.49e10},
pd.Timestamp("2025-06-30"): {"Total Revenue": 9.40e10, "Net Income": 2.34e10},
}
)
# Need at least 5 columns to trigger statement-derived YoY. # Need at least 5 columns to trigger statement-derived YoY.
income_df_with_yoy = pd.DataFrame( income_df_with_yoy = pd.DataFrame(
{ {
@@ -106,18 +104,10 @@ class TestYfinanceFundamentalAdapter(unittest.TestCase):
pd.Timestamp("2025-12-31"): {"Operating Cash Flow": 3.5e10}, pd.Timestamp("2025-12-31"): {"Operating Cash Flow": 3.5e10},
} }
) )
# Use dates relative to now so the 365-day TTM window always
# contains all 4 events regardless of when the test runs (#2204).
now_ny = datetime.now(pytz.timezone("America/New_York"))
dividends = pd.Series( dividends = pd.Series(
[0.26, 0.26, 0.26, 0.27], [0.26, 0.26, 0.26, 0.27],
index=pd.DatetimeIndex( index=pd.DatetimeIndex(
[ _DIVIDEND_EVENT_DATES,
(now_ny - timedelta(days=330)).strftime("%Y-%m-%d"),
(now_ny - timedelta(days=240)).strftime("%Y-%m-%d"),
(now_ny - timedelta(days=150)).strftime("%Y-%m-%d"),
(now_ny - timedelta(days=60)).strftime("%Y-%m-%d"),
],
tz="America/New_York", tz="America/New_York",
), ),
name="Dividends", name="Dividends",
@@ -147,6 +137,7 @@ class TestYfinanceFundamentalAdapter(unittest.TestCase):
# info.dividendYield (0.36) is intentionally ignored when TTM cash exists. # info.dividendYield (0.36) is intentionally ignored when TTM cash exists.
self.assertAlmostEqual(div["ttm_dividend_yield_pct"], 0.3762, places=4) self.assertAlmostEqual(div["ttm_dividend_yield_pct"], 0.3762, places=4)
self.assertEqual(div["currency"], "USD") self.assertEqual(div["currency"], "USD")
self.assertEqual(div["events"][0]["ex_dividend_date"], "2026-05-11")
self.assertEqual( self.assertEqual(
bundle["belong_boards"], bundle["belong_boards"],
[ [
@@ -160,15 +151,8 @@ class TestYfinanceFundamentalAdapter(unittest.TestCase):
# Series. Without coercion, `.items()` yields (column_name, Series), every event # Series. Without coercion, `.items()` yields (column_name, Series), every event
# is dropped, and TTM silently falls back to the annual-rate estimate — the real # is dropped, and TTM silently falls back to the annual-rate estimate — the real
# bug seen on live US/HK/JP/KR/TW reports (24.0 / "0 次" instead of the true sum). # bug seen on live US/HK/JP/KR/TW reports (24.0 / "0 次" instead of the true sum).
# Use dates relative to now so the 365-day TTM window is always satisfied (#2204).
now_ny = datetime.now(pytz.timezone("America/New_York"))
idx = pd.DatetimeIndex( idx = pd.DatetimeIndex(
[ _DIVIDEND_EVENT_DATES,
(now_ny - timedelta(days=330)).strftime("%Y-%m-%d"),
(now_ny - timedelta(days=240)).strftime("%Y-%m-%d"),
(now_ny - timedelta(days=150)).strftime("%Y-%m-%d"),
(now_ny - timedelta(days=60)).strftime("%Y-%m-%d"),
],
tz="America/New_York", tz="America/New_York",
) )
dividends_df = pd.DataFrame({"Dividends": [0.26, 0.26, 0.26, 0.27]}, index=idx) dividends_df = pd.DataFrame({"Dividends": [0.26, 0.26, 0.26, 0.27]}, index=idx)
@@ -191,7 +175,7 @@ class TestYfinanceFundamentalAdapter(unittest.TestCase):
def test_ttm_dividend_window_uses_as_of_date_cutoff(self) -> None: def test_ttm_dividend_window_uses_as_of_date_cutoff(self) -> None:
idx = pd.DatetimeIndex( idx = pd.DatetimeIndex(
["2025-08-11", "2025-11-10", "2026-02-09", "2026-05-11"], _DIVIDEND_EVENT_DATES,
tz="America/New_York", tz="America/New_York",
) )
dividends = pd.Series([0.26, 0.26, 0.26, 0.27], index=idx, name="Dividends") dividends = pd.Series([0.26, 0.26, 0.26, 0.27], index=idx, name="Dividends")