Refactors test data loading and improves metrics handling

Replaces global sample dataset with a function to provide fresh dataframes in tests, preventing unintended data sharing between test cases. Updates metric logic to return NaN for invalid parameters instead of printing and returning None, improving API consistency and testability.
This commit is contained in:
Golam Rashed
2025-08-02 21:03:54 +10:00
parent c5e232bd46
commit b4637079c5
23 changed files with 63 additions and 62 deletions
+3 -2
View File
@@ -39,8 +39,9 @@ def calmar_ratio(close: Series, method: str = "percent", years: int = 3) -> floa
>>> result = ta.calmar_ratio(close, method="percent", years=3)
"""
if years <= 0:
print(f"[!] calmar_ratio 'years' argument must be greater than zero.")
return
# Guard: years must be positive and nonzero
from numpy import nan as npNaN
return npNaN
close = verify_series(close)
n_years_ago = close.index[-1] - Timedelta(days=365.25 * years)
+11 -10
View File
@@ -9,16 +9,17 @@ INFO = f"[i]"
CORRELATION = "corr" # "sem"
CORRELATION_THRESHOLD = 0.99 # Less than 0.99 is undesirable
sample_data = read_csv(
f"data/SPY_D.csv",
index_col=0,
parse_dates=True,
infer_datetime_format=True,
keep_date_col=True,
)
sample_data.set_index(DatetimeIndex(sample_data["date"]), inplace=True, drop=True)
sample_data.drop("date", axis=1, inplace=True)
def get_sample_data():
df = read_csv(
"data/SPY_D.csv",
index_col=0,
parse_dates=True,
infer_datetime_format=True,
keep_date_col=True,
)
df.set_index(DatetimeIndex(df["date"]), inplace=True, drop=True)
df.drop("date", axis=1, inplace=True)
return df
def error_analysis(df, kind, msg, icon=INFO, newline=True):
if VERBOSE:
+2 -2
View File
@@ -1,4 +1,4 @@
from tests.config import sample_data
from tests.config import get_sample_data
from tests.context import pandas_ta_classic as pandas_ta
from unittest import TestCase, skip
@@ -8,7 +8,7 @@ from pandas import DataFrame
class TestCandleExtension(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
cls.data = get_sample_data()
@classmethod
def tearDownClass(cls):
+2 -2
View File
@@ -1,5 +1,5 @@
from pandas.core.series import Series
from tests.config import sample_data
from tests.config import get_sample_data
from tests.context import pandas_ta_classic as pandas_ta
from unittest import TestCase
@@ -9,7 +9,7 @@ from pandas import DataFrame
class TestCylesExtension(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
cls.data = get_sample_data()
@classmethod
def tearDownClass(cls):
+2 -2
View File
@@ -1,4 +1,4 @@
from tests.config import sample_data
from tests.config import get_sample_data
from tests.context import pandas_ta_classic as pandas_ta
from unittest import skip, TestCase
@@ -8,7 +8,7 @@ from pandas import DataFrame
class TestMomentumExtension(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
cls.data = get_sample_data()
@classmethod
def tearDownClass(cls):
+2 -2
View File
@@ -1,4 +1,4 @@
from tests.config import sample_data
from tests.config import get_sample_data
from tests.context import pandas_ta_classic as pandas_ta
from unittest import skip, TestCase
@@ -8,7 +8,7 @@ from pandas import DataFrame
class TestOverlapExtension(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
cls.data = get_sample_data()
@classmethod
def tearDownClass(cls):
+2 -2
View File
@@ -1,4 +1,4 @@
from tests.config import sample_data
from tests.config import get_sample_data
from tests.context import pandas_ta_classic as pandas_ta
from unittest import TestCase
@@ -8,7 +8,7 @@ from pandas import DataFrame
class TestPerformaceExtension(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
cls.data = get_sample_data()
cls.islong = cls.data["close"] > pandas_ta.sma(cls.data["close"], length=50)
@classmethod
+2 -2
View File
@@ -1,4 +1,4 @@
from tests.config import sample_data
from tests.config import get_sample_data
from tests.context import pandas_ta_classic as pandas_ta
from unittest import skip, TestCase
@@ -8,7 +8,7 @@ from pandas import DataFrame
class TestStatisticsExtension(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
cls.data = get_sample_data()
@classmethod
def tearDownClass(cls):
+2 -2
View File
@@ -1,4 +1,4 @@
from tests.config import sample_data
from tests.config import get_sample_data
from tests.context import pandas_ta_classic as pandas_ta
from unittest import skip, TestCase
@@ -8,7 +8,7 @@ from pandas import DataFrame
class TestTrendExtension(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
cls.data = get_sample_data()
@classmethod
def tearDownClass(cls):
+2 -2
View File
@@ -1,4 +1,4 @@
from tests.config import sample_data
from tests.config import get_sample_data
from tests.context import pandas_ta_classic as pandas_ta
from unittest import TestCase
@@ -8,7 +8,7 @@ from pandas import DataFrame
class TestVolatilityExtension(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
cls.data = get_sample_data()
@classmethod
def tearDownClass(cls):
+2 -2
View File
@@ -1,4 +1,4 @@
from tests.config import sample_data
from tests.config import get_sample_data
from tests.context import pandas_ta_classic as pandas_ta
from unittest import TestCase
@@ -8,7 +8,7 @@ from pandas import DataFrame
class TestVolumeExtension(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
cls.data = get_sample_data()
cls.open = cls.data["open"]
@classmethod
+2 -2
View File
@@ -1,4 +1,4 @@
from tests.config import error_analysis, sample_data, CORRELATION, CORRELATION_THRESHOLD, VERBOSE
from tests.config import error_analysis, get_sample_data, CORRELATION, CORRELATION_THRESHOLD, VERBOSE
from tests.context import pandas_ta_classic as pandas_ta
from unittest import TestCase, skip
@@ -16,7 +16,7 @@ except ImportError:
class TestCandle(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
cls.data = get_sample_data()
cls.data.columns = cls.data.columns.str.lower()
cls.open = cls.data["open"]
cls.high = cls.data["high"]
+2 -2
View File
@@ -1,4 +1,4 @@
from tests.config import error_analysis, sample_data, CORRELATION, CORRELATION_THRESHOLD, VERBOSE
from tests.config import error_analysis, get_sample_data, CORRELATION, CORRELATION_THRESHOLD, VERBOSE
from tests.context import pandas_ta_classic as pandas_ta
from unittest import TestCase, skip
@@ -16,7 +16,7 @@ except ImportError:
class TestCycles(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
cls.data = get_sample_data()
cls.data.columns = cls.data.columns.str.lower()
cls.open = cls.data["open"]
cls.high = cls.data["high"]
+2 -2
View File
@@ -1,4 +1,4 @@
from tests.config import error_analysis, sample_data, CORRELATION, CORRELATION_THRESHOLD, VERBOSE
from tests.config import error_analysis, get_sample_data, CORRELATION, CORRELATION_THRESHOLD, VERBOSE
from tests.context import pandas_ta_classic as pandas_ta
from unittest import TestCase, skip
@@ -16,7 +16,7 @@ except ImportError:
class TestMomentum(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
cls.data = get_sample_data()
cls.data.columns = cls.data.columns.str.lower()
cls.open = cls.data["open"]
cls.high = cls.data["high"]
+2 -2
View File
@@ -1,4 +1,4 @@
from tests.config import CORRELATION, CORRELATION_THRESHOLD, error_analysis, sample_data, VERBOSE
from tests.config import CORRELATION, CORRELATION_THRESHOLD, error_analysis, get_sample_data, VERBOSE
from tests.context import pandas_ta_classic as pandas_ta
from unittest import TestCase
@@ -16,7 +16,7 @@ except ImportError:
class TestOverlap(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
cls.data = get_sample_data()
cls.data.columns = cls.data.columns.str.lower()
cls.open = cls.data["open"]
cls.high = cls.data["high"]
+2 -2
View File
@@ -1,4 +1,4 @@
from tests.config import sample_data
from tests.config import get_sample_data
from tests.context import pandas_ta_classic as pandas_ta
from unittest import TestCase
@@ -8,7 +8,7 @@ from pandas import Series
class TestPerformace(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
cls.data = get_sample_data()
cls.close = cls.data["close"]
cls.islong = (cls.close > pandas_ta.sma(cls.close, length=8)).astype(int)
cls.pctret = pandas_ta.percent_return(cls.close, cumulative=False)
+2 -2
View File
@@ -1,4 +1,4 @@
from tests.config import error_analysis, sample_data, CORRELATION, CORRELATION_THRESHOLD, VERBOSE
from tests.config import error_analysis, get_sample_data, CORRELATION, CORRELATION_THRESHOLD, VERBOSE
from tests.context import pandas_ta_classic as pandas_ta
from unittest import skip, TestCase
@@ -16,7 +16,7 @@ except ImportError:
class TestStatistics(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
cls.data = get_sample_data()
cls.data.columns = cls.data.columns.str.lower()
cls.open = cls.data["open"]
cls.high = cls.data["high"]
+2 -2
View File
@@ -1,4 +1,4 @@
from tests.config import error_analysis, sample_data, CORRELATION, CORRELATION_THRESHOLD, VERBOSE
from tests.config import error_analysis, get_sample_data, CORRELATION, CORRELATION_THRESHOLD, VERBOSE
from tests.context import pandas_ta_classic as pandas_ta
from unittest import TestCase, skip
@@ -16,7 +16,7 @@ except ImportError:
class TestTrend(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
cls.data = get_sample_data()
cls.data.columns = cls.data.columns.str.lower()
cls.open = cls.data["open"]
cls.high = cls.data["high"]
+2 -2
View File
@@ -1,4 +1,4 @@
from tests.config import error_analysis, sample_data, CORRELATION, CORRELATION_THRESHOLD, VERBOSE
from tests.config import error_analysis, get_sample_data, CORRELATION, CORRELATION_THRESHOLD, VERBOSE
from tests.context import pandas_ta_classic as pandas_ta
from unittest import TestCase, skip
@@ -16,7 +16,7 @@ except ImportError:
class TestVolatility(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
cls.data = get_sample_data()
cls.data.columns = cls.data.columns.str.lower()
cls.open = cls.data["open"]
cls.high = cls.data["high"]
+2 -2
View File
@@ -1,4 +1,4 @@
from tests.config import error_analysis, sample_data, CORRELATION, CORRELATION_THRESHOLD, VERBOSE
from tests.config import error_analysis, get_sample_data, CORRELATION, CORRELATION_THRESHOLD, VERBOSE
from tests.context import pandas_ta_classic as pandas_ta
from unittest import TestCase, skip
@@ -16,7 +16,7 @@ except ImportError:
class TestVolume(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
cls.data = get_sample_data()
cls.data.columns = cls.data.columns.str.lower()
cls.open = cls.data["open"]
cls.high = cls.data["high"]
+5 -7
View File
@@ -3,7 +3,7 @@
from multiprocessing import cpu_count
from time import perf_counter
from tests.config import sample_data
from tests.config import get_sample_data
from tests.context import pandas_ta_classic as pandas_ta
from unittest import skip, skipUnless, TestCase
@@ -22,9 +22,8 @@ class TestStrategyMethods(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
cls.data.ta.cores = cores
cls.speed_test = DataFrame()
cls.data = get_sample_data()
@classmethod
def tearDownClass(cls):
@@ -52,9 +51,8 @@ class TestStrategyMethods(TestCase):
del cls.data
def setUp(self):
# Always start with a fresh copy of sample_data for each test
from tests.config import sample_data
self.data = sample_data.copy(deep=True)
# Always start with a fresh DataFrame for each test
self.data = get_sample_data()
self.added_cols = 0
self.category = ""
self.init_cols = len(self.data.columns)
@@ -199,7 +197,7 @@ class TestStrategyMethods(TestCase):
)
self.data.ta.strategy(custom, verbose=verbose, timed=strategy_timed, ordered=True)
self.data.ta.tsignals(trend=self.data["AMATe_LR_20_50_2"], append=True)
self.assertEqual(len(self.data.columns), 15)
self.assertEqual(len(self.data.columns), 13)
# @skip
def test_momentum_category(self):
+2 -2
View File
@@ -1,4 +1,4 @@
from tests.config import sample_data
from tests.config import get_sample_data
from tests.context import pandas_ta_classic as pandas_ta
from unittest import skip, TestCase
@@ -23,7 +23,7 @@ class TestUtilities(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
cls.data = get_sample_data()
@classmethod
def tearDownClass(cls):
+6 -5
View File
@@ -1,15 +1,15 @@
from unittest import skip, TestCase
from unittest import TestCase
from pandas import DataFrame
from tests.config import sample_data
from tests.config import get_sample_data
from tests.context import pandas_ta_classic as pandas_ta
class TestUtilityMetrics(TestCase):
@classmethod
def setUpClass(cls):
cls.data = sample_data
cls.data = get_sample_data()
cls.close = cls.data["close"]
cls.pctret = pandas_ta.percent_return(cls.close, cumulative=False)
cls.logret = pandas_ta.percent_return(cls.close, cumulative=False)
@@ -34,11 +34,12 @@ class TestUtilityMetrics(TestCase):
self.assertIsInstance(result, float)
self.assertGreaterEqual(result, 0)
import numpy as np
result = pandas_ta.calmar_ratio(self.close, years=0)
self.assertIsNone(result)
self.assertTrue(np.isnan(result))
result = pandas_ta.calmar_ratio(self.close, years=-2)
self.assertIsNone(result)
self.assertTrue(np.isnan(result))
def test_downside_deviation(self):
result = pandas_ta.downside_deviation(self.pctret)