Files
2026-03-23 23:34:28 +05:30

92 lines
3.3 KiB
ReStructuredText

Writing a plugin
================
The plugin registry lets you register custom indicator functions and call them by name
alongside built-in indicators. This page describes the **plugin contract**, how to
register and run plugins, and a full example.
Plugin contract
---------------
A plugin is a **callable** (function or callable object) that satisfies:
1. **Signature**
- At least one positional argument that is array-like (e.g. ``close``, ``high``, ``low``).
- Optional ``*args`` and ``**kwargs`` for parameters (e.g. ``timeperiod=14``).
- :func:`ferro_ta.registry.run` forwards all ``*args`` and ``**kwargs`` to the callable.
2. **Return type**
- A single ``numpy.ndarray``, or
- A tuple of ``numpy.ndarray`` (for multi-output indicators).
- Output length should match input length (same number of bars); document any exception.
3. **Behaviour**
- The callable may use ``ferro_ta`` internally (e.g. call :func:`ferro_ta.RSI` and then apply another transformation).
- Plugins run with the caller's privileges; there is no sandboxing.
Validation
----------
:func:`ferro_ta.registry.register` checks that the provided object is callable. If not,
it raises ``TypeError``. No strict signature check is performed at registration time
so that valid plugins (e.g. with default arguments) are not rejected.
Step-by-step
------------
1. **Write a function** that accepts at least one array-like and returns one or more
arrays of the same length as the first argument.
2. **Register it** with :func:`ferro_ta.registry.register`:
.. code-block:: python
from ferro_ta.registry import register
register("MY_INDICATOR", my_indicator_function)
3. **Call it by name** with :func:`ferro_ta.registry.run`:
.. code-block:: python
from ferro_ta.registry import run
result = run("MY_INDICATOR", close, timeperiod=14)
4. **List all indicators** (built-in and registered) with :func:`ferro_ta.registry.list_indicators`.
Full example
------------
The following plugin computes a smoothed RSI (RSI of RSI, or "double RSI") and is
included in the repo as ``examples/custom_indicator.py``:
.. code-block:: python
"""Example plugin: smoothed RSI (RSI applied to RSI values)."""
import numpy as np
from ferro_ta.registry import register, run, list_indicators
from ferro_ta import RSI, SMA
def SMOOTH_RSI(close, timeperiod=14, smooth=3):
"""Smoothed RSI: RSI then SMA of the RSI series."""
rsi = RSI(close, timeperiod=timeperiod)
return SMA(rsi, timeperiod=smooth)
if __name__ == "__main__":
register("SMOOTH_RSI", SMOOTH_RSI)
close = np.array([44.34, 44.09, 44.15, 43.61, 44.33, 44.83, 45.10, 45.15, 44.61, 44.33])
out = run("SMOOTH_RSI", close, timeperiod=5, smooth=2)
print("SMOOTH_RSI:", out)
assert "SMOOTH_RSI" in list_indicators()
API reference
-------------
- :func:`ferro_ta.registry.register` — Register a callable under a name.
- :func:`ferro_ta.registry.unregister` — Remove a registered indicator.
- :func:`ferro_ta.registry.get` — Return the callable for a name.
- :func:`ferro_ta.registry.run` — Look up by name and call with given args/kwargs.
- :func:`ferro_ta.registry.list_indicators` — Sorted list of all registered names.
- :exc:`ferro_ta.registry.FerroTARegistryError` — Raised when a name is not found.
See :mod:`ferro_ta.registry` for full docstrings.