| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| Parent folder | |||
| ta4j-core-0.26.0-javadoc.jar | 2026-10-05 | 6.4 MB | |
| ta4j-core-0.26.0-sources.jar | 2026-10-05 | 1.5 MB | |
| ta4j-core-0.26.0-tests.jar | 2026-10-05 | 3.9 MB | |
| ta4j-core-0.26.0.jar | 2026-10-05 | 2.6 MB | |
| ta4j-examples-0.26.0-javadoc.jar | 2026-10-05 | 1.0 MB | |
| ta4j-examples-0.26.0-sources.jar | 2026-10-05 | 3.0 MB | |
| ta4j-examples-0.26.0.jar | 2026-10-05 | 3.5 MB | |
| 0.26.0 source code.tar.gz | 2026-10-05 | 7.0 MB | |
| 0.26.0 source code.zip | 2026-10-05 | 8.2 MB | |
| README.md | 2026-10-05 | 17.4 kB | |
| Totals: 10 Items | 37.2 MB | 1 | |
0.26.0 (2026-10-05)
Upgrading from 0.25.0: backtest and analysis results change, and backtesting a live series that changes mid-run now throws. Runs no longer use prices from after their window, walk-forward folds close their open positions (paying the exit cost), and mark-to-market curves follow period-return conventions for holding costs and for positions opened before the window. Backtests and analyses of a
ConcurrentBarSeriesthat changes underneath them now throwIllegalStateExceptioninstead of silently mixing old and new bars, and they no longer lock the series while strategies run, which removes a deadlock with live feeds. Read Breaking before upgrading.
Breaking
- Backtests of a changing series fail loudly instead of returning mixed results.
BacktestExecutorand walk-forward runs capture the series window when they start and throwIllegalStateExceptionif a bar inside it is replaced, updated in place (including the forming last bar) or evicted before they finish; bars appended after the window are ignored. Previously such changes were silently mixed into the results. A rollingConcurrentBarSerieswith a maximum bar count evicts on every append, so a backtest over it fails whenever a bar arrives mid-run. What to do: pause writes while backtesting, or build the strategies on a stable copy such asseries.getSubSeries(series.getBeginIndex(), series.getEndIndex()), which also leaves out the forming bar. - No lookahead past a run's window. A run no longer keeps evaluating the strategy on bars after its end to close a position, and a next-open fill for a signal on the last bar no longer lands on the bar after the run (for walk-forward folds, the next fold's first bar). A position still open at the end is marked at the window's last close or ignored, per the criterion's
OpenPositionHandling, and a position that closes after an analysis window is charged holding cost only up to the window end. Why it matters: those results used prices the run should not have seen. Execution models and position sizers now receive the series through a read-only view that ends at the run's last bar. - Walk-forward folds end flat. A position still open at a fold's last bar is exited at that close and charged the transaction cost, instead of vanishing when the next fold starts flat. Fold criteria now include that exit cost, and fold records can be chained into one out-of-sample history. Plain
BarSeriesManagerandBacktestExecutorruns still leave the position open unless you wrap their execution model in the newExitOnRunEndModel. - Mark-to-market curves follow period-return conventions.
CashFlow,CumulativePnL,Returnsand every criterion built on them (drawdowns, Calmar, Sharpe, Sortino, Omega, VaR and more) change in three cases: holding costs now accrue at every held bar, so the curve ends at the realized value instead of charging one period's average per mark; a position entered before the analysed window (for example on a rolling series) is valued from the window's first close, likeEnterAndHoldCriterion's benchmark, instead of crediting pre-window gains to the first bar and skewing volatility and drawdown; and a position opened and closed on the first bar now reports its return there.REALIZEDcurves keep the entry price as cost basis. - Analyses of a changing series fail loudly too. Analysis curves retry their capture when bars they read change meanwhile and throw
IllegalStateExceptionafter 8 attempts; adding a position withcalculatePositionto a curve whose bars changed since it was built also throws. Build a new curve, or analyse a stable copy.
Added
- Portfolio backtesting (
CF-148): backtest a basket of instruments held at target weights with the same workflow as a single-series backtest.PortfolioSeriesaligns several bar series by bar end time,PortfolioAllocationsets long-only target weights (whatever is left stays in cash, soMap.of("SPY", 0.6)works), andPortfolioSeriesManager.run(...)buys and holds or rebalances on aRebalancePolicy: every bar, every nth bar, on chosen indexes, on the first bar of a calendar period, or when weights drift past a threshold, combinable withor/and. Trades are sized after costs, so cash never goes negative and fixed fees don't cause churn. Each rebalance is reported in aPortfolioSnapshot(holdings, achieved weights, turnover, costs, and whether it completed, partially filled or was skipped), andPortfolioExecutionResultexports a value series that works with the existing criteria. See theStaticPortfolioBacktestexample. - Portfolio correlations and minimum-variance weights: see how a portfolio's assets move together and let ta4j pick lower-risk weights.
PortfolioCorrelationsreturns price, simple-return and log-return correlation matrices with hierarchical clustering that matches SciPy's complete linkage.MinimumVarianceOptimizerturns aPortfolioSeriesinto a long-only minimum-variancePortfolioAllocation, optionally capping each asset (new MinimumVarianceOptimizer(series, 0.25)), ready to run throughPortfolioSeriesManager. It shrinks the covariance estimate by default (Ledoit-Wolf, as in scikit-learn'sLedoitWolf), so short histories or many assets don't pile the weight into a few names;withCovarianceEstimator(CovarianceEstimator.SAMPLE)uses the plain sample covariance. TheDiversifiedPortfolioAnalysisexample loads adjusted Yahoo prices and writes correlation heatmaps, dendrograms, CSV tables and an HTML report, without new dependencies. - Close open positions when a run ends: wrap any execution model in
ExitOnRunEndModel, for examplenew ExitOnRunEndModel(new TradeOnNextOpenModel()), and a position still open at the end of a run is closed at the last close, paying the transaction cost, instead of being left open for criteria to mark to market. Walk-forward folds do this by default. Custom execution models can use the newTradeExecutionModel.onRunEndoverload that receives the run's series. - Consistent reads from a live series:
BarSeries.withReadLock(...)reads aConcurrentBarSeries' bounds and bars as one consistent snapshot while a feed keeps writing (on other series it just runs the action). Keep it to short, bar-only reads; evaluating indicators inside it can deadlock. Analysis curves report the window they captured throughPerformanceIndicator.getBeginIndex()/getEndIndex(), andBarSeriesUtils.deltaYears(Instant, Instant, NumFactory)computes year fractions from bar times you captured. - Cap the threads a backtest batch uses: new
BacktestExecutor.executeWithRuntimeReport(..., parallelism)overloads, for fixed amounts or aPositionSizer, run a batch on at mostparallelismplatform threads without nested parallel streams, so a backtest inside a boundedForkJoinPoolor thread budget stays within it. powin numeric indicator chains:NumericIndicator.pow(Number)raises any composed indicator to a constant exponent, so chains likeNumericIndicator.of(relativeVolume).max(0.25).min(4).pow(0.5)no longer needUnaryOperationIndicator.pow.- Adaptive Kalman noise example:
AdaptiveKalmanNoiseExampleand theadaptive-kalman-noise.mdwalkthrough show a Kalman filter that adapts to volatility and volume: ATR-based process noise and relative-volume measurement noise fed intoKinematicKalmanFilterIndicatorthroughKalmanNoiseIndicator, with same-bar or lagged (--lag-noise) noise, explicit handling of missing or zero volume, and MAE/RMSE comparisons against fixed-noise and last-close baselines. Library defaults are unchanged. - Experimental Elliott wave pattern research (
CF-525): package-private groundwork, not yet public API, for recognizing Elliott structures (5-wave motive, 3-wave corrective and full 5-3 cycles) from confirmed pivots, with explicit forming, complete, ambiguous and invalidated outcomes, plus a reproducible internal study harness. It changes no public behavior.
Changed
- Backtests run on your series, with its indexes, without locking it:
BarSeriesManager, position-sizing contexts and the Elliott/Wyckoff facades use the series you pass instead of an index-resetting copy, so strategies keyed to absolute indexes (includingInteger.MAX_VALUEranges) fire on offset and rolling series. No series lock is held while strategies run, so a liveConcurrentBarSerieskeeps accepting writes, and parallel scheduling applies to every series type. Each execution reports the window it ran on as an immutable, index-preservingbarSeries();executeWithWalkForwardshares one window across both phases. - Leaner bar change tracking: bars that no series holds no longer carry change-tracking state, and bars held by one series use a compact form; only bars shared by several series keep a full registry. Every series holding a bar now refreshes its indicator caches when the bar changes, including when a price update fails halfway or a
BaseBarsubclass skips the superclass notification. - For contributors: faster test runs: Surefire runs test classes in four bounded JVM forks (
ta4j.test.forkCount); the benchmark and analysis-demo lanes stay on one fork so their timings and shared outputs don't interfere.
Fixed
-
Fresh swing and forecast results on revisionless series (CF-638):
AbstractRecentSwingIndicator,ProminenceSwingDetector,AdaptiveZigZagSwingDetectorandEwmaReturnForecastStateIndicatorrevalidate retained bar values for series without history revisions. Repeated queries, appends after interior edits and mutations during calculation reset stale swing or EWMA state, including registered SMA/ATR inputs. EWMA refreshes shared return caches before accepting appended ranges, and direct detector-backed price reads refresh cached fallback sources. Each fallback history comparison costs O(retained bars); an initial or replayed detector-backed recent-swing scan can perform O(retained bars²) validation work across its historical queries, in addition to the underlying swing algorithm's cost. Revision-aware series keep constant-time history checks. Queries retry until the observed history is stable; continuous mutation can delay completion. -
Deadlocks between backtests, indicators and live feeds: indicator caches lock themselves before reading bars, so code that evaluated indicators or user cost models while holding the series read lock could deadlock with another reader once a feed writer was waiting. Backtests, walk-forward runs, analysis curves, Calmar, Omega, return over max drawdown,
InvestedIntervaland the Elliott/Wyckoff indicators and facades now read bars in short scopes and run everything else outside the lock. Bars mutated directly (for examplebar.addPrice(...)) can be seen before the series publishes the change; update them through the series when readers must never see that (a stricter fix is tracked in [#1643]). - No deadlocks when concurrent series share bars: inside
ConcurrentBarSeries.withWriteLock(...), change notifications for bars shared with other series are delivered once the outermost lock is released, so two series sharing bars can no longer deadlock each other; bars that survive a head eviction are rechecked. - Elliott swing detection on a live series:
FractalSwingDetectorreads the series' revision, bounds and bars in one consistent step, replays without blocking the feed, and rebuilds when bars change, including customBarclasses. Series without revision tracking revalidate every retained bar a query already read, so an earlier bar mutated in place can no longer leave pivots built from mixed bar states. It throwsIllegalStateExceptionafter 8 changed attempts instead of returning pivots from mixed data. - Indicators refresh after a failed realtime-bar update: when a trade update to a
BaseRealtimeBarfails partway through aggregating side or liquidity, the series holding it is still told the bar changed, so its indicators don't keep serving values from before the partial update. - Kalman filters start at the first usable bar:
KalmanFilterIndicatorand the kinematic Kalman indicators and forecasts no longer start from a zero placeholder when the first bars have no usable price or noise, for example during an ATR warm-up. They start at the first valid observation with zero velocity, so early estimates aren't dragged toward zero and show no invented momentum. Data that is valid from the first bar gives the same results as before. - Analyses measure exactly their window and one bar history: Calmar and Omega stop at an explicitly bounded record's end instead of scoring the flat tail after it; drawdowns use each curve's own window and measure lengths from its start;
MonteCarloMaximumDrawdownCriterionno longer iterates from entries before the window, which could exhaust memory nearInteger.MAX_VALUE; curves keep the window they captured when a live series rolls, check the bars they read by value (covering customBarclasses and series without revision tracking) and ignore bars after a boundedfinalIndex;ExcessReturns(Sharpe, Sortino) and Calmar take bar times from the same history as their returns; and backtest verification checks only the bars a run can read. - Faster rule and strategy copies: copying rules (
getEntryRule()/getExitRule(), composite rule accessors and position-sizing strategy snapshots) no longer scans the logging backend's JVM-wide logger registry, a cost that grew with every logger created in the JVM. - Security: safer JSON loading of rules and strategies: deserializing a
RuleorStrategyno longer initializes arbitrary classes named in itstypefield, and unknown or non-rule types fail with the sameUnknown rule/strategy typeerror.ComponentDescriptor#getTypeClassresolvesorg.ta4j.coresimple names. - Edge cases: VaR and Expected Shortfall return
NaNwhen a return in the distribution is undefined instead of sortingNaNinto the tail; return over max drawdown returns its neutral value for a null record, like Calmar and Omega; a backtest that started on an empty series no longer fails when bars are appended; curves no longer evaluate holding-cost models for positions outside their window; and loops over windows ending atInteger.MAX_VALUEno longer wrap around. - Bounded backtests and Omega returns honor their exact start: execution-series views hide bars before a delegate's logical begin and report that hidden prefix as removed, so indicators such as
ParabolicSarIndicatorno longer index before it;ExitOnRunEndModeldoes not force-close positions when a run processes no bars; backtest results reject a logical end that contracted during the run; and Omega retains a closed return seeded at the bounded start even when open-position marks are ignored.CashFlowkeeps same-bar realized exits at the bounded analysis end. Curves on a pruned or rolling series now carry the realized result of positions closed before the retained begin instead of restarting at 1/0, while positions before an explicit record or requested start stay excluded; curves recheck every bar field (not only close and end time) before trusting user cost models; position-based drawdown criteria treat an exit after the analysed end as open at that end, per the configured open-position handling; and drawdowns of customPerformanceIndicatorcurves stop at an explicitly bounded record's end. - Criteria judge positions as of the analysed end: a position (or lot) that exits after the analysed end counts as open there, so open unrealized profit and open cost basis sum every lot active at that end (and skip open lots entered after it),
NumberOfPositionsCriterioncounts it as open, windowed analyses mark it to market (or drop it underOpenPositionHandling.IGNORE), and Sharpe/Sortino trade samples respectIGNOREfor it. Sharpe and Sortino sample only the captured window and its captured bar times, so bars appended or replaced mid-analysis no longer add artificial returns. Return over max drawdown and Monte Carlo drawdown no longer count realized P&L carried in from before a rolling series' retained begin as a return of the window. - Window edges are measured, not guessed: curves verify the bars before their window that a holding-cost model may read, so a bar replaced there mid-analysis triggers a recapture instead of a stale borrow or funding cost.
InvestedInterval,InPositionPercentageCriterion,NumberOfBarsCriterionandPositionDurationCriterionstop at the record's logical end and honorOpenPositionHandling.IGNORE, so a risk-free cash leg no longer skews Sharpe and Sortino. Return over max drawdown, Calmar, the drawdown criteria and Monte Carlo drawdown start from the equity entering the window, newPerformanceIndicator#getBaselineValue(), so a same-bar round trip or a loss realized on a window's first bar counts instead of cancelling out. - Faster
DecimalNumfractional powers:DecimalNum.pow(Num)computes the whole-number part of the exponent at working precision plus guard digits instead of exactly, so large exponents (such as annualizing over years of bars) no longer take seconds; negative fractional exponents are supported too, and values using an unlimitedMathContextstay exact.