1- import pytest
2- import polars as pl
3- import inspect
41import importlib
2+ import inspect
53import pkgutil
4+
5+ import polars as pl
6+ import pytest
7+
68from project_x_py .indicators .base import BaseIndicator
79
10+
811def _concrete_indicator_classes ():
912 # Recursively discover all non-abstract subclasses of BaseIndicator in project_x_py.indicators.*
1013 import project_x_py .indicators
14+
1115 seen = set ()
1216 result = []
1317
@@ -27,7 +31,9 @@ def onclass(cls):
2731 result .append (cls )
2832
2933 # Walk all modules in project_x_py.indicators package
30- for finder , name , ispkg in pkgutil .walk_packages (project_x_py .indicators .__path__ , project_x_py .indicators .__name__ + "." ):
34+ for _ , name , _ in pkgutil .walk_packages (
35+ project_x_py .indicators .__path__ , project_x_py .indicators .__name__ + "."
36+ ):
3137 try :
3238 mod = importlib .import_module (name )
3339 except Exception :
@@ -37,7 +43,10 @@ def onclass(cls):
3743 # Remove duplicates, sort by class name for determinism
3844 return sorted (set (result ), key = lambda cls : cls .__name__ )
3945
40- @pytest .mark .parametrize ("indicator_cls" , _concrete_indicator_classes (), ids = lambda cls : cls .__name__ )
46+
47+ @pytest .mark .parametrize (
48+ "indicator_cls" , _concrete_indicator_classes (), ids = lambda cls : cls .__name__
49+ )
4150def test_indicator_calculate_adds_new_column (indicator_cls , sample_ohlcv_df ):
4251 """
4352 For every indicator class: instantiate with default ctor, call .calculate() or __call__ on sample data.
@@ -53,17 +62,23 @@ def test_indicator_calculate_adds_new_column(indicator_cls, sample_ohlcv_df):
5362 except Exception :
5463 out_df = instance .calculate (sample_ohlcv_df )
5564
56- assert isinstance (out_df , pl .DataFrame ), f"{ indicator_cls .__name__ } output is not a polars.DataFrame"
65+ assert isinstance (out_df , pl .DataFrame ), (
66+ f"{ indicator_cls .__name__ } output is not a polars.DataFrame"
67+ )
5768 assert out_df .height == sample_ohlcv_df .height , (
5869 f"{ indicator_cls .__name__ } output row count { out_df .height } != input { sample_ohlcv_df .height } "
5970 )
6071 new_cols = set (out_df .columns ) - input_cols
6172 assert new_cols , f"{ indicator_cls .__name__ } did not add any new columns"
6273
74+
6375def _get_new_column_names (indicator_cls , input_cols , df ):
6476 return set (df .columns ) - set (input_cols )
6577
66- @pytest .mark .parametrize ("indicator_cls" , _concrete_indicator_classes (), ids = lambda cls : cls .__name__ )
78+
79+ @pytest .mark .parametrize (
80+ "indicator_cls" , _concrete_indicator_classes (), ids = lambda cls : cls .__name__
81+ )
6782def test_indicator_caching_returns_same_object (indicator_cls , sample_ohlcv_df ):
6883 """
6984 Calling the indicator twice with the same df on the same instance should return the exact same DataFrame object (proves internal cache).
@@ -74,4 +89,4 @@ def test_indicator_caching_returns_same_object(indicator_cls, sample_ohlcv_df):
7489 out2 = instance (sample_ohlcv_df )
7590 assert out1 is out2 , (
7691 f"{ indicator_cls .__name__ } did not return identical object on repeated call (cache broken?)"
77- )
92+ )
0 commit comments