release: cut v1.0.0
Prepare the first public 1.0.0 release and finish the remaining CI hardening work. Highlights: - align Python, Rust, WASM, Conda, API, MCP, and docs version metadata to 1.0.0 - promote package metadata to Production/Stable and update stability/versioning docs for the stable series - move the accumulated Unreleased notes into a dated 1.0.0 changelog section and keep a fresh top-level Unreleased block - strengthen the changelog checker so it validates a single top-level Unreleased section - fix the CI/package support mismatch by declaring Python >=3.10 consistently and gating pandas-ta extras to Python 3.12+ - restore Sphinx autodoc compatibility for documented ferro_ta.<module> imports by registering module aliases - make the TA-Lib benchmark guardrail less flaky by checking median and tail-percentile speedups instead of failing on a single mild outlier - switch PyPI publishing to OIDC-only trusted publishing and wire the changelog check into the required CI gate - apply the Ruff-driven cleanup across the Python and test tree and refresh uv/cargo lockfiles Validated locally: - python3 scripts/check_changelog.py - uv run --with ruff ruff check python tests - uv run --with ruff ruff format --check python tests - uv lock --check - sphinx-build -b html docs docs/_build -W --keep-going - build/install the ferro_ta 1.0.0 wheel successfully
This commit is contained in:
@@ -1,10 +1,27 @@
|
||||
"""Unit tests for ferro_ta.indicators.overlap"""
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from ferro_ta.indicators.overlap import (
|
||||
SMA, EMA, WMA, DEMA, TEMA, TRIMA, KAMA, T3, MA,
|
||||
MACD, MACDFIX, MACDEXT, BBANDS, SAR, SAREXT,
|
||||
MAMA, MAVP, MIDPOINT, MIDPRICE,
|
||||
BBANDS,
|
||||
DEMA,
|
||||
EMA,
|
||||
KAMA,
|
||||
MA,
|
||||
MACD,
|
||||
MACDEXT,
|
||||
MACDFIX,
|
||||
MAMA,
|
||||
MAVP,
|
||||
MIDPOINT,
|
||||
MIDPRICE,
|
||||
SAR,
|
||||
SAREXT,
|
||||
SMA,
|
||||
T3,
|
||||
TEMA,
|
||||
TRIMA,
|
||||
WMA,
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -26,6 +43,7 @@ SMALL5_LOW = np.array([9.0, 10.0, 11.0, 12.0, 13.0])
|
||||
# SMA
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestSMA:
|
||||
def test_known_values(self):
|
||||
result = SMA(SMALL5, timeperiod=3)
|
||||
@@ -50,6 +68,7 @@ class TestSMA:
|
||||
# EMA
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestEMA:
|
||||
def test_known_values(self):
|
||||
# k = 2/(3+1) = 0.5; seed = SMA(3) = 11.0
|
||||
@@ -80,13 +99,14 @@ class TestEMA:
|
||||
# WMA
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestWMA:
|
||||
def test_known_values(self):
|
||||
arr = np.arange(1.0, 6.0)
|
||||
result = WMA(arr, timeperiod=3)
|
||||
# weights 1,2,3 / 6
|
||||
expected_2 = (1*1 + 2*2 + 3*3) / 6.0 # 14/6
|
||||
expected_3 = (1*2 + 2*3 + 3*4) / 6.0 # 20/6
|
||||
expected_2 = (1 * 1 + 2 * 2 + 3 * 3) / 6.0 # 14/6
|
||||
expected_3 = (1 * 2 + 2 * 3 + 3 * 4) / 6.0 # 20/6
|
||||
assert np.isnan(result[0]) and np.isnan(result[1])
|
||||
np.testing.assert_allclose(result[2], expected_2, rtol=1e-10)
|
||||
np.testing.assert_allclose(result[3], expected_3, rtol=1e-10)
|
||||
@@ -103,10 +123,11 @@ class TestWMA:
|
||||
# DEMA
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestDEMA:
|
||||
def test_nan_warmup(self):
|
||||
result = DEMA(_CLOSE, timeperiod=5)
|
||||
assert np.all(np.isnan(result[:8])) # DEMA needs 2*(tp-1) bars
|
||||
assert np.all(np.isnan(result[:8])) # DEMA needs 2*(tp-1) bars
|
||||
|
||||
def test_length(self):
|
||||
assert len(DEMA(_CLOSE, 5)) == N
|
||||
@@ -131,6 +152,7 @@ class TestDEMA:
|
||||
# TEMA
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestTEMA:
|
||||
def test_nan_warmup(self):
|
||||
result = TEMA(_CLOSE, timeperiod=5)
|
||||
@@ -150,6 +172,7 @@ class TestTEMA:
|
||||
# TRIMA
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestTRIMA:
|
||||
def test_known_values(self):
|
||||
arr = np.arange(1.0, 11.0)
|
||||
@@ -171,6 +194,7 @@ class TestTRIMA:
|
||||
# KAMA
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestKAMA:
|
||||
def test_nan_warmup(self):
|
||||
result = KAMA(_CLOSE, timeperiod=10)
|
||||
@@ -195,6 +219,7 @@ class TestKAMA:
|
||||
# T3
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestT3:
|
||||
def test_nan_warmup(self):
|
||||
arr = np.linspace(10.0, 30.0, 100)
|
||||
@@ -223,6 +248,7 @@ class TestT3:
|
||||
# MA
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestMA:
|
||||
def test_default_is_sma(self):
|
||||
result_ma = MA(_CLOSE, timeperiod=10, matype=0)
|
||||
@@ -242,6 +268,7 @@ class TestMA:
|
||||
# MACD
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestMACD:
|
||||
def test_returns_three_arrays(self):
|
||||
result = MACD(_CLOSE, 12, 26, 9)
|
||||
@@ -266,6 +293,7 @@ class TestMACD:
|
||||
# MACDFIX
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestMACDFIX:
|
||||
def test_returns_three_arrays(self):
|
||||
result = MACDFIX(_CLOSE)
|
||||
@@ -285,6 +313,7 @@ class TestMACDFIX:
|
||||
# MACDEXT
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestMACDEXT:
|
||||
def test_returns_three_arrays(self):
|
||||
result = MACDEXT(_CLOSE)
|
||||
@@ -303,6 +332,7 @@ class TestMACDEXT:
|
||||
# BBANDS
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestBBANDS:
|
||||
def test_returns_three_arrays(self):
|
||||
result = BBANDS(_CLOSE, 20)
|
||||
@@ -331,6 +361,7 @@ class TestBBANDS:
|
||||
# SAR
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestSAR:
|
||||
def test_length(self):
|
||||
result = SAR(_HIGH, _LOW)
|
||||
@@ -349,6 +380,7 @@ class TestSAR:
|
||||
# SAREXT
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestSAREXT:
|
||||
def test_length(self):
|
||||
result = SAREXT(_HIGH, _LOW)
|
||||
@@ -367,6 +399,7 @@ class TestSAREXT:
|
||||
# MAMA
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestMAMA:
|
||||
def test_returns_two_arrays(self):
|
||||
result = MAMA(_CLOSE)
|
||||
@@ -393,6 +426,7 @@ class TestMAMA:
|
||||
# MAVP
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestMAVP:
|
||||
def test_length(self):
|
||||
arr = np.linspace(10.0, 30.0, 50)
|
||||
@@ -412,6 +446,7 @@ class TestMAVP:
|
||||
# MIDPOINT
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestMIDPOINT:
|
||||
def test_known_values(self):
|
||||
arr = np.array([10.0, 12.0, 14.0, 16.0, 18.0])
|
||||
@@ -433,6 +468,7 @@ class TestMIDPOINT:
|
||||
# MIDPRICE
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestMIDPRICE:
|
||||
def test_known_values(self):
|
||||
result = MIDPRICE(SMALL5_HIGH, SMALL5_LOW, timeperiod=3)
|
||||
|
||||
Reference in New Issue
Block a user