## 0.22.0 (2025-12-29)

### Breaking Changes
- **CachedIndicator and RecursiveCachedIndicator synchronization changes**: `CachedIndicator` and `RecursiveCachedIndicator` no longer use `synchronized` methods. Thread safety is now achieved through internal locking using `ReentrantReadWriteLock`. **Action required**: Code that relied on external synchronization using indicator instances (e.g., `synchronized(indicator) { ... }`) must be updated to use explicit external locks.
- **SqueezeProIndicator return type change**: `SqueezeProIndicator` return type changed from `Boolean` to `Num`. Use `getSqueezeLevel(int)` or `isInSqueeze(int)` for compression state instead of direct boolean checks.

### Added
- Added GitHub Actions workflow linting (`actionlint`) to catch workflow expression and shell syntax errors early.
- **Num.isValid() utility method**: Added static `Num.isValid(Num)` method as the logical complement of `Num.isNaNOrNull()`, providing a convenient way to check if a value is non-null and represents a real number. Used extensively throughout the Elliott Wave indicators package for robust NaN handling.
- **SqueezeProIndicator correction**: Corrected **SqueezeProIndicator** to mirror TradingView/LazyBear squeeze momentum output: SMA-based Bollinger vs. Keltner compression tiers (high/mid/low), SMA true range width (not Wilder ATR), and the LazyBear detrended-price momentum baseline used by `linreg` (with regression tests on AAPL/NEE 2025-12-05).
- **SuperTrend indicator usability improvements**: Enhanced the SuperTrend indicator suite with intuitive helper methods and comprehensive documentation for traders:
    - Added `isUpTrend(int)`, `isDownTrend(int)`, and `trendChanged(int)` methods to `SuperTrendIndicator` for easy trend detection and signal generation without manual band comparisons
    - Comprehensive Javadoc with formulas, trading signals, usage examples, and references to authoritative sources (Investopedia, TradingView)
    - Added `package-info.java` documentation for the supertrend package
    - Expanded unit test coverage with trend reversal scenarios, serialization roundtrip tests, and ratcheting behavior verification

- **Trendline and swing point analysis suite**: Added a comprehensive set of indicators for automated trendline drawing and swing point detection. These indicators solve the common problem of manually identifying support/resistance levels and drawing trendlines by automating the process while maintaining the same logic traders use when drawing lines manually. Useful for breakout strategies, trend-following systems, and Elliott Wave analysis.
    - **Automated trendline indicators**: `TrendLineSupportIndicator` and `TrendLineResistanceIndicator` project support and resistance lines by connecting confirmed swing points. The indicators select the best trendline from a configurable lookback window using a scoring system based on swing point touches, proximity to current price, and recency. Historical values are automatically backfilled so trendlines stay straight and anchored on actual pivot points (not confirmation bars), avoiding the visual artifacts common in other implementations. Works with any price indicator (high/low/open/close/VWAP) and handles plateau detection, NaN values, and irregular time gaps—trendlines remain straight even across weekends and holidays. Supports both dynamic recalculation (updates on each bar) and static mode (freeze the first established line and project forward).
    - **ZigZag pattern detection**: Added ZigZag indicator suite (`ZigZagStateIndicator`, `ZigZagPivotHighIndicator`, `ZigZagPivotLowIndicator`, `RecentZigZagSwingHighIndicator`, `RecentZigZagSwingLowIndicator`) that filters out insignificant price movements to reveal underlying trend structure. Unlike window-based swing indicators that require fixed lookback periods, ZigZag adapts dynamically using percentage or absolute price thresholds, making it particularly useful in volatile markets where fixed windows can miss significant moves. Pivot signals fire immediately when reversals are confirmed (no confirmation bar delay), and you can track support/resistance levels dynamically for mean-reversion strategies. Supports both fixed thresholds (e.g., 2% price movement) and dynamic thresholds based on indicators like ATR for volatility-adaptive detection.
    - **Fractal-based swing detection**: `RecentFractalSwingHighIndicator` and `RecentFractalSwingLowIndicator` implement Bill Williams' fractal approach, identifying swing points by requiring a specified number of surrounding bars to be strictly higher or lower. Includes plateau-aware logic to handle flat tops and bottoms, making it robust for real-world market data. Good choice if you prefer the classic fractal methodology or need consistent swing detection across different market conditions.
    - **Unified swing infrastructure**: All swing indicators share a common foundation (`AbstractRecentSwingIndicator` base class and `RecentSwingIndicator` interface) that provides consistent caching, automatic purging of stale swing points, and a unified API for accessing swing point indexes and values. Makes it straightforward to build custom swing detection algorithms or integrate with trendline tools—implement the interface and you get caching and lifecycle management automatically.
    - **Swing point visualization**: `SwingPointMarkerIndicator` highlights confirmed swing points on charts without drawing connecting lines, useful for visualizing where pivots occur. Works with any swing detection algorithm (fractal-based, ZigZag, or custom implementations).

- **Unified data source interface for seamless market data loading**: Finally, a consistent way to load market data regardless of where it comes from! The new `BarSeriesDataSource` interface lets you work with trading domain concepts (ticker, interval, date range) instead of wrestling with file paths, API endpoints, or parsing logic. Want to switch from CSV files to Yahoo Finance API? Just swap the data source implementation—your strategy code stays exactly the same. Implementations include:
  - `YahooFinanceBarSeriesDataSource`: Fetch live data from Yahoo Finance (stocks, ETFs, crypto) with optional response caching to speed up development and avoid API rate limits
  - `CoinbaseBarSeriesDataSource`: Load historical crypto data from Coinbase's public API with automatic caching
  - `CsvFileBarSeriesDataSource`: Load OHLCV data from CSV files with intelligent filename pattern matching (e.g., `AAPL-PT1D-20230102_20231231.csv`)
  - `JsonFileBarSeriesDataSource`: Load Coinbase/Binance-style JSON bar data with flexible date filtering
  - `BitStampCsvTradesFileBarSeriesDataSource`: Aggregate Bitstamp trade-level CSV data into bars on-the-fly
  
  All data sources support the same domain-driven API: `loadSeries(ticker, interval, start, end)`. No more remembering whether your CSV uses `_bars_from_` or `-PT1D-` in the filename, or which API endpoint returns what format. The interface handles the implementation details so you can focus on building strategies. File-based sources automatically search for matching files, while API-based sources fetch and cache data transparently. See the new `CoinbaseBacktest` and `YahooFinanceBacktest` examples for complete workflows.
  
- **Elliott Wave analysis package**: Added a comprehensive suite of indicators for Elliott Wave analysis, enabling automated wave counting, pattern recognition, and confidence scoring. The package handles the inherent ambiguity in Elliott Wave analysis by generating multiple plausible wave interpretations with confidence percentages, making it practical for algorithmic trading strategies. All indicators work with swing point data from `RecentSwingIndicator` implementations (fractal or ZigZag), automatically filtering and compressing swings to identify Elliott wave structures. The system treats `DoubleNum` NaN values as invalid using `Num.isNaNOrNull()` and `Num.isValid()`, preventing NaN pivots, ratios, and targets from leaking into analysis.
  - **Core swing processing**: `ElliottSwingIndicator` generates Elliott swings from `RecentSwingIndicator` swing points, `ElliottSwingCompressor` filters and compresses swings to identify wave structures, and `ElliottWaveCountIndicator` counts waves in the current pattern. `ElliottSwingMetadata` tracks swing relationships and wave structure.
  - **Fibonacci analysis**: `ElliottRatioIndicator` calculates Fibonacci retracement/extension ratios between waves, `ElliottChannelIndicator` projects parallel trend channels for wave validation, and `ElliottConfluenceIndicator` identifies price levels where multiple Fibonacci ratios converge. `ElliottFibonacciValidator` provides continuous proximity scoring methods (`waveTwoProximityScore()`, `waveThreeProximityScore()`, etc.) returning 0.0-1.0 instead of boolean pass/fail.
  - **Wave state tracking**: `ElliottPhaseIndicator` tracks the current wave phase (impulse waves 1-5, corrective waves A-B-C), and `ElliottInvalidationIndicator` surfaces invalidations when wave counts break down. `ElliottPhase` enum represents wave phases with degree information.
  - **Confidence scoring and scenario generation**: `ElliottConfidence` record captures confidence metrics with weighted factor scores (Fibonacci proximity 35%, time proportions 20%, alternation quality 15%, channel adherence 15%, structure completeness 15%). `ElliottConfidenceScorer` is a stateless utility that calculates confidence from swing data with configurable weights. `ElliottScenario` represents a single wave interpretation with confidence, invalidation level, and Fibonacci-based price targets. `ElliottScenarioSet` is an immutable container holding ranked alternative scenarios with convenience methods (`base()`, `alternatives()`, `consensus()`, `hasStrongConsensus()`, `confidenceSpread()`). `ScenarioType` enum classifies pattern types (IMPULSE, CORRECTIVE_ZIGZAG, CORRECTIVE_FLAT, CORRECTIVE_TRIANGLE, CORRECTIVE_COMPLEX, UNKNOWN). `ElliottScenarioGenerator` generates alternative interpretations by exploring different starting points, pattern types, and degree variations, with automatic pruning of low-confidence scenarios. `ElliottScenarioIndicator` is a CachedIndicator returning `ElliottScenarioSet` for each bar, integrating scenario generation with the indicator framework. `ElliottScenarioComparator` provides utilities for comparing scenarios with methods like `divergenceScore()`, `sharedInvalidation()`, `consensusPhase()`, and `commonTargetRange()`.
  - **Price projections and invalidation levels**: `ElliottProjectionIndicator` returns Fibonacci-based price targets from the base case scenario. `ElliottInvalidationLevelIndicator` returns the specific price level that would invalidate the current count, with PRIMARY/CONSERVATIVE/AGGRESSIVE modes.
  - **Facade and utilities**: `ElliottWaveFacade` provides a high-level API with scenario-based methods: `scenarios()`, `primaryScenario(int)`, `alternativeScenarios(int)`, `confidenceForPhase(int, ElliottPhase)`, `hasScenarioConsensus(int)`, `scenarioConsensus(int)`, `scenarioSummary(int)`. `ElliottDegree` enum represents wave degrees (SUBMINUETTE through GRAND_SUPERCYCLE) with methods for higher/lower degree navigation. `ElliottChannel` represents parallel trend channels for wave validation.
  - **Example and integration**: `ElliottWaveAnalysis` example demonstrates confidence percentages, scenario probability distributions, and alternative scenario display, showing how to integrate Elliott Wave analysis into trading strategies.

- **Chart annotation support**: Added `BarSeriesLabelIndicator` to the ta4j-examples charting package, enabling sparse bar-index labels for chart annotations. The indicator returns label Y-values (typically prices) at labeled indices and `NaN` elsewhere, with integrated support in `TradingChartFactory` for rendering text annotations on charts. Useful for marking significant events, wave labels, or custom annotations on trading charts.
- **Channel overlay helpers**: Added a `PriceChannel` interface (upper/lower/median, shared validity/width/contains helpers, and a boundary enum), a `ChannelBoundaryIndicator` charting helper, and `ChartBuilder.withChannelOverlay(...)` convenience methods (including a channel-indicator overload) for plotting channel boundaries like Elliott channels, Bollinger bands, and Keltner channels. Charting now validates that channel overlays include both upper and lower boundaries, provides TradingView-inspired muted defaults with channel fill shading and a dashed, lower-opacity median line, and exposes a fluent channel styling stage including interior fill controls.

### Changed
- **Elliott Wave scenario probability JSON output**: Scenario probability values are now serialized as decimal ratios (0.0-1.0) rounded to two decimals instead of percentages, while log output remains in percent.
- **CachedIndicator and RecursiveCachedIndicator performance improvements**: Major refactoring to improve indicator caching performance:
    - **O(1) eviction**: Replaced ArrayList-based storage with a fixed-size ring buffer (`CachedBuffer`). When `maximumBarCount` is set, evicting old values now takes constant time instead of O(n) per-bar copies.
    - **Read-optimized locking**: Migrated from `synchronized` blocks to a non-fair `ReentrantReadWriteLock` with an optimistic (lock-free) fast path for cache hits. Cache misses and invalidation remain write-locked, while read-heavy workloads scale without serializing threads.
    - **Reduced allocation churn**: Eliminated `Collections.nCopies()` allocations in the common "advance by 1 bar" case. The ring buffer writes directly to slots without creating intermediate lists.
    - **Last-bar mutation caching**: Repeated calls to `getValue(endIndex)` on an unchanged in-progress bar now reuse the cached result. The cache automatically invalidates when the bar is mutated (via `addTrade()` or `addPrice()`) or replaced (via `addBar(..., true)`).
    - **Deadlock avoidance**: Fixed lock-order deadlock risk between last-bar caching and the main cache by ensuring last-bar computations and invalidation never run while holding locks needed by cache writes.
    - **Iterative prefill under single lock**: `RecursiveCachedIndicator` now uses `CachedBuffer.prefillUntil()` to compute gap values iteratively under one write lock, avoiding repeated lock re-entry and series lookups.
    - **Last-bar computation timeout**: Added 5-second timeout for waiting threads to prevent indefinite hangs if the thread owning a last-bar computation dies unexpectedly.
- **ta4j-examples runners and regression coverage**: Added main-source runners (`CachedIndicatorBenchmark`, `RuleNameBenchmark`, `TrendLineAndSwingPointAnalysis`) and a robust regression test (`TrendLineAndSwingPointAnalysisTest`) so full builds stay quiet while still validating trendlines/swing points and enabling chart generation via `main`.
- **BacktestPerformanceTuningHarness**: Expanded the example into a configurable tuning harness that sweeps strategy counts, bar counts, and `maximumBarCount` hints, capturing runtime + GC overhead and identifying the last linear ("sweet spot") configuration. Includes an optional heap sweep that forks a child JVM per `-Xmx` value.
- **Comprehensive README overhaul**: Completely rewrote the README to make Ta4j more approachable for new users while keeping it useful for experienced developers. Added a dedicated "Sourcing market data" section that walks through getting started with Yahoo Finance (no API key required!), explains all available data source implementations, and shows how to create custom data sources. Reorganized content with clearer navigation, better code examples, and practical tips throughout. The Quickstart example now includes proper error handling and demonstrates real-world data loading patterns. New users can go from zero to running their first backtest in minutes, while experienced quants can quickly find the advanced features they need.

### Removed
- Deleted `BuyAndSellSignalsToChartTest.java`, `CashFlowToChartTest.java`, `StrategyAnalysisTest.java`, `TradeCostTest.java`, `IndicatorsToChartTest.java`, `IndicatorsToCsvTest.java` from the ta4j-examples project. Despite designated as "tests", they simply launched the main of the associated class.
- Consolidated `SwingPointAnalysis.java` and `TrendLineAnalysis.java` into `TrendLineAndSwingPointAnalysis.java` to provide a unified analysis example combining both features.
- Removed `TopStrategiesExampleBacktest.java` and `TrendLineDefaultCapsTest.java` as part of example consolidation and test cleanup.

### Fixed
- **Rule naming now lightweight**: `Rule#getName()` is now a simple label (defaults to the class name) and no longer triggers JSON serialization. Composite rules build readable names from child labels without serialization side effects.
- **Explicit rule serialization APIs**: Added `Rule#toJson(BarSeries)`/`Rule#fromJson(BarSeries, String)` so serialization happens only when explicitly requested. Custom names are preserved via `__customName` metadata, independent of `getName()`.
- **Rule name visibility**: Made rule names volatile and added regression coverage so custom names set on one thread are always visible on others, preventing fallback to expensive default-name serialization under concurrency.
- Fixed GitHub Actions `release-scheduler.yml` failure in the `Compute new version` step (bash syntax) and hardened job `if:` conditions.
- Fixed **SuperTrendUpperBandIndicator** NaN contamination by recovering once ATR leaves its unstable window and aligning unstable-bar counts across SuperTrend components so values stabilize after ATR warms up.
- **Consistent NaN handling across all SuperTrend indicators**: `SuperTrendLowerBandIndicator` and `SuperTrendIndicator` now return NaN during the unstable period (when ATR values are not yet reliable), consistent with `SuperTrendUpperBandIndicator` and the project convention that indicators should return NaN during their unstable period. Previously, these indicators returned zero during the unstable period, which was inconsistent with other indicators and could lead to misleading calculations.
- **Test improvements for headless environment compatibility**: Updated strategy and charting tests (`RSI2StrategyTest`, `CCICorrectionStrategyTest`, `GlobalExtremaStrategyTest`, `MovingMomentumStrategyTest`, `ChartWorkflowTest`) to use `SwingChartDisplayer.DISABLE_DISPLAY_PROPERTY` instead of `Assume.assumeFalse()` for headless environment checks. This allows tests to run reliably in both headless and non-headless environments without skipping, improving CI/CD compatibility and test coverage.
