diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index d48c1f9..15dbf01 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -10,43 +10,8 @@ on: permissions: contents: read - pages: write jobs: - # ------------------------------------------------------------------------- - # Dependency audits — cargo audit (Rust) and pip-audit (Python) - # ------------------------------------------------------------------------- - audit: - name: Dependency audit (cargo + pip) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - - name: Install cargo-audit and run cargo audit - run: | - cargo install cargo-audit - cargo audit - - - name: Install cargo-deny and run cargo deny check - run: | - cargo install cargo-deny --locked - cargo deny check - - - name: Set up Python 3.12 - uses: actions/setup-python@v6 - with: - python-version: "3.12" - - - name: Install uv - run: pip install uv - - - name: Install pip-audit via uv and run pip-audit - run: | - uv run --with pip-audit pip-audit - - - name: Verify uv.lock is up-to-date - run: uv lock --check - # ------------------------------------------------------------------------- # Version consistency — Cargo.toml and pyproject.toml must have same version # ------------------------------------------------------------------------- @@ -76,285 +41,28 @@ jobs: fi echo "OK: versions match ($CARGO_VERSION)" - rust: - name: Rust (fmt + clippy) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: dtolnay/rust-toolchain@v1 - with: - toolchain: stable - components: rustfmt, clippy - - run: cargo fmt --all -- --check - - run: cargo clippy --release -- -D warnings - - name: Verify benchmarks compile (ferro_ta_core) - run: cargo bench -p ferro_ta_core --no-run - # ------------------------------------------------------------------------- - # Rust coverage — optional, runs in a separate non-blocking job + # Modularized CI suites. + # Keep release publishing jobs in this file so trusted-publisher bindings + # remain anchored to CI.yml for PyPI and crates.io. # ------------------------------------------------------------------------- - rust-coverage: - name: Rust coverage (tarpaulin, optional) - runs-on: ubuntu-latest - continue-on-error: true - steps: - - uses: actions/checkout@v6 - - uses: dtolnay/rust-toolchain@v1 - with: - toolchain: stable - - name: Install cargo-tarpaulin - run: cargo install cargo-tarpaulin --locked - - name: Collect Rust coverage (ferro_ta_core) - run: cargo tarpaulin -p ferro_ta_core --out Xml --output-dir coverage/ - - name: Upload Rust coverage artifact - uses: actions/upload-artifact@v7 - with: - name: rust-coverage - path: coverage/ - if-no-files-found: ignore + rust-suite: + name: Rust and audit + uses: ./.github/workflows/ci-rust.yml - lint: - name: Lint (ruff) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 + python-suite: + name: Python quality and tests + uses: ./.github/workflows/ci-python.yml - - name: Set up Python 3.12 - uses: actions/setup-python@v6 - with: - python-version: "3.12" - - - name: Install uv - run: pip install uv - - - name: Run ruff check via uv - run: uv run --with ruff ruff check python/ tests/ - - name: Run ruff format check via uv - run: uv run --with ruff ruff format --check python/ tests/ - - typecheck: - name: Type checking (mypy + pyright) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - - name: Set up Python 3.12 - uses: actions/setup-python@v6 - with: - python-version: "3.12" - - - name: Install uv - run: pip install uv - - - name: Run mypy on ferro_ta via uv - run: uv run --with mypy --with numpy mypy python/ferro_ta --ignore-missing-imports --no-error-summary - - - name: Run pyright on ferro_ta via uv - run: uv run --with pyright pyright python/ferro_ta - - test: - name: Test (ubuntu-latest / Python ${{ matrix.python-version }}) - runs-on: ubuntu-latest - strategy: - # Python 3.10–3.13 supported (requires-python in pyproject.toml) - fail-fast: false - matrix: - python-version: ["3.10", "3.11", "3.12", "3.13"] - steps: - - uses: actions/checkout@v6 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v6 - with: - python-version: ${{ matrix.python-version }} - - - name: Install maturin and test dependencies - run: | - pip install maturin numpy pytest pytest-cov pandas polars hypothesis pyyaml - - - name: Build and install ferro_ta (dev mode) - run: | - maturin build --release --out dist - pip install dist/*.whl - - - name: Run unit tests with coverage - run: pytest tests/unit/ tests/integration/ -v --cov=ferro_ta --cov-report=xml --cov-report=term-missing --cov-fail-under=65 - - - name: Upload coverage report - uses: actions/upload-artifact@v7 - if: matrix.python-version == '3.12' - with: - name: coverage-python-${{ matrix.python-version }} - path: coverage.xml - - # ------------------------------------------------------------------------- - # WASM binding — build and test with wasm-pack - # ------------------------------------------------------------------------- wasm: - name: WASM binding (wasm-pack test --node) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 + name: WASM binding + uses: ./.github/workflows/ci-wasm.yml - - name: Install Rust (stable) - uses: dtolnay/rust-toolchain@v1 - with: - toolchain: stable - targets: wasm32-unknown-unknown - - - name: Install wasm-pack - run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - - - name: Build and test WASM binding - working-directory: wasm - run: wasm-pack test --node - - - name: Build WASM package (nodejs target) - working-directory: wasm - run: wasm-pack build --target nodejs --out-dir pkg - - - name: Upload WASM package artifact - uses: actions/upload-artifact@v7 - with: - name: wasm-pkg - path: wasm/pkg/ - - # ------------------------------------------------------------------------- - # ferro_ta vs TA-Lib speed comparison — required for PRs. - # Performance regressions block merging. - # ------------------------------------------------------------------------- - benchmark-vs-talib: - name: Benchmark vs TA-Lib - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - - name: Set up Python 3.12 - uses: actions/setup-python@v6 - with: - python-version: "3.12" - - - name: Install TA-Lib C library (Ubuntu) - run: | - sudo apt-get update - sudo apt-get install -y build-essential curl - curl -sL https://sourceforge.net/projects/ta-lib/files/ta-lib/0.4.0/ta-lib-0.4.0-src.tar.gz/download -o ta-lib-0.4.0-src.tar.gz - tar -xzf ta-lib-0.4.0-src.tar.gz - cd ta-lib - ./configure --prefix=/usr - make - sudo make install - sudo ldconfig - - - name: Install maturin and ta-lib Python package - run: | - pip install maturin numpy - pip install ta-lib - - - name: Build and install ferro_ta - run: | - maturin build --release --out dist - pip install dist/*.whl - - - name: Run benchmark comparison - run: | - python benchmarks/bench_vs_talib.py --sizes 10000 100000 --json benchmark_vs_talib.json - - - name: Enforce benchmark regression policy - run: | - python3 benchmarks/check_vs_talib_regression.py --input benchmark_vs_talib.json - - - name: Upload benchmark results - uses: actions/upload-artifact@v7 - with: - name: benchmark-vs-talib - path: benchmark_vs_talib.json - - # ------------------------------------------------------------------------- - # Pure Rust core library — build and test without PyO3 / numpy - # ------------------------------------------------------------------------- - rust-core: - name: Rust core library (ferro_ta_core) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: dtolnay/rust-toolchain@v1 - with: - toolchain: stable - - name: Build core crate - run: cargo build -p ferro_ta_core - - name: Test core crate - run: cargo test -p ferro_ta_core - - # ------------------------------------------------------------------------- - # Fuzzing (short run in CI to catch panics) — optional, non-blocking - # This job is explicitly marked continue-on-error because cargo-fuzz - # requires nightly Rust and may not be available in all environments. - # Failures here are visible in the job log but do not block the PR. - # ------------------------------------------------------------------------- - fuzz: - name: Fuzz targets (short CI run, optional) - runs-on: ubuntu-latest - continue-on-error: true - steps: - - uses: actions/checkout@v6 - - uses: dtolnay/rust-toolchain@v1 - with: - toolchain: nightly - - name: Install cargo-fuzz - run: cargo install cargo-fuzz --locked - - name: Run fuzz_sma (10000 iterations) - working-directory: fuzz - run: cargo fuzz run fuzz_sma -- -runs=10000 -max_len=512 - - name: Run fuzz_rsi (10000 iterations) - working-directory: fuzz - run: cargo fuzz run fuzz_rsi -- -runs=10000 -max_len=512 - - name: Upload fuzz artifacts on crash - uses: actions/upload-artifact@v7 - if: always() - with: - name: fuzz-artifacts - path: fuzz/artifacts/ - if-no-files-found: ignore - - # ------------------------------------------------------------------------- - # Sphinx documentation build - # ------------------------------------------------------------------------- docs: - name: Documentation (Sphinx build) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - - name: Set up Python 3.12 - uses: actions/setup-python@v6 - with: - python-version: "3.12" - - - name: Install maturin and docs dependencies - run: | - pip install maturin numpy - pip install sphinx sphinx-rtd-theme - - - name: Build and install ferro_ta wheel - run: | - maturin build --release --out dist - pip install dist/*.whl - - - name: Build Sphinx documentation - run: sphinx-build -b html docs docs/_build -W --keep-going - - - name: Upload docs artifact - uses: actions/upload-artifact@v7 - with: - name: sphinx-docs - path: docs/_build/ - - - name: Upload GitHub Pages artifact - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - uses: actions/upload-pages-artifact@v4 - with: - path: docs/_build/ + name: Documentation + uses: ./.github/workflows/ci-docs.yml + with: + upload-pages-artifact: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} # ------------------------------------------------------------------------- # Deploy docs to GitHub Pages (on push to main only) @@ -384,16 +92,11 @@ jobs: name: CI complete (required gate) runs-on: ubuntu-latest needs: - - audit - changelog-check - version-check - - rust - - rust-core - - lint - - typecheck - - test + - rust-suite + - python-suite - wasm - - benchmark-vs-talib - docs if: always() steps: @@ -401,21 +104,31 @@ jobs: run: | results='${{ toJSON(needs) }}' echo "Job results: $results" - failed=$(echo "$results" | python3 -c " - import json, sys - needs = json.load(sys.stdin) - failed = [name for name, data in needs.items() if data['result'] not in ('success', 'skipped')] + failed="$( + RESULTS_JSON="$results" python3 - <<'PY' + import json + import os + + needs = json.loads(os.environ["RESULTS_JSON"]) + failed = [ + name + for name, data in needs.items() + if data["result"] not in ("success", "skipped") + ] if failed: - print(' '.join(failed)) - ") - if [ -n \"\$failed\" ]; then - echo \"FAILED jobs: \$failed\" + print(" ".join(failed)) + PY + )" + if [ -n "$failed" ]; then + echo "FAILED jobs: $failed" exit 1 fi - echo \"All required CI jobs passed.\" + echo "All required CI jobs passed." # ------------------------------------------------------------------------- # Build wheels for all platforms and publish to PyPI on release + # Keep release jobs here because trusted-publisher configuration points to + # CI.yml specifically. # ------------------------------------------------------------------------- build-wheels: name: Build wheels (${{ matrix.os }}) @@ -503,7 +216,7 @@ jobs: - uses: actions/checkout@v6 - name: Set up Python 3.12 - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: "3.12" @@ -516,7 +229,7 @@ jobs: pip install dist/*.whl - name: Generate Python SBOM (CycloneDX via anchore/sbom-action) - uses: anchore/sbom-action@v0.17 + uses: anchore/sbom-action@v0.24.0 with: artifact-name: ferro-ta-python-sbom.spdx.json output-file: ferro-ta-python-sbom.spdx.json diff --git a/.github/workflows/ci-docs.yml b/.github/workflows/ci-docs.yml new file mode 100644 index 0000000..f662568 --- /dev/null +++ b/.github/workflows/ci-docs.yml @@ -0,0 +1,49 @@ +name: CI Docs + +on: + workflow_call: + inputs: + upload-pages-artifact: + required: false + type: boolean + default: false + +permissions: + contents: read + +jobs: + docs: + name: Documentation (Sphinx build) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Set up Python 3.12 + uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Install maturin and docs dependencies + run: | + pip install maturin numpy + pip install sphinx sphinx-rtd-theme + + - name: Build and install ferro_ta wheel + run: | + maturin build --release --out dist + pip install dist/*.whl + + - name: Build Sphinx documentation + run: sphinx-build -b html docs docs/_build -W --keep-going + + - name: Upload docs artifact + uses: actions/upload-artifact@v7 + with: + name: sphinx-docs + path: docs/_build/ + + - name: Upload GitHub Pages artifact + if: ${{ inputs.upload-pages-artifact }} + uses: actions/upload-pages-artifact@v4 + with: + path: docs/_build/ diff --git a/.github/workflows/ci-python.yml b/.github/workflows/ci-python.yml new file mode 100644 index 0000000..dc87723 --- /dev/null +++ b/.github/workflows/ci-python.yml @@ -0,0 +1,127 @@ +name: CI Python + +on: + workflow_call: + +permissions: + contents: read + +jobs: + lint: + name: Lint (ruff) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Set up Python 3.12 + uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Install uv + run: pip install uv + + - name: Run ruff check via uv + run: uv run --with ruff ruff check python/ tests/ + - name: Run ruff format check via uv + run: uv run --with ruff ruff format --check python/ tests/ + + typecheck: + name: Type checking (mypy + pyright) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Set up Python 3.12 + uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Install uv + run: pip install uv + + - name: Run mypy on ferro_ta via uv + run: uv run --with mypy --with numpy mypy python/ferro_ta --ignore-missing-imports --no-error-summary + + - name: Run pyright on ferro_ta via uv + run: uv run --with pyright pyright python/ferro_ta + + test: + name: Test (ubuntu-latest / Python ${{ matrix.python-version }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.10", "3.11", "3.12", "3.13"] + steps: + - uses: actions/checkout@v6 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.python-version }} + + - name: Install maturin and test dependencies + run: | + pip install maturin numpy pytest pytest-cov pandas polars hypothesis pyyaml + + - name: Build and install ferro_ta (dev mode) + run: | + maturin build --release --out dist + pip install dist/*.whl + + - name: Run unit tests with coverage + run: pytest tests/unit/ tests/integration/ -v --cov=ferro_ta --cov-report=xml --cov-report=term-missing --cov-fail-under=65 + + - name: Upload coverage report + uses: actions/upload-artifact@v7 + if: matrix.python-version == '3.12' + with: + name: coverage-python-${{ matrix.python-version }} + path: coverage.xml + + benchmark-vs-talib: + name: Benchmark vs TA-Lib + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Set up Python 3.12 + uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Install TA-Lib C library (Ubuntu) + run: | + sudo apt-get update + sudo apt-get install -y build-essential curl + curl -sL https://sourceforge.net/projects/ta-lib/files/ta-lib/0.4.0/ta-lib-0.4.0-src.tar.gz/download -o ta-lib-0.4.0-src.tar.gz + tar -xzf ta-lib-0.4.0-src.tar.gz + cd ta-lib + ./configure --prefix=/usr + make + sudo make install + sudo ldconfig + + - name: Install maturin and ta-lib Python package + run: | + pip install maturin numpy + pip install ta-lib + + - name: Build and install ferro_ta + run: | + maturin build --release --out dist + pip install dist/*.whl + + - name: Run benchmark comparison + run: | + python benchmarks/bench_vs_talib.py --sizes 10000 100000 --json benchmark_vs_talib.json + + - name: Enforce benchmark regression policy + run: python3 benchmarks/check_vs_talib_regression.py --input benchmark_vs_talib.json + + - name: Upload benchmark results + uses: actions/upload-artifact@v7 + with: + name: benchmark-vs-talib + path: benchmark_vs_talib.json diff --git a/.github/workflows/ci-rust.yml b/.github/workflows/ci-rust.yml new file mode 100644 index 0000000..4a6639b --- /dev/null +++ b/.github/workflows/ci-rust.yml @@ -0,0 +1,110 @@ +name: CI Rust + +on: + workflow_call: + +permissions: + contents: read + +jobs: + audit: + name: Dependency audit (cargo + pip) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Install cargo-audit and run cargo audit + run: | + cargo install cargo-audit + cargo audit + + - name: Install cargo-deny and run cargo deny check + run: | + cargo install cargo-deny --locked + cargo deny check + + - name: Set up Python 3.12 + uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Install uv + run: pip install uv + + - name: Install pip-audit via uv and run pip-audit + run: uv run --with pip-audit pip-audit + + - name: Verify uv.lock is up-to-date + run: uv lock --check + + rust: + name: Rust (fmt + clippy) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@v1 + with: + toolchain: stable + components: rustfmt, clippy + - run: cargo fmt --all -- --check + - run: cargo clippy --release -- -D warnings + - name: Verify benchmarks compile (ferro_ta_core) + run: cargo bench -p ferro_ta_core --no-run + + rust-core: + name: Rust core library (ferro_ta_core) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@v1 + with: + toolchain: stable + - name: Build core crate + run: cargo build -p ferro_ta_core + - name: Test core crate + run: cargo test -p ferro_ta_core + + rust-coverage: + name: Rust coverage (tarpaulin, optional) + runs-on: ubuntu-latest + continue-on-error: true + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@v1 + with: + toolchain: stable + - name: Install cargo-tarpaulin + run: cargo install cargo-tarpaulin --locked + - name: Collect Rust coverage (ferro_ta_core) + run: cargo tarpaulin -p ferro_ta_core --out Xml --output-dir coverage/ + - name: Upload Rust coverage artifact + uses: actions/upload-artifact@v7 + with: + name: rust-coverage + path: coverage/ + if-no-files-found: ignore + + fuzz: + name: Fuzz targets (short CI run, optional) + runs-on: ubuntu-latest + continue-on-error: true + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@v1 + with: + toolchain: nightly + - name: Install cargo-fuzz + run: cargo install cargo-fuzz --locked + - name: Run fuzz_sma (10000 iterations) + working-directory: fuzz + run: cargo fuzz run fuzz_sma -- -runs=10000 -max_len=512 + - name: Run fuzz_rsi (10000 iterations) + working-directory: fuzz + run: cargo fuzz run fuzz_rsi -- -runs=10000 -max_len=512 + - name: Upload fuzz artifacts on crash + uses: actions/upload-artifact@v7 + if: always() + with: + name: fuzz-artifacts + path: fuzz/artifacts/ + if-no-files-found: ignore diff --git a/.github/workflows/ci-wasm.yml b/.github/workflows/ci-wasm.yml new file mode 100644 index 0000000..91d493f --- /dev/null +++ b/.github/workflows/ci-wasm.yml @@ -0,0 +1,37 @@ +name: CI WASM + +on: + workflow_call: + +permissions: + contents: read + +jobs: + wasm: + name: WASM binding (wasm-pack test --node) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Install Rust (stable) + uses: dtolnay/rust-toolchain@v1 + with: + toolchain: stable + targets: wasm32-unknown-unknown + + - name: Install wasm-pack + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + + - name: Build and test WASM binding + working-directory: wasm + run: wasm-pack test --node + + - name: Build WASM package (nodejs target) + working-directory: wasm + run: wasm-pack build --target nodejs --out-dir pkg + + - name: Upload WASM package artifact + uses: actions/upload-artifact@v7 + with: + name: wasm-pkg + path: wasm/pkg/ diff --git a/.github/workflows/wasm-publish.yml b/.github/workflows/wasm-publish.yml index eb69bbc..9c845be 100644 --- a/.github/workflows/wasm-publish.yml +++ b/.github/workflows/wasm-publish.yml @@ -4,6 +4,10 @@ on: release: types: [published] +permissions: + contents: read + id-token: write + jobs: publish: name: Build and publish to npm @@ -12,13 +16,15 @@ jobs: - uses: actions/checkout@v6 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: - node-version: "20" + node-version: "24" registry-url: "https://registry.npmjs.org" - name: Install Rust and wasm-pack uses: dtolnay/rust-toolchain@stable + with: + targets: wasm32-unknown-unknown - run: cargo install wasm-pack - name: Build WASM package @@ -29,5 +35,3 @@ jobs: - name: Publish to npm working-directory: wasm run: npm publish --access public - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index ca43b30..c819211 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,28 @@ and the project uses [Semantic Versioning](https://semver.org/). ## [Unreleased] +## [1.0.1] — 2026-03-24 + +### Added + +- `crates/ferro_ta_core/README.md` is now shipped with the published Rust crate, and + `ferro_ta_core` metadata now points documentation to docs.rs. + +### Fixed + +- CI has been modularized into focused workflow files (`ci-rust.yml`, + `ci-python.yml`, `ci-wasm.yml`, `ci-docs.yml`) while keeping the release + publishing jobs in `CI.yml` for PyPI and crates.io trusted-publisher + compatibility. +- The `ci-complete` gate no longer fails successful runs because of an escaped + shell variable, and the release SBOM job now uses a valid `anchore/sbom-action` + version. +- The npm publish workflow now uses GitHub OIDC trusted publishing, installs + the `wasm32-unknown-unknown` target, and no longer depends on an `NPM_TOKEN` + secret. +- The WASM npm package now removes the generated `pkg/.gitignore` during + `prepack`, so the published tarball includes the built `pkg/` artifacts. + ## [1.0.0] — 2026-03-23 *(initial stable release)* ### Performance @@ -252,5 +274,6 @@ and the project uses [Semantic Versioning](https://semver.org/). --- -[Unreleased]: https://github.com/pratikbhadane24/ferro-ta/compare/v1.0.0...HEAD +[Unreleased]: https://github.com/pratikbhadane24/ferro-ta/compare/v1.0.1...HEAD +[1.0.1]: https://github.com/pratikbhadane24/ferro-ta/compare/v1.0.0...v1.0.1 [1.0.0]: https://github.com/pratikbhadane24/ferro-ta/releases/tag/v1.0.0 diff --git a/Cargo.lock b/Cargo.lock index 0d95e25..ff595fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -207,7 +207,7 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "ferro_ta" -version = "1.0.0" +version = "1.0.1" dependencies = [ "criterion", "ferro_ta_core", @@ -222,7 +222,7 @@ dependencies = [ [[package]] name = "ferro_ta_core" -version = "1.0.0" +version = "1.0.1" dependencies = [ "criterion", "wide", diff --git a/Cargo.toml b/Cargo.toml index 52c2802..e394178 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ resolver = "2" [package] name = "ferro_ta" -version = "1.0.0" +version = "1.0.1" edition = "2021" license = "MIT" publish = false @@ -23,7 +23,7 @@ ndarray = "0.16" rayon = "1.10" log = "0.4" pyo3-log = "0.12" -ferro_ta_core = { path = "crates/ferro_ta_core", version = "1.0.0" } +ferro_ta_core = { path = "crates/ferro_ta_core", version = "1.0.1" } [dev-dependencies] criterion = { version = "0.8", features = ["html_reports"] } diff --git a/crates/ferro_ta_core/Cargo.toml b/crates/ferro_ta_core/Cargo.toml index 03fca4a..093efd5 100644 --- a/crates/ferro_ta_core/Cargo.toml +++ b/crates/ferro_ta_core/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "ferro_ta_core" -version = "1.0.0" +version = "1.0.1" edition = "2021" description = "Pure Rust core indicator library — no PyO3, no numpy dependency" license = "MIT" +readme = "README.md" repository = "https://github.com/pratikbhadane24/ferro-ta" homepage = "https://github.com/pratikbhadane24/ferro-ta#readme" -documentation = "https://github.com/pratikbhadane24/ferro-ta#readme" +documentation = "https://docs.rs/ferro_ta_core" keywords = ["technical-analysis", "trading", "indicators", "finance", "ta-lib"] categories = ["finance", "mathematics"] diff --git a/crates/ferro_ta_core/README.md b/crates/ferro_ta_core/README.md new file mode 100644 index 0000000..e4859ce --- /dev/null +++ b/crates/ferro_ta_core/README.md @@ -0,0 +1,79 @@ +# ferro_ta_core + +`ferro_ta_core` is the pure Rust indicator engine behind [`ferro-ta`](https://github.com/pratikbhadane24/ferro-ta). + +It provides allocation-friendly indicator functions over `&[f64]` slices without any +PyO3, NumPy, or Python runtime dependency, which makes it a good fit for: + +- Rust-native technical analysis workloads +- custom services and backtesting engines +- future non-Python bindings such as WASM and other FFI layers + +## Installation + +```toml +[dependencies] +ferro_ta_core = "1.0.1" +``` + +## Design + +- Pure functions over Rust slices +- No Python or NumPy dependency +- Shared core for the Python package and WASM bindings +- Output shape matches TA-Lib-style full-length series with `NaN` warm-up values where applicable + +## Modules + +- `overlap` - moving averages, MACD, Bollinger Bands +- `momentum` - RSI, MOM +- `volatility` - ATR, TRANGE +- `volume` - OBV +- `statistic` - STDDEV +- `math` - rolling SUM/MAX/MIN helpers + +## Example + +```rust +use ferro_ta_core::overlap; + +fn main() { + let close = vec![1.0, 2.0, 3.0, 4.0, 5.0]; + let sma = overlap::sma(&close, 3); + + assert!(sma[0].is_nan()); + assert!(sma[1].is_nan()); + assert!((sma[2] - 2.0).abs() < 1e-10); +} +``` + +## Relationship To `ferro-ta` + +The published Python package: + +- crate: `ferro_ta` +- PyPI package: `ferro-ta` + +wraps this crate with PyO3 bindings and adds: + +- NumPy conversion +- pandas/polars wrappers +- streaming classes +- batch helpers +- higher-level Python tooling + +If you only need Rust indicator functions, use `ferro_ta_core` directly. + +## Development + +From the repository root: + +```bash +cargo build -p ferro_ta_core +cargo test -p ferro_ta_core +cargo bench -p ferro_ta_core --no-run +``` + +## License + +MIT diff --git a/pyproject.toml b/pyproject.toml index 53cd5c4..5ad0309 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "maturin" [project] name = "ferro-ta" -version = "1.0.0" +version = "1.0.1" description = "A fast Technical Analysis library — TA-Lib alternative powered by Rust and PyO3" readme = "README.md" license = { text = "MIT" } diff --git a/wasm/package.json b/wasm/package.json index fa16527..6c4f0ce 100644 --- a/wasm/package.json +++ b/wasm/package.json @@ -1,12 +1,13 @@ { "name": "ferro-ta-wasm", - "version": "1.0.0", + "version": "1.0.1", "description": "WebAssembly bindings for ferro-ta technical analysis indicators", "main": "pkg/ferro_ta_wasm.js", "types": "pkg/ferro_ta_wasm.d.ts", "files": ["pkg"], "scripts": { "build": "wasm-pack build --target nodejs --out-dir pkg", + "prepack": "npm run build && node -e \"require('fs').rmSync('pkg/.gitignore', { force: true })\"", "test": "wasm-pack test --node" }, "license": "MIT",