Update version numbers across Rust, Python, and documentation files to 1.1.0. Enhance the .gitignore to include macOS dSYM files and plans directory. Introduce new dependencies in the Rust core library and update the README to reflect recent performance benchmarks and backtesting engine capabilities. Add new artifacts to the benchmarks manifest and improve documentation for the backtesting engine API.
272 lines
14 KiB
ReStructuredText
272 lines
14 KiB
ReStructuredText
Release Notes
|
||
=============
|
||
|
||
These docs track package version ``1.2.0``.
|
||
|
||
1.2.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.2.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>`_.
|