5aa0949bce
Two new indicators in a brand-new "Ichimoku & alternative charts" family: - `Ichimoku` (Ichimoku Kinko Hyo): the full five-line cloud system (Tenkan-sen, Kijun-sen, Senkou Span A/B, Chikou Span). Classic (9, 26, 52, 26) defaults; configurable. Forward displacement is handled in an O(1) ring buffer so the visible Senkou A/B at bar n are the values computed at bar n-displacement. - `HeikinAshi`: recursive candle smoothing transform emitting a four-field synthetic candle. Seeds ha_open from (open+close)/2 on the first bar. Touchpoints: core + unit tests, mod.rs/lib.rs re-exports, Python + Node + WASM bindings (multi-output via PyArray2 / interleaved Vec<f64> / Object+Float64Array), Python tests across smoke/new-indicators/ input-validation, Node parity tests, fuzz target (Candle), benches, README family table + counter (71 -> 73, 8 -> 9 families), CHANGELOG. Note: Renko, Kagi, and Point & Figure from the family-13 ideas list are intentionally skipped. They are bar generators (the bar boundary is defined by price moves, not by a fixed time interval) rather than indicators that consume a candle stream, and belong in wickra-data as candle/tick transforms alongside the existing tick-to-candle aggregator and resampler.
71 lines
2.1 KiB
Python
71 lines
2.1 KiB
Python
"""Input-validation tests: malformed NumPy inputs raise ValueError, not panics."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import numpy as np
|
|
import pytest
|
|
|
|
import wickra as ta
|
|
|
|
|
|
def test_non_contiguous_array_raises_value_error():
|
|
# A strided view is not C-contiguous; batch() must reject it cleanly.
|
|
base = np.linspace(1.0, 100.0, 60)
|
|
non_contiguous = base[::2]
|
|
assert not non_contiguous.flags["C_CONTIGUOUS"]
|
|
with pytest.raises(ValueError):
|
|
ta.SMA(5).batch(non_contiguous)
|
|
|
|
|
|
def test_ascontiguousarray_recovers():
|
|
base = np.linspace(1.0, 100.0, 60)
|
|
fixed = np.ascontiguousarray(base[::2])
|
|
out = ta.SMA(5).batch(fixed)
|
|
assert out.shape == fixed.shape
|
|
|
|
|
|
def test_unequal_length_candle_batch_raises(ohlc_series):
|
|
high, low, close = ohlc_series
|
|
short = low[:-1]
|
|
with pytest.raises(ValueError):
|
|
ta.ATR(14).batch(high, short, close)
|
|
with pytest.raises(ValueError):
|
|
ta.WilliamsR(14).batch(high, short, close)
|
|
with pytest.raises(ValueError):
|
|
ta.Aroon(14).batch(high, short)
|
|
|
|
|
|
def test_roc_and_trix_have_default_periods():
|
|
# ROC/TRIX gained constructor defaults matching the TA-Lib convention.
|
|
assert ta.ROC().period == 10
|
|
assert ta.TRIX() is not None
|
|
|
|
|
|
def test_ichimoku_rejects_zero_and_non_increasing_periods():
|
|
with pytest.raises(ValueError):
|
|
ta.Ichimoku(0, 26, 52, 26)
|
|
with pytest.raises(ValueError):
|
|
ta.Ichimoku(9, 26, 52, 0)
|
|
# Periods must satisfy tenkan < kijun < senkou_b.
|
|
with pytest.raises(ValueError):
|
|
ta.Ichimoku(26, 9, 52, 26)
|
|
with pytest.raises(ValueError):
|
|
ta.Ichimoku(9, 52, 52, 26)
|
|
|
|
|
|
def test_family_10_ehlers_rejects_invalid_parameters():
|
|
with pytest.raises(ValueError):
|
|
ta.SuperSmoother(0)
|
|
with pytest.raises(ValueError):
|
|
ta.FisherTransform(0)
|
|
with pytest.raises(ValueError):
|
|
ta.InverseFisherTransform(0.0)
|
|
with pytest.raises(ValueError):
|
|
ta.DecyclerOscillator(30, 10)
|
|
with pytest.raises(ValueError):
|
|
ta.RoofingFilter(48, 10)
|
|
with pytest.raises(ValueError):
|
|
ta.MAMA(0.05, 0.5)
|
|
with pytest.raises(ValueError):
|
|
ta.EmpiricalModeDecomposition(20, 0.0)
|