v1.9.6 libamber.so — the dynamic C API seam

The array language
markets run on.

Amber is a small, fast, self-contained engine written in portable C99: columnar, vectorised, in-memory, with the working vocabulary of q/kdb+ — dictionaries, tables and keyed tables, the full join family including a native as-of kernel, qSQL select/by, and column attributes implemented in C that turn search from O(n) into O(log n).

2261×
find, 5M rows, sorted attribute
0.70×
of C -O3 on 10M reductions
287
tests passing
amber — 1-minute VWAP over 5,000,000 trades live
$ git clone https://github.com/BonucciAndrea/amber.git && cd amber && ./a
C99 · gcc + clang/ AVX2 · ARM NEON · scalar/ Apache Arrow C Data Interface/ amberd TCP wire protocol/ AGPLv3
Engine

Built for the shape data actually has

A tick is not a row. It is one value in each of nine columns, and every question you ask of a trading day is a question about columns. Amber is columnar all the way down — storage, evaluation, the wire, and the handoff to Arrow.

Zero-copy memory layout

A column is one contiguous, cache-line-aligned payload. amber_get_vector_ptr() hands you that pointer — not a copy of it. NumPy's base, Arrow's data buffer and the engine's column are the same address, asserted by pointer equality in the test suites.

Native TCP wire protocol

amberd is ~450 lines of C: one engine, many connections, a one-line request and a length-prefixed reply in text, json, jsonc or raw. Small enough to reimplement in an afternoon — which Go, TypeScript and Python clients all have.

Array evaluation, not row loops

Every expression compiles to a flat opcode array and runs on a real stack VM. Vector ops dispatch into AVX2 / NEON kernels and split across cores above 100,000 elements. \disasm decodes the bytecode the compiler actually emitted.

Columnar storage & attributes

All four kdb-style attributes are implemented in C — `s sorted, `u unique, `p parted, `g grouped. Sorted turns ? into a binary search: 1.73 s → 1.4 ms on 2M rows, same answer.

Native as-of join

aj matches each trade to its most recent quote with a branch-free lower_bound over each symbol group's sorted nanosecond slice, off a thread-local 16 MB bump arena that keeps malloc jitter out of the hot path.

Diagnostics that read like Rust

An error[E0104] line, a --> locator, a gutter-aligned source line, ^^^ underlines and a = help: note. One report, never two — and switchable at runtime so a trap loop stays quiet.

Language

q vocabulary, K notation

If you have written q, you already know most of Amber: select … by … from … where …, aj, wj, xbar, wavg, meta, ([]…) table literals and keyed tables all mean what you expect. Bare qSQL works at the prompt with no sel"…" wrapper.

The notation underneath is terse array grammar. Dyadic library functions take brackets (aj[c;x;y]), there is no >= or <=, and symbols cannot contain _ — because _ is a verb.

session.k
/ tables are first-class and render without `show`
t:([]sym:`AAPL`MSFT`AAPL; px:187.3 411.2 187.4; sz:100 250 50)

/ qSQL, typed bare at the prompt
select vwap:wavg[sz;px] by sym from t

/ 1-minute OHLCV bars — the classic tickerplant query
tb:+@[+trades; ,`time; minbar[1]@]
qby[tb; `sym`time;
    `o`h`l`c`v!({first x`px};{max x`px};{min x`px};{last x`px};{sum x`sz})]

/ TAQ: the prevailing quote for every print
m:aj[`sym`time; trades; quotes]

/ sorted attribute => O(log n) lookups
v:asc 2000000?1000000000
`at v                       / `s
v ? 12345 67890             / binary search, ~1244x faster

/ zero-copy Arrow C Data Interface, no libarrow linkage
p:arrow.export t            / (schemaAddr; arrayAddr)
amber-tick

A whole market, generated and stored

amber-tick writes a partitioned, splayed column store the engine reads natively — 38M prints, 89M quotes, ten-level book snapshots and LULD halts across five sessions and 500 names, from a simulator whose realism is checked: twenty-one published stylized facts, all passing on every generated store.

arrivals
discrete-time Hawkes process — bursts, not flat Poisson
quotes first
so a print can never fall outside the NBBO
epoch
nanoseconds since 2000-01-01 — Amber's own, not Unix
files
each column is one -8! value, byte-identical to the engine's
Build a 5M-row store in 60 seconds →
AAPL 187.32 tape 1s · lit + TRF

Simulated tape: mean-reverting tick returns (bid-ask bounce) with clustered arrivals.

Architecture

One engine, three seams

Everything that consumes Amber lives outside the engine repository and reaches it through libamber.so, the in-process ext/ registry, or a TCP socket. The engine gained one build flag, one export map and one header section for all of it.

Amber engine portable C99 · gcc + clang a.c b.c evaluator + VM simd.c parallel.c kernels arena.c 16 MB HFT arena ser.c -8! / -9! binary ar.c Arrow C Data Interface ext.h the extension seam libamber.so ~60 amber_* entry points ext/ registry in-process · runtime verbs amberd :5010 TCP · text json jsonc raw python-amber — NumPy · pandas · Polars amber-arrow — ArrowArrayStream · Flight amber-jupyter — one process, one heap amber-ai — local co-pilot, loopback only grafana-amber — Go backend, columnar vscode-amber — LSP, \parse only amber-tick — tickerplant · RDB · HDB
Benchmarks

Measured against C, and against everyone else

Ten engines, four workloads, one specification. Every answer is an integer that fits float64 exactly, so summation order cannot change it — the harness compares answers bit-exactly against the C reference and prints WRONG instead of a time for any engine that disagrees.

Workload (10M rows unless noted) C -O3AmberAmber qSQL CBQNNumPyDuckDBngn/k
Vector arithmetic + mask8.2254.5665.7330.9127.4722.00254.82
Reductions — sum + max + dot21.9215.4052.464.098.6229.00237.93
Group-by — 100 groups6.4161.45119.6328.8311.5414.00276.51
Inner join — 1M × 1,000 sparse keys0.833.429.851.8011.349.00364.13

Median of 5 timed runs after 2 warm-ups, milliseconds, kernel time only. Amber is reported twice on purpose: array-primitive code is the fair peer of ngn/k and CBQN; the qSQL layer is the fair peer of DuckDB's planner. The gap between the rows is the query layer's overhead, and it is meant to be visible. Full results →

Ecosystem

Nothing here patches the engine

Each satellite reaches Amber through a published seam and nothing else. Delete any of them and the engine is byte-for-byte what it was.

python-amber

libamber.so

The engine in your Python process. pip install amber, then NumPy views that point at the engine's own column buffers — no server, no socket, no serialisation.

Read more →

amber-arrow

libamber.so

An ArrowArrayStream whose batches are windows onto one export, not slices of it. Plus amberd, the TCP query server, and an Arrow Flight daemon.

Read more →

amber-jupyter

python-amber

Amber cells and %%python cells in one process, so a column crosses the boundary as a pointer. HTML grids with type tags, capped at 500 rows on purpose.

Read more →

amber-tick

store + runtime

A US-equity simulator, a splayed partitioned store, TAQ analytics, and a live tickerplant → RDB → HDB pipeline with a replayable sequenced log.

Read more →

grafana-amber

amberd socket

Live dashboards over bare qSQL, column-oriented on the wire. A Go backend, a mage build, and time macros that speak Amber's epoch rather than Unix's.

Read more →

vscode-amber

LSP

qSQL-aware completion where the clause decides the vocabulary, hovers that explain idioms like x@<x, and diagnostics that never evaluate your code.

Read more →

amber-ai

ext/ seam

A schema-aware co-pilot that reads your live tables before it answers, talks only to 127.0.0.1:11434, and cannot hang or break the REPL.

Read more →

Amber Notepad

WebAssembly

The real C interpreter compiled to wasm32 with plain clang — editor, AST and bytecode panes, twelve bundled programs, entirely offline.

Open the scratchpad →

Tutorials

start here

Three end-to-end walkthroughs: a 5-million-row tick store, a Grafana dashboard pipeline, and Arrow record batches into Python without a copy.

Read more →

One command. No install.

Amber compiles from source on first run and installs nothing system-wide. There is no bin/, no QHOME, no dotfile — deleting the folder uninstalls it completely.

bash
git clone https://github.com/BonucciAndrea/amber.git
cd amber
chmod +x a build.sh install.sh
./a                      # builds with -O3, then opens the REPL