Files

94 lines
3.3 KiB
Python
Raw Permalink Normal View History

2026-03-23 23:34:28 +05:30
"""
Data-driven binding layer — generic wrapper for Rust indicator calls.
This module provides a single helper that performs validation, array conversion
(_to_f64), Rust call, and error normalization. Indicator modules can use it to
reduce repetitive wrapper code; a manifest (see _indicator_manifest.yaml) describes
each indicator so that wrappers or code generation can be driven from data.
Usage (manual wrapper):
from ferro_ta._binding import binding_call
def SMA(close, timeperiod=30):
return binding_call(
_sma,
array_params=["close"],
timeperiod_param="timeperiod",
close=close,
timeperiod=timeperiod,
)
Future: A code generator can read the manifest and emit either full wrapper
functions or binding_call(...) invocations so that ~6000 lines of repetitive
wrapper code are generated from the manifest.
"""
from __future__ import annotations
from collections.abc import Callable
from typing import Any, Optional
from ferro_ta._utils import _to_f64
from ferro_ta.core.exceptions import (
2026-03-23 23:57:30 +05:30
_normalize_rust_error,
2026-03-23 23:34:28 +05:30
check_equal_length,
check_timeperiod,
)
def binding_call(
rust_fn: Callable[..., Any],
*,
array_params: list[str],
timeperiod_param: Optional[str] = None,
timeperiod_min: int = 1,
equal_length_groups: Optional[list[list[str]]] = None,
**kwargs: Any,
) -> Any:
"""Call a Rust indicator with validation and array conversion.
Parameters
----------
rust_fn : callable
The Rust function from _ferro_ta (e.g. _sma).
array_params : list of str
Names of keyword arguments that are array-like; they are converted
with _to_f64 and passed in order as positional args to rust_fn.
timeperiod_param : str, optional
If set, the value of kwargs[timeperiod_param] is validated with
check_timeperiod(..., minimum=timeperiod_min).
timeperiod_min : int
Minimum allowed value for timeperiod_param (default 1).
equal_length_groups : list of list of str, optional
Each inner list is a group of param names that must have equal length;
check_equal_length is called with that group.
**kwargs
Keyword arguments to pass. Array params are converted and passed
positionally; non-array params are passed as keyword arguments to
rust_fn (caller must ensure rust_fn signature matches).
Returns
-------
Result of rust_fn(...). Typically numpy.ndarray or tuple of ndarray.
Raises
------
FerroTAValueError, FerroTAInputError
Via check_timeperiod / check_equal_length or _normalize_rust_error.
"""
if timeperiod_param is not None and timeperiod_param in kwargs:
check_timeperiod(
kwargs[timeperiod_param],
name=timeperiod_param,
minimum=timeperiod_min,
)
if equal_length_groups is not None:
for group in equal_length_groups:
check_equal_length(**{k: kwargs[k] for k in group if k in kwargs})
# Build positional args for rust_fn in array_params order, then remaining kwargs
pos_args = [_to_f64(kwargs[p]) for p in array_params if p in kwargs]
rest_kw = {k: v for k, v in kwargs.items() if k not in array_params}
try:
return rust_fn(*pos_args, **rest_kw)
except ValueError as e:
_normalize_rust_error(e)