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.