go-talib: technical analysis indicators in pure Go
A pure Go port of TA-Lib (http://ta-lib.org)
At a glance
- What is it?
- go-talib is a pure Go port of the TA-Lib indicator library, giving Go programs RSI, MACD and the rest of the standard set as functions over float64 slices, with parity tests against the reference Python implementation.
- Who is it for?
- go-talib is a good fit for Go services that compute technical analysis indicators and want a single static binary with no cgo toolchain in the build pipeline, and its parity test suite against ta-lib-python gives you a concrete way to check the port before trusting it.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 128 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem go-talib solves is cgo in a build pipeline
Technical analysis indicators have a well known reference implementation: TA-Lib, a C library. If you write Go and need RSI, MACD or a moving average, your options are to bind the C library through cgo, to reimplement the formulas yourself, or to use a port.
go-talib is the port route. The project describes itself as a pure Go port of TA-Lib, and the repository is a single Go module, `github.com/markcheno/go-talib`, requiring Go 1.23.2. Pure Go here means no cgo, so cross-compilation is ordinary, the build container needs no C toolchain, and the resulting binary has no native dependency to ship.
The intended user is a Go developer working on trading or market data code. The topics attached to the repository name the domain precisely: financial-analysis, golang, ta-lib, technical-analysis, technical-analysis-indicators and technical-analysis-library.
The trade-off is the one inherent to any port. A port is only as correct as the effort spent comparing it against the original, and it can drift when the original changes. This project addresses that directly, which is the most interesting thing about it: the test suite does not just check internal consistency, it checks the Go output against the reference Python implementation for every indicator.
Indicators are functions over float64 slices, not objects
The API is uniform to the point of being mechanical. Every indicator takes one or more `[]float64` price series, plus parameters, and returns a `[]float64`, or a tuple of them, that is the same length as the input.
The same-length contract is the part that changes how you write the calling code. A lookback-based API instead makes the caller allocate a separate output buffer of a different size and track the offset. Here the returned slice lines up index for index with the input series, which means a moving average, an RSI and a price series can be zipped together without any bookkeeping.
The cost of that convenience is a zero-filled head. The leading lookback region that cannot be computed, because the indicator needs more history than exists yet, is zero-filled. So an RSI with a 14 period will begin with a run of zeros. Code that treats a zero as a real value will misbehave, and that filtering is left to the caller.
The example in the README shows the shape of a call:
rsi2 := talib.Rsi(spy.Close, 2)
fmt.Println(rsi2)The package is imported as `github.com/markcheno/go-talib` and used as `talib` inside the code, and the function name is the indicator name in title case.
Short series and invalid periods return zeros, not panics
Error handling is where a port usually betrays its origins, and here the behaviour is documented explicitly. Inputs that are too short for the requested period, or an invalid, non-positive period, return a zero-filled slice of the correct length rather than panicking.
That is a deliberate choice, and it has consequences worth being honest about. A caller cannot distinguish "the indicator is undefined here" from "the period argument was garbage" by looking at the result: both cases come back as zeros. The slice length is always right, so the failure is silent by design, which keeps a trading service running when a data feed hands it a short window instead of crashing it.
The trade-off is that a configuration mistake in a period argument will quietly produce a chart full of zeros rather than an error message. The edge cases are handled in the repository by a separate test file, `edge_test.go`, alongside the main `talib_test.go` and the benchmark file `bench_test.go`.
For anyone building a service on top of this, the sensible discipline is to validate the period arguments at your own boundary before calling in, rather than relying on the library to complain. The library's guarantee is that it will not panic, not that it will tell you why the output is empty.
Installing the package and running a first indicator
The install step is a single `go get`:
go get github.com/markcheno/go-talibThen import it in your source:
import "github.com/markcheno/go-talib"and refer to it as `talib` in your code. There is no configuration file, no code generation step and no build tag to set.
The README's worked example is a full program that pulls quotes and computes an indicator:
package main
import (
"fmt"
"github.com/markcheno/go-quote"
"github.com/markcheno/go-talib"
)
func main() {
spy, _ := quote.NewQuoteFromTiingo("spy", "2016-01-01", "2016-04-01", quote.Daily, true)
fmt.Print(spy.CSV())
rsi2 := talib.Rsi(spy.Close, 2)
fmt.Println(rsi2)
}Two things to note before you copy it. The data fetching comes from a separate project, `github.com/markcheno/go-quote`, and a `quote` package, so the example needs two dependencies rather than one. And the error returned by `quote.NewQuoteFromTiingo` is discarded with `_`, which is exactly the sort of thing you should not do in your own code, since a failed fetch leaves you with an empty series and therefore a zero-filled indicator result.
For a first real check, skip the network entirely: feed a `[]float64` of prices you already have into `talib.Rsi` and confirm the length of the result matches the length of your input.
The test suite compares against the Python reference implementation
The most interesting part of the repository is not the port, it is the parity testing. The tests compare every indicator against the reference Python TA-Lib, the `ta-lib-python` project, so the suite needs a `python` with `numpy` and `TA-Lib` available.
Provisioning that interpreter is handled by a `justfile`, which uses uv to create a project-local `.venv` holding `numpy` and `TA-Lib` and puts it on `PATH` so the harness's `python` resolves to it. The recipes are:
just test # set up the venv (first run) and run the full suite
just test-one TestRsi # run a single test or pattern
just bench MidP # run benchmarksThe justfile also documents the shape of the plumbing. The `setup` recipe creates the environment with `uv venv` and installs the two packages with `uv pip install`. A hidden `_ensure-venv` recipe checks whether `import talib, numpy` succeeds and recreates the environment if not, so the test recipe is idempotent. The test recipe itself runs `go test -count=1 ./...` with the venv on the `PATH`.
This design has a consequence for contributors: running the full suite needs Python with those two packages, and the justfile comments note that even the benchmark recipe needs the venv, because the package is gated in `TestMain`. There are separate `build`, `vet` and `fmt` recipes that need no Python at all, and `clean` removes the environment.
Because parity is checked against the reference implementation rather than against hand-written expectations, the suite answers the question that matters for a port: does this Go code compute the same numbers as TA-Lib.
A single-file library with no release tags
The repository is small and flat: `.gitignore`, `CLAUDE.md`, `LICENSE`, `README.md`, `bench_test.go`, `edge_test.go`, `go.mod`, `go.sum`, `justfile`, `talib.go`, `talib_test.go`. All the indicators live in `talib.go`.
That has an upside and a downside. The upside is that reading the library means reading one file, and there is no internal package structure to learn. The downside is that a single file holding every indicator does not scale gracefully, and it is a hint about the size of the project rather than a design choice to copy.
The licence is MIT, with the LICENSE file at the repository root. The licence field on the repository says MIT, and the README points to the LICENSE file for details. That is permissive enough to use in a commercial product without a copyleft obligation, subject to keeping the copyright notice, though that is a reading of the licence text rather than legal advice.
On maintenance, the repository is not archived and the last push was on 2026-06-17, which is inside the last six months. The catch is that the repository has no GitHub releases, so there is no tag list and no release notes to read. If you depend on it, pinning to a commit hash is the only way to get a reproducible build, and you should check `talib.go` yourself for the indicator set you need, because the README does not enumerate which indicators are implemented.
A pure Go port versus binding the C library directly
The alternative to go-talib is to bind the original C TA-Lib into your Go program through cgo, and the difference in approach is worth stating plainly.
A cgo binding inherits the C library's behaviour exactly, including its edge cases, because it is the same code. Its costs are the build toolchain, the need to cross-compile with a C compiler for every target, and a native dependency that has to be present wherever the binary runs. A pure Go port costs you the cgo toolchain requirement and gives you a statically linked binary, and it costs you the guarantee that you are running the reference implementation, because the port is a reimplementation that has to be checked for parity.
That check is exactly what the parity test suite in this repository exists to provide, comparing every indicator against the reference Python TA-Lib. So the decision is not blind faith versus certainty, it is whether running the comparison is part of your workflow.
A third option is to write the indicator formulas yourself in Go, which is reasonable for one or two indicators and painful for the full set, since the value of a reference library is in the long tail of formulas that nobody wants to re-derive and debug.
Editorial conclusion
go-talib is a good fit for Go services that compute technical analysis indicators and want a single static binary with no cgo toolchain in the build pipeline, and its parity test suite against ta-lib-python gives you a concrete way to check the port before trusting it. It is the wrong choice if you need indicators that this port does not implement, or if you need the original library's full function set and maintenance guarantees, since the repository ships a single source file, has no GitHub releases, and the last push was on 2026-06-17. Before adopting, read talib.go to confirm the indicators you need are present, run the parity suite with just test to see the comparison against the Python reference, and decide how you will pin a version given that no release tags exist.
Frequently asked questions
How do I install go-talib?
Run go get github.com/markcheno/go-talib, then import "github.com/markcheno/go-talib" and use talib as the package name. The module requires Go 1.23.2.
What does go-talib return for an indicator?
Every indicator takes one or more []float64 price series plus parameters and returns a []float64, or a tuple of them, with the same length as the input. The leading lookback region that cannot be computed is zero-filled.
What happens if the input series is too short or the period is invalid?
The call returns a zero-filled slice of the correct length instead of panicking, so a short series or a non-positive period produces zeros rather than an error.
How does go-talib verify that its results match TA-Lib?
The test suite compares every indicator against the reference Python TA-Lib, so it needs a python with numpy and TA-Lib available. The justfile provisions that interpreter with uv, and just test runs the full suite.
Does go-talib need cgo or a C library to build?
No. It is described as a pure Go port, so the module builds with the standard Go toolchain and the resulting binary carries no native TA-Lib dependency.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/markcheno-go-talib)