Skip to content

Quickstart: Java ​

Verified against the Rust reference. Every one of Wickra's 514 indicators is replayed through all 10 languages and checked bit-for-bit against the Rust core's golden fixtures in CI — the math here is provably identical to every other binding (how).

A five-minute tour of the Wickra Java binding — a language shim on the C ABI hub built on the Java Foreign Function & Memory API (Panama). By the end you will have streamed an RSI, run a batch SMA, and read a multi-output MACD, all as idiomatic Java.

Requirements ​

Java 22 or later (the FFM API is final since Java 22; no preview flag). The FFM API is restricted, so pass --enable-native-access=ALL-UNNAMED when you run your application.

Install ​

The binding ships on Maven Central as org.wickra:wickra with the native library prebuilt for every supported platform (Linux, macOS, Windows — x64 and arm64); there is nothing to compile.

Maven:

xml
<dependency>
  <groupId>org.wickra</groupId>
  <artifactId>wickra</artifactId>
  <version>2.0.0</version>
</dependency>

Gradle:

kotlin
implementation("org.wickra:wickra:2.0.0")

The class shape ​

Every indicator is an AutoCloseable class over an opaque native handle, with the same operations as the C ABI underneath:

java
import org.wickra.Sma;

try (Sma sma = new Sma(14)) {      // throws IllegalArgumentException on invalid params
    int w = sma.warmupPeriod();    // updates until ready: 14
    double v = sma.update(42.0);   // NaN while warming up
    boolean ready = sma.isReady(); // false until warmed up
    sma.reset();                   // back to a fresh state
} // freed at the end of the try-with-resources scope

The alt-chart bar builders (RenkoBars, KagiBars, …) have no warmupPeriod / isReady — a candle can complete 0..n bars, so they have no warmup.

update never revisits the history behind the tick. Prefer try-with-resources for deterministic cleanup; a Cleaner also frees the handle once the wrapper becomes unreachable, so a missed close() never leaks permanently.

Streaming and batch side by side ​

java
import org.wickra.Rsi;
import org.wickra.Sma;

// 1. Streaming: feed Wilder's textbook example into RSI(14).
double[] prices = {
    44.34, 44.09, 44.15, 43.61, 44.33, 44.83, 45.10, 45.42,
    45.84, 46.08, 45.89, 46.03, 45.61, 46.28, 46.28, 46.00,
};
try (Rsi rsi = new Rsi(14)) {
    for (int i = 0; i < prices.length; i++) {
        double r = rsi.update(prices[i]);
        if (Double.isFinite(r)) { // NaN during the 15-bar warmup
            System.out.printf("tick %2d  rsi=%.4f%n", i + 1, r);
        }
    }
}

// 2. Batch: one SMA(3) call over five prices writes NaN at warmup.
try (Sma sma = new Sma(3)) {
    double[] out = sma.batch(new double[] {1, 2, 3, 4, 5});
    // out -> { NaN, NaN, 2.0, 3.0, 4.0 }
}

The first RSI value lands on tick 15. Batch output is bit-for-bit identical to feeding the same inputs through update one at a time.

The opt-in fast batch ​

Every single-output batch has batchInto over a caller array and over native MemorySegments, and a batchFast twin in all three forms; MACD, MacdFix and Bollinger Bands have a record-array batchFast. None of them copies: the segments go straight to the C ABI, and the array forms hand their arrays over in place through a downcall linked critical with heap access, so the garbage collector waits for the call:

java
import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;

double[] out = new double[prices.length];
try (Sma sma = new Sma(20)) {
    sma.batchInto(prices, out);                 // exact, into a reused array
}
try (Ema ema = new Ema(20); Arena arena = Arena.ofConfined()) {
    MemorySegment in = arena.allocateFrom(ValueLayout.JAVA_DOUBLE, prices);
    MemorySegment values = arena.allocate(ValueLayout.JAVA_DOUBLE, prices.length);
    ema.batchFastInto(in, values);              // zero-copy
}

The fast batch runs a SIMD kernel where the indicator has one (moving averages, RSI, ATR, MACD and MACDFIX, Bollinger Bands, the Chaikin oscillator, skewness, Pearson correlation and more). The kernel reassociates the arithmetic, so each value agrees with the exact batch to within a few units in the last place rather than bit for bit; NaN placement and length are identical, and the result is the same on every platform. Where an indicator has no kernel, the fast batch is the exact batch. Keep the exact batch wherever you compare against streaming bit for bit; reach for the fast one when throughput is the point. See Streaming vs Batch.

Multi-output indicators ​

Indicators with several outputs (MACD, Bollinger, ADX, …) return a record — null while warming up, a value once one is ready:

java
import org.wickra.MacdIndicator;
import org.wickra.MacdOutput;

try (MacdIndicator macd = new MacdIndicator(12, 26, 9)) {
    for (double price : feed) {
        MacdOutput m = macd.update(price);
        if (m != null) {
            System.out.printf("macd=%.4f signal=%.4f hist=%.4f%n",
                m.macd(), m.signal(), m.histogram());
        }
    }
}

Candle-input indicators (ATR, ADX, OBV, …) take the OHLCV fields plus a timestamp, e.g. atr.update(open, high, low, close, volume, timestamp).

A deeper example ​

examples/java runs a basket of indicators over an OHLCV series; the directory also has a multi-indicator streaming demo, multi-timeframe resampling, a parallel-streams fan-out, three strategy examples, and Binance fetch/live examples. Build the C ABI once, install the binding, then run any of them:

bash
cargo build -p wickra-c --release
mvn -f bindings/java install -DskipTests
mvn -f examples/java exec:exec -Dexec.mainClass=org.wickra.examples.Streaming

See also ​