288b1546b2
* feat: broaden CPU and platform coverage across PyPI, nodes, and crates.io Replace static SIMD with runtime CPU-feature dispatch and expand the release wheel matrix so one set of artifacts runs on any target CPU and platform without illegal-instruction crashes. Rust core: - Add multiversion runtime dispatch (crates/ferro_ta_core/src/simd.rs); drop compile-time `wide`. `simd` feature is now default-on and forwarded through the pyo3 crate, and stays compatible with #![forbid(unsafe_code)]. Packaging: - abi3-py310: one cp310-abi3 wheel per platform (covers CPython 3.10+). - CI matrix adds Linux aarch64 + musllinux (x86_64/aarch64) and Windows arm64. Node/Docker + docs: - api/Dockerfile: document baseline+dispatch (no target-cpu pin) and add a fail-fast import check; aarch64 containers now install cleanly. - Rewrite docs/guides/simd.md; fix stale `wide` mention in ADR 0003. - Add ADR 0006 (CPU coverage strategy). Also bundles in-flight release prep already staged in the tree (DTW exception types, SBOM/provenance security, supporting docs). * fix(ci): clear cargo-deny and pip-audit failures; apply dependency bumps cargo-deny (advisories): - Ignore pyo3 RUSTSEC-2026-0176 / RUSTSEC-2026-0177 in deny.toml with a documented rationale: ferro-ta uses neither affected code path (PyList/PyTuple nth iterators; PyCFunction::new_closure). Upstream fix needs pyo3 >=0.29 (large API migration), tracked as a follow-up. pip-audit: - Bump dev lockfile idna 3.18, pytest 9.1.1, urllib3 2.7.0 to clear PYSEC-2026-215, CVE-2025-71176, PYSEC-2026-141/142. Dependency bumps (supersede open dependabot PRs; they auto-close on merge): - cargo: log 0.4.32, serde_json 1.0.150, rayon 1.12.0 - api/requirements.txt: uvicorn>=0.49.0, pydantic>=2.13.4, ferro-ta>=1.1.4 - CI actions: deploy-pages v5, upload-pages-artifact v5, action-gh-release v3 The open `wide` 1.5.0 bump (PR #24) is obsolete — the crate is removed in this branch. * chore: address CodeRabbit review; remove docs/adr section CodeRabbit findings: - CI sbom job: add `attestations: write` so attest-build-provenance can run (it had only contents:write + id-token:write). - simd.rs: vectorize `wma_seed` with lane-local accumulators — it was scalar behind the multiversion wrapper, adding dispatch overhead for no SIMD gain. - CHANGELOG: consolidate the duplicate `### Changed` heading. - python/ferro_ta/__init__.py: also re-export the `FerroTaError` alias. - docs/guides/dtw.md: soften "byte-for-byte" parity to within-tolerance. Remove docs/adr/ at maintainer request and clean up the ADR links in the SIMD and DTW guides. The ADR files remain in commit 9506a30 if ever needed.
168 lines
4.7 KiB
TOML
168 lines
4.7 KiB
TOML
[build-system]
|
|
requires = ["maturin>=1.0,<2.0"]
|
|
build-backend = "maturin"
|
|
|
|
[project]
|
|
name = "ferro-ta"
|
|
version = "1.1.4"
|
|
description = "Rust-powered Python technical analysis library with a TA-Lib-compatible API"
|
|
readme = "README.md"
|
|
license = { text = "MIT" }
|
|
requires-python = ">=3.10"
|
|
keywords = [
|
|
"technical-analysis", "trading", "finance", "rust", "pyo3",
|
|
"ta-lib", "indicators", "candlestick", "pandas", "numpy",
|
|
]
|
|
classifiers = [
|
|
"Development Status :: 5 - Production/Stable",
|
|
"Intended Audience :: Financial and Insurance Industry",
|
|
"Intended Audience :: Developers",
|
|
"License :: OSI Approved :: MIT License",
|
|
"Operating System :: OS Independent",
|
|
"Programming Language :: Python :: 3",
|
|
"Programming Language :: Python :: 3.10",
|
|
"Programming Language :: Python :: 3.11",
|
|
"Programming Language :: Python :: 3.12",
|
|
"Programming Language :: Python :: 3.13",
|
|
"Programming Language :: Rust",
|
|
"Topic :: Office/Business :: Financial",
|
|
"Topic :: Scientific/Engineering :: Mathematics",
|
|
"Typing :: Typed",
|
|
]
|
|
dependencies = ["numpy>=1.20"]
|
|
|
|
[project.optional-dependencies]
|
|
test = ["pytest>=7.0", "hypothesis>=6.0"]
|
|
benchmark = ["pytest>=7.0", "pytest-benchmark>=4.0"]
|
|
pandas = ["pandas>=1.0"]
|
|
polars = ["polars>=0.19"]
|
|
docs = ["sphinx>=7.0", "sphinx-rtd-theme>=1.3"]
|
|
comparison = [
|
|
"pytest>=7.0",
|
|
"ta-lib>=0.4",
|
|
"pandas-ta>=0.3; python_version >= '3.12'",
|
|
"ta>=0.10",
|
|
"pandas>=1.0",
|
|
"vectorbt>=0.28",
|
|
"backtrader>=1.9",
|
|
"backtesting>=0.6",
|
|
"quantstats>=0.0.81",
|
|
]
|
|
gpu = ["torch>=2.0"]
|
|
options = []
|
|
mcp = ["mcp>=1.0"]
|
|
all = ["pandas>=1.0", "polars>=0.19", "pytest>=7.0", "pytest-benchmark>=4.0"]
|
|
dev = [
|
|
"pytest>=7.0",
|
|
"pytest-cov>=4.0",
|
|
"hypothesis>=6.0",
|
|
"pandas>=1.0",
|
|
"polars>=0.19",
|
|
"pre-commit>=3.0",
|
|
"ruff>=0.3",
|
|
"mypy>=1.0",
|
|
"pyright>=1.1",
|
|
"maturin>=1.0,<2.0",
|
|
"pyyaml>=6.0",
|
|
"matplotlib>=3.5",
|
|
"fastapi>=0.135.1",
|
|
"httpx>=0.24",
|
|
"scipy>=1.10",
|
|
]
|
|
|
|
[project.urls]
|
|
Homepage = "https://github.com/pratikbhadane24/ferro-ta"
|
|
Repository = "https://github.com/pratikbhadane24/ferro-ta"
|
|
"Bug Tracker" = "https://github.com/pratikbhadane24/ferro-ta/issues"
|
|
|
|
[tool.pytest.ini_options]
|
|
testpaths = ["tests/unit", "tests/integration"]
|
|
|
|
[tool.coverage.run]
|
|
source = ["ferro_ta"]
|
|
omit = ["*/_ferro_ta*", "*/mcp/*"]
|
|
|
|
[tool.coverage.report]
|
|
show_missing = true
|
|
fail_under = 80
|
|
exclude_lines = [
|
|
"pragma: no cover",
|
|
"if TYPE_CHECKING:",
|
|
"if __name__ ==",
|
|
]
|
|
|
|
[tool.maturin]
|
|
python-source = "python"
|
|
module-name = "ferro_ta._ferro_ta"
|
|
features = ["pyo3/extension-module"]
|
|
# Include the PEP 561 py.typed marker so type checkers recognize this package
|
|
include = [{ path = "python/ferro_ta/py.typed", format = "wheel" }]
|
|
|
|
[tool.mypy]
|
|
python_version = "3.10"
|
|
warn_return_any = true
|
|
ignore_missing_imports = true
|
|
# Strictness: step toward strict; fix critical issues first
|
|
disallow_untyped_defs = false
|
|
check_untyped_defs = true
|
|
# Allow for now; tighten once missing-return and no-any-return are fixed
|
|
disable_error_code = ["return", "no-any-return"]
|
|
|
|
[tool.ruff]
|
|
target-version = "py310"
|
|
line-length = 88
|
|
src = ["python", "tests"]
|
|
|
|
[tool.ruff.lint]
|
|
select = ["E", "F", "I", "N", "W", "UP"]
|
|
ignore = ["E501", "UP006", "UP045", "UP007"]
|
|
|
|
# Tests: allow underscore in class names (TestCHANDELIER_EXIT), late imports (E402), uppercase helpers (N802)
|
|
# Stubs: public API names are uppercase (SMA, RSI, etc.); Tuple used for compatibility
|
|
[tool.ruff.lint.per-file-ignores]
|
|
"tests/*" = ["N801", "N802", "N806", "E402", "E741", "F811"]
|
|
"tests/unit/*" = ["N801", "N802", "N806", "E402", "E741", "F811"]
|
|
"tests/integration/*" = ["N801", "N802", "N806", "E402", "E741", "F811"]
|
|
"benchmarks/*" = ["E402", "E741"]
|
|
"python/ferro_ta/*.py" = ["N802"] # Public API: SMA, RSI, ATR, etc.
|
|
"python/ferro_ta/__init__.py" = ["E402", "N802"] # Re-export surface by design
|
|
"python/ferro_ta/__init__.pyi" = ["N802", "E402"]
|
|
|
|
[tool.ruff.format]
|
|
quote-style = "double"
|
|
|
|
[tool.pyright]
|
|
pythonVersion = "3.10"
|
|
typeCheckingMode = "basic"
|
|
reportMissingImports = false
|
|
reportMissingTypeStubs = false
|
|
reportMissingModuleSource = false
|
|
|
|
[tool.uv]
|
|
# Use uv for dependency resolution, locking, and running commands.
|
|
# Install: pip install uv (or curl -Lsf https://astral.sh/uv/install.sh | sh)
|
|
# Sync: uv sync --extra dev
|
|
# Tests: uv run pytest tests/
|
|
# Build: uv run maturin build --release --out dist
|
|
constraint-dependencies = [
|
|
"pygments>=2.20.0",
|
|
"requests>=2.33.0",
|
|
]
|
|
|
|
[dependency-groups]
|
|
dev = [
|
|
"pytest>=7.0",
|
|
"hypothesis>=6.0",
|
|
"pandas>=1.0",
|
|
"polars>=0.19",
|
|
"pre-commit>=3.0",
|
|
"ruff>=0.3",
|
|
"mypy>=1.0",
|
|
"pyright>=1.1",
|
|
"maturin>=1.0,<2.0",
|
|
"pyyaml>=6.0",
|
|
"pandas-ta>=0.3; python_version >= '3.12'",
|
|
"ta>=0.10",
|
|
"scipy>=1.15.3",
|
|
]
|