chore: release v1.0.3
This commit is contained in:
@@ -59,8 +59,52 @@ array([ nan, nan, 11. , 12. , 13. , 13.5, 13.33...])
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from importlib.metadata import PackageNotFoundError as _PackageNotFoundError
|
||||
from importlib.metadata import version as _dist_version
|
||||
from pathlib import Path as _Path
|
||||
import re as _re
|
||||
import sys as _sys
|
||||
|
||||
try:
|
||||
import tomllib as _tomllib
|
||||
except ImportError: # pragma: no cover
|
||||
try:
|
||||
import tomli as _tomllib # type: ignore[no-redef]
|
||||
except ImportError: # pragma: no cover
|
||||
_tomllib = None # type: ignore[assignment]
|
||||
|
||||
|
||||
def _detect_version() -> str:
|
||||
try:
|
||||
return _dist_version("ferro-ta")
|
||||
except _PackageNotFoundError:
|
||||
pass
|
||||
|
||||
if _tomllib is not None:
|
||||
pyproject_toml = _Path(__file__).resolve().parents[2] / "pyproject.toml"
|
||||
if pyproject_toml.is_file():
|
||||
try:
|
||||
with pyproject_toml.open("rb") as handle:
|
||||
data = _tomllib.load(handle)
|
||||
return data.get("project", {}).get("version", "0+unknown")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
pyproject_toml = _Path(__file__).resolve().parents[2] / "pyproject.toml"
|
||||
if pyproject_toml.is_file():
|
||||
try:
|
||||
text = pyproject_toml.read_text(encoding="utf-8")
|
||||
match = _re.search(r'^version\s*=\s*"([^"]+)"', text, _re.MULTILINE)
|
||||
if match:
|
||||
return match.group(1)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return "0+unknown"
|
||||
|
||||
|
||||
__version__ = _detect_version()
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Exceptions — exported at the top level for convenient catching
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -281,6 +325,7 @@ from ferro_ta.indicators.volume import ( # noqa: F401
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"__version__",
|
||||
# Overlap Studies
|
||||
"SMA",
|
||||
"EMA",
|
||||
@@ -459,7 +504,9 @@ __all__ = [
|
||||
"VWMA",
|
||||
"CHOPPINESS_INDEX",
|
||||
# API discovery
|
||||
"about",
|
||||
"indicators",
|
||||
"methods",
|
||||
"info",
|
||||
# Logging utilities
|
||||
"enable_debug",
|
||||
@@ -585,9 +632,10 @@ from ferro_ta.tools.alerts import ( # noqa: F401, E402
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# API discovery helpers — ferro_ta.indicators() and ferro_ta.info()
|
||||
# API discovery helpers — ferro_ta.about(), ferro_ta.methods(),
|
||||
# ferro_ta.indicators(), and ferro_ta.info()
|
||||
# ---------------------------------------------------------------------------
|
||||
from ferro_ta.tools.api_info import indicators, info # noqa: F401, E402
|
||||
from ferro_ta.tools.api_info import about, indicators, info, methods # noqa: F401, E402
|
||||
|
||||
_ALIASED_SUBMODULES = {
|
||||
"batch": batch,
|
||||
|
||||
@@ -67,6 +67,7 @@ from typing import Any
|
||||
|
||||
import numpy as np
|
||||
|
||||
import ferro_ta as ft
|
||||
from ferro_ta.tools import (
|
||||
compute_indicator,
|
||||
describe_indicator,
|
||||
@@ -392,7 +393,7 @@ def _run_stdio_fallback() -> None: # pragma: no cover
|
||||
"result": {
|
||||
"protocolVersion": "2024-11-05",
|
||||
"capabilities": {"tools": {}},
|
||||
"serverInfo": {"name": "ferro-ta", "version": "1.0.0"},
|
||||
"serverInfo": {"name": "ferro-ta", "version": ft.__version__},
|
||||
},
|
||||
}
|
||||
elif method == "tools/list":
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
"""
|
||||
ferro_ta.api_info — API discovery helpers.
|
||||
|
||||
Provides :func:`indicators` and :func:`info` for exploring the ferro_ta
|
||||
indicator catalogue without reading source code.
|
||||
Provides :func:`indicators`, :func:`methods`, :func:`about`, and :func:`info`
|
||||
for exploring the ferro_ta public API without reading source code.
|
||||
|
||||
Usage
|
||||
-----
|
||||
>>> import ferro_ta
|
||||
>>> ferro_ta.indicators() # all indicators, sorted
|
||||
>>> ferro_ta.indicators(category="momentum") # filter by category
|
||||
>>> ferro_ta.methods() # public callables across modules
|
||||
>>> ferro_ta.about()["version"] # package metadata summary
|
||||
>>> ferro_ta.info(ferro_ta.SMA) # parameter docs for SMA
|
||||
|
||||
API
|
||||
---
|
||||
indicators(category=None) — Return list of dicts describing every indicator.
|
||||
methods(category=None) — Return list of public callables across modules.
|
||||
about() — Return package/version/module summary metadata.
|
||||
info(func_or_name) — Return a dict with full signature/docstring info.
|
||||
"""
|
||||
|
||||
@@ -23,7 +27,7 @@ import importlib
|
||||
import inspect
|
||||
from typing import Any
|
||||
|
||||
__all__ = ["indicators", "info"]
|
||||
__all__ = ["indicators", "methods", "about", "info"]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Category → module mapping used by indicators()
|
||||
@@ -52,6 +56,20 @@ _CATEGORY_MODULES: dict[str, str] = {
|
||||
"regime": "ferro_ta.analysis.regime",
|
||||
}
|
||||
|
||||
_METHOD_MODULES: dict[str, str] = {
|
||||
"top_level": "ferro_ta",
|
||||
**_CATEGORY_MODULES,
|
||||
"options": "ferro_ta.analysis.options",
|
||||
"futures": "ferro_ta.analysis.futures",
|
||||
"backtest": "ferro_ta.analysis.backtest",
|
||||
"options_strategy": "ferro_ta.analysis.options_strategy",
|
||||
"derivatives_payoff": "ferro_ta.analysis.derivatives_payoff",
|
||||
"attribution": "ferro_ta.analysis.attribution",
|
||||
"cross_asset": "ferro_ta.analysis.cross_asset",
|
||||
"tools": "ferro_ta.tools.tools",
|
||||
"viz": "ferro_ta.tools.viz",
|
||||
}
|
||||
|
||||
|
||||
def _iter_module_callables(
|
||||
module_name: str,
|
||||
@@ -137,6 +155,66 @@ def indicators(category: str | None = None) -> list[dict[str, Any]]:
|
||||
return result
|
||||
|
||||
|
||||
def methods(category: str | None = None) -> list[dict[str, Any]]:
|
||||
"""Return public callables across ferro_ta modules.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
category : str | None
|
||||
Optional key from :data:`_METHOD_MODULES`, such as ``"top_level"``,
|
||||
``"options"``, ``"futures"``, or ``"batch"``.
|
||||
"""
|
||||
cats: dict[str, str] = (
|
||||
{category: _METHOD_MODULES[category]}
|
||||
if category is not None
|
||||
else _METHOD_MODULES
|
||||
)
|
||||
|
||||
result: list[dict[str, Any]] = []
|
||||
seen: set[tuple[str, str]] = set()
|
||||
for cat, mod_name in cats.items():
|
||||
for name, func in _iter_module_callables(mod_name):
|
||||
key = (mod_name, name)
|
||||
if key in seen:
|
||||
continue
|
||||
seen.add(key)
|
||||
doc = inspect.getdoc(func) or ""
|
||||
first_line = doc.splitlines()[0] if doc else ""
|
||||
try:
|
||||
sig = inspect.signature(func)
|
||||
params = list(sig.parameters.keys())
|
||||
except (ValueError, TypeError):
|
||||
params = []
|
||||
result.append(
|
||||
{
|
||||
"name": name,
|
||||
"category": cat,
|
||||
"module": mod_name,
|
||||
"doc": first_line,
|
||||
"params": params,
|
||||
}
|
||||
)
|
||||
|
||||
result.sort(key=lambda d: (d["category"], d["name"]))
|
||||
return result
|
||||
|
||||
|
||||
def about() -> dict[str, Any]:
|
||||
"""Return a small metadata summary for the installed ferro_ta package."""
|
||||
import ferro_ta # noqa: PLC0415
|
||||
|
||||
top_level_exports = sorted(getattr(ferro_ta, "__all__", []))
|
||||
return {
|
||||
"name": "ferro-ta",
|
||||
"version": getattr(ferro_ta, "__version__", "0+unknown"),
|
||||
"top_level_export_count": len(top_level_exports),
|
||||
"indicator_count": len(indicators()),
|
||||
"method_count": len(methods()),
|
||||
"categories": sorted(_METHOD_MODULES.keys()),
|
||||
"top_level_exports": top_level_exports,
|
||||
}
|
||||
|
||||
|
||||
def info(func_or_name: Any) -> dict[str, Any]:
|
||||
"""Return detailed information about an indicator function.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user