Files
ferro-ta/benchmarks/wrapper_registry.py
T
Pratik Bhadane 71b6343e92 feat: refresh benchmark coverage and harden CI tooling
Refresh the benchmark and performance surface across the repo. This updates the benchmark wrappers and helper scripts, regenerates the checked-in benchmark and perf-contract artifacts, and folds in the related roadmap, compatibility, and example notebook changes that belong with this performance-focused pass.

Harden the Python CI and local pre-push flow so the same checks pass reliably in both places. The workflow and pre-push script now use module-safe uv typecheck invocations, the Python test environment installs the optional MCP dependency needed by the MCP server tests, and one-off root benchmark outputs are ignored to keep the repo clean.

Align local tooling with the current project configuration by updating the Ruff pre-commit hook, tightening the API typing and MCP server helpers, and refreshing the lockfile to pick up the audited PyJWT fix while preserving the rest of the staged source changes.
2026-03-24 14:52:20 +05:30

2823 lines
59 KiB
Python

"""
Cross-library wrapper registry — comprehensive indicator coverage.
Unified interface: execute_indicator(library, indicator, data, df=None, **kwargs)
Supported libraries: ferro_ta, talib, pandas_ta, ta, tulipy, finta
50+ indicators across all categories.
"""
from __future__ import annotations
from typing import Any
import numpy as np
def _try_import(name):
try:
import importlib
return importlib.import_module(name)
except ImportError:
return None
_talib = _try_import("talib")
_pta = _try_import("pandas_ta")
_ta = _try_import("ta")
_tl = _try_import("tulipy")
_fi_m = _try_import("finta")
_fi = getattr(_fi_m, "TA", None) if _fi_m else None
def available_libraries():
libs = ["ferro_ta"]
if _talib:
libs.append("talib")
if _pta:
libs.append("pandas_ta")
if _ta:
libs.append("ta")
if _tl:
libs.append("tulipy")
if _fi:
libs.append("finta")
return libs
def is_supported(library: str, indicator: str) -> bool:
"""Return True if a wrapper exists for the given (library, indicator) pair."""
if library not in available_libraries():
return False
return (library, indicator) in REGISTRY
def _strip_nan(arr):
a = np.asarray(arr, dtype=np.float64).ravel()
return a[np.isfinite(a)]
def _c64(a):
return np.ascontiguousarray(a, dtype=np.float64)
def _empty():
return np.array([], dtype=np.float64)
def _first_col(df, prefix):
col = next((c for c in df.columns if c.startswith(prefix)), None)
return _strip_nan(df[col].values) if col is not None else _empty()
# ============================================================
# OVERLAP
# ============================================================
def _sma_ft(d, df, timeperiod=20, **_):
import ferro_ta
return _strip_nan(ferro_ta.SMA(d["close"], timeperiod=timeperiod))
def _sma_tl(d, df, timeperiod=20, **_):
return _strip_nan(_talib.SMA(d["close"], timeperiod=timeperiod))
def _sma_pt(d, df, timeperiod=20, **_):
return _strip_nan(_pta.sma(df["close"], length=timeperiod).values)
def _sma_ta(d, df, timeperiod=20, **_):
from ta.trend import SMAIndicator
return _strip_nan(
SMAIndicator(df["close"], window=timeperiod).sma_indicator().values
)
def _sma_tu(d, df, timeperiod=20, **_):
return _strip_nan(_tl.sma(_c64(d["close"]), period=timeperiod))
def _sma_fi(d, df, timeperiod=20, **_):
return _strip_nan(_fi.SMA(df, timeperiod).values)
def _ema_ft(d, df, timeperiod=20, **_):
import ferro_ta
return _strip_nan(ferro_ta.EMA(d["close"], timeperiod=timeperiod))
def _ema_tl(d, df, timeperiod=20, **_):
return _strip_nan(_talib.EMA(d["close"], timeperiod=timeperiod))
def _ema_pt(d, df, timeperiod=20, **_):
return _strip_nan(_pta.ema(df["close"], length=timeperiod).values)
def _ema_ta(d, df, timeperiod=20, **_):
from ta.trend import EMAIndicator
return _strip_nan(
EMAIndicator(df["close"], window=timeperiod).ema_indicator().values
)
def _ema_tu(d, df, timeperiod=20, **_):
return _strip_nan(_tl.ema(_c64(d["close"]), period=timeperiod))
def _ema_fi(d, df, timeperiod=20, **_):
return _strip_nan(_fi.EMA(df, timeperiod).values)
def _wma_ft(d, df, timeperiod=14, **_):
import ferro_ta
return _strip_nan(ferro_ta.WMA(d["close"], timeperiod=timeperiod))
def _wma_tl(d, df, timeperiod=14, **_):
return _strip_nan(_talib.WMA(d["close"], timeperiod=timeperiod))
def _wma_pt(d, df, timeperiod=14, **_):
return _strip_nan(_pta.wma(df["close"], length=timeperiod).values)
def _wma_ta(d, df, **_):
return _empty()
_wma_ta._stub = True
def _wma_tu(d, df, timeperiod=14, **_):
return _strip_nan(_tl.wma(_c64(d["close"]), period=timeperiod))
def _wma_fi(d, df, timeperiod=14, **_):
return _strip_nan(_fi.WMA(df, timeperiod).values)
def _dema_ft(d, df, timeperiod=20, **_):
import ferro_ta
return _strip_nan(ferro_ta.DEMA(d["close"], timeperiod=timeperiod))
def _dema_tl(d, df, timeperiod=20, **_):
return _strip_nan(_talib.DEMA(d["close"], timeperiod=timeperiod))
def _dema_pt(d, df, timeperiod=20, **_):
return _strip_nan(_pta.dema(df["close"], length=timeperiod).values)
def _dema_ta(d, df, **_):
return _empty()
_dema_ta._stub = True
def _dema_tu(d, df, timeperiod=20, **_):
return _strip_nan(_tl.dema(_c64(d["close"]), period=timeperiod))
def _dema_fi(d, df, timeperiod=20, **_):
return _strip_nan(_fi.DEMA(df, timeperiod).values)
def _tema_ft(d, df, timeperiod=20, **_):
import ferro_ta
return _strip_nan(ferro_ta.TEMA(d["close"], timeperiod=timeperiod))
def _tema_tl(d, df, timeperiod=20, **_):
return _strip_nan(_talib.TEMA(d["close"], timeperiod=timeperiod))
def _tema_pt(d, df, timeperiod=20, **_):
return _strip_nan(_pta.tema(df["close"], length=timeperiod).values)
def _tema_ta(d, df, **_):
return _empty()
_tema_ta._stub = True
def _tema_tu(d, df, timeperiod=20, **_):
return _strip_nan(_tl.tema(_c64(d["close"]), period=timeperiod))
def _tema_fi(d, df, timeperiod=20, **_):
return _strip_nan(_fi.TEMA(df, timeperiod).values)
def _t3_ft(d, df, timeperiod=5, **_):
import ferro_ta
return _strip_nan(ferro_ta.T3(d["close"], timeperiod=timeperiod))
def _t3_tl(d, df, timeperiod=5, **_):
return _strip_nan(_talib.T3(d["close"], timeperiod=timeperiod))
def _t3_pt(d, df, timeperiod=5, **_):
return _strip_nan(_pta.t3(df["close"], length=timeperiod).values)
def _t3_ta(d, df, **_):
return _empty()
_t3_ta._stub = True
def _t3_tu(d, df, **_):
return _empty()
_t3_tu._stub = True
def _t3_fi(d, df, **_):
return _empty()
_t3_fi._stub = True
def _trima_ft(d, df, timeperiod=20, **_):
import ferro_ta
return _strip_nan(ferro_ta.TRIMA(d["close"], timeperiod=timeperiod))
def _trima_tl(d, df, timeperiod=20, **_):
return _strip_nan(_talib.TRIMA(d["close"], timeperiod=timeperiod))
def _trima_pt(d, df, timeperiod=20, **_):
return _strip_nan(_pta.trima(df["close"], length=timeperiod).values)
def _trima_ta(d, df, **_):
return _empty()
_trima_ta._stub = True
def _trima_tu(d, df, timeperiod=20, **_):
return _strip_nan(_tl.trima(_c64(d["close"]), period=timeperiod))
def _trima_fi(d, df, timeperiod=20, **_):
return _strip_nan(_fi.TRIMA(df, timeperiod).values)
def _kama_ft(d, df, timeperiod=10, **_):
import ferro_ta
return _strip_nan(ferro_ta.KAMA(d["close"], timeperiod=timeperiod))
def _kama_tl(d, df, timeperiod=10, **_):
return _strip_nan(_talib.KAMA(d["close"], timeperiod=timeperiod))
def _kama_pt(d, df, timeperiod=10, **_):
return _strip_nan(_pta.kama(df["close"], length=timeperiod).values)
def _kama_ta(d, df, **_):
return _empty()
_kama_ta._stub = True
def _kama_tu(d, df, timeperiod=10, **_):
return _strip_nan(_tl.kama(_c64(d["close"]), period=timeperiod))
def _kama_fi(d, df, **_):
return _empty()
_kama_fi._stub = True
def _hma_ft(d, df, timeperiod=16, **_):
import ferro_ta
return _strip_nan(ferro_ta.HULL_MA(d["close"], timeperiod=timeperiod))
def _hma_tl(d, df, **_):
return _empty()
_hma_tl._stub = True
def _hma_pt(d, df, timeperiod=16, **_):
return _strip_nan(_pta.hma(df["close"], length=timeperiod).values)
def _hma_ta(d, df, **_):
return _empty()
_hma_ta._stub = True
def _hma_tu(d, df, timeperiod=16, **_):
return _strip_nan(_tl.hma(_c64(d["close"]), period=timeperiod))
def _hma_fi(d, df, timeperiod=16, **_):
return _strip_nan(_fi.HMA(df, timeperiod).values)
def _vwma_ft(d, df, timeperiod=20, **_):
import ferro_ta
return _strip_nan(ferro_ta.VWMA(d["close"], d["volume"], timeperiod=timeperiod))
def _vwma_tl(d, df, **_):
return _empty()
_vwma_tl._stub = True
def _vwma_pt(d, df, timeperiod=20, **_):
r = _pta.vwma(df["close"], df["volume"], length=timeperiod)
return _strip_nan(r.values) if r is not None else _empty()
def _vwma_ta(d, df, **_):
return _empty()
_vwma_ta._stub = True
def _vwma_tu(d, df, timeperiod=20, **_):
return _strip_nan(_tl.vwma(_c64(d["close"]), _c64(d["volume"]), period=timeperiod))
def _vwma_fi(d, df, **_):
return _empty()
_vwma_fi._stub = True
def _midpoint_ft(d, df, timeperiod=14, **_):
import ferro_ta
return _strip_nan(ferro_ta.MIDPOINT(d["close"], timeperiod=timeperiod))
def _midpoint_tl(d, df, timeperiod=14, **_):
return _strip_nan(_talib.MIDPOINT(d["close"], timeperiod=timeperiod))
def _midpoint_pt(d, df, **_):
return _empty()
_midpoint_pt._stub = True
def _midpoint_ta(d, df, **_):
return _empty()
_midpoint_ta._stub = True
def _midpoint_tu(d, df, **_):
return _empty()
_midpoint_tu._stub = True
def _midpoint_fi(d, df, **_):
return _empty()
_midpoint_fi._stub = True
def _midprice_ft(d, df, timeperiod=14, **_):
import ferro_ta
return _strip_nan(ferro_ta.MIDPRICE(d["high"], d["low"], timeperiod=timeperiod))
def _midprice_tl(d, df, timeperiod=14, **_):
return _strip_nan(_talib.MIDPRICE(d["high"], d["low"], timeperiod=timeperiod))
def _midprice_pt(d, df, **_):
return _empty()
_midprice_pt._stub = True
def _midprice_ta(d, df, **_):
return _empty()
_midprice_ta._stub = True
def _midprice_tu(d, df, **_):
return _empty()
_midprice_tu._stub = True
def _midprice_fi(d, df, **_):
return _empty()
_midprice_fi._stub = True
# ============================================================
# MOMENTUM
# ============================================================
def _rsi_ft(d, df, timeperiod=14, **_):
import ferro_ta
return _strip_nan(ferro_ta.RSI(d["close"], timeperiod=timeperiod))
def _rsi_tl(d, df, timeperiod=14, **_):
return _strip_nan(_talib.RSI(d["close"], timeperiod=timeperiod))
def _rsi_pt(d, df, timeperiod=14, **_):
return _strip_nan(_pta.rsi(df["close"], length=timeperiod).values)
def _rsi_ta(d, df, timeperiod=14, **_):
from ta.momentum import RSIIndicator
return _strip_nan(RSIIndicator(df["close"], window=timeperiod).rsi().values)
def _rsi_tu(d, df, timeperiod=14, **_):
return _strip_nan(_tl.rsi(_c64(d["close"]), period=timeperiod))
def _rsi_fi(d, df, timeperiod=14, **_):
return _strip_nan(_fi.RSI(df, timeperiod).values)
def _macd_ft(d, df, fastperiod=12, slowperiod=26, signalperiod=9, **_):
import ferro_ta
m, s, h = ferro_ta.MACD(
d["close"],
fastperiod=fastperiod,
slowperiod=slowperiod,
signalperiod=signalperiod,
)
return _strip_nan(m)
def _macd_tl(d, df, fastperiod=12, slowperiod=26, signalperiod=9, **_):
m, s, h = _talib.MACD(
d["close"],
fastperiod=fastperiod,
slowperiod=slowperiod,
signalperiod=signalperiod,
)
return _strip_nan(m)
def _macd_pt(d, df, fastperiod=12, slowperiod=26, signalperiod=9, **_):
r = _pta.macd(df["close"], fast=fastperiod, slow=slowperiod, signal=signalperiod)
return _first_col(r, "MACD_")
def _macd_ta(d, df, fastperiod=12, slowperiod=26, signalperiod=9, **_):
from ta.trend import MACD
return _strip_nan(
MACD(
df["close"],
window_fast=fastperiod,
window_slow=slowperiod,
window_sign=signalperiod,
)
.macd()
.values
)
def _macd_tu(d, df, fastperiod=12, slowperiod=26, signalperiod=9, **_):
m, s, h = _tl.macd(
_c64(d["close"]),
short_period=fastperiod,
long_period=slowperiod,
signal_period=signalperiod,
)
return _strip_nan(m)
def _macd_fi(d, df, fastperiod=12, slowperiod=26, signalperiod=9, **_):
return _strip_nan(_fi.MACD(df, fastperiod, slowperiod, signalperiod)["MACD"].values)
def _stoch_ft(d, df, fastk_period=14, slowk_period=3, slowd_period=3, **_):
import ferro_ta
k, dd = ferro_ta.STOCH(
d["high"],
d["low"],
d["close"],
fastk_period=fastk_period,
slowk_period=slowk_period,
slowd_period=slowd_period,
)
return _strip_nan(k)
def _stoch_tl(d, df, fastk_period=14, slowk_period=3, slowd_period=3, **_):
k, dd = _talib.STOCH(
d["high"],
d["low"],
d["close"],
fastk_period=fastk_period,
slowk_period=slowk_period,
slowd_period=slowd_period,
)
return _strip_nan(k)
def _stoch_pt(d, df, fastk_period=14, slowk_period=3, slowd_period=3, **_):
r = _pta.stoch(df["high"], df["low"], df["close"], k=fastk_period, d=slowd_period)
return _first_col(r, "STOCHk_") if r is not None else _empty()
def _stoch_ta(d, df, fastk_period=14, **_):
from ta.momentum import StochasticOscillator
return _strip_nan(
StochasticOscillator(df["high"], df["low"], df["close"], window=fastk_period)
.stoch()
.values
)
def _stoch_tu(d, df, fastk_period=14, slowk_period=3, slowd_period=3, **_):
k, dd = _tl.stoch(
_c64(d["high"]),
_c64(d["low"]),
_c64(d["close"]),
pct_k_period=fastk_period,
pct_k_slowing_period=slowk_period,
pct_d_period=slowd_period,
)
return _strip_nan(k)
def _stoch_fi(d, df, fastk_period=14, **_):
return _strip_nan(_fi.STOCH(df, fastk_period).values)
def _cci_ft(d, df, timeperiod=14, **_):
import ferro_ta
return _strip_nan(
ferro_ta.CCI(d["high"], d["low"], d["close"], timeperiod=timeperiod)
)
def _cci_tl(d, df, timeperiod=14, **_):
return _strip_nan(
_talib.CCI(d["high"], d["low"], d["close"], timeperiod=timeperiod)
)
def _cci_pt(d, df, timeperiod=14, **_):
return _strip_nan(
_pta.cci(df["high"], df["low"], df["close"], length=timeperiod).values
)
def _cci_ta(d, df, timeperiod=14, **_):
from ta.trend import CCIIndicator
return _strip_nan(
CCIIndicator(df["high"], df["low"], df["close"], window=timeperiod).cci().values
)
def _cci_tu(d, df, timeperiod=14, **_):
return _strip_nan(
_tl.cci(_c64(d["high"]), _c64(d["low"]), _c64(d["close"]), period=timeperiod)
)
def _cci_fi(d, df, timeperiod=14, **_):
return _strip_nan(_fi.CCI(df, timeperiod).values)
def _willr_ft(d, df, timeperiod=14, **_):
import ferro_ta
return _strip_nan(
ferro_ta.WILLR(d["high"], d["low"], d["close"], timeperiod=timeperiod)
)
def _willr_tl(d, df, timeperiod=14, **_):
return _strip_nan(
_talib.WILLR(d["high"], d["low"], d["close"], timeperiod=timeperiod)
)
def _willr_pt(d, df, timeperiod=14, **_):
return _strip_nan(
_pta.willr(df["high"], df["low"], df["close"], length=timeperiod).values
)
def _willr_ta(d, df, timeperiod=14, **_):
from ta.momentum import WilliamsRIndicator
return _strip_nan(
WilliamsRIndicator(df["high"], df["low"], df["close"], lbp=timeperiod)
.williams_r()
.values
)
def _willr_tu(d, df, timeperiod=14, **_):
return _strip_nan(
_tl.willr(_c64(d["high"]), _c64(d["low"]), _c64(d["close"]), period=timeperiod)
)
def _willr_fi(d, df, timeperiod=14, **_):
return _strip_nan(_fi.WILLIAMS(df, timeperiod).values)
def _aroon_ft(d, df, timeperiod=14, **_):
import ferro_ta
dn, up = ferro_ta.AROON(d["high"], d["low"], timeperiod=timeperiod)
return _strip_nan(up)
def _aroon_tl(d, df, timeperiod=14, **_):
dn, up = _talib.AROON(d["high"], d["low"], timeperiod=timeperiod)
return _strip_nan(up)
def _aroon_pt(d, df, timeperiod=14, **_):
r = _pta.aroon(df["high"], df["low"], length=timeperiod)
return _first_col(r, "AROONU_") if r is not None else _empty()
def _aroon_ta(d, df, timeperiod=14, **_):
from ta.trend import AroonIndicator
return _strip_nan(
AroonIndicator(df["high"], df["low"], window=timeperiod).aroon_up().values
)
def _aroon_tu(d, df, timeperiod=14, **_):
dn, up = _tl.aroon(_c64(d["high"]), _c64(d["low"]), period=timeperiod)
return _strip_nan(up)
def _aroon_fi(d, df, **_):
return _empty()
_aroon_fi._stub = True
def _aroonosc_ft(d, df, timeperiod=14, **_):
import ferro_ta
return _strip_nan(ferro_ta.AROONOSC(d["high"], d["low"], timeperiod=timeperiod))
def _aroonosc_tl(d, df, timeperiod=14, **_):
return _strip_nan(_talib.AROONOSC(d["high"], d["low"], timeperiod=timeperiod))
def _aroonosc_pt(d, df, **_):
return _empty()
_aroonosc_pt._stub = True
def _aroonosc_ta(d, df, **_):
return _empty()
_aroonosc_ta._stub = True
def _aroonosc_tu(d, df, timeperiod=14, **_):
return _strip_nan(_tl.aroonosc(_c64(d["high"]), _c64(d["low"]), period=timeperiod))
def _aroonosc_fi(d, df, **_):
return _empty()
_aroonosc_fi._stub = True
def _adx_ft(d, df, timeperiod=14, **_):
import ferro_ta
return _strip_nan(
ferro_ta.ADX(d["high"], d["low"], d["close"], timeperiod=timeperiod)
)
def _adx_tl(d, df, timeperiod=14, **_):
return _strip_nan(
_talib.ADX(d["high"], d["low"], d["close"], timeperiod=timeperiod)
)
def _adx_pt(d, df, timeperiod=14, **_):
r = _pta.adx(df["high"], df["low"], df["close"], length=timeperiod)
return _first_col(r, "ADX_")
def _adx_ta(d, df, timeperiod=14, **_):
from ta.trend import ADXIndicator
return _strip_nan(
ADXIndicator(df["high"], df["low"], df["close"], window=timeperiod).adx().values
)
def _adx_tu(d, df, timeperiod=14, **_):
return _strip_nan(
_tl.adx(_c64(d["high"]), _c64(d["low"]), _c64(d["close"]), period=timeperiod)
)
def _adx_fi(d, df, **_):
return _empty()
_adx_fi._stub = True
def _mom_ft(d, df, timeperiod=10, **_):
import ferro_ta
return _strip_nan(ferro_ta.MOM(d["close"], timeperiod=timeperiod))
def _mom_tl(d, df, timeperiod=10, **_):
return _strip_nan(_talib.MOM(d["close"], timeperiod=timeperiod))
def _mom_pt(d, df, timeperiod=10, **_):
return _strip_nan(_pta.mom(df["close"], length=timeperiod).values)
def _mom_ta(d, df, **_):
return _empty()
_mom_ta._stub = True
def _mom_tu(d, df, timeperiod=10, **_):
return _strip_nan(_tl.mom(_c64(d["close"]), period=timeperiod))
def _mom_fi(d, df, timeperiod=10, **_):
return _strip_nan(_fi.MOM(df, timeperiod).values)
def _roc_ft(d, df, timeperiod=10, **_):
import ferro_ta
return _strip_nan(ferro_ta.ROC(d["close"], timeperiod=timeperiod))
def _roc_tl(d, df, timeperiod=10, **_):
return _strip_nan(_talib.ROC(d["close"], timeperiod=timeperiod))
def _roc_pt(d, df, timeperiod=10, **_):
return _strip_nan(_pta.roc(df["close"], length=timeperiod).values)
def _roc_ta(d, df, timeperiod=10, **_):
from ta.momentum import ROCIndicator
return _strip_nan(ROCIndicator(df["close"], window=timeperiod).roc().values)
def _roc_tu(d, df, timeperiod=10, **_):
return _strip_nan(_tl.roc(_c64(d["close"]), period=timeperiod) * 100.0)
def _roc_fi(d, df, timeperiod=10, **_):
return _strip_nan(_fi.ROC(df, timeperiod).values)
def _cmo_ft(d, df, timeperiod=14, **_):
import ferro_ta
return _strip_nan(ferro_ta.CMO(d["close"], timeperiod=timeperiod))
def _cmo_tl(d, df, timeperiod=14, **_):
return _strip_nan(_talib.CMO(d["close"], timeperiod=timeperiod))
def _cmo_pt(d, df, timeperiod=14, **_):
return _strip_nan(_pta.cmo(df["close"], length=timeperiod).values)
def _cmo_ta(d, df, **_):
return _empty()
_cmo_ta._stub = True
def _cmo_tu(d, df, timeperiod=14, **_):
return _strip_nan(_tl.cmo(_c64(d["close"]), period=timeperiod))
def _cmo_fi(d, df, timeperiod=14, **_):
return _strip_nan(_fi.CMO(df, timeperiod).values)
def _ppo_ft(d, df, fastperiod=12, slowperiod=26, **_):
import ferro_ta
ppo, sig, hist = ferro_ta.PPO(
d["close"], fastperiod=fastperiod, slowperiod=slowperiod
)
return _strip_nan(ppo)
def _ppo_tl(d, df, fastperiod=12, slowperiod=26, **_):
return _strip_nan(
_talib.PPO(d["close"], fastperiod=fastperiod, slowperiod=slowperiod)
)
def _ppo_pt(d, df, fastperiod=12, slowperiod=26, **_):
r = _pta.ppo(df["close"], fast=fastperiod, slow=slowperiod)
return _strip_nan(r.iloc[:, 0].values) if r is not None else _empty()
def _ppo_ta(d, df, **_):
return _empty()
_ppo_ta._stub = True
def _ppo_tu(d, df, fastperiod=12, slowperiod=26, **_):
return _strip_nan(
_tl.ppo(_c64(d["close"]), short_period=fastperiod, long_period=slowperiod)
)
def _ppo_fi(d, df, fastperiod=12, slowperiod=26, **_):
return _strip_nan(_fi.PPO(df, fastperiod, slowperiod).values)
def _trix_ft(d, df, timeperiod=18, **_):
import ferro_ta
return _strip_nan(ferro_ta.TRIX(d["close"], timeperiod=timeperiod))
def _trix_tl(d, df, timeperiod=18, **_):
return _strip_nan(_talib.TRIX(d["close"], timeperiod=timeperiod))
def _trix_pt(d, df, timeperiod=18, **_):
r = _pta.trix(df["close"], length=timeperiod)
return _strip_nan(r.iloc[:, 0].values) if r is not None else _empty()
def _trix_ta(d, df, timeperiod=18, **_):
from ta.trend import TRIXIndicator
return _strip_nan(TRIXIndicator(df["close"], window=timeperiod).trix().values)
def _trix_tu(d, df, timeperiod=18, **_):
return _strip_nan(_tl.trix(_c64(d["close"]), period=timeperiod))
def _trix_fi(d, df, timeperiod=18, **_):
return _strip_nan(_fi.TRIX(df, timeperiod).values)
def _tsf_ft(d, df, timeperiod=14, **_):
import ferro_ta
return _strip_nan(ferro_ta.TSF(d["close"], timeperiod=timeperiod))
def _tsf_tl(d, df, timeperiod=14, **_):
return _strip_nan(_talib.TSF(d["close"], timeperiod=timeperiod))
def _tsf_pt(d, df, **_):
return _empty()
_tsf_pt._stub = True
def _tsf_ta(d, df, **_):
return _empty()
_tsf_ta._stub = True
def _tsf_tu(d, df, timeperiod=14, **_):
return _strip_nan(_tl.tsf(_c64(d["close"]), period=timeperiod))
def _tsf_fi(d, df, **_):
return _empty()
_tsf_fi._stub = True
def _ultosc_ft(d, df, timeperiod1=7, timeperiod2=14, timeperiod3=28, **_):
import ferro_ta
return _strip_nan(
ferro_ta.ULTOSC(
d["high"],
d["low"],
d["close"],
timeperiod1=timeperiod1,
timeperiod2=timeperiod2,
timeperiod3=timeperiod3,
)
)
def _ultosc_tl(d, df, timeperiod1=7, timeperiod2=14, timeperiod3=28, **_):
return _strip_nan(
_talib.ULTOSC(
d["high"],
d["low"],
d["close"],
timeperiod1=timeperiod1,
timeperiod2=timeperiod2,
timeperiod3=timeperiod3,
)
)
def _ultosc_pt(d, df, **_):
return _empty()
_ultosc_pt._stub = True
def _ultosc_ta(d, df, timeperiod1=7, timeperiod2=14, timeperiod3=28, **_):
from ta.momentum import UltimateOscillator
return _strip_nan(
UltimateOscillator(
df["high"],
df["low"],
df["close"],
window1=timeperiod1,
window2=timeperiod2,
window3=timeperiod3,
)
.ultimate_oscillator()
.values
)
def _ultosc_tu(d, df, timeperiod1=7, timeperiod2=14, timeperiod3=28, **_):
return _strip_nan(
_tl.ultosc(
_c64(d["high"]),
_c64(d["low"]),
_c64(d["close"]),
short_period=timeperiod1,
medium_period=timeperiod2,
long_period=timeperiod3,
)
)
def _ultosc_fi(d, df, **_):
return _empty()
_ultosc_fi._stub = True
def _bop_ft(d, df, **_):
import ferro_ta
return _strip_nan(ferro_ta.BOP(d["open"], d["high"], d["low"], d["close"]))
def _bop_tl(d, df, **_):
return _strip_nan(_talib.BOP(d["open"], d["high"], d["low"], d["close"]))
def _bop_pt(d, df, **_):
r = _pta.bop(df["open"], df["high"], df["low"], df["close"])
return _strip_nan(r.values) if r is not None else _empty()
def _bop_ta(d, df, **_):
return _empty()
_bop_ta._stub = True
def _bop_tu(d, df, **_):
return _strip_nan(
_tl.bop(_c64(d["open"]), _c64(d["high"]), _c64(d["low"]), _c64(d["close"]))
)
def _bop_fi(d, df, **_):
return _empty()
_bop_fi._stub = True
def _plusdi_ft(d, df, timeperiod=14, **_):
import ferro_ta
return _strip_nan(
ferro_ta.PLUS_DI(d["high"], d["low"], d["close"], timeperiod=timeperiod)
)
def _plusdi_tl(d, df, timeperiod=14, **_):
return _strip_nan(
_talib.PLUS_DI(d["high"], d["low"], d["close"], timeperiod=timeperiod)
)
def _plusdi_pt(d, df, timeperiod=14, **_):
r = _pta.adx(df["high"], df["low"], df["close"], length=timeperiod)
return _first_col(r, "DMP_") if r is not None else _empty()
def _plusdi_ta(d, df, **_):
return _empty()
_plusdi_ta._stub = True
def _plusdi_tu(d, df, timeperiod=14, **_):
pdi, mdi = _tl.di(
_c64(d["high"]), _c64(d["low"]), _c64(d["close"]), period=timeperiod
)
return _strip_nan(pdi)
def _plusdi_fi(d, df, **_):
return _empty()
_plusdi_fi._stub = True
def _minusdi_ft(d, df, timeperiod=14, **_):
import ferro_ta
return _strip_nan(
ferro_ta.MINUS_DI(d["high"], d["low"], d["close"], timeperiod=timeperiod)
)
def _minusdi_tl(d, df, timeperiod=14, **_):
return _strip_nan(
_talib.MINUS_DI(d["high"], d["low"], d["close"], timeperiod=timeperiod)
)
def _minusdi_pt(d, df, **_):
return _empty()
_minusdi_pt._stub = True
def _minusdi_ta(d, df, **_):
return _empty()
_minusdi_ta._stub = True
def _minusdi_tu(d, df, timeperiod=14, **_):
pdi, mdi = _tl.di(
_c64(d["high"]), _c64(d["low"]), _c64(d["close"]), period=timeperiod
)
return _strip_nan(mdi)
def _minusdi_fi(d, df, **_):
return _empty()
_minusdi_fi._stub = True
# ============================================================
# VOLATILITY
# ============================================================
def _bb_ft(d, df, timeperiod=20, nbdevup=2.0, nbdevdn=2.0, **_):
import ferro_ta
u, m, l = ferro_ta.BBANDS(
d["close"], timeperiod=timeperiod, nbdevup=nbdevup, nbdevdn=nbdevdn
)
return _strip_nan(u)
def _bb_tl(d, df, timeperiod=20, nbdevup=2.0, nbdevdn=2.0, **_):
u, m, l = _talib.BBANDS(
d["close"], timeperiod=timeperiod, nbdevup=nbdevup, nbdevdn=nbdevdn
)
return _strip_nan(u)
def _bb_pt(d, df, timeperiod=20, nbdevup=2.0, **_):
r = _pta.bbands(df["close"], length=timeperiod, std=nbdevup)
return _first_col(r, "BBU_")
def _bb_ta(d, df, timeperiod=20, nbdevup=2.0, **_):
from ta.volatility import BollingerBands
return _strip_nan(
BollingerBands(df["close"], window=timeperiod, window_dev=nbdevup)
.bollinger_hband()
.values
)
def _bb_tu(d, df, timeperiod=20, nbdevup=2.0, **_):
lo, mi, up = _tl.bbands(_c64(d["close"]), period=timeperiod, stddev=nbdevup)
return _strip_nan(up)
def _bb_fi(d, df, timeperiod=20, **_):
return _strip_nan(_fi.BBANDS(df, timeperiod)["BB_UPPER"].values)
def _atr_ft(d, df, timeperiod=14, **_):
import ferro_ta
return _strip_nan(
ferro_ta.ATR(d["high"], d["low"], d["close"], timeperiod=timeperiod)
)
def _atr_tl(d, df, timeperiod=14, **_):
return _strip_nan(
_talib.ATR(d["high"], d["low"], d["close"], timeperiod=timeperiod)
)
def _atr_pt(d, df, timeperiod=14, **_):
return _strip_nan(
_pta.atr(df["high"], df["low"], df["close"], length=timeperiod).values
)
def _atr_ta(d, df, timeperiod=14, **_):
from ta.volatility import AverageTrueRange
return _strip_nan(
AverageTrueRange(df["high"], df["low"], df["close"], window=timeperiod)
.average_true_range()
.values
)
def _atr_tu(d, df, timeperiod=14, **_):
return _strip_nan(
_tl.atr(_c64(d["high"]), _c64(d["low"]), _c64(d["close"]), period=timeperiod)
)
def _atr_fi(d, df, timeperiod=14, **_):
return _strip_nan(_fi.ATR(df, timeperiod).values)
def _natr_ft(d, df, timeperiod=14, **_):
import ferro_ta
return _strip_nan(
ferro_ta.NATR(d["high"], d["low"], d["close"], timeperiod=timeperiod)
)
def _natr_tl(d, df, timeperiod=14, **_):
return _strip_nan(
_talib.NATR(d["high"], d["low"], d["close"], timeperiod=timeperiod)
)
def _natr_pt(d, df, timeperiod=14, **_):
return _strip_nan(
_pta.natr(df["high"], df["low"], df["close"], length=timeperiod).values
)
def _natr_ta(d, df, **_):
return _empty()
_natr_ta._stub = True
def _natr_tu(d, df, timeperiod=14, **_):
return _strip_nan(
_tl.natr(_c64(d["high"]), _c64(d["low"]), _c64(d["close"]), period=timeperiod)
)
def _natr_fi(d, df, **_):
return _empty()
_natr_fi._stub = True
def _trange_ft(d, df, **_):
import ferro_ta
return _strip_nan(ferro_ta.TRANGE(d["high"], d["low"], d["close"]))
def _trange_tl(d, df, **_):
return _strip_nan(_talib.TRANGE(d["high"], d["low"], d["close"]))
def _trange_pt(d, df, **_):
r = _pta.true_range(df["high"], df["low"], df["close"])
return _strip_nan(r.values) if r is not None else _empty()
def _trange_ta(d, df, **_):
return _empty()
_trange_ta._stub = True
def _trange_tu(d, df, **_):
return _strip_nan(_tl.tr(_c64(d["high"]), _c64(d["low"]), _c64(d["close"])))
def _trange_fi(d, df, **_):
return _strip_nan(_fi.TR(df).values)
def _stddev_ft(d, df, timeperiod=20, **_):
import ferro_ta
return _strip_nan(ferro_ta.STDDEV(d["close"], timeperiod=timeperiod))
def _stddev_tl(d, df, timeperiod=20, **_):
return _strip_nan(_talib.STDDEV(d["close"], timeperiod=timeperiod))
def _stddev_pt(d, df, timeperiod=20, **_):
r = _pta.stdev(df["close"], length=timeperiod)
return _strip_nan(r.values) if r is not None else _empty()
def _stddev_ta(d, df, **_):
return _empty()
_stddev_ta._stub = True
def _stddev_tu(d, df, timeperiod=20, **_):
return _strip_nan(_tl.stddev(_c64(d["close"]), period=timeperiod))
def _stddev_fi(d, df, timeperiod=20, **_):
return _strip_nan(_fi.MSD(df, timeperiod).values)
def _var_ft(d, df, timeperiod=20, **_):
import ferro_ta
return _strip_nan(ferro_ta.VAR(d["close"], timeperiod=timeperiod))
def _var_tl(d, df, timeperiod=20, **_):
return _strip_nan(_talib.VAR(d["close"], timeperiod=timeperiod))
def _var_pt(d, df, timeperiod=20, **_):
r = _pta.variance(df["close"], length=timeperiod)
return _strip_nan(r.values) if r is not None else _empty()
def _var_ta(d, df, **_):
return _empty()
_var_ta._stub = True
def _var_tu(d, df, timeperiod=20, **_):
return _strip_nan(_tl.var(_c64(d["close"]), period=timeperiod))
def _var_fi(d, df, **_):
return _empty()
_var_fi._stub = True
def _sar_ft(d, df, acceleration=0.02, maximum=0.2, **_):
import ferro_ta
return _strip_nan(
ferro_ta.SAR(d["high"], d["low"], acceleration=acceleration, maximum=maximum)
)
def _sar_tl(d, df, acceleration=0.02, maximum=0.2, **_):
return _strip_nan(
_talib.SAR(d["high"], d["low"], acceleration=acceleration, maximum=maximum)
)
def _sar_pt(d, df, **_):
return _empty()
_sar_pt._stub = True
def _sar_ta(d, df, **_):
return _empty()
_sar_ta._stub = True
def _sar_tu(d, df, acceleration=0.02, maximum=0.2, **_):
return _strip_nan(
_tl.psar(
_c64(d["high"]),
_c64(d["low"]),
acceleration_factor_step=acceleration,
acceleration_factor_maximum=maximum,
)
)
def _sar_fi(d, df, **_):
return _empty()
_sar_fi._stub = True
def _kc_ft(d, df, timeperiod=20, **_):
import ferro_ta
u, m, l = ferro_ta.KELTNER_CHANNELS(
d["high"], d["low"], d["close"], timeperiod=timeperiod
)
return _strip_nan(u)
def _kc_tl(d, df, **_):
return _empty()
_kc_tl._stub = True
def _kc_pt(d, df, timeperiod=20, **_):
r = _pta.kc(df["high"], df["low"], df["close"], length=timeperiod)
if r is None:
return _empty()
col = next(
(c for c in r.columns if "UCe" in c or "UB" in c or c.endswith("U")), None
)
return _strip_nan(r[col].values) if col else _first_col(r, "KC")
def _kc_ta(d, df, timeperiod=20, **_):
from ta.volatility import KeltnerChannel
return _strip_nan(
KeltnerChannel(df["high"], df["low"], df["close"], window=timeperiod)
.keltner_channel_hband()
.values
)
def _kc_tu(d, df, **_):
return _empty()
_kc_tu._stub = True
def _kc_fi(d, df, **_):
return _empty()
_kc_fi._stub = True
def _donchian_ft(d, df, timeperiod=20, **_):
import ferro_ta
u, m, l = ferro_ta.DONCHIAN(d["high"], d["low"], timeperiod=timeperiod)
return _strip_nan(u)
def _donchian_tl(d, df, **_):
return _empty()
_donchian_tl._stub = True
def _donchian_pt(d, df, timeperiod=20, **_):
r = _pta.donchian(
df["high"], df["low"], lower_length=timeperiod, upper_length=timeperiod
)
return _first_col(r, "DCU_") if r is not None else _empty()
def _donchian_ta(d, df, timeperiod=20, **_):
from ta.volatility import DonchianChannel
return _strip_nan(
DonchianChannel(df["high"], df["low"], df["close"], window=timeperiod)
.donchian_channel_hband()
.values
)
def _donchian_tu(d, df, **_):
return _empty()
_donchian_tu._stub = True
def _donchian_fi(d, df, **_):
return _empty()
_donchian_fi._stub = True
def _supertrend_ft(d, df, timeperiod=7, **_):
import ferro_ta
st, dir_ = ferro_ta.SUPERTREND(
d["high"], d["low"], d["close"], timeperiod=timeperiod
)
return _strip_nan(st)
def _supertrend_tl(d, df, **_):
return _empty()
_supertrend_tl._stub = True
def _supertrend_pt(d, df, timeperiod=7, **_):
r = _pta.supertrend(df["high"], df["low"], df["close"], length=timeperiod)
return _first_col(r, "SUPERT_") if r is not None else _empty()
def _supertrend_ta(d, df, **_):
return _empty()
_supertrend_ta._stub = True
def _supertrend_tu(d, df, **_):
return _empty()
_supertrend_tu._stub = True
def _supertrend_fi(d, df, **_):
return _empty()
_supertrend_fi._stub = True
def _chop_ft(d, df, timeperiod=14, **_):
import ferro_ta
return _strip_nan(
ferro_ta.CHOPPINESS_INDEX(
d["high"], d["low"], d["close"], timeperiod=timeperiod
)
)
def _chop_tl(d, df, **_):
return _empty()
_chop_tl._stub = True
def _chop_pt(d, df, timeperiod=14, **_):
r = _pta.chop(df["high"], df["low"], df["close"], length=timeperiod)
return _strip_nan(r.values) if r is not None else _empty()
def _chop_ta(d, df, **_):
return _empty()
_chop_ta._stub = True
def _chop_tu(d, df, **_):
return _empty()
_chop_tu._stub = True
def _chop_fi(d, df, **_):
return _empty()
_chop_fi._stub = True
# ============================================================
# VOLUME
# ============================================================
def _obv_ft(d, df, **_):
import ferro_ta
return _strip_nan(ferro_ta.OBV(d["close"], d["volume"]))
def _obv_tl(d, df, **_):
return _strip_nan(_talib.OBV(d["close"], d["volume"]))
def _obv_pt(d, df, **_):
return _strip_nan(_pta.obv(df["close"], df["volume"]).values)
def _obv_ta(d, df, **_):
from ta.volume import OnBalanceVolumeIndicator
return _strip_nan(
OnBalanceVolumeIndicator(df["close"], df["volume"]).on_balance_volume().values
)
def _obv_tu(d, df, **_):
return _strip_nan(_tl.obv(_c64(d["close"]), _c64(d["volume"])))
def _obv_fi(d, df, **_):
return _strip_nan(_fi.OBV(df).values)
def _ad_ft(d, df, **_):
import ferro_ta
return _strip_nan(ferro_ta.AD(d["high"], d["low"], d["close"], d["volume"]))
def _ad_tl(d, df, **_):
return _strip_nan(_talib.AD(d["high"], d["low"], d["close"], d["volume"]))
def _ad_pt(d, df, **_):
return _strip_nan(_pta.ad(df["high"], df["low"], df["close"], df["volume"]).values)
def _ad_ta(d, df, **_):
from ta.volume import AccDistIndexIndicator
return _strip_nan(
AccDistIndexIndicator(df["high"], df["low"], df["close"], df["volume"])
.acc_dist_index()
.values
)
def _ad_tu(d, df, **_):
return _strip_nan(
_tl.ad(_c64(d["high"]), _c64(d["low"]), _c64(d["close"]), _c64(d["volume"]))
)
def _ad_fi(d, df, **_):
return _empty()
_ad_fi._stub = True
def _adosc_ft(d, df, fastperiod=3, slowperiod=10, **_):
import ferro_ta
return _strip_nan(
ferro_ta.ADOSC(
d["high"],
d["low"],
d["close"],
d["volume"],
fastperiod=fastperiod,
slowperiod=slowperiod,
)
)
def _adosc_tl(d, df, fastperiod=3, slowperiod=10, **_):
return _strip_nan(
_talib.ADOSC(
d["high"],
d["low"],
d["close"],
d["volume"],
fastperiod=fastperiod,
slowperiod=slowperiod,
)
)
def _adosc_pt(d, df, fastperiod=3, slowperiod=10, **_):
return _strip_nan(
_pta.adosc(
df["high"],
df["low"],
df["close"],
df["volume"],
fast=fastperiod,
slow=slowperiod,
).values
)
def _adosc_ta(d, df, **_):
return _empty()
_adosc_ta._stub = True
def _adosc_tu(d, df, fastperiod=3, slowperiod=10, **_):
return _strip_nan(
_tl.adosc(
_c64(d["high"]),
_c64(d["low"]),
_c64(d["close"]),
_c64(d["volume"]),
short_period=fastperiod,
long_period=slowperiod,
)
)
def _adosc_fi(d, df, **_):
return _empty()
_adosc_fi._stub = True
def _mfi_ft(d, df, timeperiod=14, **_):
import ferro_ta
return _strip_nan(
ferro_ta.MFI(
d["high"], d["low"], d["close"], d["volume"], timeperiod=timeperiod
)
)
def _mfi_tl(d, df, timeperiod=14, **_):
return _strip_nan(
_talib.MFI(d["high"], d["low"], d["close"], d["volume"], timeperiod=timeperiod)
)
def _mfi_pt(d, df, timeperiod=14, **_):
return _strip_nan(
_pta.mfi(
df["high"], df["low"], df["close"], df["volume"], length=timeperiod
).values
)
def _mfi_ta(d, df, timeperiod=14, **_):
from ta.volume import MFIIndicator
return _strip_nan(
MFIIndicator(
df["high"], df["low"], df["close"], df["volume"], window=timeperiod
)
.money_flow_index()
.values
)
def _mfi_tu(d, df, timeperiod=14, **_):
return _strip_nan(
_tl.mfi(
_c64(d["high"]),
_c64(d["low"]),
_c64(d["close"]),
_c64(d["volume"]),
period=timeperiod,
)
)
def _mfi_fi(d, df, timeperiod=14, **_):
return _strip_nan(_fi.MFI(df, timeperiod).values)
def _vwap_ft(d, df, **_):
import ferro_ta
return _strip_nan(ferro_ta.VWAP(d["high"], d["low"], d["close"], d["volume"]))
def _vwap_tl(d, df, **_):
return _empty()
_vwap_tl._stub = True
def _vwap_pt(d, df, **_):
r = _pta.vwap(df["high"], df["low"], df["close"], df["volume"])
return _strip_nan(r.values) if r is not None else _empty()
def _vwap_ta(d, df, **_):
return _empty()
_vwap_ta._stub = True
def _vwap_tu(d, df, **_):
return _empty()
_vwap_tu._stub = True
def _vwap_fi(d, df, **_):
return _strip_nan(_fi.VWAP(df).values)
# ============================================================
# PRICE TRANSFORM
# ============================================================
def _avgprice_ft(d, df, **_):
import ferro_ta
return _strip_nan(ferro_ta.AVGPRICE(d["open"], d["high"], d["low"], d["close"]))
def _avgprice_tl(d, df, **_):
return _strip_nan(_talib.AVGPRICE(d["open"], d["high"], d["low"], d["close"]))
def _avgprice_pt(d, df, **_):
return _empty()
_avgprice_pt._stub = True
def _avgprice_ta(d, df, **_):
return _empty()
_avgprice_ta._stub = True
def _avgprice_tu(d, df, **_):
return _strip_nan(
_tl.avgprice(_c64(d["open"]), _c64(d["high"]), _c64(d["low"]), _c64(d["close"]))
)
def _avgprice_fi(d, df, **_):
return _empty()
_avgprice_fi._stub = True
def _medprice_ft(d, df, **_):
import ferro_ta
return _strip_nan(ferro_ta.MEDPRICE(d["high"], d["low"]))
def _medprice_tl(d, df, **_):
return _strip_nan(_talib.MEDPRICE(d["high"], d["low"]))
def _medprice_pt(d, df, **_):
return _empty()
_medprice_pt._stub = True
def _medprice_ta(d, df, **_):
return _empty()
_medprice_ta._stub = True
def _medprice_tu(d, df, **_):
return _strip_nan(_tl.medprice(_c64(d["high"]), _c64(d["low"])))
def _medprice_fi(d, df, **_):
return _empty()
_medprice_fi._stub = True
def _typprice_ft(d, df, **_):
import ferro_ta
return _strip_nan(ferro_ta.TYPPRICE(d["high"], d["low"], d["close"]))
def _typprice_tl(d, df, **_):
return _strip_nan(_talib.TYPPRICE(d["high"], d["low"], d["close"]))
def _typprice_pt(d, df, **_):
return _empty()
_typprice_pt._stub = True
def _typprice_ta(d, df, **_):
return _empty()
_typprice_ta._stub = True
def _typprice_tu(d, df, **_):
return _strip_nan(_tl.typprice(_c64(d["high"]), _c64(d["low"]), _c64(d["close"])))
def _typprice_fi(d, df, **_):
return _empty()
_typprice_fi._stub = True
def _wclprice_ft(d, df, **_):
import ferro_ta
return _strip_nan(ferro_ta.WCLPRICE(d["high"], d["low"], d["close"]))
def _wclprice_tl(d, df, **_):
return _strip_nan(_talib.WCLPRICE(d["high"], d["low"], d["close"]))
def _wclprice_pt(d, df, **_):
return _empty()
_wclprice_pt._stub = True
def _wclprice_ta(d, df, **_):
return _empty()
_wclprice_ta._stub = True
def _wclprice_tu(d, df, **_):
return _strip_nan(_tl.wcprice(_c64(d["high"]), _c64(d["low"]), _c64(d["close"])))
def _wclprice_fi(d, df, **_):
return _empty()
_wclprice_fi._stub = True
# ============================================================
# MATH
# ============================================================
def _sqrt_ft(d, df, **_):
import ferro_ta
return _strip_nan(ferro_ta.SQRT(d["close"]))
def _sqrt_tl(d, df, **_):
return _strip_nan(_talib.SQRT(d["close"]))
def _sqrt_pt(d, df, **_):
return _empty()
_sqrt_pt._stub = True
def _sqrt_ta(d, df, **_):
return _empty()
_sqrt_ta._stub = True
def _sqrt_tu(d, df, **_):
return _strip_nan(_tl.sqrt(_c64(d["close"])))
def _sqrt_fi(d, df, **_):
return _empty()
_sqrt_fi._stub = True
def _log10_ft(d, df, **_):
import ferro_ta
return _strip_nan(ferro_ta.LOG10(d["close"]))
def _log10_tl(d, df, **_):
return _strip_nan(_talib.LOG10(d["close"]))
def _log10_pt(d, df, **_):
return _empty()
_log10_pt._stub = True
def _log10_ta(d, df, **_):
return _empty()
_log10_ta._stub = True
def _log10_tu(d, df, **_):
return _strip_nan(_tl.log10(_c64(d["close"])))
def _log10_fi(d, df, **_):
return _empty()
_log10_fi._stub = True
def _add_ft(d, df, **_):
import ferro_ta
return _strip_nan(ferro_ta.ADD(d["high"], d["low"]))
def _add_tl(d, df, **_):
return _strip_nan(_talib.ADD(d["high"], d["low"]))
def _add_pt(d, df, **_):
return _empty()
_add_pt._stub = True
def _add_ta(d, df, **_):
return _empty()
_add_ta._stub = True
def _add_tu(d, df, **_):
return _strip_nan(_tl.add(_c64(d["high"]), _c64(d["low"])))
def _add_fi(d, df, **_):
return _empty()
_add_fi._stub = True
# ============================================================
# STATISTICS
# ============================================================
def _linearreg_ft(d, df, timeperiod=14, **_):
import ferro_ta
return _strip_nan(ferro_ta.LINEARREG(d["close"], timeperiod=timeperiod))
def _linearreg_tl(d, df, timeperiod=14, **_):
return _strip_nan(_talib.LINEARREG(d["close"], timeperiod=timeperiod))
def _linearreg_pt(d, df, **_):
return _empty()
_linearreg_pt._stub = True
def _linearreg_ta(d, df, **_):
return _empty()
_linearreg_ta._stub = True
def _linearreg_tu(d, df, timeperiod=14, **_):
return _strip_nan(_tl.linreg(_c64(d["close"]), period=timeperiod))
def _linearreg_fi(d, df, **_):
return _empty()
_linearreg_fi._stub = True
def _linreg_slope_ft(d, df, timeperiod=14, **_):
import ferro_ta
return _strip_nan(ferro_ta.LINEARREG_SLOPE(d["close"], timeperiod=timeperiod))
def _linreg_slope_tl(d, df, timeperiod=14, **_):
return _strip_nan(_talib.LINEARREG_SLOPE(d["close"], timeperiod=timeperiod))
def _linreg_slope_pt(d, df, **_):
return _empty()
_linreg_slope_pt._stub = True
def _linreg_slope_ta(d, df, **_):
return _empty()
_linreg_slope_ta._stub = True
def _linreg_slope_tu(d, df, timeperiod=14, **_):
return _strip_nan(_tl.linregslope(_c64(d["close"]), period=timeperiod))
def _linreg_slope_fi(d, df, **_):
return _empty()
_linreg_slope_fi._stub = True
def _correl_ft(d, df, timeperiod=30, **_):
import ferro_ta
return _strip_nan(ferro_ta.CORREL(d["high"], d["low"], timeperiod=timeperiod))
def _correl_tl(d, df, timeperiod=30, **_):
return _strip_nan(_talib.CORREL(d["high"], d["low"], timeperiod=timeperiod))
def _correl_pt(d, df, **_):
return _empty()
_correl_pt._stub = True
def _correl_ta(d, df, **_):
return _empty()
_correl_ta._stub = True
def _correl_tu(d, df, **_):
return _empty()
_correl_tu._stub = True
def _correl_fi(d, df, **_):
return _empty()
_correl_fi._stub = True
def _beta_ft(d, df, timeperiod=5, **_):
import ferro_ta
return _strip_nan(ferro_ta.BETA(d["high"], d["low"], timeperiod=timeperiod))
def _beta_tl(d, df, timeperiod=5, **_):
return _strip_nan(_talib.BETA(d["high"], d["low"], timeperiod=timeperiod))
def _beta_pt(d, df, **_):
return _empty()
_beta_pt._stub = True
def _beta_ta(d, df, **_):
return _empty()
_beta_ta._stub = True
def _beta_tu(d, df, **_):
return _empty()
_beta_tu._stub = True
def _beta_fi(d, df, **_):
return _empty()
_beta_fi._stub = True
# ============================================================
# CYCLE
# ============================================================
def _ht_dcperiod_ft(d, df, **_):
import ferro_ta
return _strip_nan(ferro_ta.HT_DCPERIOD(d["close"]))
def _ht_dcperiod_tl(d, df, **_):
return _strip_nan(_talib.HT_DCPERIOD(d["close"]))
def _ht_dcperiod_pt(d, df, **_):
return _empty()
_ht_dcperiod_pt._stub = True
def _ht_dcperiod_ta(d, df, **_):
return _empty()
_ht_dcperiod_ta._stub = True
def _ht_dcperiod_tu(d, df, **_):
return _empty()
_ht_dcperiod_tu._stub = True
def _ht_dcperiod_fi(d, df, **_):
return _empty()
_ht_dcperiod_fi._stub = True
def _ht_trendmode_ft(d, df, **_):
import ferro_ta
return _strip_nan(ferro_ta.HT_TRENDMODE(d["close"]).astype(float))
def _ht_trendmode_tl(d, df, **_):
return _strip_nan(_talib.HT_TRENDMODE(d["close"]).astype(float))
def _ht_trendmode_pt(d, df, **_):
return _empty()
_ht_trendmode_pt._stub = True
def _ht_trendmode_ta(d, df, **_):
return _empty()
_ht_trendmode_ta._stub = True
def _ht_trendmode_tu(d, df, **_):
return _empty()
_ht_trendmode_tu._stub = True
def _ht_trendmode_fi(d, df, **_):
return _empty()
_ht_trendmode_fi._stub = True
# ============================================================
# CANDLESTICK PATTERNS
# ============================================================
def _cdlengulfing_ft(d, df, **_):
import ferro_ta
return _strip_nan(
ferro_ta.CDLENGULFING(d["open"], d["high"], d["low"], d["close"]).astype(float)
)
def _cdlengulfing_tl(d, df, **_):
return _strip_nan(
_talib.CDLENGULFING(d["open"], d["high"], d["low"], d["close"]).astype(float)
)
def _cdlengulfing_pt(d, df, **_):
return _empty()
_cdlengulfing_pt._stub = True
def _cdlengulfing_ta(d, df, **_):
return _empty()
_cdlengulfing_ta._stub = True
def _cdlengulfing_tu(d, df, **_):
return _empty()
_cdlengulfing_tu._stub = True
def _cdlengulfing_fi(d, df, **_):
return _empty()
_cdlengulfing_fi._stub = True
def _cdldoji_ft(d, df, **_):
import ferro_ta
return _strip_nan(
ferro_ta.CDLDOJI(d["open"], d["high"], d["low"], d["close"]).astype(float)
)
def _cdldoji_tl(d, df, **_):
return _strip_nan(
_talib.CDLDOJI(d["open"], d["high"], d["low"], d["close"]).astype(float)
)
def _cdldoji_pt(d, df, **_):
return _empty()
_cdldoji_pt._stub = True
def _cdldoji_ta(d, df, **_):
return _empty()
_cdldoji_ta._stub = True
def _cdldoji_tu(d, df, **_):
return _empty()
_cdldoji_tu._stub = True
def _cdldoji_fi(d, df, **_):
return _empty()
_cdldoji_fi._stub = True
def _cdlhammer_ft(d, df, **_):
import ferro_ta
return _strip_nan(
ferro_ta.CDLHAMMER(d["open"], d["high"], d["low"], d["close"]).astype(float)
)
def _cdlhammer_tl(d, df, **_):
return _strip_nan(
_talib.CDLHAMMER(d["open"], d["high"], d["low"], d["close"]).astype(float)
)
def _cdlhammer_pt(d, df, **_):
return _empty()
_cdlhammer_pt._stub = True
def _cdlhammer_ta(d, df, **_):
return _empty()
_cdlhammer_ta._stub = True
def _cdlhammer_tu(d, df, **_):
return _empty()
_cdlhammer_tu._stub = True
def _cdlhammer_fi(d, df, **_):
return _empty()
_cdlhammer_fi._stub = True
# ============================================================
# REGISTRY BUILD
# ============================================================
REGISTRY: dict[tuple[str, Any], Any] = {}
def _reg(ind, ft, tl, pt, ta_, tu, fi):
"""
Register wrappers for a given indicator across all libraries.
Wrappers marked ._stub = True (no-op return _empty()) are not registered,
so execute_indicator raises KeyError for unsupported (lib, ind). Speed
benchmarks then skip those pairs and the table shows N/A.
"""
for lib, fn in [
("ferro_ta", ft),
("talib", tl),
("pandas_ta", pt),
("ta", ta_),
("tulipy", tu),
("finta", fi),
]:
if getattr(fn, "_stub", False):
continue
REGISTRY[(lib, ind)] = fn
_reg("SMA", _sma_ft, _sma_tl, _sma_pt, _sma_ta, _sma_tu, _sma_fi)
_reg("EMA", _ema_ft, _ema_tl, _ema_pt, _ema_ta, _ema_tu, _ema_fi)
_reg("WMA", _wma_ft, _wma_tl, _wma_pt, _wma_ta, _wma_tu, _wma_fi)
_reg("DEMA", _dema_ft, _dema_tl, _dema_pt, _dema_ta, _dema_tu, _dema_fi)
_reg("TEMA", _tema_ft, _tema_tl, _tema_pt, _tema_ta, _tema_tu, _tema_fi)
_reg("T3", _t3_ft, _t3_tl, _t3_pt, _t3_ta, _t3_tu, _t3_fi)
_reg("TRIMA", _trima_ft, _trima_tl, _trima_pt, _trima_ta, _trima_tu, _trima_fi)
_reg("KAMA", _kama_ft, _kama_tl, _kama_pt, _kama_ta, _kama_tu, _kama_fi)
_reg("HULL_MA", _hma_ft, _hma_tl, _hma_pt, _hma_ta, _hma_tu, _hma_fi)
_reg("VWMA", _vwma_ft, _vwma_tl, _vwma_pt, _vwma_ta, _vwma_tu, _vwma_fi)
_reg(
"MIDPOINT",
_midpoint_ft,
_midpoint_tl,
_midpoint_pt,
_midpoint_ta,
_midpoint_tu,
_midpoint_fi,
)
_reg(
"MIDPRICE",
_midprice_ft,
_midprice_tl,
_midprice_pt,
_midprice_ta,
_midprice_tu,
_midprice_fi,
)
_reg("RSI", _rsi_ft, _rsi_tl, _rsi_pt, _rsi_ta, _rsi_tu, _rsi_fi)
_reg("MACD", _macd_ft, _macd_tl, _macd_pt, _macd_ta, _macd_tu, _macd_fi)
_reg("STOCH", _stoch_ft, _stoch_tl, _stoch_pt, _stoch_ta, _stoch_tu, _stoch_fi)
_reg("CCI", _cci_ft, _cci_tl, _cci_pt, _cci_ta, _cci_tu, _cci_fi)
_reg("WILLR", _willr_ft, _willr_tl, _willr_pt, _willr_ta, _willr_tu, _willr_fi)
_reg("AROON", _aroon_ft, _aroon_tl, _aroon_pt, _aroon_ta, _aroon_tu, _aroon_fi)
_reg(
"AROONOSC",
_aroonosc_ft,
_aroonosc_tl,
_aroonosc_pt,
_aroonosc_ta,
_aroonosc_tu,
_aroonosc_fi,
)
_reg("ADX", _adx_ft, _adx_tl, _adx_pt, _adx_ta, _adx_tu, _adx_fi)
_reg("MOM", _mom_ft, _mom_tl, _mom_pt, _mom_ta, _mom_tu, _mom_fi)
_reg("ROC", _roc_ft, _roc_tl, _roc_pt, _roc_ta, _roc_tu, _roc_fi)
_reg("CMO", _cmo_ft, _cmo_tl, _cmo_pt, _cmo_ta, _cmo_tu, _cmo_fi)
_reg("PPO", _ppo_ft, _ppo_tl, _ppo_pt, _ppo_ta, _ppo_tu, _ppo_fi)
_reg("TRIX", _trix_ft, _trix_tl, _trix_pt, _trix_ta, _trix_tu, _trix_fi)
_reg("TSF", _tsf_ft, _tsf_tl, _tsf_pt, _tsf_ta, _tsf_tu, _tsf_fi)
_reg("ULTOSC", _ultosc_ft, _ultosc_tl, _ultosc_pt, _ultosc_ta, _ultosc_tu, _ultosc_fi)
_reg("BOP", _bop_ft, _bop_tl, _bop_pt, _bop_ta, _bop_tu, _bop_fi)
_reg("PLUS_DI", _plusdi_ft, _plusdi_tl, _plusdi_pt, _plusdi_ta, _plusdi_tu, _plusdi_fi)
_reg(
"MINUS_DI",
_minusdi_ft,
_minusdi_tl,
_minusdi_pt,
_minusdi_ta,
_minusdi_tu,
_minusdi_fi,
)
_reg("BBANDS", _bb_ft, _bb_tl, _bb_pt, _bb_ta, _bb_tu, _bb_fi)
_reg("ATR", _atr_ft, _atr_tl, _atr_pt, _atr_ta, _atr_tu, _atr_fi)
_reg("NATR", _natr_ft, _natr_tl, _natr_pt, _natr_ta, _natr_tu, _natr_fi)
_reg("TRANGE", _trange_ft, _trange_tl, _trange_pt, _trange_ta, _trange_tu, _trange_fi)
_reg("STDDEV", _stddev_ft, _stddev_tl, _stddev_pt, _stddev_ta, _stddev_tu, _stddev_fi)
_reg("VAR", _var_ft, _var_tl, _var_pt, _var_ta, _var_tu, _var_fi)
_reg("SAR", _sar_ft, _sar_tl, _sar_pt, _sar_ta, _sar_tu, _sar_fi)
_reg("KELTNER_CHANNELS", _kc_ft, _kc_tl, _kc_pt, _kc_ta, _kc_tu, _kc_fi)
_reg(
"DONCHIAN",
_donchian_ft,
_donchian_tl,
_donchian_pt,
_donchian_ta,
_donchian_tu,
_donchian_fi,
)
_reg(
"SUPERTREND",
_supertrend_ft,
_supertrend_tl,
_supertrend_pt,
_supertrend_ta,
_supertrend_tu,
_supertrend_fi,
)
_reg("CHOPPINESS_INDEX", _chop_ft, _chop_tl, _chop_pt, _chop_ta, _chop_tu, _chop_fi)
_reg("OBV", _obv_ft, _obv_tl, _obv_pt, _obv_ta, _obv_tu, _obv_fi)
_reg("AD", _ad_ft, _ad_tl, _ad_pt, _ad_ta, _ad_tu, _ad_fi)
_reg("ADOSC", _adosc_ft, _adosc_tl, _adosc_pt, _adosc_ta, _adosc_tu, _adosc_fi)
_reg("MFI", _mfi_ft, _mfi_tl, _mfi_pt, _mfi_ta, _mfi_tu, _mfi_fi)
_reg("VWAP", _vwap_ft, _vwap_tl, _vwap_pt, _vwap_ta, _vwap_tu, _vwap_fi)
_reg(
"AVGPRICE",
_avgprice_ft,
_avgprice_tl,
_avgprice_pt,
_avgprice_ta,
_avgprice_tu,
_avgprice_fi,
)
_reg(
"MEDPRICE",
_medprice_ft,
_medprice_tl,
_medprice_pt,
_medprice_ta,
_medprice_tu,
_medprice_fi,
)
_reg(
"TYPPRICE",
_typprice_ft,
_typprice_tl,
_typprice_pt,
_typprice_ta,
_typprice_tu,
_typprice_fi,
)
_reg(
"WCLPRICE",
_wclprice_ft,
_wclprice_tl,
_wclprice_pt,
_wclprice_ta,
_wclprice_tu,
_wclprice_fi,
)
_reg("SQRT", _sqrt_ft, _sqrt_tl, _sqrt_pt, _sqrt_ta, _sqrt_tu, _sqrt_fi)
_reg("LOG10", _log10_ft, _log10_tl, _log10_pt, _log10_ta, _log10_tu, _log10_fi)
_reg("ADD", _add_ft, _add_tl, _add_pt, _add_ta, _add_tu, _add_fi)
_reg(
"LINEARREG",
_linearreg_ft,
_linearreg_tl,
_linearreg_pt,
_linearreg_ta,
_linearreg_tu,
_linearreg_fi,
)
_reg(
"LINEARREG_SLOPE",
_linreg_slope_ft,
_linreg_slope_tl,
_linreg_slope_pt,
_linreg_slope_ta,
_linreg_slope_tu,
_linreg_slope_fi,
)
_reg("CORREL", _correl_ft, _correl_tl, _correl_pt, _correl_ta, _correl_tu, _correl_fi)
_reg("BETA", _beta_ft, _beta_tl, _beta_pt, _beta_ta, _beta_tu, _beta_fi)
_reg(
"HT_DCPERIOD",
_ht_dcperiod_ft,
_ht_dcperiod_tl,
_ht_dcperiod_pt,
_ht_dcperiod_ta,
_ht_dcperiod_tu,
_ht_dcperiod_fi,
)
_reg(
"HT_TRENDMODE",
_ht_trendmode_ft,
_ht_trendmode_tl,
_ht_trendmode_pt,
_ht_trendmode_ta,
_ht_trendmode_tu,
_ht_trendmode_fi,
)
_reg(
"CDLENGULFING",
_cdlengulfing_ft,
_cdlengulfing_tl,
_cdlengulfing_pt,
_cdlengulfing_ta,
_cdlengulfing_tu,
_cdlengulfing_fi,
)
_reg(
"CDLDOJI",
_cdldoji_ft,
_cdldoji_tl,
_cdldoji_pt,
_cdldoji_ta,
_cdldoji_tu,
_cdldoji_fi,
)
_reg(
"CDLHAMMER",
_cdlhammer_ft,
_cdlhammer_tl,
_cdlhammer_pt,
_cdlhammer_ta,
_cdlhammer_tu,
_cdlhammer_fi,
)
# ============================================================
# METADATA
# ============================================================
INDICATOR_DEFAULTS: dict[str, dict] = {
"SMA": {"timeperiod": 20},
"EMA": {"timeperiod": 20},
"WMA": {"timeperiod": 14},
"DEMA": {"timeperiod": 20},
"TEMA": {"timeperiod": 20},
"T3": {"timeperiod": 5},
"TRIMA": {"timeperiod": 20},
"KAMA": {"timeperiod": 10},
"HULL_MA": {"timeperiod": 16},
"VWMA": {"timeperiod": 20},
"MIDPOINT": {"timeperiod": 14},
"MIDPRICE": {"timeperiod": 14},
"RSI": {"timeperiod": 14},
"MACD": {"fastperiod": 12, "slowperiod": 26, "signalperiod": 9},
"STOCH": {"fastk_period": 14, "slowk_period": 3, "slowd_period": 3},
"CCI": {"timeperiod": 14},
"WILLR": {"timeperiod": 14},
"AROON": {"timeperiod": 14},
"AROONOSC": {"timeperiod": 14},
"ADX": {"timeperiod": 14},
"MOM": {"timeperiod": 10},
"ROC": {"timeperiod": 10},
"CMO": {"timeperiod": 14},
"PPO": {"fastperiod": 12, "slowperiod": 26},
"TRIX": {"timeperiod": 18},
"TSF": {"timeperiod": 14},
"ULTOSC": {"timeperiod1": 7, "timeperiod2": 14, "timeperiod3": 28},
"BOP": {},
"PLUS_DI": {"timeperiod": 14},
"MINUS_DI": {"timeperiod": 14},
"BBANDS": {"timeperiod": 20, "nbdevup": 2.0, "nbdevdn": 2.0},
"ATR": {"timeperiod": 14},
"NATR": {"timeperiod": 14},
"TRANGE": {},
"STDDEV": {"timeperiod": 20},
"VAR": {"timeperiod": 20},
"SAR": {"acceleration": 0.02, "maximum": 0.2},
"KELTNER_CHANNELS": {"timeperiod": 20},
"DONCHIAN": {"timeperiod": 20},
"SUPERTREND": {"timeperiod": 7},
"CHOPPINESS_INDEX": {"timeperiod": 14},
"OBV": {},
"AD": {},
"ADOSC": {"fastperiod": 3, "slowperiod": 10},
"MFI": {"timeperiod": 14},
"VWAP": {},
"AVGPRICE": {},
"MEDPRICE": {},
"TYPPRICE": {},
"WCLPRICE": {},
"SQRT": {},
"LOG10": {},
"ADD": {},
"LINEARREG": {"timeperiod": 14},
"LINEARREG_SLOPE": {"timeperiod": 14},
"CORREL": {"timeperiod": 30},
"BETA": {"timeperiod": 5},
"HT_DCPERIOD": {},
"HT_TRENDMODE": {},
"CDLENGULFING": {},
"CDLDOJI": {},
"CDLHAMMER": {},
}
INDICATOR_NAMES = list(INDICATOR_DEFAULTS.keys())
LIBRARY_NAMES = ["ferro_ta", "talib", "pandas_ta", "ta", "tulipy", "finta"]
INDICATOR_CATEGORIES: dict[str, list[str]] = {
"Overlap": [
"SMA",
"EMA",
"WMA",
"DEMA",
"TEMA",
"T3",
"TRIMA",
"KAMA",
"HULL_MA",
"VWMA",
"MIDPOINT",
"MIDPRICE",
],
"Momentum": [
"RSI",
"MACD",
"STOCH",
"CCI",
"WILLR",
"AROON",
"AROONOSC",
"ADX",
"MOM",
"ROC",
"CMO",
"PPO",
"TRIX",
"TSF",
"ULTOSC",
"BOP",
"PLUS_DI",
"MINUS_DI",
],
"Volatility": [
"BBANDS",
"ATR",
"NATR",
"TRANGE",
"STDDEV",
"VAR",
"SAR",
"KELTNER_CHANNELS",
"DONCHIAN",
"SUPERTREND",
"CHOPPINESS_INDEX",
],
"Volume": ["OBV", "AD", "ADOSC", "MFI", "VWAP"],
"Price Transform": ["AVGPRICE", "MEDPRICE", "TYPPRICE", "WCLPRICE"],
"Math": ["SQRT", "LOG10", "ADD"],
"Statistics": ["LINEARREG", "LINEARREG_SLOPE", "CORREL", "BETA"],
"Cycle": ["HT_DCPERIOD", "HT_TRENDMODE"],
"Pattern": ["CDLENGULFING", "CDLDOJI", "CDLHAMMER"],
}
# Cumulative: compare first-differences not absolute values
CUMULATIVE_INDICATORS = {"OBV", "AD", "ADOSC"}
# Binary output: use agreement rate not allclose
BINARY_INDICATORS = {"CDLENGULFING", "CDLDOJI", "CDLHAMMER", "HT_TRENDMODE"}
def execute_indicator(library, indicator, data, df=None, **kwargs):
"""Run indicator from library on data dict, return 1-D float64 array."""
if library not in available_libraries():
raise KeyError(f"Library not available in this environment: {library!r}")
key = (library, indicator)
if key not in REGISTRY:
raise KeyError(f"No wrapper for {key!r}")
if df is None:
from benchmarks.data_generator import get_pandas_ohlcv
df = get_pandas_ohlcv(data)
params = {**INDICATOR_DEFAULTS.get(indicator, {}), **kwargs}
return REGISTRY[key](data, df, **params)