扩展指标
This commit is contained in:
@@ -0,0 +1,498 @@
|
||||
"""
|
||||
Extended Indicators — Popular indicators not in the TA-Lib standard set.
|
||||
|
||||
All indicator logic is implemented in Rust (PyO3) for maximum performance.
|
||||
This module provides the public Python API with:
|
||||
- Input validation
|
||||
- ``_to_f64`` conversion
|
||||
- pandas/polars-compatible return values (numpy arrays)
|
||||
|
||||
Functions
|
||||
---------
|
||||
VWAP — Volume Weighted Average Price (cumulative or rolling)
|
||||
SUPERTREND — ATR-based trend-following signal
|
||||
ICHIMOKU — Ichimoku Cloud
|
||||
DONCHIAN — Donchian Channels
|
||||
PIVOT_POINTS — Classic / Fibonacci / Camarilla pivot levels
|
||||
KELTNER_CHANNELS — EMA ± ATR bands
|
||||
HULL_MA — Hull Moving Average (WMA-based)
|
||||
CHANDELIER_EXIT — ATR-based stop-loss / exit levels
|
||||
VWMA — Volume Weighted Moving Average
|
||||
CHOPPINESS_INDEX — Market choppiness / trending strength index
|
||||
|
||||
Rust backend
|
||||
------------
|
||||
All computations delegate to Rust functions in the ``_ferro_ta`` extension::
|
||||
|
||||
from ferro_ta._ferro_ta import supertrend, donchian, vwap, ...
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
from numpy.typing import ArrayLike
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Import Rust implementations
|
||||
# ---------------------------------------------------------------------------
|
||||
from ferro_ta._ferro_ta import (
|
||||
chandelier_exit as _rust_chandelier_exit,
|
||||
)
|
||||
from ferro_ta._ferro_ta import (
|
||||
choppiness_index as _rust_choppiness_index,
|
||||
)
|
||||
from ferro_ta._ferro_ta import (
|
||||
donchian as _rust_donchian,
|
||||
)
|
||||
from ferro_ta._ferro_ta import (
|
||||
hull_ma as _rust_hull_ma,
|
||||
)
|
||||
from ferro_ta._ferro_ta import (
|
||||
ichimoku as _rust_ichimoku,
|
||||
)
|
||||
from ferro_ta._ferro_ta import (
|
||||
keltner_channels as _rust_keltner_channels,
|
||||
)
|
||||
from ferro_ta._ferro_ta import (
|
||||
pivot_points as _rust_pivot_points,
|
||||
)
|
||||
from ferro_ta._ferro_ta import (
|
||||
supertrend as _rust_supertrend,
|
||||
)
|
||||
from ferro_ta._ferro_ta import (
|
||||
vwap as _rust_vwap,
|
||||
)
|
||||
from ferro_ta._ferro_ta import (
|
||||
vwma as _rust_vwma,
|
||||
)
|
||||
from ferro_ta._utils import _to_f64
|
||||
from ferro_ta.core.exceptions import FerroTAValueError, _normalize_rust_error
|
||||
|
||||
|
||||
def VWAP(
|
||||
high: ArrayLike,
|
||||
low: ArrayLike,
|
||||
close: ArrayLike,
|
||||
volume: ArrayLike,
|
||||
timeperiod: int = 0,
|
||||
) -> np.ndarray:
|
||||
"""Volume Weighted Average Price.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
high : array-like
|
||||
Sequence of high prices.
|
||||
low : array-like
|
||||
Sequence of low prices.
|
||||
close : array-like
|
||||
Sequence of closing prices.
|
||||
volume : array-like
|
||||
Sequence of volumes.
|
||||
timeperiod : int, optional
|
||||
Rolling window length. ``0`` (default) computes a cumulative VWAP
|
||||
from bar 0 (session VWAP). Any value ``>= 1`` uses a rolling window
|
||||
of that length; the first ``timeperiod - 1`` values are ``NaN``.
|
||||
|
||||
Returns
|
||||
-------
|
||||
numpy.ndarray
|
||||
Array of VWAP values.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Typical price is used: ``(high + low + close) / 3``.
|
||||
Implemented in Rust for maximum performance.
|
||||
"""
|
||||
if timeperiod < 0:
|
||||
raise FerroTAValueError("timeperiod must be >= 0 for VWAP")
|
||||
h = _to_f64(high)
|
||||
lo = _to_f64(low)
|
||||
c = _to_f64(close)
|
||||
v = _to_f64(volume)
|
||||
try:
|
||||
return np.asarray(_rust_vwap(h, lo, c, v, timeperiod))
|
||||
except ValueError as e:
|
||||
_normalize_rust_error(e)
|
||||
|
||||
|
||||
def SUPERTREND(
|
||||
high: ArrayLike,
|
||||
low: ArrayLike,
|
||||
close: ArrayLike,
|
||||
timeperiod: int = 7,
|
||||
multiplier: float = 3.0,
|
||||
) -> tuple[np.ndarray, np.ndarray]:
|
||||
"""Supertrend indicator.
|
||||
|
||||
An ATR-based trend-following indicator. Returns the Supertrend line and a
|
||||
direction array.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
high : array-like
|
||||
Sequence of high prices.
|
||||
low : array-like
|
||||
Sequence of low prices.
|
||||
close : array-like
|
||||
Sequence of closing prices.
|
||||
timeperiod : int, optional
|
||||
ATR period (default 7).
|
||||
multiplier : float, optional
|
||||
ATR multiplier for band width (default 3.0).
|
||||
|
||||
Returns
|
||||
-------
|
||||
supertrend : numpy.ndarray
|
||||
The Supertrend line values. ``NaN`` during the warmup period.
|
||||
direction : numpy.ndarray
|
||||
``1`` = uptrend (price above Supertrend), ``-1`` = downtrend.
|
||||
``0`` during warmup.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Implemented in Rust — the sequential band-adjustment loop that was
|
||||
previously a Python bottleneck now runs at native speed.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> import numpy as np
|
||||
>>> from ferro_ta import SUPERTREND
|
||||
>>> h = np.array([10.0, 11.0, 12.0, 11.0, 10.0, 9.0, 8.0, 9.0, 10.0, 11.0,
|
||||
... 12.0, 13.0, 14.0, 13.0, 12.0])
|
||||
>>> l = h - 1.0
|
||||
>>> c = (h + l) / 2.0
|
||||
>>> st, dir_ = SUPERTREND(h, l, c)
|
||||
"""
|
||||
h = _to_f64(high)
|
||||
lo = _to_f64(low)
|
||||
c = _to_f64(close)
|
||||
try:
|
||||
st, d = _rust_supertrend(h, lo, c, timeperiod, multiplier)
|
||||
except ValueError as e:
|
||||
_normalize_rust_error(e)
|
||||
return np.asarray(st), np.asarray(d)
|
||||
|
||||
|
||||
def ICHIMOKU(
|
||||
high: ArrayLike,
|
||||
low: ArrayLike,
|
||||
close: ArrayLike,
|
||||
tenkan_period: int = 9,
|
||||
kijun_period: int = 26,
|
||||
senkou_b_period: int = 52,
|
||||
displacement: int = 26,
|
||||
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
|
||||
"""Ichimoku Cloud (Ichimoku Kinko Hyo).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
high : array-like
|
||||
low : array-like
|
||||
close : array-like
|
||||
tenkan_period : int, default 9
|
||||
Conversion line (Tenkan-sen) period.
|
||||
kijun_period : int, default 26
|
||||
Base line (Kijun-sen) period.
|
||||
senkou_b_period : int, default 52
|
||||
Leading Span B period.
|
||||
displacement : int, default 26
|
||||
Displacement / cloud offset for Senkou A & B.
|
||||
|
||||
Returns
|
||||
-------
|
||||
tenkan, kijun, senkou_a, senkou_b, chikou : numpy.ndarray
|
||||
Each is a 1-D float64 array of the same length as the inputs.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Implemented in Rust with O(n) monotonic deque for all rolling windows.
|
||||
"""
|
||||
h = _to_f64(high)
|
||||
lo = _to_f64(low)
|
||||
c = _to_f64(close)
|
||||
try:
|
||||
t, k, sa, sb, ch = _rust_ichimoku(
|
||||
h, lo, c, tenkan_period, kijun_period, senkou_b_period, displacement
|
||||
)
|
||||
except ValueError as e:
|
||||
_normalize_rust_error(e)
|
||||
return (
|
||||
np.asarray(t),
|
||||
np.asarray(k),
|
||||
np.asarray(sa),
|
||||
np.asarray(sb),
|
||||
np.asarray(ch),
|
||||
)
|
||||
|
||||
|
||||
def DONCHIAN(
|
||||
high: ArrayLike,
|
||||
low: ArrayLike,
|
||||
timeperiod: int = 20,
|
||||
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
||||
"""Donchian Channels — rolling highest high / lowest low.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
high : array-like
|
||||
low : array-like
|
||||
timeperiod : int, default 20
|
||||
|
||||
Returns
|
||||
-------
|
||||
upper, middle, lower : numpy.ndarray
|
||||
Rolling highest high, midpoint, and lowest low.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Implemented in Rust with O(n) monotonic deque (no Python loop).
|
||||
"""
|
||||
h = _to_f64(high)
|
||||
lo = _to_f64(low)
|
||||
try:
|
||||
upper, middle, lower = _rust_donchian(h, lo, timeperiod)
|
||||
except ValueError as e:
|
||||
_normalize_rust_error(e)
|
||||
return np.asarray(upper), np.asarray(middle), np.asarray(lower)
|
||||
|
||||
|
||||
def PIVOT_POINTS(
|
||||
high: ArrayLike,
|
||||
low: ArrayLike,
|
||||
close: ArrayLike,
|
||||
method: str = "classic",
|
||||
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
|
||||
"""Pivot Points — support / resistance levels.
|
||||
|
||||
Computes pivot points for each bar using the *previous bar's* H/L/C.
|
||||
The first bar output is NaN.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
high : array-like
|
||||
low : array-like
|
||||
close : array-like
|
||||
method : {'classic', 'fibonacci', 'camarilla'}, default 'classic'
|
||||
|
||||
Returns
|
||||
-------
|
||||
pivot, r1, s1, r2, s2 : numpy.ndarray
|
||||
|
||||
Notes
|
||||
-----
|
||||
**Classic**: P=(H+L+C)/3; R1=2P−L; S1=2P−H; R2=P+(H−L); S2=P−(H−L)
|
||||
|
||||
**Fibonacci**: P=(H+L+C)/3; R1=P+0.382*(H−L); S1=P−0.382*(H−L);
|
||||
R2=P+0.618*(H−L); S2=P−0.618*(H−L)
|
||||
|
||||
**Camarilla**: P=(H+L+C)/3; R1=C+1.1*(H−L)/12; S1=C−1.1*(H−L)/12;
|
||||
R2=C+1.1*(H−L)/6; S2=C−1.1*(H−L)/6
|
||||
"""
|
||||
valid_methods = {"classic", "fibonacci", "camarilla"}
|
||||
if method.lower() not in valid_methods:
|
||||
raise FerroTAValueError(
|
||||
f"Unknown pivot method '{method}'. Use 'classic', 'fibonacci', or 'camarilla'."
|
||||
)
|
||||
h = _to_f64(high)
|
||||
lo = _to_f64(low)
|
||||
c = _to_f64(close)
|
||||
try:
|
||||
pivot, r1, s1, r2, s2 = _rust_pivot_points(h, lo, c, method)
|
||||
except ValueError as e:
|
||||
_normalize_rust_error(e)
|
||||
return (
|
||||
np.asarray(pivot),
|
||||
np.asarray(r1),
|
||||
np.asarray(s1),
|
||||
np.asarray(r2),
|
||||
np.asarray(s2),
|
||||
)
|
||||
|
||||
|
||||
def KELTNER_CHANNELS(
|
||||
high: ArrayLike,
|
||||
low: ArrayLike,
|
||||
close: ArrayLike,
|
||||
timeperiod: int = 20,
|
||||
atr_period: int = 10,
|
||||
multiplier: float = 2.0,
|
||||
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
||||
"""Keltner Channels — EMA ± (multiplier × ATR).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
high : array-like
|
||||
low : array-like
|
||||
close : array-like
|
||||
timeperiod : int, default 20
|
||||
EMA period for the middle band.
|
||||
atr_period : int, default 10
|
||||
ATR period for band width.
|
||||
multiplier : float, default 2.0
|
||||
ATR multiplier.
|
||||
|
||||
Returns
|
||||
-------
|
||||
upper, middle, lower : numpy.ndarray
|
||||
|
||||
Notes
|
||||
-----
|
||||
Implemented in Rust — EMA and ATR computed inline without Python calls.
|
||||
"""
|
||||
h = _to_f64(high)
|
||||
lo = _to_f64(low)
|
||||
c = _to_f64(close)
|
||||
try:
|
||||
upper, middle, lower = _rust_keltner_channels(
|
||||
h, lo, c, timeperiod, atr_period, multiplier
|
||||
)
|
||||
except ValueError as e:
|
||||
_normalize_rust_error(e)
|
||||
return np.asarray(upper), np.asarray(middle), np.asarray(lower)
|
||||
|
||||
|
||||
def HULL_MA(
|
||||
close: ArrayLike,
|
||||
timeperiod: int = 16,
|
||||
) -> np.ndarray:
|
||||
"""Hull Moving Average (HMA).
|
||||
|
||||
A fast-responding moving average that reduces lag.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
close : array-like
|
||||
timeperiod : int, default 16
|
||||
|
||||
Returns
|
||||
-------
|
||||
numpy.ndarray
|
||||
|
||||
Notes
|
||||
-----
|
||||
Formula: ``HMA(n) = WMA(2 * WMA(n/2) - WMA(n), sqrt(n))``
|
||||
|
||||
Implemented in Rust — all WMA computations are in-process.
|
||||
"""
|
||||
c = _to_f64(close)
|
||||
try:
|
||||
return np.asarray(_rust_hull_ma(c, timeperiod))
|
||||
except ValueError as e:
|
||||
_normalize_rust_error(e)
|
||||
|
||||
|
||||
def CHANDELIER_EXIT(
|
||||
high: ArrayLike,
|
||||
low: ArrayLike,
|
||||
close: ArrayLike,
|
||||
timeperiod: int = 22,
|
||||
multiplier: float = 3.0,
|
||||
) -> tuple[np.ndarray, np.ndarray]:
|
||||
"""Chandelier Exit — ATR-based trailing stop levels.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
high : array-like
|
||||
low : array-like
|
||||
close : array-like
|
||||
timeperiod : int, default 22
|
||||
Lookback period for highest high / lowest low and ATR.
|
||||
multiplier : float, default 3.0
|
||||
ATR multiplier.
|
||||
|
||||
Returns
|
||||
-------
|
||||
long_exit, short_exit : numpy.ndarray
|
||||
|
||||
Notes
|
||||
-----
|
||||
Implemented in Rust with O(n) monotonic deque for rolling max/min.
|
||||
"""
|
||||
h = _to_f64(high)
|
||||
lo = _to_f64(low)
|
||||
c = _to_f64(close)
|
||||
try:
|
||||
long_exit, short_exit = _rust_chandelier_exit(h, lo, c, timeperiod, multiplier)
|
||||
except ValueError as e:
|
||||
_normalize_rust_error(e)
|
||||
return np.asarray(long_exit), np.asarray(short_exit)
|
||||
|
||||
|
||||
def VWMA(
|
||||
close: ArrayLike,
|
||||
volume: ArrayLike,
|
||||
timeperiod: int = 20,
|
||||
) -> np.ndarray:
|
||||
"""Volume Weighted Moving Average.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
close : array-like
|
||||
volume : array-like
|
||||
timeperiod : int, default 20
|
||||
|
||||
Returns
|
||||
-------
|
||||
numpy.ndarray
|
||||
|
||||
Notes
|
||||
-----
|
||||
``VWMA = sum(close * volume, n) / sum(volume, n)``
|
||||
Implemented in Rust with O(n) prefix-sum approach.
|
||||
"""
|
||||
c = _to_f64(close)
|
||||
v = _to_f64(volume)
|
||||
try:
|
||||
return np.asarray(_rust_vwma(c, v, timeperiod))
|
||||
except ValueError as e:
|
||||
_normalize_rust_error(e)
|
||||
|
||||
|
||||
def CHOPPINESS_INDEX(
|
||||
high: ArrayLike,
|
||||
low: ArrayLike,
|
||||
close: ArrayLike,
|
||||
timeperiod: int = 14,
|
||||
) -> np.ndarray:
|
||||
"""Choppiness Index — measures market choppiness (range-bound vs trending).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
high : array-like
|
||||
low : array-like
|
||||
close : array-like
|
||||
timeperiod : int, default 14
|
||||
|
||||
Returns
|
||||
-------
|
||||
numpy.ndarray
|
||||
Values in ``[0, 100]``. Values near 100 indicate choppy/range-bound
|
||||
markets; values near 0 indicate strong trends.
|
||||
|
||||
Notes
|
||||
-----
|
||||
``CI = 100 * log10(sum(ATR(1), n) / (highest_high − lowest_low)) / log10(n)``
|
||||
|
||||
Implemented in Rust with O(n) monotonic deques (no Python loop).
|
||||
"""
|
||||
h = _to_f64(high)
|
||||
lo = _to_f64(low)
|
||||
c = _to_f64(close)
|
||||
try:
|
||||
return np.asarray(_rust_choppiness_index(h, lo, c, timeperiod))
|
||||
except ValueError as e:
|
||||
_normalize_rust_error(e)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"VWAP",
|
||||
"SUPERTREND",
|
||||
"ICHIMOKU",
|
||||
"DONCHIAN",
|
||||
"PIVOT_POINTS",
|
||||
"KELTNER_CHANNELS",
|
||||
"HULL_MA",
|
||||
"CHANDELIER_EXIT",
|
||||
"VWMA",
|
||||
"CHOPPINESS_INDEX",
|
||||
]
|
||||
Reference in New Issue
Block a user