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:
Pratik Bhadane
2026-03-23 23:57:30 +05:30
parent 7a5a220dfe
commit 307beeca02
47 changed files with 1822 additions and 573 deletions
+43 -7
View File
@@ -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)