## 0.22.4 (2026-03-15)

### Added
- **Window-aware criterion evaluation API**: `AnalysisCriterion` can now analyze exactly the slice you care about, including specific bar ranges, date/time ranges, lookback bars, and lookback durations, via `AnalysisWindow`/`AnalysisContext` and `AnalysisCriterion#calculate(series, tradingRecord, window[, context])`, with strict/clamp history policies and configurable open-position handling.
- **Price-structure aggregators**: Added `RangeBarAggregator`, `VolumeBarAggregator`, and `RenkoBarAggregator` for range-, volume-, and Renko-brick derived bar series with externally configurable thresholds and comprehensive fixture-driven regression coverage.
- **One-shot multi-timeframe Elliott Wave analysis**: You can now run `ElliottWaveAnalysisRunner` once and get an `ElliottWaveAnalysisResult` with base + neighboring degree outputs, ranked scenarios, and confidence context in one place.
- **Reusable walk-forward framework with backtest integration**: Added `WalkForwardEngine`, `WalkForwardTuner`, `WalkForwardObjective`, and `StrategyWalkForwardExecutor`, plus `BacktestExecutor` entrypoints so you can run backtest-only, walk-forward-only, or both in one consistent flow.
- **Weighted strategy ranking across execution results**: `TradingStatementExecutionResult` and `BacktestExecutionResult#getTopStrategiesWeighted(...)` now support normalized weighted ranking (for example net profit + drawdown + trade count) with pluggable normalization and deterministic ordering. You can jump in with `WeightedCriterion.of(...)`, `RankingProfile.weighted(...)`, or the direct weighted overloads, and the README plus `SimpleMovingAverageRangeBacktest` now show a concrete “net profit + RoMaD” shortlist flow you can copy directly.
- **Shared scoring/weighting primitives for library extensions**: Added `NamedScoreFunction<I, S>` and `WeightedValue<T>` so indicator, confidence, and walk-forward components can reuse the same scoring and weighted-aggregation contracts.
- **Live Elliott preset demo support**: `ElliottWavePresetDemo` now accepts live tickers (for example `BTC-USD`, `ETH-USD`, `SPY`) so you can run the same EW workflow on non-ossified daily data.

### Changed (Trading Record and Execution Flow)
- **Elliott APIs and demos now follow runner-centric naming and defaults**: The project has moved from legacy analyzer naming to `ElliottWaveAnalysisRunner`, examples are organized under `analysis.elliottwave.{demo,backtest,support}`, and demo defaults now emphasize auto-degree selection with multi-degree context.
- **HighRewardElliottWaveStrategy momentum confirmation now uses MACD-V**: The strategy now uses `VolatilityNormalizedMACDIndicator` and drops redundant exit-rule guarding to keep rule flow cleaner.
- **Release automation now favors safer incremental bumps**: `release-scheduler.yml` and `semver-rules-override.txt` now drive explicit go/no-go decisions with `patch|minor` outputs only, normalize noisy AI bump values (for example ` MAJOR ` or ` minor `), and keep major bumps disabled so automated releases stay predictable for library consumers and maintainers (`#1477`).
- **`@since` policy is now explicit for contributors**: `.github/CONTRIBUTING.md` now clearly requires introducing release versions without `-SNAPSHOT` (for example `@since 0.22.4`), plus a documented 5-minor volatility window so teams can adopt new APIs with clearer risk expectations (`#1477`).

### Fixed
- **Net momentum can now jump straight to later bars without falling over**: `NetMomentumIndicator` now handles large first-lookups and constrained `maximumBarCount` series without blowing the stack, so replay/backtest flows can request a late bar first, warm up on pruned rolling windows, and keep the same momentum values they would get from sequential evaluation.
- **Release workflow drift guardrails**: Release maintainers now get near-immediate drift detection because `release-health.yml` runs on every `master` push and after `Publish Release to Maven Central` completes, and `publish-release.yml` now hard-fails if the pushed release tag does not resolve to the expected `releaseCommit` or is not reachable from `origin/<default_branch>`.
- **Rolling variance now matches sample-statistics expectations by default**: `VarianceIndicator` now computes sample variance out of the box (`n-1` divisor), so spreadsheet-style checks like issue `#1152` line up directly. You can now choose behavior explicitly with `SampleType`/factory helpers across `VarianceIndicator`, `StandardDeviationIndicator`, `StandardErrorIndicator`, `SigmaIndicator`, and `CorrelationCoefficientIndicator` (for example: `VarianceIndicator.ofSample(...)`, `VarianceIndicator.ofPopulation(...)`, `StandardDeviationIndicator.ofSample(...)`, or `CorrelationCoefficientIndicator.ofPopulation(...)`).
- **Enter-and-hold wrappers now keep return format metadata intact**: `EnterAndHoldCriterion` now forwards `getReturnRepresentation()` from its wrapped criterion, so downstream consumers can reliably detect whether outputs are decimal, percentage, multiplicative, or log without special casing wrapper criteria.
- **Elliott analysis hardening**: Enforced bounded `ElliottDegree.RecommendedHistory` ranges, hardened ossified resource loading for classpath edge cases, and simplified redundant trend-bias null guarding in EW analysis reporting.
- **Walk-forward fold metadata stability**: Fixed fold-value reporting so criterion maps and fold views remain stable and deterministic when consumers rely on fold order for downstream comparisons.

### Breaking
- **Trade and record handling now has one obvious happy path**: Build fill-aware trades with `Trade.fromFill(...)` or `Trade.fromFills(...)`, then pass them to `TradingRecord#operate(...)`. The older live-only wrappers (`ExecutionFill`, `LiveTrade`, `LiveTradingRecord`, and `PositionLedger`) are still available in 0.22.x as deprecated migration shims, but they are no longer the API you should reach for first.
- **Open-position APIs now use `Position` end to end**: `TradingRecord#getOpenPositions()` returns open `Position` snapshots, `getCurrentPosition()` remains the canonical net-open view, and `getNetOpenPosition()` is now just a compatibility alias. The separate `OpenPosition` type is gone.
- **Lot bookkeeping is finally internal again**: `PositionBook` and `PositionLot` now stay inside `BaseTradingRecord`, while FIFO, LIFO, average-cost, and specific-id matching keep the same external behavior.

### Changed (Backtest Execution Models and Custom Records)
- **Partial-fill recording is smoother in live-style flows**: You can now stream one fill at a time with `TradingRecord.operate(fill)` when fills arrive incrementally, or keep using `Trade.fromFills(...)` plus `operate(...)` when you already have the whole batch for one logical order. `TradeFillRecordingExample` in `ta4j-examples` now walks through both styles and also shows how `FIFO`, `LIFO`, `AVG_COST`, and `SPECIFIC_ID` change partial-exit matching.
- **Bring your own trading record in backtests**: `BarSeriesManager` can now run directly against a caller-provided `TradingRecord` (`run(strategy, tradingRecord[, amount, start, end])`) and can also be configured with a default `TradingRecordFactory`, so you can keep standard `BaseTradingRecord` runs or wire custom record implementations without changing existing `run(...)` calls.
- **`BacktestExecutor` now picks up the same backtest wiring without extra boilerplate**: You can construct it directly with a `TradeExecutionModel` for the common slippage/stop-limit case, or hand it a preconfigured `BarSeriesManager` so custom `TradingRecordFactory` behavior flows through normal backtest, top-K, and walk-forward execution.
- **Execution-model examples are easier to copy straight into your own backtests**: `TradingRecordParityBacktest` in `ta4j-examples` now walks through next-open, current-close, and slippage fills side by side, then verifies the same behavior with provided and factory-configured `BaseTradingRecord` runs.
- **Stop-limit/live parity hardening**: `StopLimitExecutionModel` now expires stale pending orders before accepting new signals (so old orders cannot block fresh ones), commits partial expiry fills on unified `BaseTradingRecord` exit flows for better real-world fill progression, and keeps rejection metadata for the unfilled remainder.
