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,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
|
||||
|
||||
Reference in New Issue
Block a user