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
+27 -5
View File
@@ -1,9 +1,18 @@
"""Unit tests for ferro_ta.indicators.extended"""
import numpy as np
import pytest
from ferro_ta.indicators.extended import (
VWAP, SUPERTREND, ICHIMOKU, DONCHIAN, PIVOT_POINTS,
KELTNER_CHANNELS, HULL_MA, CHANDELIER_EXIT, VWMA, CHOPPINESS_INDEX,
CHANDELIER_EXIT,
CHOPPINESS_INDEX,
DONCHIAN,
HULL_MA,
ICHIMOKU,
KELTNER_CHANNELS,
PIVOT_POINTS,
SUPERTREND,
VWAP,
VWMA,
)
# ---------------------------------------------------------------------------
@@ -23,6 +32,7 @@ _VOL = RNG.uniform(1000, 5000, N)
# VWAP
# ---------------------------------------------------------------------------
class TestVWAP:
def test_length(self):
result = VWAP(_H, _L, _C, _VOL)
@@ -46,6 +56,7 @@ class TestVWAP:
# SUPERTREND
# ---------------------------------------------------------------------------
class TestSUPERTREND:
def test_returns_two_arrays(self):
result = SUPERTREND(_H, _L, _C)
@@ -69,6 +80,7 @@ class TestSUPERTREND:
# ICHIMOKU
# ---------------------------------------------------------------------------
class TestICHIMOKU:
def test_returns_five_arrays(self):
result = ICHIMOKU(_H, _L, _C)
@@ -80,7 +92,9 @@ class TestICHIMOKU:
assert len(arr) == N
def test_tenkan_warmup(self):
tenkan, kijun, senkou_a, senkou_b, chikou = ICHIMOKU(_H, _L, _C, tenkan_period=9)
tenkan, kijun, senkou_a, senkou_b, chikou = ICHIMOKU(
_H, _L, _C, tenkan_period=9
)
assert np.all(np.isnan(tenkan[:8]))
def test_finite_after_warmup(self):
@@ -94,6 +108,7 @@ class TestICHIMOKU:
# DONCHIAN
# ---------------------------------------------------------------------------
class TestDONCHIAN:
def test_returns_three_arrays(self):
result = DONCHIAN(_H, _L)
@@ -126,6 +141,7 @@ class TestDONCHIAN:
# PIVOT_POINTS
# ---------------------------------------------------------------------------
class TestPIVOT_POINTS:
def test_returns_five_arrays(self):
result = PIVOT_POINTS(_H, _L, _C)
@@ -138,7 +154,7 @@ class TestPIVOT_POINTS:
def test_classic_pivot_formula(self):
# PP = (H + L + C) / 3
pp, r1, s1, r2, s2 = PIVOT_POINTS(_H, _L, _C, method='classic')
pp, r1, s1, r2, s2 = PIVOT_POINTS(_H, _L, _C, method="classic")
valid = ~np.isnan(pp)
expected_pp = (_H[:-1] + _L[:-1] + _C[:-1]) / 3.0
np.testing.assert_allclose(pp[valid], expected_pp[valid[1:]], rtol=1e-6)
@@ -152,6 +168,7 @@ class TestPIVOT_POINTS:
# KELTNER_CHANNELS
# ---------------------------------------------------------------------------
class TestKELTNER_CHANNELS:
def test_returns_three_arrays(self):
result = KELTNER_CHANNELS(_H, _L, _C)
@@ -175,6 +192,7 @@ class TestKELTNER_CHANNELS:
# HULL_MA
# ---------------------------------------------------------------------------
class TestHULL_MA:
def test_length(self):
assert len(HULL_MA(_C, timeperiod=16)) == N
@@ -199,6 +217,7 @@ class TestHULL_MA:
# CHANDELIER_EXIT
# ---------------------------------------------------------------------------
class TestCHANDELIER_EXIT:
def test_returns_two_arrays(self):
result = CHANDELIER_EXIT(_H, _L, _C)
@@ -223,6 +242,7 @@ class TestCHANDELIER_EXIT:
# VWMA
# ---------------------------------------------------------------------------
class TestVWMA:
def test_length(self):
assert len(VWMA(_C, _VOL, timeperiod=20)) == N
@@ -241,6 +261,7 @@ class TestVWMA:
vol = np.ones(N) * 1000.0
vwma = VWMA(_C, vol, timeperiod=20)
from ferro_ta.indicators.overlap import SMA
sma = SMA(_C, timeperiod=20)
valid = ~np.isnan(vwma) & ~np.isnan(sma)
np.testing.assert_allclose(vwma[valid], sma[valid], rtol=1e-8)
@@ -250,6 +271,7 @@ class TestVWMA:
# CHOPPINESS_INDEX
# ---------------------------------------------------------------------------
class TestCHOPPINESS_INDEX:
def test_length(self):
assert len(CHOPPINESS_INDEX(_H, _L, _C, timeperiod=14)) == N