fd1bb137d6
* feat: implement Dynamic Time Warping (DTW) functionality - Added DTW distance computation and optimal warping path functions in Rust. - Introduced corresponding Python bindings for DTW, DTW_DISTANCE, and BATCH_DTW. - Enhanced WASM support with a new dtw_distance function. - Included comprehensive unit tests for DTW functionality, validating against the dtaidistance library and ensuring mathematical properties. * chore: update ferro-ta version to 1.1.4 - Bumped version number of ferro-ta to 1.1.4 in uv.lock and Cargo.lock files. - Ensured consistency across package dependencies for the updated version.
272 lines
14 KiB
ReStructuredText
272 lines
14 KiB
ReStructuredText
Release Notes
|
||
=============
|
||
|
||
These docs track package version ``1.1.4``.
|
||
|
||
1.1.0-audit (2026-03-28)
|
||
------------------------
|
||
|
||
**Comprehensive audit: 90 findings addressed**
|
||
|
||
*Code quality & correctness*
|
||
|
||
- **Welford's algorithm for BBANDS**: replaced naive ``sum_sq/N - mean^2`` variance
|
||
with numerically stable Welford's rolling algorithm in both batch and streaming BBANDS.
|
||
Fixes catastrophic cancellation for large-valued series (e.g., prices near 1e12).
|
||
- **FFI boundary safety**: ``transpose_to_series_major()`` in ``batch/mod.rs`` now
|
||
returns ``PyResult`` instead of using ``expect()``. Remaining ``as_slice().expect()``
|
||
calls in ``allow_threads`` closures are documented with SAFETY comments (structurally
|
||
infallible after C-contiguous transpose).
|
||
- **Clippy clean**: resolved all clippy warnings — complex type in ``adx_all`` extracted
|
||
to ``AdxAllResult`` type alias; ``welford_step`` helper annotated with
|
||
``#[allow(clippy::too_many_arguments)]``.
|
||
|
||
*Performance*
|
||
|
||
- **``target-cpu=native``**: new ``.cargo/config.toml`` enables native CPU instruction
|
||
set (AVX2, NEON, etc.) for all non-WASM targets. CI can override via ``RUSTFLAGS``.
|
||
|
||
*Testing*
|
||
|
||
- **Streaming unit tests**: 37 new tests in ``tests/unit/streaming/test_streaming.py``
|
||
covering ``StreamingSMA``, ``StreamingEMA``, ``StreamingRSI`` — batch parity, warmup
|
||
NaN behavior, reset, edge cases, and large dataset numerical stability.
|
||
- **Edge case tests**: 31 new tests in ``tests/unit/test_edge_cases.py`` — empty arrays,
|
||
single elements, all-NaN input, NaN propagation, extreme values (1e300, 1e-300),
|
||
constant series, period boundary conditions, OHLCV edge cases, and dtype coercion
|
||
(float32, int64).
|
||
- **Property-based tests**: expanded Hypothesis tests for EMA, BBANDS, MACD, ATR, WMA,
|
||
and OBV with algebraic invariants (upper >= middle >= lower, histogram == macd - signal,
|
||
ATR non-negative, etc.).
|
||
- **Pandas/polars integration tests**: new ``test_dataframe_integration.py`` verifying
|
||
transparent ``pd.Series`` and ``polars.Series`` support across SMA, EMA, RSI, BBANDS,
|
||
MACD, and end-to-end DataFrame workflows.
|
||
- **Fuzzing**: expanded from 2 to 9 fuzz targets — added EMA, BBANDS, MACD, ATR, STOCH,
|
||
MFI, and WMA with output invariant assertions.
|
||
- **Test helpers**: new ``tests/unit/helpers.py`` consolidating duplicated assertion
|
||
patterns (``nan_count``, ``finite``, ``assert_nan_warmup``, ``assert_output_length``,
|
||
``assert_range``, ``make_ohlcv``).
|
||
|
||
*Documentation*
|
||
|
||
- **README benchmarks**: updated to match actual artifact data — MFI 3.25x, WMA 2.20x,
|
||
BBANDS 1.97x, SMA 1.93x; corrected win count from 6 to 7 at 100k bars.
|
||
- **Rust doc comments**: added comprehensive ``///`` documentation to all public functions
|
||
in ``ferro_ta_core`` — overlap (SMA, EMA, WMA, BBANDS, MACD), momentum (RSI, STOCH,
|
||
ADX family), volatility (ATR, TRANGE), volume (OBV, MFI), statistic (STDDEV), and math
|
||
(sum, max, min, sliding_max, sliding_min).
|
||
|
||
*Linting*
|
||
|
||
- **Ruff clean**: fixed import sorting, unused imports, trailing whitespace, and
|
||
formatting across all Python files.
|
||
- **cargo fmt**: all Rust code formatted.
|
||
|
||
1.1.0 (2026-03-28)
|
||
------------------
|
||
|
||
**Phase 1 — Simulation fidelity**
|
||
|
||
- **Bid-ask spread model**: new ``CommissionModel.spread_bps`` field (basis points).
|
||
Half-spread is deducted per leg (entry and exit), modelling real market microstructure costs.
|
||
- **Breakeven stop**: new ``backtest_ohlcv_core`` parameter ``breakeven_pct`` and
|
||
``BacktestEngine.with_breakeven_stop(pct)``. Once profit reaches ``pct``, the
|
||
effective stop-loss is moved to the entry price, guaranteeing at worst a breakeven exit.
|
||
- **Bracket order priority**: when both stop-loss and take-profit are breached on the
|
||
same bar, the level closer to the bar's open price fires first (previously SL always won).
|
||
|
||
**Phase 2 — Portfolio & risk**
|
||
|
||
- **Short borrow cost**: new ``CommissionModel.short_borrow_rate_annual`` field.
|
||
Accrued per bar for short positions at the specified annualised rate.
|
||
- **Leverage / margin modeling**: new ``BacktestEngine.with_leverage(margin_ratio, margin_call_pct)``.
|
||
Tracks margin usage and triggers a margin-call force-close when equity falls below
|
||
``margin_call_pct × initial_margin``.
|
||
- **Loss circuit breakers**: new ``BacktestEngine.with_loss_limits(daily, total)``.
|
||
Halts all trading when a per-bar loss or total drawdown threshold is breached.
|
||
- **Portfolio constraints**: new ``BacktestEngine.with_portfolio_constraints(max_asset_weight,
|
||
max_gross_exposure, max_net_exposure)`` for multi-asset backtests.
|
||
|
||
**Phase 3 — Data & UX**
|
||
|
||
- **Bar aggregation** (``ferro_ta.analysis.resample``): ``resample_ohlcv()``, ``align_to_coarse()``,
|
||
``resample_ohlcv_labels()`` — pure-NumPy OHLCV resampling from any fine TF to any coarser TF.
|
||
- **Multi-timeframe engine** (``ferro_ta.analysis.multitf``): ``MultiTimeframeEngine`` — compute
|
||
strategy signals on coarser bars and execute on finer bars, with automatic signal alignment.
|
||
- **Dividend/split adjustment** (``ferro_ta.analysis.adjust``): ``adjust_ohlcv()``,
|
||
``adjust_for_splits()``, ``adjust_for_dividends()`` — backward-adjusted price series for
|
||
equity/index strategies.
|
||
- **Visualization** (``ferro_ta.analysis.plot``): ``plot_backtest()`` — interactive Plotly chart
|
||
with equity curve, drawdown panel, position panel, trade markers, and optional benchmark overlay.
|
||
|
||
**Phase 4 — Differentiation**
|
||
|
||
- **Regime detection** (``ferro_ta.analysis.regime``): ``detect_volatility_regime()``,
|
||
``detect_trend_regime()``, ``detect_combined_regime()``, ``RegimeFilter`` — pure-NumPy
|
||
6-state market regime labeling and signal filtering; no external ML dependencies.
|
||
- **Portfolio optimization** (``ferro_ta.analysis.optimize``): ``PortfolioOptimizer``,
|
||
``mean_variance_optimize()``, ``risk_parity_optimize()``, ``max_sharpe_optimize()`` —
|
||
minimum-variance, risk-parity, and maximum-Sharpe portfolios via SLSQP (requires scipy).
|
||
- **Paper trading bridge** (``ferro_ta.analysis.live``): ``PaperTrader`` — event-driven
|
||
bar-by-bar simulator matching ``backtest_ohlcv_core`` logic exactly; supports streaming
|
||
data, live state inspection, and seamless strategy migration from backtesting to live.
|
||
|
||
1.1.0 (2026-03-27)
|
||
------------------
|
||
|
||
**Advanced commission and fee model (Indian market support)**
|
||
|
||
- New ``CommissionModel`` class (pure Rust in ``ferro_ta_core``, exposed via
|
||
PyO3 and WASM) replaces the broken flat ``commission_per_trade`` scalar. The
|
||
old code subtracted an absolute currency amount from a 1.0-normalised equity
|
||
curve — equivalent to a 2 000 % error on a ₹1 lakh account. The new model
|
||
correctly converts every charge to a fraction of ``initial_capital`` before
|
||
deducting it from the equity curve.
|
||
- ``CommissionModel`` supports: proportional brokerage (``rate_of_value``),
|
||
flat per-order fee (``flat_per_order``), per-lot fee (``per_lot``), brokerage
|
||
cap (``max_brokerage``), Securities Transaction Tax (``stt_rate`` with
|
||
configurable buy/sell sides), exchange transaction charges, SEBI regulatory
|
||
charges, 18 % GST on brokerage + exchange + regulatory levies, and stamp duty
|
||
on buy leg only.
|
||
- Built-in presets: ``CommissionModel.equity_delivery_india()``,
|
||
``CommissionModel.equity_intraday_india()``,
|
||
``CommissionModel.futures_india()``, ``CommissionModel.options_india()``,
|
||
``CommissionModel.proportional(rate)``, ``CommissionModel.zero()``.
|
||
- JSON persistence: ``model.to_json()`` / ``CommissionModel.from_json(s)``,
|
||
``model.save(path)`` / ``CommissionModel.load(path)``.
|
||
- ``BacktestEngine.with_commission_model(model)`` — pass a full
|
||
``CommissionModel``; old ``with_commission(rate)`` kept as a shim.
|
||
- New ``initial_capital`` parameter (default ₹1,00,000) on both
|
||
``backtest_core`` and ``backtest_ohlcv_core``; also exposed as
|
||
``BacktestEngine.with_initial_capital(capital)``.
|
||
|
||
**Currency system — INR default with lakh/crore formatting**
|
||
|
||
- New ``Currency`` immutable descriptor in the Python layer with constants
|
||
``INR``, ``USD``, ``EUR``, ``GBP``, ``JPY``, ``USDT``.
|
||
- ``INR`` is the default currency for ``BacktestEngine``; change via
|
||
``engine.with_currency("USD")`` or ``engine.with_currency(EUR)``.
|
||
- ``currency.format(amount)`` produces Indian lakh/crore grouping for INR
|
||
(e.g. ``₹1,23,45,678.00``) and standard Western grouping for other
|
||
currencies.
|
||
- Module-level helper ``format_currency(amount, currency=INR)``.
|
||
- ``AdvancedBacktestResult`` gains ``currency``, ``initial_capital``, and
|
||
``equity_abs`` (absolute currency equity curve) slots.
|
||
- ``summary()`` now includes ``initial_capital``, ``final_capital``,
|
||
``absolute_pnl``, and ``currency`` keys.
|
||
- ``AdvancedBacktestResult.__repr__`` shows the final capital in the correct
|
||
currency symbol (e.g. ``final=₹1,23,450.00``).
|
||
- Trade log gains a ``pnl_abs`` column (PnL in absolute currency units).
|
||
- ``to_equity_dataframe()`` now includes an ``equity_abs`` column.
|
||
|
||
**Trailing stop loss**
|
||
|
||
- ``backtest_ohlcv_core`` (and ``BacktestEngine.with_trailing_stop(pct)``)
|
||
now supports a trailing stop implemented intrabar in Rust: the high-water
|
||
mark is updated each bar; the position is exited at
|
||
``trail_high × (1 − pct)`` when ``low[i]`` crosses below it (long trades),
|
||
or ``trail_low × (1 + pct)`` for short trades.
|
||
|
||
**Benchmark comparison metrics**
|
||
|
||
- ``compute_performance_metrics`` accepts an optional ``benchmark_returns``
|
||
array. When provided, ``summary()`` includes: ``benchmark_total_return``,
|
||
``benchmark_cagr``, ``benchmark_annualized_vol``, ``benchmark_sharpe``,
|
||
``alpha`` (active return), ``beta``, ``tracking_error``, and
|
||
``information_ratio``.
|
||
- ``BacktestEngine.with_benchmark(close_array)`` — pass benchmark close prices.
|
||
|
||
**Volatility-target position sizing**
|
||
|
||
- New ``"volatility_target"`` method for ``with_position_sizing()``:
|
||
``engine.with_position_sizing("volatility_target", target_vol=0.15, vol_window=20)``.
|
||
Signals are pre-scaled in Python by ``clip(target_vol / rolling_annualised_vol, 0, 3)``
|
||
before the Rust core call, keeping the hot loop unchanged.
|
||
|
||
**Backtesting engine v2 — full feature set**
|
||
|
||
- ``BacktestEngine`` now supports true two-pass Kelly / half-Kelly position
|
||
sizing: a unit-signal pass computes win statistics, then the core engine
|
||
re-runs with signals scaled by the Kelly fraction.
|
||
- Added ``fixed_fractional`` position sizing method:
|
||
``engine.with_position_sizing("fixed_fractional", fraction=0.5)``.
|
||
- New ``StreamingBacktest`` Rust class for bar-by-bar incremental backtesting
|
||
(no bulk arrays needed); exposes ``.on_bar()``, ``.summary()``, ``.reset()``.
|
||
- ``AdvancedBacktestResult.to_equity_dataframe(freq)`` — returns equity,
|
||
returns, and drawdown as a ``pd.DataFrame`` with a synthetic DatetimeIndex.
|
||
- ``AdvancedBacktestResult.summary()`` — concise dict of the 9 most commonly
|
||
cited metrics plus ``n_trades``.
|
||
|
||
**Core indicator speedup**
|
||
|
||
- ADX-family indicators (``adx_all`` public API): all six series (PDM, MDM,
|
||
+DI, -DI, DX, ADX) can now be computed from a single TR/PDM/MDM pass via
|
||
``ferro_ta.adx_all()``, eliminating the 6× redundant computation that
|
||
occurred when callers fetched each series independently.
|
||
- ``adxr`` now reuses a single ``adx_inner`` call internally (was calling
|
||
``adx()`` which re-ran the inner loop).
|
||
|
||
1.0.6 (2026-03-24)
|
||
------------------
|
||
|
||
- Added a repo-managed pre-push gate so the core Rust, Python, docs, and WASM
|
||
checks can be run locally before release.
|
||
- Expanded Rust-backed analysis/data helpers, broadened the WASM exports, and
|
||
added cross-surface API manifest verification plus Node conformance checks.
|
||
- Refreshed benchmark coverage and perf artifacts, aligned Python CI with the
|
||
local tooling flow, and updated the locked security fixes needed for a clean
|
||
release pass.
|
||
|
||
1.0.4 (2026-03-24)
|
||
------------------
|
||
|
||
- Expanded the optional MCP server from a small hand-written subset to the
|
||
broader public ferro-ta callable surface, including stateful class support
|
||
through stored-instance management tools.
|
||
- Split the root documentation so the full TA-Lib compatibility matrix lives in
|
||
``TA_LIB_COMPATIBILITY.md`` while the README stays product-first and shorter.
|
||
- Refreshed MCP docs/tests and updated locked low-risk Python dependencies as
|
||
part of the release cleanup pass.
|
||
- Stopped tracking the stray ``.coverage`` artifact and aligned ignore rules
|
||
for local coverage outputs.
|
||
|
||
1.0.3 (2026-03-24)
|
||
------------------
|
||
|
||
- Added top-level package metadata helpers such as ``ferro_ta.__version__``,
|
||
``ferro_ta.about()``, and ``ferro_ta.methods()``.
|
||
- Added a standalone derivatives benchmark artifact for selected options
|
||
pricing, IV, Greeks, and Black-76 comparisons.
|
||
- Simplified release version bumps with a single script and updated release
|
||
guidance.
|
||
- Fixed Python CI/type-stub gaps around the new metadata API and corrected the
|
||
tag-driven GitHub Release workflow trigger used for publish automation.
|
||
|
||
1.0.2 (2026-03-24)
|
||
------------------
|
||
|
||
- Improved rolling statistical kernels and several Python analysis hotspots.
|
||
- Added reproducible perf-contract artifacts, TA-Lib regression guards, and
|
||
updated benchmark tooling.
|
||
- Tightened the public benchmark documentation so claims, caveats, and evidence
|
||
live closer together.
|
||
|
||
1.0.1 (2026-03-24)
|
||
------------------
|
||
|
||
- Improved release automation for PyPI, crates.io, and npm.
|
||
- Fixed CI workflow issues that caused otherwise healthy release jobs to fail.
|
||
- Ensured the published WASM package includes its built ``pkg/`` artifacts.
|
||
|
||
1.0.0 (2026-03-23)
|
||
------------------
|
||
|
||
- First stable release of the Rust-backed Python technical analysis library.
|
||
- Shipped broad TA-Lib coverage, streaming APIs, extended indicators, and the
|
||
initial Sphinx documentation set.
|
||
- Added the benchmark suite, release playbook, and compatibility/testing
|
||
scaffolding for stable releases.
|
||
|
||
For the canonical project changelog, including the full per-version details,
|
||
see `CHANGELOG.md <https://github.com/pratikbhadane24/ferro-ta/blob/main/CHANGELOG.md>`_.
|