Source code for ferro_ta.indicators.math_ops

"""
Math Operators & Math Transforms — TA-Lib compatibility shims.

Rolling functions (SUM, MAX, MIN, MAXINDEX, MININDEX) are implemented in Rust
using O(n) monotonic deque / prefix-sum algorithms.  All other functions are
thin NumPy wrappers (element-wise operations).

Functions
---------
Math Operators:
  ADD        — Element-wise addition
  SUB        — Element-wise subtraction
  MULT       — Element-wise multiplication
  DIV        — Element-wise division
  SUM        — Rolling sum over *timeperiod* bars  (Rust)
  MAX        — Rolling maximum over *timeperiod* bars  (Rust)
  MIN        — Rolling minimum over *timeperiod* bars  (Rust)
  MAXINDEX   — Index of rolling maximum over *timeperiod* bars  (Rust)
  MININDEX   — Index of rolling minimum over *timeperiod* bars  (Rust)

Math Transforms (element-wise):
  ACOS ASIN ATAN CEIL COS COSH EXP FLOOR LN LOG10 SIN SINH SQRT TAN TANH

Rust backend
------------
Rolling operators delegate to::

    from ferro_ta._ferro_ta import rolling_sum, rolling_max, rolling_min, ...
"""

from __future__ import annotations

import numpy as np
from numpy.typing import ArrayLike

# ---------------------------------------------------------------------------
# Import Rust rolling operators
# ---------------------------------------------------------------------------
from ferro_ta._ferro_ta import (
    rolling_max as _rust_rolling_max,
)
from ferro_ta._ferro_ta import (
    rolling_maxindex as _rust_rolling_maxindex,
)
from ferro_ta._ferro_ta import (
    rolling_min as _rust_rolling_min,
)
from ferro_ta._ferro_ta import (
    rolling_minindex as _rust_rolling_minindex,
)
from ferro_ta._ferro_ta import (
    rolling_sum as _rust_rolling_sum,
)
from ferro_ta._utils import _to_f64
from ferro_ta.core.exceptions import _normalize_rust_error

# ---------------------------------------------------------------------------
# Math Operators
# ---------------------------------------------------------------------------


[docs] def ADD(real0: ArrayLike, real1: ArrayLike) -> np.ndarray: """Element-wise addition: real0 + real1. Parameters ---------- real0, real1 : array-like Input arrays (same length). Returns ------- numpy.ndarray[float64] """ try: return np.add(_to_f64(real0), _to_f64(real1)) except ValueError as e: _normalize_rust_error(e)
[docs] def SUB(real0: ArrayLike, real1: ArrayLike) -> np.ndarray: """Element-wise subtraction: real0 - real1. Parameters ---------- real0, real1 : array-like Input arrays (same length). Returns ------- numpy.ndarray[float64] """ try: return np.subtract(_to_f64(real0), _to_f64(real1)) except ValueError as e: _normalize_rust_error(e)
[docs] def MULT(real0: ArrayLike, real1: ArrayLike) -> np.ndarray: """Element-wise multiplication: real0 * real1. Parameters ---------- real0, real1 : array-like Input arrays (same length). Returns ------- numpy.ndarray[float64] """ try: return np.multiply(_to_f64(real0), _to_f64(real1)) except ValueError as e: _normalize_rust_error(e)
[docs] def DIV(real0: ArrayLike, real1: ArrayLike) -> np.ndarray: """Element-wise division: real0 / real1. Parameters ---------- real0, real1 : array-like Input arrays (same length). Returns ------- numpy.ndarray[float64] """ try: # Suppress divide-by-zero warnings while preserving inf/NaN outputs. with np.errstate(divide="ignore", invalid="ignore"): return np.divide(_to_f64(real0), _to_f64(real1)) except ValueError as e: _normalize_rust_error(e)
[docs] def SUM(real: ArrayLike, timeperiod: int = 30) -> np.ndarray: """Rolling sum over *timeperiod* bars. Parameters ---------- real : array-like timeperiod : int, default 30 Returns ------- numpy.ndarray[float64] NaN for the first ``timeperiod - 1`` bars. Notes ----- Implemented in Rust using O(n) prefix-sum algorithm. """ try: arr = _to_f64(real) return np.asarray(_rust_rolling_sum(arr, timeperiod)) except ValueError as e: _normalize_rust_error(e)
[docs] def MAX(real: ArrayLike, timeperiod: int = 30) -> np.ndarray: """Rolling maximum over *timeperiod* bars. Parameters ---------- real : array-like timeperiod : int, default 30 Returns ------- numpy.ndarray[float64] NaN for the first ``timeperiod - 1`` bars. Notes ----- Implemented in Rust using O(n) monotonic deque algorithm. """ try: arr = _to_f64(real) return np.asarray(_rust_rolling_max(arr, timeperiod)) except ValueError as e: _normalize_rust_error(e)
[docs] def MIN(real: ArrayLike, timeperiod: int = 30) -> np.ndarray: """Rolling minimum over *timeperiod* bars. Parameters ---------- real : array-like timeperiod : int, default 30 Returns ------- numpy.ndarray[float64] NaN for the first ``timeperiod - 1`` bars. Notes ----- Implemented in Rust using O(n) monotonic deque algorithm. """ try: arr = _to_f64(real) return np.asarray(_rust_rolling_min(arr, timeperiod)) except ValueError as e: _normalize_rust_error(e)
[docs] def MAXINDEX(real: ArrayLike, timeperiod: int = 30) -> np.ndarray: """Index of the rolling maximum over *timeperiod* bars. The index is the absolute position in the input array. Parameters ---------- real : array-like timeperiod : int, default 30 Returns ------- numpy.ndarray[int64] -1 for the first ``timeperiod - 1`` bars (warmup period). Notes ----- Implemented in Rust using O(n) monotonic deque algorithm. """ try: arr = _to_f64(real) return np.asarray(_rust_rolling_maxindex(arr, timeperiod)) except ValueError as e: _normalize_rust_error(e)
[docs] def MININDEX(real: ArrayLike, timeperiod: int = 30) -> np.ndarray: """Index of the rolling minimum over *timeperiod* bars. The index is the absolute position in the input array. Parameters ---------- real : array-like timeperiod : int, default 30 Returns ------- numpy.ndarray[int64] -1 for the first ``timeperiod - 1`` bars (warmup period). Notes ----- Implemented in Rust using O(n) monotonic deque algorithm. """ try: arr = _to_f64(real) return np.asarray(_rust_rolling_minindex(arr, timeperiod)) except ValueError as e: _normalize_rust_error(e)
# --------------------------------------------------------------------------- # Math Transforms (element-wise) # ---------------------------------------------------------------------------
[docs] def ACOS(real: ArrayLike) -> np.ndarray: """Arc cosine (element-wise). Returns NaN outside [-1, 1].""" with np.errstate(invalid="ignore"): return np.arccos(_to_f64(real))
[docs] def ASIN(real: ArrayLike) -> np.ndarray: """Arc sine (element-wise). Returns NaN outside [-1, 1].""" with np.errstate(invalid="ignore"): return np.arcsin(_to_f64(real))
[docs] def ATAN(real: ArrayLike) -> np.ndarray: """Arc tangent (element-wise).""" return np.arctan(_to_f64(real))
[docs] def CEIL(real: ArrayLike) -> np.ndarray: """Ceiling (element-wise).""" return np.ceil(_to_f64(real))
[docs] def COS(real: ArrayLike) -> np.ndarray: """Cosine (element-wise).""" return np.cos(_to_f64(real))
[docs] def COSH(real: ArrayLike) -> np.ndarray: """Hyperbolic cosine (element-wise).""" return np.cosh(_to_f64(real))
[docs] def EXP(real: ArrayLike) -> np.ndarray: """Exponential (element-wise).""" return np.exp(_to_f64(real))
[docs] def FLOOR(real: ArrayLike) -> np.ndarray: """Floor (element-wise).""" return np.floor(_to_f64(real))
[docs] def LN(real: ArrayLike) -> np.ndarray: """Natural logarithm (element-wise). Returns NaN for non-positive inputs.""" with np.errstate(divide="ignore", invalid="ignore"): return np.log(_to_f64(real))
[docs] def LOG10(real: ArrayLike) -> np.ndarray: """Base-10 logarithm (element-wise). Returns NaN for non-positive inputs.""" with np.errstate(divide="ignore", invalid="ignore"): return np.log10(_to_f64(real))
[docs] def SIN(real: ArrayLike) -> np.ndarray: """Sine (element-wise).""" return np.sin(_to_f64(real))
[docs] def SINH(real: ArrayLike) -> np.ndarray: """Hyperbolic sine (element-wise).""" return np.sinh(_to_f64(real))
[docs] def SQRT(real: ArrayLike) -> np.ndarray: """Square root (element-wise). Returns NaN for negative inputs.""" with np.errstate(invalid="ignore"): return np.sqrt(_to_f64(real))
[docs] def TAN(real: ArrayLike) -> np.ndarray: """Tangent (element-wise).""" return np.tan(_to_f64(real))
[docs] def TANH(real: ArrayLike) -> np.ndarray: """Hyperbolic tangent (element-wise).""" return np.tanh(_to_f64(real))
__all__ = [ # Math Operators "ADD", "SUB", "MULT", "DIV", "SUM", "MAX", "MIN", "MAXINDEX", "MININDEX", # Math Transforms "ACOS", "ASIN", "ATAN", "CEIL", "COS", "COSH", "EXP", "FLOOR", "LN", "LOG10", "SIN", "SINH", "SQRT", "TAN", "TANH", ]