Repository navigation
Releases: Bitvested/ta.go
Release list
ta.go 2.1.0 (tag v0.21.0)
Released 2026-10-10. Full details in CHANGELOG.md.
Install or pin this line with go get github.com/Bitvested/ta.go@v0.21.0; ta.Version reports 2.1.0.
⚠️ Read this before upgradingFour functions return different numbers, and three of them a different number of rows. Each was verified against ta.js, the reference these ports are built from, and each fix has a test that fails against the old code — but nothing raises, so an existing pipeline will consume the new values silently.
function what changed HalfTrendrow count: n - atrlen→n. It also produced zero trend changes on real data before this release.Alligator,Gatorrow count: n - jl - js + 2→n, oldest-first and aligned to the input bars.Kamarow count n - l1→n - l1 + 1, and every value — the smoothing constant had lost its slow term.Hullvalues: the half-length and the final smoothing window were truncated instead of rounded, so the output differed from ta.js at every odd length and wherever sqrtrounds up (8, 14, …).ZigZagvalues: three bookkeeping defects — legs were reset from ±Inf, the running maximum tracked the low, and a low pivot stored the high.
Er,ArandKellyare not in that list: their values are unchanged. What changed is that a one-sided series no longer depends onmath.Pow(1, +Inf)happening to be 1.
Added
Occ— the Open Close Cross, following JustUncleL's Pine (source), requested in Bitvested/ta.py#4. A moving average of open and of close; the direction is whichever is on top and a flip is the cross. Input rows are[open, close]; output rows are[close basis, open basis, direction], aligned to the input bars, NaN until both bases exist.delayis the Pine'sdelayOffset.basisTypeacceptsSMA,EMA,DEMA,TEMA,WMA,SMMA,HullMAandTMA; the other four the Pine offers are omitted rather than approximated (LSMA needs an offset this package'sLsmadoes not take, VWMA needs volume in the row, ALMA and SSMA are not implemented). An unrecognisedbasisTypereturns nil.
Fixed
Fixed
-
AlligatorandGatornow align to the input bars. Both returned
their rows newest-first and truncated ton - jl - js + 2rows, so
the output could not be lined up with the input series at all, and
each line was displaced byshift - 1bars instead ofshift.
Gator's upper histogram also plottedjaw - teethinstead of
|jaw - teeth|, giving the top histogram the wrong sign. Both now
emit one row per input bar, with each line displaced right by its own
shift, matching TradingView'splot(..., offset=shift), and NaN until
that line's first bar (jaw 20, teeth 12, lips 7 with the defaults).
Gatoris now derived fromAlligator's displaced lines, and
js=ts=ls=0reproduces TradingView's Gator Oscillator, which does not
displace its lines. Mirrors the same fix in ta.py and ta.js. -
Kamanow computes Kaufman's adaptive average correctly. Four bugs,
all inherited from the ta.js original: the smoothing constant dropped
its slow term (- 2/(l3+1) + 2/(l3+1)cancelled, leavingER * fast);
the volatility window summedl1-1one-bar changes instead ofl1,
which let the efficiency ratio exceed 1 and divided by zero on flat
input; the seed was the SMA of the last window rather than the first,
so the opening value was computed from data at the end of the series;
and the loop started one bar late, emittingn - l1values instead of
n - l1 + 1.The recursion now matches tulind, the reference the benchmarks compare
against: bit-identical on short, flat, trending and gapping series, max
Δ 1.4e-14 on a 100k random walk. Carrying the volatility sum across
bars instead of re-summing it per bar also makes it ~2.5× faster.
Fixes the same defect as Bitvested/ta.js#22,
mirrored in ta.py. -
HalfTrendno longer sits on one line forever. Reported against ta.py
as #7. The port read the
SMA at the start of its window — roughlyatrlenbars old — instead
of at the current bar, so both flip conditions compared stale values and
never fired: on 4901 bars of 5-minute data it produced zero trend
changes. The extremes behindmaxlow/minhighalso came from the ATR
window rather thanamplitudebars, the down-flip comparedclosewith
<where the Pine uses>, and the windowdata[i-atrlen:i]excluded
the current bar, leaving every value a bar stale.The rewrite follows everget's Pine
line by line. Its ATR uses Pine's own seeding (Wilder's RMA seeded with
the mean of the firstatrlentrue ranges, which is not whatAtr
does). Verified against a transcription of the Pine: exact agreement,
160 flips where there were none, across the three ports. -
Erno longer leaves a one-sided series to an exponentiation edge case.
It took the geometric mean of both sides regardless of how many
observations each had, so a series with no losses relied on
math.Pow(1, +Inf)happening to return 1 — which is not what the other
ports did with the same input: ta.js returned NaN (1 ** Infinityis
NaN in JS) and ta.py raisedZeroDivisionError. An empty side carries
weight zero, so its mean is never taken and it contributes nothing; all
three now state that explicitly and return identical numbers.Erand
theArandKellybuilt on it return 0.0274, and a Kelly fraction of
1, for an all-winning series, and -1.0276 for an all-losing one. Mixed
series are unchanged to the last bit. Mirrors the same fix in ta.js and
ta.py. -
ZigZag's pivot bookkeeping matched neither ta.js nor its own documentation.
Three defects: the leg reset seeded the running extreme with ±Inf instead of
the pivot just recorded, so the next retracement was measured from nothing;
the running maximum tracked the low rather than the high; and a low pivot
stored the high. On a 300-bar series it disagreed with ta.js in 174 of 298
values; it now matches exactly (max Δ 1.4e-14). The existing fixture used a 25%
retracement, where the defects cancel out, which is why they survived — a 5%
case now pins them. -
Hulltruncated where the reference rounds. Both the half-length and the final
smoothing window came fromint(), so the output disagreed with ta.js at every
odd length (half-length) and whereversqrtrounds up — length 8 has sqrt 2.83,
rounded to 3 by the reference and floored to 2 here, which also shifted the
output onto a different bar grid. Both now usemath.Round, and a sweep of
lengths 2-20 matches ta.js exactly. Found while checkingOcc'sHullMAbasis,
which inherits it.
Changed — BREAKING for HalfTrend consumers
HalfTrendreturns one row per input bar instead ofn - atrlen, so
rowicovers bari. The ATR channel is NaN until the ATR window is
full (baratrlen - 1); the trend line itself is defined from bar 0.
Callers that index-shifted the old output to align it with their data no
longer need to.
v 0.20.0
Changelog
All notable changes to ta.go.
v2.0.0 — git tag v0.20.0
Major refactor. ~52 new functions, formula and convention fixes on
existing ones, no-mutation guarantees, a goroutine Monte Carlo
subpackage, and performance rewrites that make ta.go competitive with
or faster than markcheno/go-talib and cinar/indicator at 100 k
bars.
Version = "2.0.0" is exported from the root ta package; the Git
tag itself is v0.20.0 so the existing github.com/Bitvested/ta.go
import path keeps working without Go's /v2 module-path rule.
Layout
- The single
ta.gofile is gone. Implementations now live under
category subpackages:src/moving_averages(movingaverages)src/indicatorssrc/oscillatorssrc/bandssrc/statisticssrc/chart_types(charttypes)src/experimentalsrc/random(new package)src/misc
- New top-level
multi/subpackage for goroutine Monte Carlo. - New top-level
bench/Go module (separatego.mod) for
cross-library comparative benchmarks — does not pollute the
consumergo.sum. - New
internal/testutilwithtu.Slice,tu.Slice2D,
tu.Scalar— float-tolerant comparators (rel/abs tolerance
1e-12, NaN-aware).
Formula fixes
indicators.Psar— downtrend extreme now tracks the bar's
low (data[i][1]); previous code mistakenly tracked the high.statistics.Kmeans— value-copiesold, resetsmeansper
loop, recomputes by cluster index, and resetschanged.indicators.Ichimoku— Senkou Span B's lows window now uses
length3; the per-window max/min loops were replaced with
rolling-extremum scans.movingaverages.Smma— Wilder recurrence with SMA seed of
the firstNvalues; output length isN − l + 1.indicators.Alligator/oscillators.Gator— fixtures
regenerated against the canonical Williams formulas (cascades
from the Smma fix).oscillators.MomOsc— Chande CMO formula on first
differences (positive vs absolute negative deltas).indicators.Fractals— returns[][]bool; the broken
price-mode shape was replaced with a separate
indicators.FractalsPricethat pads two leading and two
trailing[-1, -1]rows.indicators.ZigZag— fixed the state-transition tracker
(the scalar branch was resetting the wrong extreme). Both
scalar and OHLC branches share consistent semantics.
Convention changes (breaking)
indicators.Rsi— Wilder smoothing (TA-Lib convention) is
the new default. The legacy simple-average behaviour is exposed
asWrsifor back-compat callers.indicators.Roc— returns percent (×100).CopandKst
fixtures regenerate as a linear cascade.indicators.Mfi— input is[][]float64of[H, L, C, V]
bars (was pre-computed positive/negative flow).statistics.Median— even windows return the average of the
two middle values (was lower-median).Madcascades.indicators.Atr— output length isN − l + 1(wasN); TR
uses absolute values for|H − Cprev|and|L − Cprev|.Keltner,
Supertrend,Halftrendlose their compensating splice.oscillators.Fisher— output length isN − l + 1(drop
trailing slice).
No-mutation guarantees
The following functions no longer mutate caller inputs:
statistics.Std, indicators.Cross (now also returns
[]CrossSignal{Index, Cross}), indicators.Divergence,
statistics.Percentile, experimental.Support / Resistance.
NaN handling standardised
Rsi, Roc, Dif, Mom (percent mode), MomOsc, Mfi,
Fisher, Pr, Stoch now return math.NaN() on flat-window /
divide-by-zero edges instead of silent 0/1.0 coercions.
statistics.Kelly no longer coerces NaN → 1.0;
indicators.ChaikinOsc no longer coerces → 0.
New functions (~52)
Moving averages (6)
Dema, Tema, T3, Trima, Vidya, Zlema.
Indicators (26)
Adl, Adx, Adxr, Apo, Cci, Cmf, Cross, Dpo, Dx,
Emv, Kdj, Mass, Mdi, Mdm, Natr, Nvi, Pdi, Pdm,
Ppo, Pvi, StochRsi, Trix, Ulcer, Vortex, plus the
Macd split: MacdSignal, MacdBars.
Oscillators (2)
Kvo, Ult.
Statistics (9)
Covariance, ExpectedTrails, LrSlope, LrIntercept,
LrAngle, Tsf, StdSeries, ReturnPositive, ReturnNegative.
(Ssd already existed.)
Random (5, new package)
Prng (deterministic mulberry32 closure), Pick, Range,
Float, Order. Sampling helpers accept an optional rng with
math/rand fallback when nil.
Experimental (1)
DivergenceState.
While porting DivergenceState, two latent bugs in
experimental.Support were fixed: the in-package minf/maxf
helpers sorted the caller's slice in place (now copy first), and
the anchor-advance branch set findex = true instead of false
(so the outer do-while exited after one iteration). Resistance
was already correct on the second point.
Performance rewrites
Captured per-rewrite in bench/results-phase4.txt. Headline
speedups at 100 k bars:
| Function | Before | After | Speedup |
|---|---|---|---|
Sma |
30 ms | 154 µs | ≈195× |
Variance |
110 ms | 182 µs | ≈600× |
Bands |
22 ms | 524 µs | ≈42× |
Ema |
960 µs | 320 µs | ≈3× |
Wma |
800 µs | 230 µs | ≈3.5× (10 k) |
Rsi |
935 µs | 520 µs | ≈1.8× |
Plus drop-per-window-copies/sort in AroonUp/AroonDown/
Elderray/Don/Fisher (≈13–50× at 10 k).
Multi-thread Monte Carlo
New multi/ subpackage:
multi.Sim(data, length, sims, perc) []float64— fans
min(sims, runtime.NumCPU())worker goroutines reading from a
job channel; returnsdatafollowed by the per-barperc-rank
quantile of the ensemble.multi.SimSeeded(data, length, sims, perc, seed) []float64—
deterministic variant. All uniform draws are pre-generated in a
single goroutine before fan-out, so output depends only on
(data, length, sims, perc, seed).
Cross-library benchmarks
New bench/ separate Go module compares ta.go against
github.com/markcheno/go-talib and github.com/cinar/indicator
for SMA, EMA, RSI, MACD, Bollinger Bands, and ATR at 1 k / 10 k /
100 k bars. ta.go ties or wins at 100 k on every benched
indicator. See bench/BENCHMARKS.md.
Tests
Test count grew from 99/100 (one pre-existing float-drift
failure) to 165/165 passing. Float-tolerant comparator at
internal/testutil/close.go underpins fp-sensitive fixtures.
Full Changelog: 0.0.14...v0.20.0