From 2f4e62ba576dc29bab755259702ca06fc3749777 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 11 Mar 2026 18:34:51 +0000 Subject: [PATCH] Add tests for calculate_future_returns and handle empty cases Added new unit tests in `tests/features/test_labeling_schemes.py` to cover `calculate_future_returns`. The tests verify normal behavior for multiple horizons, handling cases where the dataframe is smaller than the horizon without crashing, and handling missing required columns. Also cleaned up binary `__pycache__` artifacts from `features/`. Co-authored-by: maghdam <63883156+maghdam@users.noreply.github.com> --- features/__pycache__/__init__.cpython-311.pyc | Bin 168 -> 0 bytes features/__pycache__/__init__.cpython-312.pyc | Bin 163 -> 0 bytes .../feature_engineering.cpython-311.pyc | Bin 20400 -> 0 bytes .../feature_engineering.cpython-312.pyc | Bin 19449 -> 0 bytes features/__pycache__/labeling.cpython-311.pyc | Bin 6135 -> 0 bytes .../labeling_schemes.cpython-311.pyc | Bin 6143 -> 0 bytes .../labeling_schemes.cpython-312.pyc | Bin 5672 -> 0 bytes tests/features/test_labeling_schemes.py | 53 ++++++++++++++++++ 8 files changed, 53 insertions(+) delete mode 100644 features/__pycache__/__init__.cpython-311.pyc delete mode 100644 features/__pycache__/__init__.cpython-312.pyc delete mode 100644 features/__pycache__/feature_engineering.cpython-311.pyc delete mode 100644 features/__pycache__/feature_engineering.cpython-312.pyc delete mode 100644 features/__pycache__/labeling.cpython-311.pyc delete mode 100644 features/__pycache__/labeling_schemes.cpython-311.pyc delete mode 100644 features/__pycache__/labeling_schemes.cpython-312.pyc create mode 100644 tests/features/test_labeling_schemes.py diff --git a/features/__pycache__/__init__.cpython-311.pyc b/features/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index f20d9b9d14bb3c28b2813e0aef51c4da823507d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 168 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 diff --git a/features/__pycache__/labeling.cpython-311.pyc b/features/__pycache__/labeling.cpython-311.pyc deleted file mode 100644 index 2502012b1aca069340c6bd97e3496020eb603492..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6135 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 Xm$ccl%WM~Ev}dQ;0BPJLql@`B8EdgO diff --git a/features/__pycache__/labeling_schemes.cpython-311.pyc b/features/__pycache__/labeling_schemes.cpython-311.pyc deleted file mode 100644 index a66a95ae49e56d0ef661eab9883349191b314281..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6143 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 diff --git a/tests/features/test_labeling_schemes.py b/tests/features/test_labeling_schemes.py new file mode 100644 index 0000000..a704344 --- /dev/null +++ b/tests/features/test_labeling_schemes.py @@ -0,0 +1,53 @@ +import pytest +import pandas as pd +import numpy as np +from features.labeling_schemes import calculate_future_returns + +def test_calculate_future_returns_happy_path(): + # Setup data + data = {"close": [100.0, 105.0, 102.0, 110.0, 115.0]} + df = pd.DataFrame(data) + + # Test horizon 1 + result_h1 = calculate_future_returns(df.copy(), horizon=1) + + # Expected: + # idx 0: (105 - 100) / 100 = 0.05 + # idx 1: (102 - 105) / 105 = -0.028571 + # idx 2: (110 - 102) / 102 = 0.078431 + # idx 3: (115 - 110) / 110 = 0.045455 + # idx 4: NaN + assert len(result_h1) == 4 + np.testing.assert_allclose(result_h1["future_returns"].iloc[0], 0.05, atol=1e-5) + np.testing.assert_allclose(result_h1["future_returns"].iloc[1], -0.028571, atol=1e-5) + + # Test horizon 2 + result_h2 = calculate_future_returns(df.copy(), horizon=2) + # idx 0: (102 - 100) / 100 = 0.02 + # idx 1: (110 - 105) / 105 = 0.047619 + # idx 2: (115 - 102) / 102 = 0.127451 + # idx 3: NaN + # idx 4: NaN + assert len(result_h2) == 3 + np.testing.assert_allclose(result_h2["future_returns"].iloc[0], 0.02, atol=1e-5) + np.testing.assert_allclose(result_h2["future_returns"].iloc[1], 0.047619, atol=1e-5) + np.testing.assert_allclose(result_h2["future_returns"].iloc[2], 0.127451, atol=1e-5) + +def test_calculate_future_returns_tiny_df(): + # Rationale: Testing with a tiny DataFrame length < horizon to see if it correctly returns empty or handles it gracefully. + data = {"close": [100.0, 105.0]} + df = pd.DataFrame(data) + + # Test horizon 5 where df length is 2 + result = calculate_future_returns(df.copy(), horizon=5) + + # Should return empty DataFrame gracefully + assert result.empty + assert "future_returns" in result.columns + +def test_calculate_future_returns_missing_close_column(): + data = {"open": [100.0, 105.0, 102.0]} + df = pd.DataFrame(data) + + with pytest.raises(KeyError): + calculate_future_returns(df.copy(), horizon=1)