23d636fd97
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).
81 lines
1.7 KiB
Go
81 lines
1.7 KiB
Go
// Run SMA(20) batch over a panel of assets, serial vs goroutine fan-out, and
|
|
// report the speedup.
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"runtime"
|
|
"strconv"
|
|
"sync"
|
|
"time"
|
|
|
|
wickra "github.com/wickra-lib/wickra/bindings/go"
|
|
"github.com/wickra-lib/wickra/examples/go/internal/market"
|
|
)
|
|
|
|
func main() {
|
|
assets := argInt(1, 500)
|
|
bars := argInt(2, 20_000)
|
|
|
|
panel := make([][]float64, assets)
|
|
for a := 0; a < assets; a++ {
|
|
panel[a] = market.SyntheticPricesFrom(bars, 50.0+float64(a)*0.1)
|
|
}
|
|
|
|
// Warm up so the comparison is fair.
|
|
if warm, err := wickra.NewSma(20); err == nil {
|
|
warm.Batch(panel[0])
|
|
warm.Close()
|
|
}
|
|
|
|
sink := 0.0
|
|
start := time.Now()
|
|
for a := 0; a < assets; a++ {
|
|
sma, _ := wickra.NewSma(20)
|
|
result := sma.Batch(panel[a])
|
|
sma.Close()
|
|
sink += result[len(result)-1]
|
|
}
|
|
serial := time.Since(start)
|
|
|
|
lasts := make([]float64, assets)
|
|
start = time.Now()
|
|
var wg sync.WaitGroup
|
|
work := make(chan int, assets)
|
|
for w := 0; w < runtime.GOMAXPROCS(0); w++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
for a := range work {
|
|
sma, _ := wickra.NewSma(20)
|
|
result := sma.Batch(panel[a])
|
|
sma.Close()
|
|
lasts[a] = result[len(result)-1]
|
|
}
|
|
}()
|
|
}
|
|
for a := 0; a < assets; a++ {
|
|
work <- a
|
|
}
|
|
close(work)
|
|
wg.Wait()
|
|
parallel := time.Since(start)
|
|
|
|
serialMs := float64(serial.Microseconds()) / 1000.0
|
|
parallelMs := float64(parallel.Microseconds()) / 1000.0
|
|
fmt.Printf("%d assets x %d bars, SMA(20) batch:\n", assets, bars)
|
|
fmt.Printf(" serial %8.1f ms\n", serialMs)
|
|
fmt.Printf(" parallel %8.1f ms (%.1fx speedup)\n", parallelMs, serialMs/max(parallelMs, 1e-9))
|
|
_ = sink
|
|
}
|
|
|
|
func argInt(i, def int) int {
|
|
if len(os.Args) > i {
|
|
if v, err := strconv.Atoi(os.Args[i]); err == nil {
|
|
return v
|
|
}
|
|
}
|
|
return def
|
|
}
|