refactor(core): lazy-load AnalysisIndicators and category subpackages

Replace eager wildcard-imports with on-demand attribute resolution.
Cold import drops from ~930 ms to ~280 ms because no indicator function
runs at import time. Public API is preserved.

* `_lazy_subpackage.install_lazy_subpackage` installs `__getattr__` /
  `__dir__` on every category package so `from <cat> import <name>`
  imports `<cat>/<name>.py` only on first access. Supports aliases (e.g.
  `math.max` -> `math.rolling_max`) and special targets (e.g.
  `candles.cdl` lives inside `candles/cdl_pattern.py`).
* `_indicator_loader._make_ta_wrapper` builds a per-indicator
  `AnalysisIndicators` method from the underlying function signature,
  invoked lazily by the new `AnalysisIndicators.__getattr__`. The old
  hand-written wrapper class (2400+ lines) is gone.
* Top-level `pandas_ta_classic.__getattr__` resolves bare names like
  `ta.rsi` and individual candle submodules (`ta.cdl_2crows`). It also
  exposes `ALL_PATTERNS` and emits a `DeprecationWarning` for the legacy
  `CDL_PATTERN_NAMES` alias.
* `core.pyi` + `tools/gen_core_stub.py` give IDEs and mypy full
  signatures for every accessor method; the stub is generated from
  Category at build time.
* All ten category `__init__.py` files collapse to a single
  `install_lazy_subpackage(__name__)` call.
* `math` joins `_VALID_CATEGORIES` so the loader discovers its operators
  alongside other indicators.
* `tests/test_indicator_candle.py` switches to the new public
  `ALL_PATTERNS` name.
This commit is contained in:
rmarcink
2026-06-27 17:47:39 +02:00
parent 736387863d
commit 59ccbb40ca
18 changed files with 1368 additions and 2582 deletions
+59
View File
@@ -1,6 +1,7 @@
name = "pandas-ta-classic"
import logging
from typing import Any
logging.getLogger(__name__).addHandler(logging.NullHandler())
@@ -28,3 +29,61 @@ __description__ = (
"Can be called from a Pandas DataFrame or standalone like TA-Lib. Correlation tested with TA-Lib."
"This is the classic/community maintained version."
)
def __getattr__(name: str) -> Any:
"""Lazy-load indicator functions for direct module-level access (e.g. ta.rsi(...)).
For regular indicators (in Category), returns the callable function.
For individual candle-pattern submodules (cdl_2crows, cdl_3blackcrows, etc.
that live in pandas_ta_classic.candles but are NOT in Category), returns the
submodule directly — preserving the ta.cdl_2crows.cdl_2crows(...) access
pattern used by some tests and the old wildcard-import behaviour.
"""
import importlib
import sys
from pandas_ta_classic._indicator_loader import (
_find_indicator_func,
_INDICATOR_TO_CATEGORY,
)
# Regular indicators in Category → return the function
cat = _INDICATOR_TO_CATEGORY.get(name)
if cat is not None:
func = _find_indicator_func(name)
if func is not None:
setattr(sys.modules[__name__], name, func) # cache in module dict
return func
# ALL_PATTERNS: canonical public name for the candle pattern name list
if name == "ALL_PATTERNS":
from pandas_ta_classic.candles.cdl_pattern import ALL_PATTERNS
setattr(sys.modules[__name__], name, ALL_PATTERNS)
return ALL_PATTERNS
# CDL_PATTERN_NAMES: deprecated alias — use ALL_PATTERNS
if name == "CDL_PATTERN_NAMES":
import warnings
warnings.warn(
"CDL_PATTERN_NAMES is deprecated; use ALL_PATTERNS instead.",
DeprecationWarning,
stacklevel=2,
)
from pandas_ta_classic.candles.cdl_pattern import ALL_PATTERNS
setattr(sys.modules[__name__], "ALL_PATTERNS", ALL_PATTERNS)
return ALL_PATTERNS
# Individual candle-pattern submodules (cdl_*) not tracked in Category
# → return the submodule (mimics old `from candles import *` behaviour)
if name.startswith("cdl_"):
try:
mod = importlib.import_module(f"pandas_ta_classic.candles.{name}")
setattr(sys.modules[__name__], name, mod) # cache in module dict
return mod
except ImportError:
pass
raise AttributeError(f"module 'pandas_ta_classic' has no attribute '{name}'")
+153
View File
@@ -0,0 +1,153 @@
"""Lazy indicator function loader and AnalysisIndicators wrapper factory."""
import functools
import importlib
import inspect
from typing import Any, Callable, Optional
from pandas_ta_classic._meta import Category
# Maps function param name → DataFrame column key
_COLUMN_PARAM_TO_COL_KEY: dict[str, str] = {
"close": "close",
"high": "high",
"low": "low",
"open_": "open", # param 'open_' reads column 'open'
"volume": "volume",
}
# Extended column params with multi-alias pop logic: {param_name: (aliases_to_pop, default_col)}
_SERIES_COLUMN_PARAMS: dict[str, tuple] = {
"series_a": (("a", "series_a"), "close"),
"series_b": (("b", "series_b"), "close"),
}
# Reverse lookup: indicator name → category name (built once at import)
_INDICATOR_TO_CATEGORY: dict[str, str] = {
ind: cat for cat, indicators in Category.items() for ind in indicators
}
def _find_indicator_func(name: str) -> Optional[Callable]:
"""Lazy-import and return the indicator function, or None if unknown."""
cat = _INDICATOR_TO_CATEGORY.get(name)
if cat is None:
return None
try:
mod = importlib.import_module(f"pandas_ta_classic.{cat}.{name}")
func = getattr(mod, name, None)
# Importing a submodule causes Python to bind parent.name = <module>,
# overwriting any re-exported function of the same name in __init__.py.
# Restore the function binding so sibling imports stay correct.
import sys
parent = sys.modules.get(f"pandas_ta_classic.{cat}")
if parent is not None and getattr(parent, name, None) is mod:
setattr(parent, name, func)
return func
except ImportError:
return None
def _make_ta_wrapper(func: Callable) -> Callable:
"""
Build a bound-method wrapper for *func* suitable for AnalysisIndicators.
Column params (close, high, low, open_, volume) are extracted from the
host DataFrame via self._get_column; all other params flow through as
positional *args (mapped to their non-column parameter names) or **kwargs.
"""
sig = inspect.signature(func)
# A column param is "required" (always fetched from the DataFrame) when:
# - it has no default value in the function signature, OR
# - it is one of the primary OHLCV columns (close, high, low, volume).
# open_ is optional: only fetched when (a) it has no default (required by
# the function) or (b) the caller explicitly passes open=<col> in kwargs.
_ALWAYS_FETCH = {"close", "high", "low", "volume"}
col_params_required = [
p
for p in sig.parameters
if p in _COLUMN_PARAM_TO_COL_KEY
and (p in _ALWAYS_FETCH or sig.parameters[p].default is inspect.Parameter.empty)
]
col_params_optional = [
p
for p in sig.parameters
if p in _COLUMN_PARAM_TO_COL_KEY
and p not in _ALWAYS_FETCH
and sig.parameters[p].default is not inspect.Parameter.empty
]
# Ordered list of non-column positional/keyword parameters (for *args binding)
non_col_positional = [
pname
for pname, param in sig.parameters.items()
if pname not in _COLUMN_PARAM_TO_COL_KEY
and pname not in _SERIES_COLUMN_PARAMS
and param.kind
in (
inspect.Parameter.POSITIONAL_OR_KEYWORD,
inspect.Parameter.POSITIONAL_ONLY,
)
]
# Series column params present in this function's signature
series_col_params = [p for p in sig.parameters if p in _SERIES_COLUMN_PARAMS]
@functools.wraps(func)
def wrapper(self: Any, *args: Any, **kwargs: Any) -> Any:
call_kwargs: dict[str, Any] = {}
# Map positional args to their non-column parameter names
for i, arg in enumerate(args):
if i < len(non_col_positional):
call_kwargs[non_col_positional[i]] = arg
# Always extract required column values from DataFrame
for param_name in col_params_required:
col_key = _COLUMN_PARAM_TO_COL_KEY[param_name]
col_val = kwargs.pop(col_key, col_key)
call_kwargs[param_name] = self._get_column(col_val)
# Extract optional column values only when explicitly requested by user
for param_name in col_params_optional:
col_key = _COLUMN_PARAM_TO_COL_KEY[param_name]
if col_key in kwargs:
col_val = kwargs.pop(col_key)
call_kwargs[param_name] = self._get_column(col_val)
# Handle series_a / series_b: pop aliases in order, fallback to default column
for param_name in series_col_params:
aliases, default_col = _SERIES_COLUMN_PARAMS[param_name]
col_val = default_col
for alias in aliases:
if alias in kwargs:
col_val = kwargs.pop(alias)
break
# Clean up any remaining aliases that weren't used
for alias in aliases:
kwargs.pop(alias, None)
call_kwargs[param_name] = self._get_column(col_val)
# Fill in None for any required non-column positional args not yet provided
# (allows functions like long_run/short_run to return None when called with
# no fast/slow args, which _post_process then converts to self._df)
for pname, param in sig.parameters.items():
if (
pname not in _COLUMN_PARAM_TO_COL_KEY
and pname not in _SERIES_COLUMN_PARAMS
and pname not in call_kwargs
and pname not in kwargs
and param.default is inspect.Parameter.empty
and param.kind
in (
inspect.Parameter.POSITIONAL_OR_KEYWORD,
inspect.Parameter.POSITIONAL_ONLY,
)
):
call_kwargs[pname] = None
result = func(**call_kwargs, **kwargs)
return self._post_process(result, **kwargs)
# Rewrite __signature__: drop col params (they come from the DataFrame),
# prepend 'self', keep everything else (length, offset, **kwargs, etc.)
new_params = [inspect.Parameter("self", inspect.Parameter.POSITIONAL_OR_KEYWORD)]
for pname, param in sig.parameters.items():
if pname not in _COLUMN_PARAM_TO_COL_KEY and pname not in _SERIES_COLUMN_PARAMS:
new_params.append(param)
wrapper.__signature__ = sig.replace(parameters=new_params)
return wrapper
+102
View File
@@ -0,0 +1,102 @@
"""Shared lazy-loading installer for category subpackages.
The 10 category subpackages (candles, cycles, math, momentum, overlap,
performance, statistics, trend, volatility, volume) each expose a flat
namespace where ``pandas_ta_classic.<cat>.<name>`` resolves to the
function ``<name>`` defined in ``pandas_ta_classic.<cat>.<name>.py``
(one indicator per submodule). To avoid importing every submodule at
package import time, each category installs a ``types.ModuleType``
subclass that resolves names lazily on first access.
This module factors that shared logic into :func:`install_lazy_subpackage`.
"""
from __future__ import annotations
import importlib
import sys
import types
from typing import Any, Mapping, Optional
def install_lazy_subpackage(
module_name: str,
*,
aliases: Optional[Mapping[str, str]] = None,
special: Optional[Mapping[str, tuple]] = None,
) -> None:
"""Replace ``sys.modules[module_name]`` with a lazy-loading module.
Parameters
----------
module_name:
Fully-qualified subpackage name (typically ``__name__`` from the
caller).
aliases:
Mapping of requested attribute name to canonical submodule/attr
name. Used by ``math`` so that ``max`` resolves to
``rolling_max`` (which lives in ``math/rolling_max.py``).
special:
Mapping of requested attribute name to ``(submodule, attr)``
pairs where the attribute lives in a *different* submodule than
the requested name. Used by ``candles`` so that ``ALL_PATTERNS``
resolves to the ``ALL_PATTERNS`` attribute of
``candles/cdl_pattern.py``.
"""
aliases_map: Mapping[str, str] = aliases or {}
special_map: Mapping[str, tuple] = special or {}
original = sys.modules[module_name]
class _LazySubpackage(types.ModuleType):
"""Lazy-loading package module: resolves names to attrs in submodules."""
def __getattribute__(self, name: str) -> Any:
if name.startswith("__") and name.endswith("__"):
return super().__getattribute__(name)
val = super().__getattribute__(name)
pkg = super().__getattribute__("__name__")
# If Python registered a submodule as attr, unwrap it to the function.
if isinstance(val, types.ModuleType) and val.__name__ == f"{pkg}.{name}":
if name in special_map:
submod_name, attr = special_map[name]
mod = importlib.import_module(f"{pkg}.{submod_name}")
value = getattr(mod, attr)
object.__setattr__(self, name, value)
return value
canonical = aliases_map.get(name, name)
func = getattr(val, canonical, None)
if func is not None and callable(func):
object.__setattr__(self, name, func)
return func
return val
def __getattr__(self, name: str) -> Any:
pkg = object.__getattribute__(self, "__name__")
if name in special_map:
submod_name, attr = special_map[name]
mod = importlib.import_module(f"{pkg}.{submod_name}")
value = getattr(mod, attr)
object.__setattr__(self, name, value)
return value
canonical = aliases_map.get(name, name)
try:
mod = importlib.import_module(f"{pkg}.{canonical}")
func = getattr(mod, canonical, None)
if func is not None:
object.__setattr__(self, name, func)
return func
except ImportError:
pass
raise AttributeError(f"module {pkg!r} has no attribute {name!r}")
new_mod = _LazySubpackage(module_name)
# Copy module attributes from the original so docstring, path, spec, etc. survive.
new_mod.__doc__ = getattr(original, "__doc__", None)
if hasattr(original, "__path__"):
new_mod.__path__ = original.__path__ # type: ignore[attr-defined]
new_mod.__spec__ = original.__spec__
new_mod.__file__ = original.__file__
new_mod.__package__ = original.__package__
sys.modules[module_name] = new_mod
+1
View File
@@ -64,6 +64,7 @@ _CANDLE_TOP_LEVEL = {"cdl_doji", "cdl_inside", "cdl_pattern", "cdl_z", "ha"}
_VALID_CATEGORIES = {
"candles",
"cycles",
"math",
"momentum",
"overlap",
"performance",
+12 -5
View File
@@ -1,5 +1,12 @@
from .cdl_doji import cdl_doji
from .cdl_inside import cdl_inside
from .cdl_pattern import cdl_pattern, cdl, ALL_PATTERNS as CDL_PATTERN_NAMES
from .cdl_z import cdl_z
from .ha import ha
from pandas_ta_classic._lazy_subpackage import install_lazy_subpackage
# Names that live inside cdl_pattern.py rather than their own submodule.
# CDL_PATTERN_NAMES is a deprecated alias for ALL_PATTERNS.
install_lazy_subpackage(
__name__,
special={
"cdl": ("cdl_pattern", "cdl"),
"ALL_PATTERNS": ("cdl_pattern", "ALL_PATTERNS"),
"CDL_PATTERN_NAMES": ("cdl_pattern", "ALL_PATTERNS"),
},
)
+72 -2353
View File
File diff suppressed because it is too large Load Diff
+838
View File
@@ -0,0 +1,838 @@
# This file is auto-generated by tools/gen_core_stub.py — do not edit manually.
from typing import Any, Optional, Sequence, Union
from pandas import Series, DataFrame
class AnalysisIndicators:
# candles
def cdl_doji(
self,
length: Optional[int] = None,
factor: Optional[float] = None,
scalar: Optional[float] = None,
asint: bool = True,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[Series]: ...
def cdl_inside(self, asbool: bool = False, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def cdl_pattern(
self, name: Union[str, Sequence[str]] = 'all', scalar: Optional[float] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[DataFrame]: ...
def cdl_z(
self, length: Optional[int] = None, full: Optional[bool] = None, ddof: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[DataFrame]: ...
def ha(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[DataFrame]: ...
# cycles
def dsp(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def ebsw(self, length: Optional[int] = None, bars: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def ht_dcperiod(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def ht_dcphase(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def ht_phasor(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[DataFrame]: ...
def ht_sine(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[DataFrame]: ...
def ht_trendmode(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def msw(self, period: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[DataFrame]: ...
# math
def acos(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def add(self, series_a: Series, series_b: Series, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def asin(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def atan(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def ceil(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def cos(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def cosh(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def div(self, series_a: Series, series_b: Series, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def exp(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def floor(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def ln(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def log10(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def maxindex(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def minindex(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def minmax(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[DataFrame]: ...
def minmaxindex(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[DataFrame]: ...
def mult(self, series_a: Series, series_b: Series, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def npabs(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def npround(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def rolling_max(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def rolling_min(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def rolling_sum(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def sin(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def sinh(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def sqrt(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def sub(self, series_a: Series, series_b: Series, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def tan(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def tanh(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def todeg(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def torad(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def trunc(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
# momentum
def ao(self, fast: Optional[int] = None, slow: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def apo(
self,
fast: Optional[int] = None,
slow: Optional[int] = None,
mamode: Optional[str] = None,
talib: Optional[bool] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[Series]: ...
def bias(self, length: Optional[int] = None, mamode: Optional[str] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def bop(self, scalar: Optional[float] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def brar(
self, length: Optional[int] = None, scalar: Optional[float] = None, drift: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[DataFrame]: ...
def cci(
self, length: Optional[int] = None, c: Optional[float] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def cfo(
self, length: Optional[int] = None, scalar: Optional[float] = None, drift: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def cg(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def cmo(
self,
length: Optional[int] = None,
scalar: Optional[float] = None,
talib: Optional[bool] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[Series]: ...
def coppock(
self, length: Optional[int] = None, fast: Optional[int] = None, slow: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def cti(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def dm(
self,
length: Optional[int] = None,
mamode: Optional[str] = None,
talib: Optional[bool] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def er(
self, length: Optional[int] = None, drift: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Union[Series, DataFrame, None]: ...
def eri(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[DataFrame]: ...
def fisher(
self, length: Optional[int] = None, signal: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[DataFrame]: ...
def fosc(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def inertia(
self,
length: Optional[int] = None,
rvi_length: Optional[int] = None,
scalar: Optional[float] = None,
refined: Optional[bool] = None,
thirds: Optional[bool] = None,
mamode: Optional[str] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[Series]: ...
def kdj(self, length: Optional[int] = None, signal: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[DataFrame]: ...
def kst(
self,
roc1: Optional[int] = None,
roc2: Optional[int] = None,
roc3: Optional[int] = None,
roc4: Optional[int] = None,
sma1: Optional[int] = None,
sma2: Optional[int] = None,
sma3: Optional[int] = None,
sma4: Optional[int] = None,
signal: Optional[int] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def lrsi(self, length: Optional[int] = None, gamma: Optional[float] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def macd(
self,
fast: Optional[int] = None,
slow: Optional[int] = None,
signal: Optional[int] = None,
talib: Optional[bool] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def macdext(
self,
fast: Optional[int] = None,
slow: Optional[int] = None,
signal: Optional[int] = None,
fastmatype: Optional[int] = None,
slowmatype: Optional[int] = None,
signalmatype: Optional[int] = None,
talib: Optional[bool] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def macdfix(
self, signal: Optional[int] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[DataFrame]: ...
def mom(self, length: Optional[int] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def pgo(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def po(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def ppo(
self,
fast: Optional[int] = None,
slow: Optional[int] = None,
signal: Optional[int] = None,
scalar: Optional[float] = None,
mamode: Optional[str] = None,
talib: Optional[bool] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def psl(
self, length: Optional[int] = None, scalar: Optional[float] = None, drift: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def pvo(
self,
fast: Optional[int] = None,
slow: Optional[int] = None,
signal: Optional[int] = None,
scalar: Optional[float] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def qqe(
self,
length: Optional[int] = None,
smooth: Optional[int] = None,
factor: Optional[float] = None,
mamode: Optional[str] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def roc(
self, length: Optional[int] = None, scalar: Optional[float] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def rocp(self, length: Optional[int] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def rocr(self, length: Optional[int] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def rocr100(
self, length: Optional[int] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def rsi(
self,
length: Optional[int] = None,
scalar: Optional[float] = None,
talib: Optional[bool] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Union[Series, DataFrame, None]: ...
def rsx(
self, length: Optional[int] = None, drift: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Union[Series, DataFrame, None]: ...
def rvgi(
self, length: Optional[int] = None, swma_length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[DataFrame]: ...
def slope(
self,
length: Optional[int] = None,
as_angle: Optional[bool] = None,
to_degrees: Optional[bool] = None,
vertical: Optional[bool] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[Series]: ...
def smc_sweep(
self, length: Optional[int] = None, wick_mult: Optional[float] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def smi(
self,
fast: Optional[int] = None,
slow: Optional[int] = None,
signal: Optional[int] = None,
scalar: Optional[float] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def squeeze(
self,
bb_length: Optional[int] = None,
bb_std: Optional[float] = None,
kc_length: Optional[int] = None,
kc_scalar: Optional[float] = None,
mom_length: Optional[int] = None,
mom_smooth: Optional[int] = None,
use_tr: Optional[bool] = None,
mamode: Optional[str] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def squeeze_pro(
self,
bb_length: Optional[int] = None,
bb_std: Optional[float] = None,
kc_length: Optional[int] = None,
kc_scalar_wide: Optional[float] = None,
kc_scalar_normal: Optional[float] = None,
kc_scalar_narrow: Optional[float] = None,
mom_length: Optional[int] = None,
mom_smooth: Optional[int] = None,
use_tr: Optional[bool] = None,
mamode: Optional[str] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def stc(
self,
tclength: Optional[int] = None,
fast: Optional[int] = None,
slow: Optional[int] = None,
factor: Optional[float] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def stoch(
self,
k: Optional[int] = None,
d: Optional[int] = None,
smooth_k: Optional[int] = None,
mamode: Optional[str] = None,
talib: Optional[bool] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def stochf(
self,
fastk: Optional[int] = None,
fastd: Optional[int] = None,
mamode: Optional[str] = None,
talib: Optional[bool] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def stochrsi(
self,
length: Optional[int] = None,
rsi_length: Optional[int] = None,
k: Optional[int] = None,
d: Optional[int] = None,
mamode: Optional[str] = None,
talib: Optional[bool] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def td_seq(self, asint: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[DataFrame]: ...
def trix(
self,
length: Optional[int] = None,
signal: Optional[int] = None,
scalar: Optional[float] = None,
talib: Optional[bool] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def trixh(
self,
length: Optional[int] = None,
signal: Optional[int] = None,
scalar: Optional[float] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def tsi(
self,
fast: Optional[int] = None,
slow: Optional[int] = None,
signal: Optional[int] = None,
scalar: Optional[float] = None,
mamode: Optional[str] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def uo(
self,
fast: Optional[int] = None,
medium: Optional[int] = None,
slow: Optional[int] = None,
fast_w: Optional[float] = None,
medium_w: Optional[float] = None,
slow_w: Optional[float] = None,
talib: Optional[bool] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[Series]: ...
def vwmacd(
self, fast: Optional[int] = None, slow: Optional[int] = None, signal: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[DataFrame]: ...
def willr(self, length: Optional[int] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
# overlap
def alma(
self,
length: Optional[int] = None,
sigma: Optional[float] = None,
distribution_offset: Optional[float] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[Series]: ...
def avgprice(self, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def dema(self, length: Optional[int] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def ema(self, length: Optional[int] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def fwma(self, length: Optional[int] = None, asc: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def hilo(
self,
high_length: Optional[int] = None,
low_length: Optional[int] = None,
mamode: Optional[str] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def hl2(self, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def hlc3(self, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def hma(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def ht_trendline(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def hwma(
self, na: Optional[float] = None, nb: Optional[float] = None, nc: Optional[float] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def ichimoku(
self,
tenkan: Optional[int] = None,
kijun: Optional[int] = None,
senkou: Optional[int] = None,
include_chikou: bool = True,
offset: Optional[int] = None,
**kwargs: Any,
) -> tuple[Optional[DataFrame], Optional[DataFrame]]: ...
def jma(
self, length: Union[int, float, None] = None, phase: Optional[float] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def kama(
self,
length: Optional[int] = None,
fast: Optional[int] = None,
slow: Optional[int] = None,
talib: Optional[bool] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[Series]: ...
def linreg(self, length: Optional[int] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def linregangle(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def linregintercept(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def linregslope(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def ma(self, name: Optional[str] = None, source: Optional[Series] = None, **kwargs: Any) -> Optional[Series]: ...
def mama(
self, fastlimit: Optional[float] = None, slowlimit: Optional[float] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[DataFrame]: ...
def mavp(
self,
periods: Optional[Series] = None,
minperiod: Optional[int] = None,
maxperiod: Optional[int] = None,
mamode: Optional[int] = None,
talib: Optional[bool] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[Series]: ...
def mcgd(self, length: Optional[int] = None, offset: Optional[int] = None, c: Optional[float] = None, **kwargs: Any) -> Optional[Series]: ...
def medprice(self, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def midpoint(
self, length: Optional[int] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def midprice(
self, length: Optional[int] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def mmar(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[DataFrame]: ...
def ohlc4(self, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def pwma(self, length: Optional[int] = None, asc: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def rainbow(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[DataFrame]: ...
def rma(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def sinwma(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def sma(self, length: Optional[int] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def ssf(self, length: Optional[int] = None, poles: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def supertrend(
self, length: Optional[int] = None, multiplier: Optional[float] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[DataFrame]: ...
def swma(self, length: Optional[int] = None, asc: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def t3(
self, length: Optional[int] = None, a: Optional[float] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def tema(self, length: Optional[int] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def trima(self, length: Optional[int] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def tsf(self, length: Optional[int] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def typprice(self, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def vidya(self, length: Optional[int] = None, drift: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def vwap(self, anchor: Optional[str] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def vwma(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def wcp(self, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def wma(
self, length: Optional[int] = None, asc: Optional[bool] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def zlma(self, length: Optional[int] = None, mamode: Optional[str] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
# performance
def drawdown(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[DataFrame]: ...
def log_return(
self, length: Optional[int] = None, cumulative: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def percent_return(
self, length: Optional[int] = None, cumulative: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
# statistics
def beta(
self, benchmark: Optional[Series] = None, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def correl(
self, benchmark: Optional[Series] = None, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def entropy(
self, length: Optional[int] = None, base: Optional[float] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def kurtosis(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def mad(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def md(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def median(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def quantile(self, length: Optional[int] = None, q: Optional[float] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def skew(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def stderr(self, length: Optional[int] = None, ddof: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def stdev(
self, length: Optional[int] = None, ddof: Optional[int] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def tos_stdevall(
self, length: Optional[int] = None, stds: Optional[list[int]] = None, ddof: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[DataFrame]: ...
def variance(
self, length: Optional[int] = None, ddof: Optional[int] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def zscore(self, length: Optional[int] = None, std: Optional[float] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
# trend
def adx(
self,
length: Optional[int] = None,
lensig: Optional[int] = None,
scalar: Optional[float] = None,
mamode: Optional[str] = None,
talib: Optional[bool] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def adxr(
self,
length: Optional[int] = None,
lensig: Optional[int] = None,
scalar: Optional[float] = None,
mamode: Optional[str] = None,
talib: Optional[bool] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def amat(
self,
fast: Optional[int] = None,
slow: Optional[int] = None,
lookback: Optional[int] = None,
mamode: Optional[str] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def aroon(
self, length: Optional[int] = None, scalar: Optional[float] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[DataFrame]: ...
def chop(
self,
length: Optional[int] = None,
atr_length: Optional[int] = None,
ln: Optional[bool] = None,
scalar: Optional[float] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[Series]: ...
def cksp(
self,
p: Optional[int] = None,
x: Optional[float] = None,
q: Optional[int] = None,
tvmode: Optional[bool] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def cpr(
self,
open: Series,
method: str = 'classic',
timeframe: str = 'daily',
interval: Optional[str] = None,
levels: str = 'standard',
width_analysis: bool = True,
price_position: bool = True,
virgin_cpr: bool = False,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def decay(
self, kind: Optional[str] = None, length: Optional[int] = None, mode: Optional[str] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def decreasing(
self,
length: Optional[int] = None,
strict: Optional[bool] = None,
asint: Optional[bool] = None,
percent: Optional[float] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[Series]: ...
def dpo(self, length: Optional[int] = None, centered: bool = True, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def dx(
self,
length: Optional[int] = None,
scalar: Optional[float] = None,
mamode: Optional[str] = None,
talib: Optional[bool] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[Series]: ...
def edecay(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def increasing(
self,
length: Optional[int] = None,
strict: Optional[bool] = None,
asint: Optional[bool] = None,
percent: Optional[float] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[Series]: ...
def long_run(self, fast: Series, slow: Series, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def minus_dm(
self, length: Optional[int] = None, talib: Optional[bool] = None, drift: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def plus_dm(
self, length: Optional[int] = None, talib: Optional[bool] = None, drift: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def pmax(
self,
length: Optional[int] = None,
multiplier: Optional[float] = None,
mamode: Optional[str] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[Series]: ...
def psar(
self,
af0: Optional[float] = None,
af: Optional[float] = None,
max_af: Optional[float] = None,
talib: Optional[bool] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def qstick(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def sarext(
self,
startvalue: Optional[float] = None,
offsetonreverse: Optional[float] = None,
accelerationinitlong: Optional[float] = None,
accelerationlong: Optional[float] = None,
accelerationmaxlong: Optional[float] = None,
accelerationinitshort: Optional[float] = None,
accelerationshort: Optional[float] = None,
accelerationmaxshort: Optional[float] = None,
talib: Optional[bool] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[Series]: ...
def short_run(
self, fast: Series, slow: Series, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def tsignals(
self,
trend: Series,
asbool: Optional[bool] = None,
trend_reset: int = 0,
trade_offset: Optional[int] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def ttm_trend(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[DataFrame]: ...
def vhf(self, length: Optional[int] = None, drift: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def vortex(
self, length: Optional[int] = None, drift: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[DataFrame]: ...
def xsignals(
self,
signal: Series,
xa: float,
xb: float,
above: bool = True,
long: bool = True,
asbool: Optional[bool] = None,
trend_reset: int = 0,
trade_offset: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
# volatility
def aberration(
self, length: Optional[int] = None, atr_length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[DataFrame]: ...
def accbands(
self,
length: Optional[int] = None,
c: Optional[float] = None,
drift: Optional[int] = None,
mamode: Optional[str] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def atr(
self,
length: Optional[int] = None,
mamode: Optional[str] = None,
talib: Optional[bool] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[Series]: ...
def avolume(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def bbands(
self,
length: Optional[int] = None,
std: Optional[float] = None,
ddof: int = 0,
mamode: Optional[str] = None,
talib: Optional[bool] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def ce(
self,
length: Optional[int] = None,
multiplier: Optional[float] = None,
mamode: Optional[str] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def cvi(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def donchian(
self, lower_length: Optional[int] = None, upper_length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[DataFrame]: ...
def hvol(
self, length: Optional[int] = None, annualization: Optional[float] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def hwc(
self,
na: Optional[float] = None,
nb: Optional[float] = None,
nc: Optional[float] = None,
nd: Optional[float] = None,
scalar: Optional[float] = None,
channel_eval: Optional[bool] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def kc(
self, length: Optional[int] = None, scalar: Optional[float] = None, mamode: Optional[str] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[DataFrame]: ...
def massi(self, fast: Optional[int] = None, slow: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def natr(
self,
length: Optional[int] = None,
scalar: Optional[float] = None,
mamode: Optional[str] = None,
talib: Optional[bool] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[Series]: ...
def pdist(self, drift: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def rvi(
self,
length: Optional[int] = None,
scalar: Optional[float] = None,
refined: Optional[bool] = None,
thirds: Optional[bool] = None,
mamode: Optional[str] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[Series]: ...
def thermo(
self,
length: Optional[int] = None,
long: Optional[float] = None,
short: Optional[float] = None,
mamode: Optional[str] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def true_range(
self, talib: Optional[bool] = None, drift: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def ui(self, length: Optional[int] = None, scalar: Optional[float] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
# volume
def ad(self, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def adosc(
self, fast: Optional[int] = None, slow: Optional[int] = None, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def aobv(
self,
fast: Optional[int] = None,
slow: Optional[int] = None,
max_lookback: Optional[int] = None,
min_lookback: Optional[int] = None,
mamode: Optional[str] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def cmf(self, length: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def efi(
self, length: Optional[int] = None, mamode: Optional[str] = None, drift: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def emv(self, drift: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def eom(
self, length: Optional[int] = None, divisor: Optional[float] = None, drift: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def kvo(
self,
fast: Optional[int] = None,
slow: Optional[int] = None,
signal: Optional[int] = None,
mamode: Optional[str] = None,
drift: Optional[int] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[DataFrame]: ...
def marketfi(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def mfi(
self, length: Optional[int] = None, talib: Optional[bool] = None, drift: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any
) -> Optional[Series]: ...
def nvi(self, length: Optional[int] = None, initial: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def obv(self, talib: Optional[bool] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def pvi(self, length: Optional[int] = None, initial: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def pvol(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def pvr(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def pvt(self, drift: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def vfi(
self,
length: Optional[int] = None,
coef: Optional[float] = None,
vcoef: Optional[float] = None,
mamode: Optional[str] = None,
offset: Optional[int] = None,
**kwargs: Any,
) -> Optional[Series]: ...
def vosc(self, fast: Optional[int] = None, slow: Optional[int] = None, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
def vp(self, width: Optional[int] = None, **kwargs: Any) -> Optional[DataFrame]: ...
def wad(self, offset: Optional[int] = None, **kwargs: Any) -> Optional[Series]: ...
+3 -8
View File
@@ -1,8 +1,3 @@
from .dsp import dsp
from .ebsw import ebsw
from .ht_dcperiod import ht_dcperiod
from .ht_dcphase import ht_dcphase
from .ht_phasor import ht_phasor
from .ht_sine import ht_sine
from .ht_trendmode import ht_trendmode
from .msw import msw
from pandas_ta_classic._lazy_subpackage import install_lazy_subpackage
install_lazy_subpackage(__name__)
+6 -35
View File
@@ -6,39 +6,10 @@ ATAN, CEIL, COS, COSH, EXP, FLOOR, LN, LOG10, SIN, SINH, SQRT, TAN, TANH)
groups, plus tulipy extras (ABS, ROUND, TRUNC, TODEG, TORAD).
"""
# Math Operators
from .add import add
from .sub import sub
from .div import div
from .mult import mult
from .rolling_max import rolling_max
from .rolling_min import rolling_min
from .rolling_sum import rolling_sum
from .maxindex import maxindex
from .minindex import minindex
from .minmax import minmax
from .minmaxindex import minmaxindex
from pandas_ta_classic._lazy_subpackage import install_lazy_subpackage
# Math Transforms
from .acos import acos
from .asin import asin
from .atan import atan
from .ceil import ceil
from .cos import cos
from .cosh import cosh
from .exp import exp
from .floor import floor
from .ln import ln
from .log10 import log10
from .sin import sin
from .sinh import sinh
from .sqrt import sqrt
from .tan import tan
from .tanh import tanh
# Tulipy extras
from .npabs import npabs
from .npround import npround
from .trunc import trunc
from .todeg import todeg
from .torad import torad
# TA-Lib uses MAX/MIN/SUM; canonical names here are rolling_max/rolling_min/rolling_sum
install_lazy_subpackage(
__name__,
aliases={"max": "rolling_max", "min": "rolling_min", "sum": "rolling_sum"},
)
+3 -53
View File
@@ -1,53 +1,3 @@
from .ao import ao
from .apo import apo
from .bias import bias
from .bop import bop
from .brar import brar
from .cci import cci
from .cfo import cfo
from .cg import cg
from .cmo import cmo
from .coppock import coppock
from .cti import cti
from .dm import dm
from .er import er
from .eri import eri
from .fisher import fisher
from .fosc import fosc
from .inertia import inertia
from .kdj import kdj
from .kst import kst
from .lrsi import lrsi
from .macd import macd
from .macdext import macdext
from .macdfix import macdfix
from .mom import mom
from .pgo import pgo
from .po import po
from .ppo import ppo
from .psl import psl
from .pvo import pvo
from .qqe import qqe
from .roc import roc
from .rocp import rocp
from .rocr import rocr
from .rocr100 import rocr100
from .rsi import rsi
from .rsx import rsx
from .rvgi import rvgi
from .slope import slope
from .smc_sweep import smc_sweep
from .smi import smi
from .squeeze import squeeze
from .squeeze_pro import squeeze_pro
from .stc import stc
from .stoch import stoch
from .stochf import stochf
from .stochrsi import stochrsi
from .td_seq import td_seq
from .trix import trix
from .trixh import trixh
from .tsi import tsi
from .uo import uo
from .vwmacd import vwmacd
from .willr import willr
from pandas_ta_classic._lazy_subpackage import install_lazy_subpackage
install_lazy_subpackage(__name__)
+3 -46
View File
@@ -1,46 +1,3 @@
from .alma import alma
from .avgprice import avgprice
from .dema import dema
from .ema import ema
from .fwma import fwma
from .hilo import hilo
from .hl2 import hl2
from .hlc3 import hlc3
from .hma import hma
from .ht_trendline import ht_trendline
from .hwma import hwma
from .ichimoku import ichimoku
from .jma import jma
from .kama import kama
from .linreg import linreg
from .linregangle import linregangle
from .linregintercept import linregintercept
from .linregslope import linregslope
from .ma import ma
from .medprice import medprice
from .mama import mama
from .mavp import mavp
from .mcgd import mcgd
from .midpoint import midpoint
from .midprice import midprice
from .mmar import mmar
from .ohlc4 import ohlc4
from .pwma import pwma
from .rainbow import rainbow
from .rma import rma
from .sinwma import sinwma
from .sma import sma
from .ssf import ssf
from .supertrend import supertrend
from .swma import swma
from .t3 import t3
from .tema import tema
from .trima import trima
from .tsf import tsf
from .typprice import typprice
from .vidya import vidya
from .vwap import vwap
from .vwma import vwma
from .wcp import wcp
from .wma import wma
from .zlma import zlma
from pandas_ta_classic._lazy_subpackage import install_lazy_subpackage
install_lazy_subpackage(__name__)
+3 -3
View File
@@ -1,3 +1,3 @@
from .drawdown import drawdown
from .log_return import log_return
from .percent_return import percent_return
from pandas_ta_classic._lazy_subpackage import install_lazy_subpackage
install_lazy_subpackage(__name__)
+3 -14
View File
@@ -1,14 +1,3 @@
from .beta import beta
from .correl import correl
from .entropy import entropy
from .kurtosis import kurtosis
from .mad import mad
from .md import md
from .median import median
from .quantile import quantile
from .skew import skew
from .stderr import stderr
from .stdev import stdev
from .tos_stdevall import tos_stdevall
from .variance import variance
from .zscore import zscore
from pandas_ta_classic._lazy_subpackage import install_lazy_subpackage
install_lazy_subpackage(__name__)
+3 -26
View File
@@ -1,26 +1,3 @@
from .adx import adx
from .adxr import adxr
from .dx import dx
from .amat import amat
from .aroon import aroon
from .chop import chop
from .cksp import cksp
from .cpr import cpr
from .decay import decay
from .decreasing import decreasing
from .dpo import dpo
from .edecay import edecay
from .increasing import increasing
from .long_run import long_run
from .minus_dm import minus_dm
from .pmax import pmax
from .plus_dm import plus_dm
from .psar import psar
from .qstick import qstick
from .sarext import sarext
from .short_run import short_run
from .tsignals import tsignals
from .ttm_trend import ttm_trend
from .vhf import vhf
from .vortex import vortex
from .xsignals import xsignals
from pandas_ta_classic._lazy_subpackage import install_lazy_subpackage
install_lazy_subpackage(__name__)
+3 -18
View File
@@ -1,18 +1,3 @@
from .aberration import aberration
from .accbands import accbands
from .avolume import avolume
from .cvi import cvi
from .atr import atr
from .bbands import bbands
from .ce import ce
from .donchian import donchian
from .hvol import hvol
from .hwc import hwc
from .kc import kc
from .massi import massi
from .natr import natr
from .pdist import pdist
from .rvi import rvi
from .thermo import thermo
from .true_range import true_range
from .ui import ui
from pandas_ta_classic._lazy_subpackage import install_lazy_subpackage
install_lazy_subpackage(__name__)
+3 -20
View File
@@ -1,20 +1,3 @@
from .ad import ad
from .adosc import adosc
from .aobv import aobv
from .cmf import cmf
from .efi import efi
from .emv import emv
from .eom import eom
from .kvo import kvo
from .marketfi import marketfi
from .mfi import mfi
from .nvi import nvi
from .obv import obv
from .pvi import pvi
from .pvol import pvol
from .pvr import pvr
from .pvt import pvt
from .vfi import vfi
from .vosc import vosc
from .vp import vp
from .wad import wad
from pandas_ta_classic._lazy_subpackage import install_lazy_subpackage
install_lazy_subpackage(__name__)
+1 -1
View File
@@ -57,7 +57,7 @@ class TestCandle(TestCase):
def test_cdl_pattern(self):
result = pandas_ta.cdl_pattern(self.open, self.high, self.low, self.close, name="all")
self.assertIsInstance(result, DataFrame)
self.assertEqual(len(result.columns), len(pandas_ta.CDL_PATTERN_NAMES))
self.assertEqual(len(result.columns), len(pandas_ta.ALL_PATTERNS))
result = pandas_ta.cdl_pattern(self.open, self.high, self.low, self.close, name="doji")
self.assertIsInstance(result, DataFrame)
+100
View File
@@ -0,0 +1,100 @@
#!/usr/bin/env python
"""Generate pandas_ta_classic/core.pyi from indicator function signatures."""
import importlib
import inspect
import sys
from pathlib import Path
# Ensure the project root is on sys.path
ROOT = Path(__file__).parent.parent
sys.path.insert(0, str(ROOT))
from pandas_ta_classic._meta import Category
from pandas_ta_classic._indicator_loader import (
_find_indicator_func,
_COLUMN_PARAM_TO_COL_KEY,
)
_RETURN_MAP = {
"ichimoku": "tuple[Optional[DataFrame], Optional[DataFrame]]",
}
def _build_method_stub(name: str, func) -> str:
sig = inspect.signature(func)
# Build param list: self + non-column params
parts = ["self"]
for pname, param in sig.parameters.items():
if pname in _COLUMN_PARAM_TO_COL_KEY:
continue
if param.kind == inspect.Parameter.VAR_KEYWORD:
parts.append("**kwargs: Any")
else:
annotation = ""
if param.annotation is not inspect.Parameter.empty:
annotation = f": {_format_annotation(param.annotation)}"
default = ""
if param.default is not inspect.Parameter.empty:
default = f" = {repr(param.default)}"
parts.append(f"{pname}{annotation}{default}")
# Return type
ret = "Optional[Union[Series, DataFrame]]"
if name in _RETURN_MAP:
ret = _RETURN_MAP[name]
elif sig.return_annotation is not inspect.Parameter.empty:
ret = _format_annotation(sig.return_annotation)
params_str = ", ".join(parts)
return f" def {name}({params_str}) -> {ret}: ..."
def _format_annotation(ann) -> str:
if ann is type(None):
return "None"
if hasattr(ann, "__origin__"):
raw = str(ann).replace("typing.", "")
raw = raw.replace("pandas.core.series.Series", "Series")
raw = raw.replace("pandas.core.frame.DataFrame", "DataFrame")
raw = raw.replace("NoneType", "None")
raw = raw.replace("collections.abc.Sequence", "Sequence")
return raw
name = getattr(ann, "__name__", None) or str(ann)
# Normalise fully-qualified pandas names
name = name.replace("pandas.core.series.Series", "Series")
name = name.replace("pandas.core.frame.DataFrame", "DataFrame")
return name
def main():
lines = [
"# This file is auto-generated by tools/gen_core_stub.py — do not edit manually.",
"from typing import Any, Optional, Sequence, Union",
"from pandas import Series, DataFrame",
"",
"class AnalysisIndicators:",
]
for cat, indicators in sorted(Category.items()):
lines.append(f" # {cat}")
for ind_name in sorted(indicators):
func = _find_indicator_func(ind_name)
if func is None:
continue
try:
stub = _build_method_stub(ind_name, func)
lines.append(stub)
except Exception as e:
lines.append(
f" def {ind_name}(self, **kwargs: Any) -> Optional[Union[Series, DataFrame]]: ... # stub-gen failed: {e}"
)
lines.append("")
out_path = ROOT / "pandas_ta_classic" / "core.pyi"
out_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
print(f"Wrote {out_path} ({len(lines)} lines)")
if __name__ == "__main__":
main()