Add the Go binding over the C ABI hub (#228)
Adds a Go binding (`bindings/go`) over the C ABI hub — the second language stecker after C#. ## What's here - **`bindings/go`** — a cgo binding exposing all 514 indicators as idiomatic Go types with `New<Indicator>` constructors and `Update`/`Batch`/`Reset`/`Close` methods. The wrappers in `indicators_gen.go` are generated from `bindings/c/include/wickra.h` (same archetype taxonomy as the C# generator: scalar/batch, multi-output, bars, profile, profile-values, array-input). Opaque handles are freed by `Close()` with a `runtime.SetFinalizer` backstop; pointer arguments are caller-owned, panics never cross the boundary. - **`examples/go`** — the full example suite mirroring C/C#: streaming, backtest, multi_timeframe, parallel_assets (goroutine fan-out), three strategies, and `fetch_btcusdt`/`live_binance`. - **CI** — a `go` job builds the C ABI library, stages it, and runs `gofmt`/`go vet`/`go test` plus the offline examples on Linux, macOS and Windows. - **Docs** — Go added to the README languages table, project layout, building/testing, CONTRIBUTING binding table + regenerate note, ARCHITECTURE, examples index, issue/PR templates, the About-description template, and the other binding READMEs. ## Linking / distribution The binding links the prebuilt C ABI library via cgo (`libwickra.so`/`.dylib`/`wickra.dll` staged under `bindings/go/lib`, gitignored). The native libraries are already shipped per target triple by the existing `c-abi-build` release job; distribution is via the subdirectory module tag `bindings/go/vX.Y.Z` (gated), so `release.yml` needs no new publish job. No Rust crate or `Cargo.toml` change — the Go module is standalone and additive. Not for merge yet (gated, per request).
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
library plus a generated `wickra.h` — no system dependencies.**
|
||||
|
||||
Wickra is a multi-language technical-analysis library with a Rust core and
|
||||
bindings for Python, Node.js and WebAssembly, plus a C ABI for C/C++, C# and any
|
||||
bindings for Python, Node.js and WebAssembly, plus a C ABI for C/C++, C#, Go and any
|
||||
other C-capable language. Every indicator is an O(1)
|
||||
streaming state machine, so live trading bots and historical backtests share
|
||||
the exact same implementation. This package is the **C ABI hub**: it compiles the
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
prebuilt native library, no system dependencies.**
|
||||
|
||||
Wickra is a multi-language technical-analysis library with a Rust core and
|
||||
bindings for Python, Node.js and WebAssembly, plus a C ABI for C/C++, C# and any
|
||||
bindings for Python, Node.js and WebAssembly, plus a C ABI for C/C++, C#, Go and any
|
||||
other C-capable language. Every indicator is an O(1)
|
||||
streaming state machine, so live trading bots and historical backtests share
|
||||
the exact same implementation. This package is the .NET binding; it consumes the
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
# Wickra — Go
|
||||
|
||||
[](https://github.com/wickra-lib/wickra/actions/workflows/ci.yml)
|
||||
[](https://codecov.io/gh/wickra-lib/wickra)
|
||||
[](https://pkg.go.dev/github.com/wickra-lib/wickra/bindings/go)
|
||||
[](https://github.com/wickra-lib/wickra#license)
|
||||
|
||||
**Streaming-first technical indicators for Go, over the Wickra C ABI hub via cgo.**
|
||||
|
||||
Wickra is a multi-language technical-analysis library with a Rust core and
|
||||
bindings for Python, Node.js and WebAssembly, plus a C ABI for C/C++, C#, Go and
|
||||
any other C-capable language. Every indicator is an O(1) streaming state machine,
|
||||
so live trading bots and historical backtests share the exact same
|
||||
implementation. This package is the Go binding; it consumes the C ABI hub through
|
||||
cgo and exposes all 514 streaming-first indicators as idiomatic types.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
go get github.com/wickra-lib/wickra/bindings/go
|
||||
```
|
||||
|
||||
The binding uses cgo, so a C compiler is required, and it links against the
|
||||
prebuilt Wickra C ABI library. Build that library from the workspace and stage
|
||||
it under this package's `lib/` directory:
|
||||
|
||||
```bash
|
||||
cargo build -p wickra-c --release
|
||||
cp target/release/libwickra.so bindings/go/lib/ # Linux
|
||||
cp target/release/libwickra.dylib bindings/go/lib/ # macOS
|
||||
cp target/release/wickra.dll bindings/go/lib/ # Windows (also on PATH at run time)
|
||||
```
|
||||
|
||||
On Linux and macOS the library path is baked in via rpath; on Windows the DLL
|
||||
must be discoverable at run time (next to the executable or on `PATH`).
|
||||
|
||||
## Quick start
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
wickra "github.com/wickra-lib/wickra/bindings/go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Batch: run an indicator over a whole series (NaN at warmup positions).
|
||||
prices := make([]float64, 1000)
|
||||
for i := range prices {
|
||||
prices[i] = 100.0 + float64(i)*0.1
|
||||
}
|
||||
sma, _ := wickra.NewSma(20)
|
||||
defer sma.Close()
|
||||
values := sma.Batch(prices)
|
||||
|
||||
// Streaming: the same indicator, fed tick by tick in O(1).
|
||||
rsi, _ := wickra.NewRsi(14)
|
||||
defer rsi.Close()
|
||||
for _, price := range prices {
|
||||
value := rsi.Update(price) // NaN during warmup, no recomputation
|
||||
if value > 70 {
|
||||
fmt.Println("overbought")
|
||||
}
|
||||
}
|
||||
_ = values
|
||||
}
|
||||
```
|
||||
|
||||
`Batch(prices)` and feeding the same prices through `Update()` produce identical
|
||||
values — the equivalence is enforced by the test suite. Multi-output indicators
|
||||
(MACD, Bollinger, ADX, …) return `(Output, bool)`, with `false` while warming up.
|
||||
Every indicator owns a native handle freed by `Close()`; a finalizer is wired as
|
||||
a backstop, but call `Close()` (e.g. with `defer`) to release memory promptly.
|
||||
|
||||
## Documentation
|
||||
|
||||
The full indicator catalogue, guides, quickstarts, and API reference live in the
|
||||
main repository and documentation site:
|
||||
|
||||
- **Repository & full indicator list:** <https://github.com/wickra-lib/wickra>
|
||||
- **Docs** (quickstarts, cookbook, TA-Lib migration): <https://docs.wickra.org>
|
||||
- **Runnable examples:** [`examples/go/`](https://github.com/wickra-lib/wickra/tree/main/examples/go)
|
||||
|
||||
Wickra ships native bindings for Python, Node.js, WebAssembly and Rust, plus a
|
||||
C ABI hub that any C-capable language (C, C++, C#, Go, Java, R) links against —
|
||||
all exposing the same indicators from the shared, `unsafe`-forbidden Rust core.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
Wickra is an indicator toolkit, not a trading system. The values it computes are
|
||||
deterministic transforms of the input data — they are not financial advice and
|
||||
do not predict the market. Any use in a live trading context is at your own risk.
|
||||
The library is provided **as is**, without warranty of any kind.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of [Apache-2.0](https://github.com/wickra-lib/wickra/blob/main/LICENSE-APACHE)
|
||||
or [MIT](https://github.com/wickra-lib/wickra/blob/main/LICENSE-MIT) at your option.
|
||||
@@ -0,0 +1,3 @@
|
||||
module github.com/wickra-lib/wickra/bindings/go
|
||||
|
||||
go 1.23
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
# Prebuilt Wickra C ABI libraries are provisioned locally / in CI, not committed.
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
*.a
|
||||
*.lib
|
||||
*.exp
|
||||
@@ -0,0 +1,25 @@
|
||||
// Package wickra provides idiomatic Go bindings for the Wickra
|
||||
// technical-analysis library over its C ABI hub.
|
||||
//
|
||||
// Each indicator is an opaque-handle type with a New<Indicator> constructor and
|
||||
// Update/Batch/Reset/Close methods. Handles are freed by Close and, as a
|
||||
// backstop, by a finalizer; call Close explicitly to release native memory
|
||||
// promptly. The binding links against the prebuilt Wickra C ABI library
|
||||
// (libwickra.so/.dylib or wickra.dll) staged under ./lib — see the package
|
||||
// README for how to provision it.
|
||||
package wickra
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -I${SRCDIR}/../c/include
|
||||
#cgo linux LDFLAGS: -L${SRCDIR}/lib -lwickra -Wl,-rpath,${SRCDIR}/lib
|
||||
#cgo darwin LDFLAGS: -L${SRCDIR}/lib -lwickra -Wl,-rpath,${SRCDIR}/lib
|
||||
#cgo windows LDFLAGS: -L${SRCDIR}/lib -l:wickra.dll
|
||||
#include "wickra.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import "errors"
|
||||
|
||||
// ErrInvalidParams is returned by a New<Indicator> constructor when the native
|
||||
// constructor rejects the supplied parameters (for example a zero period).
|
||||
var ErrInvalidParams = errors.New("wickra: invalid indicator parameters")
|
||||
@@ -0,0 +1,151 @@
|
||||
package wickra
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// One indicator per FFI archetype, exercising the full New/Update/Batch/Reset/
|
||||
// Close surface against the real native library.
|
||||
|
||||
func TestScalarKnownValue(t *testing.T) {
|
||||
s, err := NewSma(3)
|
||||
if err != nil {
|
||||
t.Fatalf("NewSma: %v", err)
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
var last float64
|
||||
for _, v := range []float64{1, 2, 3, 4, 5} {
|
||||
last = s.Update(v)
|
||||
}
|
||||
if math.Abs(last-4.0) > 1e-9 {
|
||||
t.Fatalf("sma(3) last = %v, want 4.0", last)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScalarBatchMatchesStreaming(t *testing.T) {
|
||||
input := []float64{1, 2, 3, 4, 5, 6, 7, 8}
|
||||
|
||||
stream, _ := NewSma(3)
|
||||
defer stream.Close()
|
||||
want := make([]float64, len(input))
|
||||
for i, v := range input {
|
||||
want[i] = stream.Update(v)
|
||||
}
|
||||
|
||||
batchInd, _ := NewSma(3)
|
||||
defer batchInd.Close()
|
||||
got := batchInd.Batch(input)
|
||||
|
||||
if len(got) != len(want) {
|
||||
t.Fatalf("batch len = %d, want %d", len(got), len(want))
|
||||
}
|
||||
for i := range want {
|
||||
if math.IsNaN(want[i]) && math.IsNaN(got[i]) {
|
||||
continue
|
||||
}
|
||||
if math.Abs(got[i]-want[i]) > 1e-9 {
|
||||
t.Fatalf("batch[%d] = %v, streaming = %v", i, got[i], want[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiOutput(t *testing.T) {
|
||||
m, err := NewMacdIndicator(3, 6, 3)
|
||||
if err != nil {
|
||||
t.Fatalf("NewMacdIndicator: %v", err)
|
||||
}
|
||||
defer m.Close()
|
||||
|
||||
var ok bool
|
||||
var out MacdOutput
|
||||
for i := 0; i < 30; i++ {
|
||||
out, ok = m.Update(100 + float64(i))
|
||||
}
|
||||
if !ok {
|
||||
t.Fatal("macd never produced a value after warmup")
|
||||
}
|
||||
if math.IsNaN(out.Macd) {
|
||||
t.Fatal("macd value is NaN after warmup")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBars(t *testing.T) {
|
||||
rb, err := NewRangeBars(2.0)
|
||||
if err != nil {
|
||||
t.Fatalf("NewRangeBars: %v", err)
|
||||
}
|
||||
defer rb.Close()
|
||||
|
||||
total := 0
|
||||
for _, p := range []float64{100, 101, 103, 104, 99, 96, 102, 108, 95, 110} {
|
||||
bars := rb.Update(p, p, p, p, 1, 0)
|
||||
total += len(bars)
|
||||
}
|
||||
if total == 0 {
|
||||
t.Fatal("range bars produced no bars over a 15-point move")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProfile(t *testing.T) {
|
||||
vp, err := NewVolumeProfile(10, 24)
|
||||
if err != nil {
|
||||
t.Fatalf("NewVolumeProfile: %v", err)
|
||||
}
|
||||
defer vp.Close()
|
||||
|
||||
var ok bool
|
||||
var snap VolumeProfileOutputScalars
|
||||
for i := 0; i < 50; i++ {
|
||||
price := 100 + 5*math.Sin(float64(i)*0.3)
|
||||
snap, ok = vp.Update(price, price+1, price-1, price, 1000, int64(i))
|
||||
}
|
||||
if !ok {
|
||||
t.Fatal("volume profile never produced a snapshot")
|
||||
}
|
||||
if len(snap.Values) == 0 {
|
||||
t.Fatal("volume profile returned an empty values buffer")
|
||||
}
|
||||
}
|
||||
|
||||
func TestArrayInput(t *testing.T) {
|
||||
ob, err := NewOrderBookImbalanceFull()
|
||||
if err != nil {
|
||||
t.Fatalf("NewOrderBookImbalanceFull: %v", err)
|
||||
}
|
||||
defer ob.Close()
|
||||
|
||||
bidPrice := []float64{99.9, 99.8, 99.7}
|
||||
bidSize := []float64{5, 3, 2}
|
||||
askPrice := []float64{100.1, 100.2, 100.3}
|
||||
askSize := []float64{1, 1, 1}
|
||||
v := ob.Update(bidPrice, bidSize, askPrice, askSize)
|
||||
if math.IsNaN(v) {
|
||||
t.Fatal("order-book imbalance is NaN on a populated book")
|
||||
}
|
||||
}
|
||||
|
||||
func TestResetReturnsToWarmup(t *testing.T) {
|
||||
s, _ := NewSma(3)
|
||||
defer s.Close()
|
||||
for _, v := range []float64{1, 2, 3} {
|
||||
s.Update(v)
|
||||
}
|
||||
s.Reset()
|
||||
if got := s.Update(10); !math.IsNaN(got) {
|
||||
t.Fatalf("after reset first update = %v, want NaN (warmup)", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidParams(t *testing.T) {
|
||||
if _, err := NewSma(0); err == nil {
|
||||
t.Fatal("NewSma(0) should return ErrInvalidParams")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloseIsIdempotent(t *testing.T) {
|
||||
s, _ := NewSma(3)
|
||||
s.Close()
|
||||
s.Close() // must not panic or double-free
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
prebuilt native binary, no system dependencies.**
|
||||
|
||||
Wickra is a multi-language technical-analysis library with a Rust core and
|
||||
bindings for Python, Node.js and WebAssembly, plus a C ABI for C/C++, C# and any
|
||||
bindings for Python, Node.js and WebAssembly, plus a C ABI for C/C++, C#, Go and any
|
||||
other C-capable language. Every indicator is an O(1)
|
||||
streaming state machine, so live trading bots and historical backtests share
|
||||
the exact same implementation. This package is the Node.js binding (napi-rs);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
system dependencies, no C build tooling.**
|
||||
|
||||
Wickra is a multi-language technical-analysis library with a Rust core and
|
||||
bindings for Python, Node.js and WebAssembly, plus a C ABI for C/C++, C# and any
|
||||
bindings for Python, Node.js and WebAssembly, plus a C ABI for C/C++, C#, Go and any
|
||||
other C-capable language. Every indicator is an O(1)
|
||||
streaming state machine, so live trading bots and historical backtests share
|
||||
the exact same implementation. This package is the Python binding (PyO3); it
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
wickra-wasm` — pure WebAssembly, runs anywhere a modern JS engine does.**
|
||||
|
||||
Wickra is a multi-language technical-analysis library with a Rust core and
|
||||
bindings for Python, Node.js and WebAssembly, plus a C ABI for C/C++, C# and any
|
||||
bindings for Python, Node.js and WebAssembly, plus a C ABI for C/C++, C#, Go and any
|
||||
other C-capable language. Every indicator is an O(1)
|
||||
streaming state machine, so live trading dashboards and historical backtests
|
||||
share the exact same implementation. This package is the WebAssembly binding
|
||||
|
||||
Reference in New Issue
Block a user