diff --git a/scripts/bitcoin_m2_chart.py b/scripts/bitcoin_m2_chart.py index 5f9d8a1..3ed5268 100644 --- a/scripts/bitcoin_m2_chart.py +++ b/scripts/bitcoin_m2_chart.py @@ -351,11 +351,20 @@ def main(argv: list[str] | None = None) -> plt.Figure: # 15) FETCH SERIES # ────────────────────────────────────────────────────────────────────────── try: - btc, m2 = fetch_series(start_dt, end_dt, args.btc_series, args.m2_series, args.db) + btc, m2 = fetch_series( + start_dt, end_dt, args.btc_series, args.m2_series, args.db + ) except FileNotFoundError as fnf: print(fnf, file=sys.stderr) sys.exit(1) + # ────────────────────────────────────────────────────────────────────────── + # DROP ANY ROWS WITH NULL "value" BEFORE VALIDATION + # ────────────────────────────────────────────────────────────────────────── + # (GLOBAL_M2 often has one or two trailing NaNs for "unpublished" months.) + btc = btc.dropna(subset=["value"]) + m2 = m2.dropna(subset=["value"]) + # If either series is empty, print a friendly message and exit: if btc.empty: print( diff --git a/tests/test_bitcoin_m2.py b/tests/test_bitcoin_m2.py index 473807b..49ce8a4 100644 --- a/tests/test_bitcoin_m2.py +++ b/tests/test_bitcoin_m2.py @@ -12,15 +12,25 @@ from scripts.bitcoin_m2_chart import plot_bitcoin_m2, main as btc_m2_main def sample_data(): - dates = pd.date_range("2021-01-01", periods=5, freq="D") - btc = pd.DataFrame({"value": [10, 11, 12, 13, 14]}, index=dates) - m2 = pd.DataFrame({"value": [1, 2, 3, 4, 5]}, index=dates) + btc_dates = pd.date_range("2024-07-01", periods=10, freq="D") + btc = pd.DataFrame({"value": range(10, 20)}, index=btc_dates) + m2_dates = btc_dates + m2 = pd.DataFrame({"value": range(1, 11)}, index=m2_dates) + return btc, m2 + + +def sample_data_with_nan(): + btc_dates = pd.date_range("2024-07-01", periods=10, freq="D") + btc = pd.DataFrame({"value": range(10, 20)}, index=btc_dates) + m2_dates = btc_dates + m2_values = list(range(1, 10)) + [pd.NA] + m2 = pd.DataFrame({"value": m2_values}, index=m2_dates) return btc, m2 def test_plot_bitcoin_m2_returns_figure(): btc, m2 = sample_data() - fig = plot_bitcoin_m2(btc, m2, 94, 1) + fig = plot_bitcoin_m2(btc, m2, 0, 1) assert len(fig.axes) == 2 assert any(ax.get_lines() for ax in fig.axes) @@ -32,19 +42,44 @@ def test_btc_m2_main(monkeypatch): return btc, m2 monkeypatch.setattr("scripts.bitcoin_m2_chart.fetch_series", fake_fetch) - fig = btc_m2_main([ - "--btc-series", - "BTC", - "--m2-series", - "M2", - "--width", - "8", - "--height", - "4", - "--dpi", - "150", - "--no-show", - ]) + fig = btc_m2_main( + [ + "--btc-series", + "BTC", + "--m2-series", + "M2", + "--offset", + "0", + "--width", + "8", + "--height", + "4", + "--dpi", + "150", + "--no-show", + ] + ) + assert len(fig.axes) == 2 + + +def test_main_handles_trailing_nan(monkeypatch): + btc, m2 = sample_data_with_nan() + + def fake_fetch(*_args, **_kwargs): + return btc, m2 + + monkeypatch.setattr("scripts.bitcoin_m2_chart.fetch_series", fake_fetch) + fig = btc_m2_main( + [ + "--btc-series", + "BTC", + "--m2-series", + "M2", + "--offset", + "0", + "--no-show", + ] + ) assert len(fig.axes) == 2 @@ -64,6 +99,8 @@ def test_btc_m2_main_saves_output(tmp_path, monkeypatch): "M2", "--output", str(out_file), + "--offset", + "0", "--width", "8", "--height",