test: golden-pin the four de-duplicated indicators across all bindings (#305)

* test: golden-pin the four de-duplicated indicators across all C-ABI bindings

Extend gen_golden to emit reference fixtures for AdOscillator (ADOSC),
IntradayIntensity, AwesomeOscillatorHistogram and AverageDrawdown, and replay
them through the Go / C# / Java / R golden harnesses so their corrected
definitions stay bit-identical to the Rust core in every binding. Go suite
verified locally (gcc 13 + cgo): all 9 golden tests pass; C#/Java/R use the
same fixtures and harness pattern (CI-verified). First step of extending the
golden coverage beyond the seven archetype representatives.

* test: golden-pin the scalar-output tranche (308 indicators) against Rust

Extend gen_golden with a generated emit_scalar that writes reference fixtures
for every single-f64-output indicator (scalar / candle / pairwise input) using
valid constructor params, and add a manifest-driven generic Python golden
replay that reconstructs each by its native name and checks it bit-for-bit
against the Rust output. 308 indicators now value-tied to the Rust core in
Python (pytest: 308/308). Takes golden coverage from the 7 archetype
representatives to 308+ of the catalogue.

22 scalar indicators with non-default constructor constraints are skipped by
gen_golden for now (logged), as are non-f64-output ones; multi-output, exotic
inputs and the per-indicator arg arities of the C-ABI/Node replays follow.
Generated + verified locally with the full toolchain.

* test: golden-pin the multi-output tranche (70 indicators) in Python

Add a generated emit_multi to gen_golden (per-indicator Output-field access,
one CSV column per field) and a manifest-driven generic Python replay that
checks every field of each multi-output indicator against the Rust reference.
70 multi-output indicators now value-tied to Rust in Python; combined with the
scalar tranche, 378 indicators are golden-pinned. 8 multi with non-default
param constraints and 5 with non-f64 Output fields (Option/Vec/i64) are
deferred. pytest green.

* test(golden): add 30 constraint-tuned indicators to scalar/multi golden suite

Emit golden fixtures for 22 scalar-output and 8 multi-output indicators
whose constructors need non-default parameters (Alma, Jma, Psar, T3, Mama,
DoubleBollinger, ZigZag, ...). All 408 fixtures replay bit-for-bit through
the Python binding.

* test(golden): cover 36 missed scalar/multi indicators

Add 26 single-output (LinearRegression family, HT cycle, Candle
volatility estimators, DrawdownDuration) and 10 multi-output
(BollingerBands, MACD/MACDEXT/MACDFIX, Camarilla, VWAP bands, ...)
indicators to the golden suite. 444 fixtures replay bit-for-bit
through the Python binding.

* test(golden): cover 50 exotic-input indicators

Add deterministic synthetic feeders for the DerivativesTick (17),
CrossSection (15), Trade (8), TradeQuote (3) and OrderBook (7)
families, derived from the shared OHLCV input series in both
gen_golden and a new Python replay harness (test_golden_exotic).
All 494 fixtures replay bit-for-bit through the Python binding.

* test(golden): complete 514-indicator golden coverage

Add the final tranches: 3 mixed multi-output indicators (Ichimoku,
WilliamsFractals, LeadLagCrossCorrelation), 6 histogram profiles
(time/volume seasonality + TPO/volume price profiles), 10 alt-chart
bar builders and the footprint. Every one of the 514 distinct
indicators now has a Rust-generated g_<Canonical>.csv fixture and a
generic Python replay (scalar/multi/exotic/profile/bars), all passing
bit-for-bit.

* test(golden): add generic Node replay for all 514 indicators

A manifest-driven node:test harness reconstructs every indicator by its
native class, feeds the same synthetic stream derived from the shared
golden input, and checks output bit-for-bit against the Rust reference
fixtures (scalar/multi/exotic/profile/bars). node_manifest.json is
generated from index.d.ts plus the Python-side manifests. 514/514 pass.

* test(golden): add generated Go replay for all 514 indicators

golden_all_test.go (generated by gen_golden_test.py) reconstructs every
Go indicator, feeds the shared synthetic stream and checks output
bit-for-bit against the Rust reference fixtures. A reflection-based
comparator flattens multi-output structs, profiles and bar slices so one
path covers all archetypes. This is the first C-ABI binding verified
across the full catalogue. 514/514 pass.

* test(golden): add generated C# replay for all 514 indicators

GoldenAllTests.g.cs (generated by gen_golden_test.py) reconstructs every
C# indicator, feeds the shared synthetic stream and checks output
bit-for-bit against the Rust reference fixtures via a reflection-based
flatten covering scalar/multi/profile/bar archetypes. 514/514 pass.

Also add the '#nullable enable' directive the compiler requires to the
generated Indicators.g.cs, clearing the four CS8669 warnings on the
nullable double[] profile return types.

* fix(java): marshal C ABI bool params correctly; add 514 golden replay

The Java FFM binding marshalled the cross-section state flags (newHigh,
newLow, aboveMa, onBuySignal) as JAVA_DOUBLE arrays, but the C ABI takes
them as const bool* (one byte each), so the native side read the low byte
of each 8-byte double and saw every flag as false. Add WickraNative.
boolSegment and use it across the 15 cross-section indicators. Also pass
the MacdExt MaType arguments as byte to match the uint8_t downcall
descriptor (was int, throwing WrongMethodTypeException).

Add GoldenAllTest.java (generated by gen_golden_test.py): a reflection
runner replaying all 514 indicators against the Rust reference fixtures.
The bugs above were found by this test; 514/514 now pass.

* fix(r): marshal C ABI bool flags correctly; add 514 golden replay

The R wrapper passed the cross-section state flags as (bool *)REAL(x),
reinterpreting the 8-byte doubles as 1-byte bools so the native side read
every flag as false. Add wk_bool_vec to convert each flag vector into a
real C bool buffer and use it for all 15 cross-section update wrappers.

Add test-golden-all.R + generated golden_specs.R: a reflective runner
replaying all 514 indicators against the Rust reference fixtures. The bug
above was found by this test; verified 514/514 pass locally.

* test(golden): add WASM replay for all 514 indicators

A manifest-driven node:test harness loads the nodejs-target wasm-pack
build, reconstructs every indicator by its JS class, feeds the shared
synthetic stream and checks output bit-for-bit against the Rust
reference fixtures. wasm_manifest.json is generated from the wasm .d.ts
plus the shared manifests; a recursive flattener covers scalar, multi
(Reflect objects), profile and bar shapes. 514/514 pass locally
(wasm-pack build --target nodejs, then node --test).

* test(golden): add C and C++ replay for all 514 indicators

golden_test.c (generated by gen_golden_test.py) drives every indicator
through the C ABI (wickra.h) and checks output bit-for-bit against the
Rust reference fixtures. golden_test.cpp #includes the same source so the
identical runner is compiled and run under both gcc (C) and g++ (C++) via
the CMake targets golden_test / golden_test_cpp — proving the extern "C"
header is consumable from each language. Both 514/514 (verified via ctest).

* test(golden): gofmt the generated Go golden replay

* test(golden): make the Node fixture reader CRLF-safe and pin fixtures to LF
This commit is contained in:
kingchenc
2026-06-15 04:48:51 +02:00
committed by GitHub
parent de1112ea91
commit 4f708d410d
573 changed files with 102337 additions and 77 deletions
@@ -47,10 +47,10 @@ public final class AbsoluteBreadthIndex implements AutoCloseable {
try (Arena a = Arena.ofConfined()) {
MemorySegment changeSeg = a.allocateFrom(JAVA_DOUBLE, change);
MemorySegment volumeSeg = a.allocateFrom(JAVA_DOUBLE, volume);
MemorySegment newHighSeg = a.allocateFrom(JAVA_DOUBLE, newHigh);
MemorySegment newLowSeg = a.allocateFrom(JAVA_DOUBLE, newLow);
MemorySegment aboveMaSeg = a.allocateFrom(JAVA_DOUBLE, aboveMa);
MemorySegment onBuySignalSeg = a.allocateFrom(JAVA_DOUBLE, onBuySignal);
MemorySegment newHighSeg = WickraNative.boolSegment(a, newHigh);
MemorySegment newLowSeg = WickraNative.boolSegment(a, newLow);
MemorySegment aboveMaSeg = WickraNative.boolSegment(a, aboveMa);
MemorySegment onBuySignalSeg = WickraNative.boolSegment(a, onBuySignal);
return (double) NativeMethods.WICKRA_ABSOLUTE_BREADTH_INDEX_UPDATE.invokeExact(handle, changeSeg, volumeSeg, newHighSeg, newLowSeg, aboveMaSeg, onBuySignalSeg, (long) change.length, timestamp);
} catch (Throwable t) {
throw WickraNative.rethrow(t);
@@ -47,10 +47,10 @@ public final class AdVolumeLine implements AutoCloseable {
try (Arena a = Arena.ofConfined()) {
MemorySegment changeSeg = a.allocateFrom(JAVA_DOUBLE, change);
MemorySegment volumeSeg = a.allocateFrom(JAVA_DOUBLE, volume);
MemorySegment newHighSeg = a.allocateFrom(JAVA_DOUBLE, newHigh);
MemorySegment newLowSeg = a.allocateFrom(JAVA_DOUBLE, newLow);
MemorySegment aboveMaSeg = a.allocateFrom(JAVA_DOUBLE, aboveMa);
MemorySegment onBuySignalSeg = a.allocateFrom(JAVA_DOUBLE, onBuySignal);
MemorySegment newHighSeg = WickraNative.boolSegment(a, newHigh);
MemorySegment newLowSeg = WickraNative.boolSegment(a, newLow);
MemorySegment aboveMaSeg = WickraNative.boolSegment(a, aboveMa);
MemorySegment onBuySignalSeg = WickraNative.boolSegment(a, onBuySignal);
return (double) NativeMethods.WICKRA_AD_VOLUME_LINE_UPDATE.invokeExact(handle, changeSeg, volumeSeg, newHighSeg, newLowSeg, aboveMaSeg, onBuySignalSeg, (long) change.length, timestamp);
} catch (Throwable t) {
throw WickraNative.rethrow(t);
@@ -47,10 +47,10 @@ public final class AdvanceDecline implements AutoCloseable {
try (Arena a = Arena.ofConfined()) {
MemorySegment changeSeg = a.allocateFrom(JAVA_DOUBLE, change);
MemorySegment volumeSeg = a.allocateFrom(JAVA_DOUBLE, volume);
MemorySegment newHighSeg = a.allocateFrom(JAVA_DOUBLE, newHigh);
MemorySegment newLowSeg = a.allocateFrom(JAVA_DOUBLE, newLow);
MemorySegment aboveMaSeg = a.allocateFrom(JAVA_DOUBLE, aboveMa);
MemorySegment onBuySignalSeg = a.allocateFrom(JAVA_DOUBLE, onBuySignal);
MemorySegment newHighSeg = WickraNative.boolSegment(a, newHigh);
MemorySegment newLowSeg = WickraNative.boolSegment(a, newLow);
MemorySegment aboveMaSeg = WickraNative.boolSegment(a, aboveMa);
MemorySegment onBuySignalSeg = WickraNative.boolSegment(a, onBuySignal);
return (double) NativeMethods.WICKRA_ADVANCE_DECLINE_UPDATE.invokeExact(handle, changeSeg, volumeSeg, newHighSeg, newLowSeg, aboveMaSeg, onBuySignalSeg, (long) change.length, timestamp);
} catch (Throwable t) {
throw WickraNative.rethrow(t);
@@ -47,10 +47,10 @@ public final class AdvanceDeclineRatio implements AutoCloseable {
try (Arena a = Arena.ofConfined()) {
MemorySegment changeSeg = a.allocateFrom(JAVA_DOUBLE, change);
MemorySegment volumeSeg = a.allocateFrom(JAVA_DOUBLE, volume);
MemorySegment newHighSeg = a.allocateFrom(JAVA_DOUBLE, newHigh);
MemorySegment newLowSeg = a.allocateFrom(JAVA_DOUBLE, newLow);
MemorySegment aboveMaSeg = a.allocateFrom(JAVA_DOUBLE, aboveMa);
MemorySegment onBuySignalSeg = a.allocateFrom(JAVA_DOUBLE, onBuySignal);
MemorySegment newHighSeg = WickraNative.boolSegment(a, newHigh);
MemorySegment newLowSeg = WickraNative.boolSegment(a, newLow);
MemorySegment aboveMaSeg = WickraNative.boolSegment(a, aboveMa);
MemorySegment onBuySignalSeg = WickraNative.boolSegment(a, onBuySignal);
return (double) NativeMethods.WICKRA_ADVANCE_DECLINE_RATIO_UPDATE.invokeExact(handle, changeSeg, volumeSeg, newHighSeg, newLowSeg, aboveMaSeg, onBuySignalSeg, (long) change.length, timestamp);
} catch (Throwable t) {
throw WickraNative.rethrow(t);
@@ -50,10 +50,10 @@ public final class BreadthThrust implements AutoCloseable {
try (Arena a = Arena.ofConfined()) {
MemorySegment changeSeg = a.allocateFrom(JAVA_DOUBLE, change);
MemorySegment volumeSeg = a.allocateFrom(JAVA_DOUBLE, volume);
MemorySegment newHighSeg = a.allocateFrom(JAVA_DOUBLE, newHigh);
MemorySegment newLowSeg = a.allocateFrom(JAVA_DOUBLE, newLow);
MemorySegment aboveMaSeg = a.allocateFrom(JAVA_DOUBLE, aboveMa);
MemorySegment onBuySignalSeg = a.allocateFrom(JAVA_DOUBLE, onBuySignal);
MemorySegment newHighSeg = WickraNative.boolSegment(a, newHigh);
MemorySegment newLowSeg = WickraNative.boolSegment(a, newLow);
MemorySegment aboveMaSeg = WickraNative.boolSegment(a, aboveMa);
MemorySegment onBuySignalSeg = WickraNative.boolSegment(a, onBuySignal);
return (double) NativeMethods.WICKRA_BREADTH_THRUST_UPDATE.invokeExact(handle, changeSeg, volumeSeg, newHighSeg, newLowSeg, aboveMaSeg, onBuySignalSeg, (long) change.length, timestamp);
} catch (Throwable t) {
throw WickraNative.rethrow(t);
@@ -47,10 +47,10 @@ public final class BullishPercentIndex implements AutoCloseable {
try (Arena a = Arena.ofConfined()) {
MemorySegment changeSeg = a.allocateFrom(JAVA_DOUBLE, change);
MemorySegment volumeSeg = a.allocateFrom(JAVA_DOUBLE, volume);
MemorySegment newHighSeg = a.allocateFrom(JAVA_DOUBLE, newHigh);
MemorySegment newLowSeg = a.allocateFrom(JAVA_DOUBLE, newLow);
MemorySegment aboveMaSeg = a.allocateFrom(JAVA_DOUBLE, aboveMa);
MemorySegment onBuySignalSeg = a.allocateFrom(JAVA_DOUBLE, onBuySignal);
MemorySegment newHighSeg = WickraNative.boolSegment(a, newHigh);
MemorySegment newLowSeg = WickraNative.boolSegment(a, newLow);
MemorySegment aboveMaSeg = WickraNative.boolSegment(a, aboveMa);
MemorySegment onBuySignalSeg = WickraNative.boolSegment(a, onBuySignal);
return (double) NativeMethods.WICKRA_BULLISH_PERCENT_INDEX_UPDATE.invokeExact(handle, changeSeg, volumeSeg, newHighSeg, newLowSeg, aboveMaSeg, onBuySignalSeg, (long) change.length, timestamp);
} catch (Throwable t) {
throw WickraNative.rethrow(t);
@@ -47,10 +47,10 @@ public final class CumulativeVolumeIndex implements AutoCloseable {
try (Arena a = Arena.ofConfined()) {
MemorySegment changeSeg = a.allocateFrom(JAVA_DOUBLE, change);
MemorySegment volumeSeg = a.allocateFrom(JAVA_DOUBLE, volume);
MemorySegment newHighSeg = a.allocateFrom(JAVA_DOUBLE, newHigh);
MemorySegment newLowSeg = a.allocateFrom(JAVA_DOUBLE, newLow);
MemorySegment aboveMaSeg = a.allocateFrom(JAVA_DOUBLE, aboveMa);
MemorySegment onBuySignalSeg = a.allocateFrom(JAVA_DOUBLE, onBuySignal);
MemorySegment newHighSeg = WickraNative.boolSegment(a, newHigh);
MemorySegment newLowSeg = WickraNative.boolSegment(a, newLow);
MemorySegment aboveMaSeg = WickraNative.boolSegment(a, aboveMa);
MemorySegment onBuySignalSeg = WickraNative.boolSegment(a, onBuySignal);
return (double) NativeMethods.WICKRA_CUMULATIVE_VOLUME_INDEX_UPDATE.invokeExact(handle, changeSeg, volumeSeg, newHighSeg, newLowSeg, aboveMaSeg, onBuySignalSeg, (long) change.length, timestamp);
} catch (Throwable t) {
throw WickraNative.rethrow(t);
@@ -50,10 +50,10 @@ public final class HighLowIndex implements AutoCloseable {
try (Arena a = Arena.ofConfined()) {
MemorySegment changeSeg = a.allocateFrom(JAVA_DOUBLE, change);
MemorySegment volumeSeg = a.allocateFrom(JAVA_DOUBLE, volume);
MemorySegment newHighSeg = a.allocateFrom(JAVA_DOUBLE, newHigh);
MemorySegment newLowSeg = a.allocateFrom(JAVA_DOUBLE, newLow);
MemorySegment aboveMaSeg = a.allocateFrom(JAVA_DOUBLE, aboveMa);
MemorySegment onBuySignalSeg = a.allocateFrom(JAVA_DOUBLE, onBuySignal);
MemorySegment newHighSeg = WickraNative.boolSegment(a, newHigh);
MemorySegment newLowSeg = WickraNative.boolSegment(a, newLow);
MemorySegment aboveMaSeg = WickraNative.boolSegment(a, aboveMa);
MemorySegment onBuySignalSeg = WickraNative.boolSegment(a, onBuySignal);
return (double) NativeMethods.WICKRA_HIGH_LOW_INDEX_UPDATE.invokeExact(handle, changeSeg, volumeSeg, newHighSeg, newLowSeg, aboveMaSeg, onBuySignalSeg, (long) change.length, timestamp);
} catch (Throwable t) {
throw WickraNative.rethrow(t);
@@ -25,7 +25,7 @@ public final class MacdExt implements AutoCloseable {
}
MemorySegment h;
try {
h = (MemorySegment) NativeMethods.WICKRA_MACD_EXT_NEW.invokeExact((long) fast, fastType, (long) slow, slowType, (long) signal, signalType);
h = (MemorySegment) NativeMethods.WICKRA_MACD_EXT_NEW.invokeExact((long) fast, (byte) fastType, (long) slow, (byte) slowType, (long) signal, (byte) signalType);
} catch (Throwable t) {
throw WickraNative.rethrow(t);
}
@@ -47,10 +47,10 @@ public final class McClellanOscillator implements AutoCloseable {
try (Arena a = Arena.ofConfined()) {
MemorySegment changeSeg = a.allocateFrom(JAVA_DOUBLE, change);
MemorySegment volumeSeg = a.allocateFrom(JAVA_DOUBLE, volume);
MemorySegment newHighSeg = a.allocateFrom(JAVA_DOUBLE, newHigh);
MemorySegment newLowSeg = a.allocateFrom(JAVA_DOUBLE, newLow);
MemorySegment aboveMaSeg = a.allocateFrom(JAVA_DOUBLE, aboveMa);
MemorySegment onBuySignalSeg = a.allocateFrom(JAVA_DOUBLE, onBuySignal);
MemorySegment newHighSeg = WickraNative.boolSegment(a, newHigh);
MemorySegment newLowSeg = WickraNative.boolSegment(a, newLow);
MemorySegment aboveMaSeg = WickraNative.boolSegment(a, aboveMa);
MemorySegment onBuySignalSeg = WickraNative.boolSegment(a, onBuySignal);
return (double) NativeMethods.WICKRA_MC_CLELLAN_OSCILLATOR_UPDATE.invokeExact(handle, changeSeg, volumeSeg, newHighSeg, newLowSeg, aboveMaSeg, onBuySignalSeg, (long) change.length, timestamp);
} catch (Throwable t) {
throw WickraNative.rethrow(t);
@@ -47,10 +47,10 @@ public final class McClellanSummationIndex implements AutoCloseable {
try (Arena a = Arena.ofConfined()) {
MemorySegment changeSeg = a.allocateFrom(JAVA_DOUBLE, change);
MemorySegment volumeSeg = a.allocateFrom(JAVA_DOUBLE, volume);
MemorySegment newHighSeg = a.allocateFrom(JAVA_DOUBLE, newHigh);
MemorySegment newLowSeg = a.allocateFrom(JAVA_DOUBLE, newLow);
MemorySegment aboveMaSeg = a.allocateFrom(JAVA_DOUBLE, aboveMa);
MemorySegment onBuySignalSeg = a.allocateFrom(JAVA_DOUBLE, onBuySignal);
MemorySegment newHighSeg = WickraNative.boolSegment(a, newHigh);
MemorySegment newLowSeg = WickraNative.boolSegment(a, newLow);
MemorySegment aboveMaSeg = WickraNative.boolSegment(a, aboveMa);
MemorySegment onBuySignalSeg = WickraNative.boolSegment(a, onBuySignal);
return (double) NativeMethods.WICKRA_MC_CLELLAN_SUMMATION_INDEX_UPDATE.invokeExact(handle, changeSeg, volumeSeg, newHighSeg, newLowSeg, aboveMaSeg, onBuySignalSeg, (long) change.length, timestamp);
} catch (Throwable t) {
throw WickraNative.rethrow(t);
@@ -47,10 +47,10 @@ public final class NewHighsNewLows implements AutoCloseable {
try (Arena a = Arena.ofConfined()) {
MemorySegment changeSeg = a.allocateFrom(JAVA_DOUBLE, change);
MemorySegment volumeSeg = a.allocateFrom(JAVA_DOUBLE, volume);
MemorySegment newHighSeg = a.allocateFrom(JAVA_DOUBLE, newHigh);
MemorySegment newLowSeg = a.allocateFrom(JAVA_DOUBLE, newLow);
MemorySegment aboveMaSeg = a.allocateFrom(JAVA_DOUBLE, aboveMa);
MemorySegment onBuySignalSeg = a.allocateFrom(JAVA_DOUBLE, onBuySignal);
MemorySegment newHighSeg = WickraNative.boolSegment(a, newHigh);
MemorySegment newLowSeg = WickraNative.boolSegment(a, newLow);
MemorySegment aboveMaSeg = WickraNative.boolSegment(a, aboveMa);
MemorySegment onBuySignalSeg = WickraNative.boolSegment(a, onBuySignal);
return (double) NativeMethods.WICKRA_NEW_HIGHS_NEW_LOWS_UPDATE.invokeExact(handle, changeSeg, volumeSeg, newHighSeg, newLowSeg, aboveMaSeg, onBuySignalSeg, (long) change.length, timestamp);
} catch (Throwable t) {
throw WickraNative.rethrow(t);
@@ -47,10 +47,10 @@ public final class PercentAboveMa implements AutoCloseable {
try (Arena a = Arena.ofConfined()) {
MemorySegment changeSeg = a.allocateFrom(JAVA_DOUBLE, change);
MemorySegment volumeSeg = a.allocateFrom(JAVA_DOUBLE, volume);
MemorySegment newHighSeg = a.allocateFrom(JAVA_DOUBLE, newHigh);
MemorySegment newLowSeg = a.allocateFrom(JAVA_DOUBLE, newLow);
MemorySegment aboveMaSeg = a.allocateFrom(JAVA_DOUBLE, aboveMa);
MemorySegment onBuySignalSeg = a.allocateFrom(JAVA_DOUBLE, onBuySignal);
MemorySegment newHighSeg = WickraNative.boolSegment(a, newHigh);
MemorySegment newLowSeg = WickraNative.boolSegment(a, newLow);
MemorySegment aboveMaSeg = WickraNative.boolSegment(a, aboveMa);
MemorySegment onBuySignalSeg = WickraNative.boolSegment(a, onBuySignal);
return (double) NativeMethods.WICKRA_PERCENT_ABOVE_MA_UPDATE.invokeExact(handle, changeSeg, volumeSeg, newHighSeg, newLowSeg, aboveMaSeg, onBuySignalSeg, (long) change.length, timestamp);
} catch (Throwable t) {
throw WickraNative.rethrow(t);
@@ -47,10 +47,10 @@ public final class TickIndex implements AutoCloseable {
try (Arena a = Arena.ofConfined()) {
MemorySegment changeSeg = a.allocateFrom(JAVA_DOUBLE, change);
MemorySegment volumeSeg = a.allocateFrom(JAVA_DOUBLE, volume);
MemorySegment newHighSeg = a.allocateFrom(JAVA_DOUBLE, newHigh);
MemorySegment newLowSeg = a.allocateFrom(JAVA_DOUBLE, newLow);
MemorySegment aboveMaSeg = a.allocateFrom(JAVA_DOUBLE, aboveMa);
MemorySegment onBuySignalSeg = a.allocateFrom(JAVA_DOUBLE, onBuySignal);
MemorySegment newHighSeg = WickraNative.boolSegment(a, newHigh);
MemorySegment newLowSeg = WickraNative.boolSegment(a, newLow);
MemorySegment aboveMaSeg = WickraNative.boolSegment(a, aboveMa);
MemorySegment onBuySignalSeg = WickraNative.boolSegment(a, onBuySignal);
return (double) NativeMethods.WICKRA_TICK_INDEX_UPDATE.invokeExact(handle, changeSeg, volumeSeg, newHighSeg, newLowSeg, aboveMaSeg, onBuySignalSeg, (long) change.length, timestamp);
} catch (Throwable t) {
throw WickraNative.rethrow(t);
@@ -47,10 +47,10 @@ public final class Trin implements AutoCloseable {
try (Arena a = Arena.ofConfined()) {
MemorySegment changeSeg = a.allocateFrom(JAVA_DOUBLE, change);
MemorySegment volumeSeg = a.allocateFrom(JAVA_DOUBLE, volume);
MemorySegment newHighSeg = a.allocateFrom(JAVA_DOUBLE, newHigh);
MemorySegment newLowSeg = a.allocateFrom(JAVA_DOUBLE, newLow);
MemorySegment aboveMaSeg = a.allocateFrom(JAVA_DOUBLE, aboveMa);
MemorySegment onBuySignalSeg = a.allocateFrom(JAVA_DOUBLE, onBuySignal);
MemorySegment newHighSeg = WickraNative.boolSegment(a, newHigh);
MemorySegment newLowSeg = WickraNative.boolSegment(a, newLow);
MemorySegment aboveMaSeg = WickraNative.boolSegment(a, aboveMa);
MemorySegment onBuySignalSeg = WickraNative.boolSegment(a, onBuySignal);
return (double) NativeMethods.WICKRA_TRIN_UPDATE.invokeExact(handle, changeSeg, volumeSeg, newHighSeg, newLowSeg, aboveMaSeg, onBuySignalSeg, (long) change.length, timestamp);
} catch (Throwable t) {
throw WickraNative.rethrow(t);
@@ -47,10 +47,10 @@ public final class UpDownVolumeRatio implements AutoCloseable {
try (Arena a = Arena.ofConfined()) {
MemorySegment changeSeg = a.allocateFrom(JAVA_DOUBLE, change);
MemorySegment volumeSeg = a.allocateFrom(JAVA_DOUBLE, volume);
MemorySegment newHighSeg = a.allocateFrom(JAVA_DOUBLE, newHigh);
MemorySegment newLowSeg = a.allocateFrom(JAVA_DOUBLE, newLow);
MemorySegment aboveMaSeg = a.allocateFrom(JAVA_DOUBLE, aboveMa);
MemorySegment onBuySignalSeg = a.allocateFrom(JAVA_DOUBLE, onBuySignal);
MemorySegment newHighSeg = WickraNative.boolSegment(a, newHigh);
MemorySegment newLowSeg = WickraNative.boolSegment(a, newLow);
MemorySegment aboveMaSeg = WickraNative.boolSegment(a, aboveMa);
MemorySegment onBuySignalSeg = WickraNative.boolSegment(a, onBuySignal);
return (double) NativeMethods.WICKRA_UP_DOWN_VOLUME_RATIO_UPDATE.invokeExact(handle, changeSeg, volumeSeg, newHighSeg, newLowSeg, aboveMaSeg, onBuySignalSeg, (long) change.length, timestamp);
} catch (Throwable t) {
throw WickraNative.rethrow(t);
@@ -58,6 +58,20 @@ public final class WickraNative {
return CLEANER.register(owner, new FreeAction(handle, free));
}
/**
* Allocate a C {@code bool*} buffer (one byte per element) from flag values
* supplied as doubles, treating any non-zero value as {@code true}. The C
* ABI takes the cross-section state flags as {@code const bool*}, so they
* must be one byte each rather than eight-byte doubles.
*/
public static MemorySegment boolSegment(Arena arena, double[] flags) {
byte[] bytes = new byte[flags.length];
for (int i = 0; i < flags.length; i++) {
bytes[i] = (byte) (flags[i] != 0.0 ? 1 : 0);
}
return arena.allocateFrom(java.lang.foreign.ValueLayout.JAVA_BYTE, bytes);
}
/** Re-throw a {@link MethodHandle#invokeExact} {@link Throwable} as an unchecked exception. */
public static RuntimeException rethrow(Throwable t) {
if (t instanceof RuntimeException re) {
@@ -0,0 +1,759 @@
// Code generated by gen_golden_test.py. DO NOT EDIT.
package org.wickra;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.RecordComponent;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
/**
* Reflection-driven value parity for the whole 514-indicator catalogue: each
* indicator is reconstructed by its class name, fed the synthetic stream derived
* from the shared golden input (identical to gen_golden's Rust construction) and
* checked bit-for-bit against testdata/golden/g_&lt;Canonical&gt;.csv. One runner
* flattens scalar, multi-output records, profiles and bar arrays by reflection.
*/
class GoldenAllTest {
private static final double TOL = 1e-6;
private record Spec(String canonical, String arch, double[] params, int width) {}
private static final Spec[] SPECS = {
new Spec("AbandonedBaby", "scalar_candle", new double[]{}, 0),
new Spec("Abcd", "scalar_candle", new double[]{}, 0),
new Spec("AbsoluteBreadthIndex", "cross", new double[]{}, 0),
new Spec("AccelerationBands", "multi_candle", new double[]{14.0, 2.0}, 3),
new Spec("AcceleratorOscillator", "scalar_candle", new double[]{3.0, 7.0, 14.0}, 0),
new Spec("AdOscillator", "scalar_candle", new double[]{}, 0),
new Spec("AdVolumeLine", "cross", new double[]{}, 0),
new Spec("AdaptiveCci", "scalar_candle", new double[]{14.0}, 0),
new Spec("AdaptiveCycle", "scalar_f64", new double[]{}, 0),
new Spec("AdaptiveLaguerreFilter", "scalar_f64", new double[]{20.0}, 0),
new Spec("AdaptiveRsi", "scalar_f64", new double[]{14.0}, 0),
new Spec("Adl", "scalar_candle", new double[]{}, 0),
new Spec("AdvanceBlock", "scalar_candle", new double[]{}, 0),
new Spec("AdvanceDecline", "cross", new double[]{}, 0),
new Spec("AdvanceDeclineRatio", "cross", new double[]{}, 0),
new Spec("Adx", "multi_candle", new double[]{14.0}, 3),
new Spec("Adxr", "scalar_candle", new double[]{14.0}, 0),
new Spec("Alligator", "multi_candle", new double[]{3.0, 7.0, 14.0}, 3),
new Spec("Alma", "scalar_f64", new double[]{9.0, 0.85, 6.0}, 0),
new Spec("Alpha", "pairwise", new double[]{14.0, 2.0}, 0),
new Spec("AmihudIlliquidity", "trade", new double[]{20.0}, 0),
new Spec("AnchoredRsi", "scalar_f64", new double[]{}, 0),
new Spec("AnchoredVwap", "scalar_candle", new double[]{}, 0),
new Spec("AndrewsPitchfork", "multi_candle", new double[]{14.0}, 3),
new Spec("Apo", "scalar_f64", new double[]{3.0, 7.0}, 0),
new Spec("Aroon", "multi_candle", new double[]{14.0}, 2),
new Spec("AroonOscillator", "scalar_candle", new double[]{14.0}, 0),
new Spec("Atr", "scalar_candle", new double[]{14.0}, 0),
new Spec("AtrBands", "multi_candle", new double[]{14.0, 2.0}, 3),
new Spec("AtrRatchet", "multi_candle", new double[]{14.0, 2.0, 0.5}, 2),
new Spec("AtrTrailingStop", "scalar_candle", new double[]{14.0, 2.0}, 0),
new Spec("AutoFib", "multi_candle", new double[]{}, 7),
new Spec("Autocorrelation", "scalar_f64", new double[]{10.0, 1.0}, 0),
new Spec("AutocorrelationPeriodogram", "scalar_f64", new double[]{10.0, 48.0}, 0),
new Spec("AverageDailyRange", "scalar_candle", new double[]{14.0, 0.0}, 0),
new Spec("AverageDrawdown", "scalar_f64", new double[]{14.0}, 0),
new Spec("AvgPrice", "scalar_candle", new double[]{}, 0),
new Spec("AwesomeOscillator", "scalar_candle", new double[]{3.0, 7.0}, 0),
new Spec("AwesomeOscillatorHistogram", "scalar_candle", new double[]{3.0, 7.0, 14.0}, 0),
new Spec("BalanceOfPower", "scalar_candle", new double[]{}, 0),
new Spec("BandpassFilter", "scalar_f64", new double[]{20.0, 0.3}, 0),
new Spec("Bat", "scalar_candle", new double[]{}, 0),
new Spec("BeltHold", "scalar_candle", new double[]{}, 0),
new Spec("Beta", "pairwise", new double[]{14.0}, 0),
new Spec("BetaNeutralSpread", "pairwise", new double[]{14.0}, 0),
new Spec("BetterVolume", "scalar_candle", new double[]{14.0}, 0),
new Spec("BipowerVariation", "scalar_f64", new double[]{14.0}, 0),
new Spec("BodySizePct", "scalar_candle", new double[]{}, 0),
new Spec("BollingerBands", "multi_f64", new double[]{20.0, 2.0}, 4),
new Spec("BollingerBandwidth", "scalar_f64", new double[]{14.0, 2.0}, 0),
new Spec("BomarBands", "multi_f64", new double[]{4.0, 0.85}, 3),
new Spec("BreadthThrust", "cross", new double[]{10.0}, 0),
new Spec("Breakaway", "scalar_candle", new double[]{}, 0),
new Spec("BullishPercentIndex", "cross", new double[]{}, 0),
new Spec("BurkeRatio", "scalar_f64", new double[]{14.0}, 0),
new Spec("Butterfly", "scalar_candle", new double[]{}, 0),
new Spec("CalendarSpread", "deriv", new double[]{}, 0),
new Spec("CalmarRatio", "scalar_f64", new double[]{14.0}, 0),
new Spec("Camarilla", "multi_candle", new double[]{}, 9),
new Spec("CandleVolume", "multi_candle", new double[]{14.0}, 2),
new Spec("Cci", "scalar_candle", new double[]{14.0}, 0),
new Spec("CenterOfGravity", "scalar_f64", new double[]{14.0}, 0),
new Spec("CentralPivotRange", "multi_candle", new double[]{}, 3),
new Spec("Cfo", "scalar_f64", new double[]{14.0}, 0),
new Spec("ChaikinMoneyFlow", "scalar_candle", new double[]{20.0}, 0),
new Spec("ChaikinOscillator", "scalar_candle", new double[]{3.0, 7.0}, 0),
new Spec("ChaikinVolatility", "scalar_candle", new double[]{3.0, 7.0}, 0),
new Spec("ChandeKrollStop", "multi_candle", new double[]{3.0, 2.0, 7.0}, 2),
new Spec("ChandelierExit", "multi_candle", new double[]{14.0, 2.0}, 2),
new Spec("ChoppinessIndex", "scalar_candle", new double[]{14.0}, 0),
new Spec("ClassicPivots", "multi_candle", new double[]{}, 7),
new Spec("CloseVsOpen", "scalar_candle", new double[]{}, 0),
new Spec("ClosingMarubozu", "scalar_candle", new double[]{}, 0),
new Spec("Cmo", "scalar_f64", new double[]{14.0}, 0),
new Spec("CoefficientOfVariation", "scalar_f64", new double[]{14.0}, 0),
new Spec("Cointegration", "multi_pairwise", new double[]{40.0, 1.0}, 3),
new Spec("CommonSenseRatio", "scalar_f64", new double[]{14.0}, 0),
new Spec("CompositeProfile", "multi_candle", new double[]{20.0, 24.0, 0.7}, 3),
new Spec("ConcealingBabySwallow", "scalar_candle", new double[]{}, 0),
new Spec("ConditionalValueAtRisk", "scalar_f64", new double[]{20.0, 0.95}, 0),
new Spec("ConnorsRsi", "scalar_f64", new double[]{3.0, 7.0, 14.0}, 0),
new Spec("Coppock", "scalar_f64", new double[]{3.0, 7.0, 14.0}, 0),
new Spec("CorrelationTrendIndicator", "scalar_f64", new double[]{14.0}, 0),
new Spec("Counterattack", "scalar_candle", new double[]{}, 0),
new Spec("Crab", "scalar_candle", new double[]{}, 0),
new Spec("CumulativeVolumeDelta", "trade", new double[]{}, 0),
new Spec("CumulativeVolumeIndex", "cross", new double[]{}, 0),
new Spec("CupAndHandle", "scalar_candle", new double[]{}, 0),
new Spec("CyberneticCycle", "scalar_f64", new double[]{14.0}, 0),
new Spec("Cypher", "scalar_candle", new double[]{}, 0),
new Spec("DayOfWeekProfile", "profile_bins", new double[]{0.0}, 7),
new Spec("Decycler", "scalar_f64", new double[]{14.0}, 0),
new Spec("DecyclerOscillator", "scalar_f64", new double[]{3.0, 7.0}, 0),
new Spec("Dema", "scalar_f64", new double[]{14.0}, 0),
new Spec("DemandIndex", "scalar_candle", new double[]{14.0}, 0),
new Spec("DemarkPivots", "multi_candle", new double[]{}, 3),
new Spec("DepthSlope", "ob", new double[]{}, 0),
new Spec("DerivativeOscillator", "scalar_f64", new double[]{3.0, 7.0, 14.0, 28.0}, 0),
new Spec("DetrendedStdDev", "scalar_f64", new double[]{14.0}, 0),
new Spec("DisparityIndex", "scalar_f64", new double[]{14.0}, 0),
new Spec("DistanceSsd", "pairwise", new double[]{14.0}, 0),
new Spec("Doji", "scalar_candle", new double[]{}, 0),
new Spec("DojiStar", "scalar_candle", new double[]{}, 0),
new Spec("DollarBars", "bars_candle5", new double[]{50000.0}, 0),
new Spec("Donchian", "multi_candle", new double[]{14.0}, 3),
new Spec("DonchianStop", "multi_candle", new double[]{14.0}, 2),
new Spec("DoubleBollinger", "multi_f64", new double[]{20.0, 1.0, 2.0}, 5),
new Spec("DoubleTopBottom", "scalar_candle", new double[]{}, 0),
new Spec("DownsideGapThreeMethods", "scalar_candle", new double[]{}, 0),
new Spec("Dpo", "scalar_f64", new double[]{14.0}, 0),
new Spec("DragonflyDoji", "scalar_candle", new double[]{}, 0),
new Spec("DrawdownDuration", "scalar_f64", new double[]{}, 0),
new Spec("DumplingTop", "scalar_candle", new double[]{14.0}, 0),
new Spec("Dx", "scalar_candle", new double[]{14.0}, 0),
new Spec("DynamicMomentumIndex", "scalar_f64", new double[]{14.0}, 0),
new Spec("EaseOfMovement", "scalar_candle", new double[]{14.0}, 0),
new Spec("EffectiveSpread", "trademid", new double[]{}, 0),
new Spec("EhlersStochastic", "scalar_f64", new double[]{14.0}, 0),
new Spec("Ehma", "scalar_f64", new double[]{14.0}, 0),
new Spec("ElderImpulse", "scalar_f64", new double[]{3.0, 7.0, 14.0, 28.0}, 0),
new Spec("ElderRay", "multi_candle", new double[]{14.0}, 2),
new Spec("ElderSafeZone", "multi_candle", new double[]{10.0, 2.0}, 2),
new Spec("Ema", "scalar_f64", new double[]{14.0}, 0),
new Spec("EmpiricalModeDecomposition", "scalar_f64", new double[]{20.0, 0.1}, 0),
new Spec("Engulfing", "scalar_candle", new double[]{}, 0),
new Spec("Equivolume", "multi_candle", new double[]{14.0}, 2),
new Spec("EstimatedLeverageRatio", "deriv", new double[]{}, 0),
new Spec("EvenBetterSinewave", "scalar_f64", new double[]{40.0, 10.0}, 0),
new Spec("EveningDojiStar", "scalar_candle", new double[]{}, 0),
new Spec("Evwma", "scalar_candle", new double[]{14.0}, 0),
new Spec("EwmaVolatility", "scalar_f64", new double[]{0.94}, 0),
new Spec("Expectancy", "scalar_f64", new double[]{14.0}, 0),
new Spec("FallingThreeMethods", "scalar_candle", new double[]{}, 0),
new Spec("Fama", "scalar_f64", new double[]{0.5, 0.05}, 0),
new Spec("FibArcs", "multi_candle", new double[]{}, 3),
new Spec("FibChannel", "multi_candle", new double[]{}, 4),
new Spec("FibConfluence", "multi_candle", new double[]{}, 2),
new Spec("FibExtension", "multi_candle", new double[]{}, 5),
new Spec("FibFan", "multi_candle", new double[]{}, 3),
new Spec("FibProjection", "multi_candle", new double[]{}, 4),
new Spec("FibRetracement", "multi_candle", new double[]{}, 7),
new Spec("FibTimeZones", "multi_candle", new double[]{}, 2),
new Spec("FibonacciPivots", "multi_candle", new double[]{}, 7),
new Spec("FisherRsi", "scalar_f64", new double[]{14.0}, 0),
new Spec("FisherTransform", "scalar_f64", new double[]{14.0}, 0),
new Spec("FlagPennant", "scalar_candle", new double[]{}, 0),
new Spec("Footprint", "footprint", new double[]{1.0}, 0),
new Spec("ForceIndex", "scalar_candle", new double[]{14.0}, 0),
new Spec("FractalChaosBands", "multi_candle", new double[]{14.0}, 2),
new Spec("Frama", "scalar_f64", new double[]{14.0}, 0),
new Spec("FryPanBottom", "scalar_candle", new double[]{14.0}, 0),
new Spec("FundingBasis", "deriv", new double[]{}, 0),
new Spec("FundingImpliedApr", "deriv", new double[]{1095.0}, 0),
new Spec("FundingRate", "deriv", new double[]{}, 0),
new Spec("FundingRateMean", "deriv", new double[]{20.0}, 0),
new Spec("FundingRateZScore", "deriv", new double[]{20.0}, 0),
new Spec("GainLossRatio", "scalar_f64", new double[]{14.0}, 0),
new Spec("GainToPainRatio", "scalar_f64", new double[]{14.0}, 0),
new Spec("GapSideBySideWhite", "scalar_candle", new double[]{}, 0),
new Spec("Garch11", "scalar_f64", new double[]{2e-06, 0.1, 0.88}, 0),
new Spec("GarmanKlassVolatility", "scalar_candle", new double[]{20.0, 252.0}, 0),
new Spec("Gartley", "scalar_candle", new double[]{}, 0),
new Spec("GatorOscillator", "multi_candle", new double[]{3.0, 7.0, 14.0}, 2),
new Spec("GeneralizedDema", "scalar_f64", new double[]{5.0, 0.7}, 0),
new Spec("GeometricMa", "scalar_f64", new double[]{14.0}, 0),
new Spec("GoldenPocket", "multi_candle", new double[]{}, 3),
new Spec("GrangerCausality", "pairwise", new double[]{60.0, 1.0}, 0),
new Spec("GravestoneDoji", "scalar_candle", new double[]{}, 0),
new Spec("Hammer", "scalar_candle", new double[]{}, 0),
new Spec("HangingMan", "scalar_candle", new double[]{}, 0),
new Spec("Harami", "scalar_candle", new double[]{}, 0),
new Spec("HaramiCross", "scalar_candle", new double[]{}, 0),
new Spec("HasbrouckInformationShare", "pairwise", new double[]{14.0}, 0),
new Spec("HeadAndShoulders", "scalar_candle", new double[]{}, 0),
new Spec("HeikinAshi", "multi_candle", new double[]{}, 4),
new Spec("HeikinAshiOscillator", "scalar_candle", new double[]{14.0}, 0),
new Spec("HiLoActivator", "scalar_candle", new double[]{14.0}, 0),
new Spec("HighLowIndex", "cross", new double[]{10.0}, 0),
new Spec("HighLowRange", "scalar_candle", new double[]{}, 0),
new Spec("HighLowVolumeNodes", "multi_candle", new double[]{3.0, 7.0}, 2),
new Spec("HighWave", "scalar_candle", new double[]{}, 0),
new Spec("HighpassFilter", "scalar_f64", new double[]{14.0}, 0),
new Spec("Hikkake", "scalar_candle", new double[]{}, 0),
new Spec("HikkakeModified", "scalar_candle", new double[]{}, 0),
new Spec("HilbertDominantCycle", "scalar_f64", new double[]{}, 0),
new Spec("HistoricalVolatility", "scalar_f64", new double[]{3.0, 7.0}, 0),
new Spec("Hma", "scalar_f64", new double[]{14.0}, 0),
new Spec("HoltWinters", "scalar_f64", new double[]{0.5, 0.1}, 0),
new Spec("HomingPigeon", "scalar_candle", new double[]{}, 0),
new Spec("HtDcPhase", "scalar_f64", new double[]{}, 0),
new Spec("HtPhasor", "multi_f64", new double[]{}, 2),
new Spec("HtTrendMode", "scalar_f64", new double[]{}, 0),
new Spec("HurstChannel", "multi_candle", new double[]{14.0, 2.0}, 3),
new Spec("HurstExponent", "scalar_f64", new double[]{100.0, 4.0}, 0),
new Spec("Ichimoku", "multi_candle", new double[]{9.0, 26.0, 52.0, 26.0}, 5),
new Spec("IdenticalThreeCrows", "scalar_candle", new double[]{}, 0),
new Spec("ImbalanceBars", "bars_candle4", new double[]{5.0}, 0),
new Spec("InNeck", "scalar_candle", new double[]{}, 0),
new Spec("Inertia", "scalar_candle", new double[]{3.0, 7.0}, 0),
new Spec("InformationRatio", "pairwise", new double[]{14.0}, 0),
new Spec("InitialBalance", "multi_candle", new double[]{14.0}, 2),
new Spec("InstantaneousTrendline", "scalar_f64", new double[]{14.0}, 0),
new Spec("IntradayIntensity", "scalar_candle", new double[]{}, 0),
new Spec("IntradayMomentumIndex", "scalar_candle", new double[]{14.0}, 0),
new Spec("IntradayVolatilityProfile", "profile_bins", new double[]{24.0, 0.0}, 24),
new Spec("InverseFisherTransform", "scalar_f64", new double[]{2.0}, 0),
new Spec("InvertedHammer", "scalar_candle", new double[]{}, 0),
new Spec("JarqueBera", "scalar_f64", new double[]{14.0}, 0),
new Spec("Jma", "scalar_f64", new double[]{7.0, 0.0, 2.0}, 0),
new Spec("JumpIndicator", "scalar_f64", new double[]{14.0, 2.0}, 0),
new Spec("KRatio", "scalar_f64", new double[]{14.0}, 0),
new Spec("KagiBars", "bars_close", new double[]{2.0}, 0),
new Spec("KalmanHedgeRatio", "multi_pairwise", new double[]{0.01, 0.001}, 3),
new Spec("Kama", "scalar_f64", new double[]{3.0, 7.0, 14.0}, 0),
new Spec("KaseDevStop", "multi_candle", new double[]{14.0, 2.0}, 2),
new Spec("KasePermissionStochastic", "multi_candle", new double[]{3.0, 7.0}, 2),
new Spec("KellyCriterion", "scalar_f64", new double[]{14.0}, 0),
new Spec("Keltner", "multi_candle", new double[]{3.0, 7.0, 2.0}, 3),
new Spec("KendallTau", "pairwise", new double[]{14.0}, 0),
new Spec("Kicking", "scalar_candle", new double[]{}, 0),
new Spec("KickingByLength", "scalar_candle", new double[]{}, 0),
new Spec("Kst", "multi_f64", new double[]{3.0, 7.0, 14.0, 28.0, 35.0, 42.0, 56.0, 63.0, 70.0}, 2),
new Spec("Kurtosis", "scalar_f64", new double[]{14.0}, 0),
new Spec("Kvo", "scalar_candle", new double[]{3.0, 7.0}, 0),
new Spec("KylesLambda", "trademid", new double[]{20.0}, 0),
new Spec("LadderBottom", "scalar_candle", new double[]{}, 0),
new Spec("LaguerreRsi", "scalar_f64", new double[]{0.5}, 0),
new Spec("LeadLagCrossCorrelation", "multi_pairwise", new double[]{20.0, 10.0}, 2),
new Spec("LinRegAngle", "scalar_f64", new double[]{14.0}, 0),
new Spec("LinRegChannel", "multi_f64", new double[]{14.0, 2.0}, 3),
new Spec("LinRegIntercept", "scalar_f64", new double[]{14.0}, 0),
new Spec("LinRegSlope", "scalar_f64", new double[]{14.0}, 0),
new Spec("LinearRegression", "scalar_f64", new double[]{14.0}, 0),
new Spec("LiquidationFeatures", "deriv_multi", new double[]{}, 5),
new Spec("LogReturn", "scalar_f64", new double[]{14.0}, 0),
new Spec("LongLeggedDoji", "scalar_candle", new double[]{}, 0),
new Spec("LongLine", "scalar_candle", new double[]{}, 0),
new Spec("LongShortRatio", "deriv", new double[]{}, 0),
new Spec("M2Measure", "scalar_f64", new double[]{14.0, 2.0, 0.5}, 0),
new Spec("MaEnvelope", "multi_f64", new double[]{14.0, 2.0}, 3),
new Spec("MacdExt", "multi_f64", new double[]{12.0, 0.0, 26.0, 0.0, 9.0, 0.0}, 3),
new Spec("MacdFix", "multi_f64", new double[]{9.0}, 3),
new Spec("MacdHistogram", "scalar_f64", new double[]{3.0, 7.0, 14.0}, 0),
new Spec("MacdIndicator", "multi_f64", new double[]{12.0, 26.0, 9.0}, 3),
new Spec("Mama", "multi_f64", new double[]{0.5, 0.05}, 2),
new Spec("MarketFacilitationIndex", "scalar_candle", new double[]{}, 0),
new Spec("MartinRatio", "scalar_f64", new double[]{14.0}, 0),
new Spec("Marubozu", "scalar_candle", new double[]{}, 0),
new Spec("MassIndex", "scalar_candle", new double[]{3.0, 7.0}, 0),
new Spec("MatHold", "scalar_candle", new double[]{}, 0),
new Spec("MatchingLow", "scalar_candle", new double[]{}, 0),
new Spec("MaxDrawdown", "scalar_f64", new double[]{14.0}, 0),
new Spec("McClellanOscillator", "cross", new double[]{}, 0),
new Spec("McClellanSummationIndex", "cross", new double[]{}, 0),
new Spec("McGinleyDynamic", "scalar_f64", new double[]{14.0}, 0),
new Spec("MedianAbsoluteDeviation", "scalar_f64", new double[]{14.0}, 0),
new Spec("MedianChannel", "multi_f64", new double[]{14.0, 2.0}, 3),
new Spec("MedianMa", "scalar_f64", new double[]{14.0}, 0),
new Spec("MedianPrice", "scalar_candle", new double[]{}, 0),
new Spec("Mfi", "scalar_candle", new double[]{14.0}, 0),
new Spec("Microprice", "ob", new double[]{}, 0),
new Spec("MidPoint", "scalar_f64", new double[]{14.0}, 0),
new Spec("MidPrice", "scalar_candle", new double[]{14.0}, 0),
new Spec("MinusDi", "scalar_candle", new double[]{14.0}, 0),
new Spec("MinusDm", "scalar_candle", new double[]{14.0}, 0),
new Spec("ModifiedMaStop", "multi_candle", new double[]{14.0}, 2),
new Spec("Mom", "scalar_f64", new double[]{14.0}, 0),
new Spec("MorningDojiStar", "scalar_candle", new double[]{}, 0),
new Spec("MorningEveningStar", "scalar_candle", new double[]{}, 0),
new Spec("MurreyMathLines", "multi_candle", new double[]{14.0}, 9),
new Spec("NakedPoc", "scalar_candle", new double[]{3.0, 7.0}, 0),
new Spec("Natr", "scalar_candle", new double[]{14.0}, 0),
new Spec("NewHighsNewLows", "cross", new double[]{}, 0),
new Spec("NewPriceLines", "scalar_candle", new double[]{14.0}, 0),
new Spec("Nrtr", "multi_candle", new double[]{2.0}, 2),
new Spec("Nvi", "scalar_candle", new double[]{}, 0),
new Spec("OIPriceDivergence", "deriv", new double[]{20.0}, 0),
new Spec("OIWeighted", "deriv", new double[]{}, 0),
new Spec("Obv", "scalar_candle", new double[]{}, 0),
new Spec("OiToVolumeRatio", "deriv", new double[]{}, 0),
new Spec("OmegaRatio", "scalar_f64", new double[]{14.0, 2.0}, 0),
new Spec("OnNeck", "scalar_candle", new double[]{}, 0),
new Spec("OpenInterestDelta", "deriv", new double[]{}, 0),
new Spec("OpenInterestMomentum", "deriv", new double[]{10.0}, 0),
new Spec("OpeningMarubozu", "scalar_candle", new double[]{}, 0),
new Spec("OpeningRange", "multi_candle", new double[]{14.0}, 3),
new Spec("OrderBookImbalanceFull", "ob", new double[]{}, 0),
new Spec("OrderBookImbalanceTop1", "ob", new double[]{}, 0),
new Spec("OrderBookImbalanceTopN", "ob", new double[]{5.0}, 0),
new Spec("OrderFlowImbalance", "ob", new double[]{20.0}, 0),
new Spec("OuHalfLife", "pairwise", new double[]{14.0}, 0),
new Spec("OvernightGap", "scalar_candle", new double[]{0.0}, 0),
new Spec("OvernightIntradayReturn", "multi_candle", new double[]{14.0}, 2),
new Spec("PainIndex", "scalar_f64", new double[]{14.0}, 0),
new Spec("PairSpreadZScore", "pairwise", new double[]{20.0, 20.0}, 0),
new Spec("PairwiseBeta", "pairwise", new double[]{14.0}, 0),
new Spec("ParkinsonVolatility", "scalar_candle", new double[]{20.0, 252.0}, 0),
new Spec("PearsonCorrelation", "pairwise", new double[]{14.0}, 0),
new Spec("PercentAboveMa", "cross", new double[]{}, 0),
new Spec("PercentB", "scalar_f64", new double[]{14.0, 2.0}, 0),
new Spec("PercentageTrailingStop", "scalar_f64", new double[]{2.0}, 0),
new Spec("PerpetualPremiumIndex", "deriv", new double[]{}, 0),
new Spec("Pgo", "scalar_candle", new double[]{14.0}, 0),
new Spec("PiercingDarkCloud", "scalar_candle", new double[]{}, 0),
new Spec("Pin", "trade", new double[]{20.0}, 0),
new Spec("PivotReversal", "scalar_candle", new double[]{3.0, 7.0}, 0),
new Spec("PlusDi", "scalar_candle", new double[]{14.0}, 0),
new Spec("PlusDm", "scalar_candle", new double[]{14.0}, 0),
new Spec("Pmo", "scalar_f64", new double[]{3.0, 7.0}, 0),
new Spec("PointAndFigureBars", "bars_close", new double[]{2.0, 3.0}, 0),
new Spec("PolarizedFractalEfficiency", "scalar_f64", new double[]{10.0, 5.0}, 0),
new Spec("Ppo", "scalar_f64", new double[]{3.0, 7.0}, 0),
new Spec("PpoHistogram", "scalar_f64", new double[]{3.0, 7.0, 14.0}, 0),
new Spec("ProfileShape", "scalar_candle", new double[]{3.0, 7.0}, 0),
new Spec("ProfitFactor", "scalar_f64", new double[]{14.0}, 0),
new Spec("ProjectionBands", "multi_candle", new double[]{14.0}, 3),
new Spec("ProjectionOscillator", "scalar_candle", new double[]{14.0}, 0),
new Spec("Psar", "scalar_candle", new double[]{0.02, 0.02, 0.2}, 0),
new Spec("Pvi", "scalar_candle", new double[]{}, 0),
new Spec("Qqe", "multi_f64", new double[]{3.0, 7.0, 2.0}, 2),
new Spec("Qstick", "scalar_candle", new double[]{14.0}, 0),
new Spec("QuartileBands", "multi_f64", new double[]{14.0}, 3),
new Spec("QuotedSpread", "ob", new double[]{}, 0),
new Spec("RSquared", "scalar_f64", new double[]{14.0}, 0),
new Spec("RangeBars", "bars_close", new double[]{2.0}, 0),
new Spec("RealizedSpread", "trademid", new double[]{20.0}, 0),
new Spec("RealizedVolatility", "scalar_f64", new double[]{14.0}, 0),
new Spec("RecoveryFactor", "scalar_f64", new double[]{}, 0),
new Spec("RectangleRange", "scalar_candle", new double[]{}, 0),
new Spec("Reflex", "scalar_f64", new double[]{14.0}, 0),
new Spec("RegimeLabel", "scalar_f64", new double[]{3.0, 7.0}, 0),
new Spec("RelativeStrengthAB", "multi_pairwise", new double[]{14.0, 14.0}, 3),
new Spec("RenkoBars", "bars_close", new double[]{2.0}, 0),
new Spec("RenkoTrailingStop", "scalar_f64", new double[]{2.0}, 0),
new Spec("RickshawMan", "scalar_candle", new double[]{}, 0),
new Spec("RisingThreeMethods", "scalar_candle", new double[]{}, 0),
new Spec("Rmi", "scalar_f64", new double[]{3.0, 7.0}, 0),
new Spec("Roc", "scalar_f64", new double[]{14.0}, 0),
new Spec("Rocp", "scalar_f64", new double[]{14.0}, 0),
new Spec("Rocr", "scalar_f64", new double[]{14.0}, 0),
new Spec("Rocr100", "scalar_f64", new double[]{14.0}, 0),
new Spec("RogersSatchellVolatility", "scalar_candle", new double[]{20.0, 252.0}, 0),
new Spec("RollMeasure", "trade", new double[]{20.0}, 0),
new Spec("RollingCorrelation", "pairwise", new double[]{14.0}, 0),
new Spec("RollingCovariance", "pairwise", new double[]{14.0}, 0),
new Spec("RollingIqr", "scalar_f64", new double[]{14.0}, 0),
new Spec("RollingMinMaxScaler", "scalar_f64", new double[]{14.0}, 0),
new Spec("RollingPercentileRank", "scalar_f64", new double[]{14.0}, 0),
new Spec("RollingQuantile", "scalar_f64", new double[]{20.0, 0.5}, 0),
new Spec("RollingVwap", "scalar_candle", new double[]{14.0}, 0),
new Spec("RoofingFilter", "scalar_f64", new double[]{3.0, 7.0}, 0),
new Spec("Rsi", "scalar_f64", new double[]{14.0}, 0),
new Spec("Rsx", "scalar_f64", new double[]{14.0}, 0),
new Spec("RunBars", "bars_candle4", new double[]{3.0}, 0),
new Spec("Rvi", "scalar_candle", new double[]{14.0}, 0),
new Spec("RviVolatility", "scalar_f64", new double[]{14.0}, 0),
new Spec("Rwi", "multi_candle", new double[]{14.0}, 2),
new Spec("SampleEntropy", "scalar_f64", new double[]{20.0, 2.0, 0.2}, 0),
new Spec("SarExt", "scalar_candle", new double[]{2.0, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5}, 0),
new Spec("SeasonalZScore", "scalar_candle", new double[]{14.0}, 0),
new Spec("SeparatingLines", "scalar_candle", new double[]{}, 0),
new Spec("SessionHighLow", "multi_candle", new double[]{14.0}, 2),
new Spec("SessionRange", "multi_candle", new double[]{14.0}, 3),
new Spec("SessionVwap", "scalar_candle", new double[]{14.0}, 0),
new Spec("ShannonEntropy", "scalar_f64", new double[]{3.0, 7.0}, 0),
new Spec("Shark", "scalar_candle", new double[]{}, 0),
new Spec("SharpeRatio", "scalar_f64", new double[]{14.0, 2.0}, 0),
new Spec("ShootingStar", "scalar_candle", new double[]{}, 0),
new Spec("ShortLine", "scalar_candle", new double[]{}, 0),
new Spec("SignedVolume", "trade", new double[]{}, 0),
new Spec("SineWave", "scalar_f64", new double[]{}, 0),
new Spec("SineWeightedMa", "scalar_f64", new double[]{14.0}, 0),
new Spec("SinglePrints", "scalar_candle", new double[]{3.0, 7.0}, 0),
new Spec("Skewness", "scalar_f64", new double[]{14.0}, 0),
new Spec("Sma", "scalar_f64", new double[]{14.0}, 0),
new Spec("Smi", "scalar_candle", new double[]{3.0, 7.0, 14.0}, 0),
new Spec("Smma", "scalar_f64", new double[]{14.0}, 0),
new Spec("SmoothedHeikinAshi", "multi_candle", new double[]{14.0}, 4),
new Spec("SortinoRatio", "scalar_f64", new double[]{14.0, 2.0}, 0),
new Spec("SpearmanCorrelation", "pairwise", new double[]{14.0}, 0),
new Spec("SpinningTop", "scalar_candle", new double[]{}, 0),
new Spec("SpreadAr1Coefficient", "pairwise", new double[]{14.0}, 0),
new Spec("SpreadBollingerBands", "multi_pairwise", new double[]{14.0, 2.0}, 4),
new Spec("SpreadHurst", "pairwise", new double[]{14.0}, 0),
new Spec("StalledPattern", "scalar_candle", new double[]{}, 0),
new Spec("StandardError", "scalar_f64", new double[]{14.0}, 0),
new Spec("StandardErrorBands", "multi_f64", new double[]{14.0, 2.0}, 3),
new Spec("StarcBands", "multi_candle", new double[]{3.0, 7.0, 2.0}, 3),
new Spec("Stc", "scalar_f64", new double[]{10.0, 23.0, 10.0, 0.5}, 0),
new Spec("StdDev", "scalar_f64", new double[]{14.0}, 0),
new Spec("StepTrailingStop", "scalar_f64", new double[]{2.0}, 0),
new Spec("SterlingRatio", "scalar_f64", new double[]{14.0}, 0),
new Spec("StickSandwich", "scalar_candle", new double[]{}, 0),
new Spec("StochRsi", "scalar_f64", new double[]{3.0, 7.0}, 0),
new Spec("Stochastic", "multi_candle", new double[]{3.0, 7.0}, 2),
new Spec("StochasticCci", "scalar_candle", new double[]{14.0}, 0),
new Spec("SuperSmoother", "scalar_f64", new double[]{14.0}, 0),
new Spec("SuperTrend", "multi_candle", new double[]{14.0, 2.0}, 2),
new Spec("T3", "scalar_f64", new double[]{5.0, 0.7}, 0),
new Spec("TailRatio", "scalar_f64", new double[]{14.0}, 0),
new Spec("TakerBuySellRatio", "deriv", new double[]{}, 0),
new Spec("Takuri", "scalar_candle", new double[]{}, 0),
new Spec("TasukiGap", "scalar_candle", new double[]{}, 0),
new Spec("TdCamouflage", "scalar_candle", new double[]{}, 0),
new Spec("TdClop", "scalar_candle", new double[]{}, 0),
new Spec("TdClopwin", "scalar_candle", new double[]{}, 0),
new Spec("TdCombo", "scalar_candle", new double[]{3.0, 7.0, 14.0, 28.0}, 0),
new Spec("TdCountdown", "scalar_candle", new double[]{3.0, 7.0, 14.0, 28.0}, 0),
new Spec("TdDWave", "scalar_candle", new double[]{2.0}, 0),
new Spec("TdDeMarker", "scalar_candle", new double[]{14.0}, 0),
new Spec("TdDifferential", "scalar_candle", new double[]{}, 0),
new Spec("TdLines", "multi_candle", new double[]{3.0, 7.0}, 2),
new Spec("TdMovingAverage", "multi_candle", new double[]{3.0, 7.0}, 2),
new Spec("TdOpen", "scalar_candle", new double[]{}, 0),
new Spec("TdPressure", "scalar_candle", new double[]{14.0}, 0),
new Spec("TdPropulsion", "scalar_candle", new double[]{}, 0),
new Spec("TdRangeProjection", "multi_candle", new double[]{}, 2),
new Spec("TdRei", "scalar_candle", new double[]{14.0}, 0),
new Spec("TdRiskLevel", "multi_candle", new double[]{3.0, 7.0}, 2),
new Spec("TdSequential", "multi_candle", new double[]{3.0, 7.0, 14.0, 28.0}, 3),
new Spec("TdSetup", "scalar_candle", new double[]{3.0, 7.0}, 0),
new Spec("TdTrap", "scalar_candle", new double[]{}, 0),
new Spec("Tema", "scalar_f64", new double[]{14.0}, 0),
new Spec("TermStructureBasis", "deriv", new double[]{}, 0),
new Spec("ThreeDrives", "scalar_candle", new double[]{}, 0),
new Spec("ThreeInside", "scalar_candle", new double[]{}, 0),
new Spec("ThreeLineBreak", "scalar_candle", new double[]{14.0}, 0),
new Spec("ThreeLineBreakBars", "bars_close", new double[]{3.0}, 0),
new Spec("ThreeLineStrike", "scalar_candle", new double[]{}, 0),
new Spec("ThreeOutside", "scalar_candle", new double[]{}, 0),
new Spec("ThreeSoldiersOrCrows", "scalar_candle", new double[]{}, 0),
new Spec("ThreeStarsInSouth", "scalar_candle", new double[]{}, 0),
new Spec("Thrusting", "scalar_candle", new double[]{}, 0),
new Spec("TickBars", "bars_candle5", new double[]{2.0}, 0),
new Spec("TickIndex", "cross", new double[]{}, 0),
new Spec("Tii", "scalar_f64", new double[]{3.0, 7.0}, 0),
new Spec("TimeBasedStop", "scalar_candle", new double[]{14.0}, 0),
new Spec("TimeOfDayReturnProfile", "profile_bins", new double[]{24.0, 0.0}, 24),
new Spec("TowerTopBottom", "scalar_candle", new double[]{}, 0),
new Spec("TpoProfile", "profile_pricebins", new double[]{30.0, 50.0}, 52),
new Spec("TradeImbalance", "trade", new double[]{20.0}, 0),
new Spec("TradeSignAutocorrelation", "trade", new double[]{20.0}, 0),
new Spec("TradeVolumeIndex", "scalar_candle", new double[]{2.0}, 0),
new Spec("TrendLabel", "scalar_f64", new double[]{14.0}, 0),
new Spec("TrendStrengthIndex", "scalar_f64", new double[]{14.0}, 0),
new Spec("Trendflex", "scalar_f64", new double[]{14.0}, 0),
new Spec("TreynorRatio", "pairwise", new double[]{14.0, 2.0}, 0),
new Spec("Triangle", "scalar_candle", new double[]{}, 0),
new Spec("Trima", "scalar_f64", new double[]{14.0}, 0),
new Spec("Trin", "cross", new double[]{}, 0),
new Spec("TripleTopBottom", "scalar_candle", new double[]{}, 0),
new Spec("Tristar", "scalar_candle", new double[]{}, 0),
new Spec("Trix", "scalar_f64", new double[]{14.0}, 0),
new Spec("TrueRange", "scalar_candle", new double[]{}, 0),
new Spec("Tsf", "scalar_f64", new double[]{14.0}, 0),
new Spec("TsfOscillator", "scalar_f64", new double[]{14.0}, 0),
new Spec("Tsi", "scalar_f64", new double[]{3.0, 7.0}, 0),
new Spec("Tsv", "scalar_candle", new double[]{14.0}, 0),
new Spec("TtmSqueeze", "multi_candle", new double[]{14.0, 2.0, 0.5}, 2),
new Spec("TtmTrend", "scalar_candle", new double[]{14.0}, 0),
new Spec("TurnOfMonth", "scalar_candle", new double[]{3.0, 3.0, 0.0}, 0),
new Spec("Tweezer", "scalar_candle", new double[]{}, 0),
new Spec("TwiggsMoneyFlow", "scalar_candle", new double[]{14.0}, 0),
new Spec("TwoCrows", "scalar_candle", new double[]{}, 0),
new Spec("TypicalPrice", "scalar_candle", new double[]{}, 0),
new Spec("UlcerIndex", "scalar_f64", new double[]{14.0}, 0),
new Spec("UltimateOscillator", "scalar_candle", new double[]{3.0, 7.0, 14.0}, 0),
new Spec("UniqueThreeRiver", "scalar_candle", new double[]{}, 0),
new Spec("UniversalOscillator", "scalar_f64", new double[]{14.0}, 0),
new Spec("UpDownVolumeRatio", "cross", new double[]{}, 0),
new Spec("UpsideGapThreeMethods", "scalar_candle", new double[]{}, 0),
new Spec("UpsideGapTwoCrows", "scalar_candle", new double[]{}, 0),
new Spec("UpsidePotentialRatio", "scalar_f64", new double[]{14.0, 2.0}, 0),
new Spec("ValueArea", "multi_candle", new double[]{20.0, 50.0, 0.7}, 3),
new Spec("ValueAtRisk", "scalar_f64", new double[]{20.0, 0.95}, 0),
new Spec("Variance", "scalar_f64", new double[]{14.0}, 0),
new Spec("VarianceRatio", "pairwise", new double[]{60.0, 2.0}, 0),
new Spec("VerticalHorizontalFilter", "scalar_f64", new double[]{14.0}, 0),
new Spec("Vidya", "scalar_f64", new double[]{3.0, 7.0}, 0),
new Spec("VolatilityCone", "multi_candle", new double[]{3.0, 7.0}, 5),
new Spec("VolatilityOfVolatility", "scalar_f64", new double[]{3.0, 7.0}, 0),
new Spec("VolatilityRatio", "scalar_candle", new double[]{14.0}, 0),
new Spec("VoltyStop", "scalar_candle", new double[]{14.0, 2.0}, 0),
new Spec("VolumeBars", "bars_candle5", new double[]{500.0}, 0),
new Spec("VolumeByTimeProfile", "profile_bins", new double[]{24.0, 0.0}, 24),
new Spec("VolumeOscillator", "scalar_candle", new double[]{3.0, 7.0}, 0),
new Spec("VolumePriceTrend", "scalar_candle", new double[]{}, 0),
new Spec("VolumeProfile", "profile_pricebins", new double[]{20.0, 50.0}, 52),
new Spec("VolumeRsi", "scalar_candle", new double[]{14.0}, 0),
new Spec("VolumeWeightedMacd", "multi_candle", new double[]{3.0, 7.0, 14.0}, 3),
new Spec("VolumeWeightedSr", "multi_candle", new double[]{14.0}, 2),
new Spec("Vortex", "multi_candle", new double[]{14.0}, 2),
new Spec("Vpin", "trade", new double[]{5000.0, 10.0}, 0),
new Spec("Vwap", "scalar_candle", new double[]{}, 0),
new Spec("VwapStdDevBands", "multi_candle", new double[]{2.0}, 4),
new Spec("Vwma", "scalar_candle", new double[]{14.0}, 0),
new Spec("Vzo", "scalar_candle", new double[]{14.0}, 0),
new Spec("Wad", "scalar_candle", new double[]{}, 0),
new Spec("WavePm", "scalar_f64", new double[]{3.0, 7.0}, 0),
new Spec("WaveTrend", "multi_candle", new double[]{3.0, 7.0, 14.0}, 2),
new Spec("Wedge", "scalar_candle", new double[]{}, 0),
new Spec("WeightedClose", "scalar_candle", new double[]{}, 0),
new Spec("WickRatio", "scalar_candle", new double[]{}, 0),
new Spec("WilliamsFractals", "multi_candle", new double[]{}, 2),
new Spec("WilliamsR", "scalar_candle", new double[]{14.0}, 0),
new Spec("WinRate", "scalar_f64", new double[]{14.0}, 0),
new Spec("Wma", "scalar_f64", new double[]{14.0}, 0),
new Spec("WoodiePivots", "multi_candle", new double[]{}, 5),
new Spec("YangZhangVolatility", "scalar_candle", new double[]{20.0, 252.0}, 0),
new Spec("YoyoExit", "scalar_candle", new double[]{14.0, 2.0}, 0),
new Spec("ZScore", "scalar_f64", new double[]{14.0}, 0),
new Spec("ZeroLagMacd", "multi_f64", new double[]{3.0, 7.0, 14.0}, 3),
new Spec("ZigZag", "multi_candle", new double[]{0.02}, 2),
new Spec("Zlema", "scalar_f64", new double[]{14.0}, 0),
};
private static Path goldenDir() {
java.io.File d = new java.io.File("").getAbsoluteFile();
while (d != null) {
java.io.File g = new java.io.File(d, "testdata/golden");
if (g.isDirectory()) {
return g.toPath();
}
d = d.getParentFile();
}
throw new IllegalStateException("testdata/golden not found");
}
private static double cell(String s) {
return switch (s) {
case "nan" -> Double.NaN;
case "inf" -> Double.POSITIVE_INFINITY;
case "-inf" -> Double.NEGATIVE_INFINITY;
default -> Double.parseDouble(s);
};
}
private static double[][] input() throws IOException {
List<String> lines = Files.readAllLines(goldenDir().resolve("input.csv"));
List<double[]> rows = new ArrayList<>();
for (int i = 1; i < lines.size(); i++) {
if (lines.get(i).isEmpty()) continue;
String[] p = lines.get(i).split(",");
double[] r = new double[p.length];
for (int j = 0; j < p.length; j++) r[j] = Double.parseDouble(p[j]);
rows.add(r);
}
return rows.toArray(new double[0][]);
}
// Keep blank lines (a candle on which no bar closed) so rows stay aligned.
private static double[][] fixture(String name) throws IOException {
List<String> lines = Files.readAllLines(goldenDir().resolve("g_" + name + ".csv"));
List<double[]> rows = new ArrayList<>();
for (int i = 1; i < lines.size(); i++) {
String ln = lines.get(i);
if (ln.isEmpty()) {
rows.add(new double[0]);
continue;
}
String[] p = ln.split(",");
double[] r = new double[p.length];
for (int j = 0; j < p.length; j++) r[j] = cell(p[j]);
rows.add(r);
}
return rows.toArray(new double[0][]);
}
private static double[] nanRow(int n) {
double[] r = new double[n];
java.util.Arrays.fill(r, Double.NaN);
return r;
}
private static double[] derivFields(double[] r) {
double o = r[0], h = r[1], l = r[2], c = r[3], v = r[4];
return new double[]{
(c - o) / c * 0.01, c, c - 0.5, c + 1.0, v * 10.0, v * 0.6, v * 0.4,
v * 0.55, v * 0.45, h - c, c - l,
};
}
private static double[] crossList(double[] r, int which) {
double o = r[0], c = r[3], v = r[4];
double[] out = new double[5];
for (int j = 0; j < 5; j++) {
out[j] = switch (which) {
case 0 -> (c - o) + j;
case 1 -> v + j * 10.0;
case 2 -> j % 2 == 0 ? 1.0 : 0.0; // newHigh
case 3 -> j % 3 == 0 ? 1.0 : 0.0; // newLow
case 4 -> j % 2 == 0 ? 1.0 : 0.0; // aboveMa
default -> j % 3 == 0 ? 1.0 : 0.0; // onBuySignal
};
}
return out;
}
private static double[] obList(double[] r, int which) {
double c = r[3], v = r[4];
double[] out = new double[5];
for (int k = 0; k < 5; k++) {
double kf = k + 1;
out[k] = switch (which) {
case 0 -> c - 0.1 * kf;
case 1 -> v / kf;
case 2 -> c + 0.1 * kf;
default -> v * 0.9 / kf;
};
}
return out;
}
private static double[] flattenRecord(Object o) throws Exception {
RecordComponent[] rc = o.getClass().getRecordComponents();
List<Double> list = new ArrayList<>();
for (RecordComponent c : rc) {
Object v = c.getAccessor().invoke(o);
if (v instanceof double[] arr) {
for (double d : arr) list.add(d);
} else if (v instanceof Number num) {
list.add(num.doubleValue());
}
}
double[] out = new double[list.size()];
for (int i = 0; i < out.length; i++) out[i] = list.get(i);
return out;
}
private static double[] flattenArray(Object arr) throws Exception {
int len = Array.getLength(arr);
List<Double> list = new ArrayList<>();
for (int i = 0; i < len; i++) {
for (double d : flattenRecord(Array.get(arr, i))) list.add(d);
}
double[] out = new double[list.size()];
for (int i = 0; i < out.length; i++) out[i] = list.get(i);
return out;
}
private static Object construct(Spec s) throws Exception {
Class<?> cls = Class.forName("org.wickra." + s.canonical());
Constructor<?> ctor = cls.getConstructors()[0];
Class<?>[] pt = ctor.getParameterTypes();
Object[] args = new Object[pt.length];
for (int i = 0; i < pt.length; i++) {
double v = s.params()[i];
if (pt[i] == int.class) args[i] = (int) Math.round(v);
else if (pt[i] == long.class) args[i] = (long) Math.round(v);
else args[i] = v;
}
return ctor.newInstance(args);
}
private static Method updateMethod(Object ind) {
for (Method m : ind.getClass().getMethods()) {
if (m.getName().equals("update")) return m;
}
throw new IllegalStateException("no update on " + ind.getClass());
}
private static double[] row(Spec s, Object ind, Method upd, double[] r, int i) throws Exception {
double o = r[0], h = r[1], l = r[2], c = r[3], v = r[4];
Object res = switch (s.arch()) {
case "scalar_f64", "multi_f64" -> upd.invoke(ind, c);
case "pairwise", "multi_pairwise" -> upd.invoke(ind, c, o);
case "scalar_candle", "multi_candle", "profile_bins", "profile_pricebins" ->
upd.invoke(ind, o, h, l, c, v, (long) i);
case "trade" -> upd.invoke(ind, c, v, c >= o, (long) i);
case "trademid" -> upd.invoke(ind, c, v, c >= o, (long) i, (h + l) / 2);
case "ob" -> upd.invoke(ind, obList(r, 0), obList(r, 1), obList(r, 2), obList(r, 3));
case "cross" -> upd.invoke(ind, crossList(r, 0), crossList(r, 1), crossList(r, 2),
crossList(r, 3), crossList(r, 4), crossList(r, 5), (long) i);
case "deriv", "deriv_multi" -> {
double[] d = derivFields(r);
yield upd.invoke(ind, d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], (long) i);
}
case "bars_close" -> upd.invoke(ind, c, c, c, c, 1.0, 0L);
case "bars_candle4" -> upd.invoke(ind, o, h, l, c, 1.0, 0L);
case "bars_candle5" -> upd.invoke(ind, o, h, l, c, v, 0L);
case "footprint" -> upd.invoke(ind, c, v, c >= o, (long) i);
default -> throw new IllegalStateException("arch " + s.arch());
};
return switch (s.arch()) {
case "scalar_f64", "scalar_candle", "pairwise", "trade", "trademid", "ob", "cross", "deriv" ->
new double[]{((Number) res).doubleValue()};
case "multi_f64", "multi_candle", "multi_pairwise", "deriv_multi", "profile_pricebins" ->
res == null ? nanRow(s.width()) : flattenRecord(res);
case "profile_bins" -> res == null ? nanRow(s.width()) : (double[]) res;
default -> flattenArray(res); // bars_*, footprint
};
}
@TestFactory
List<DynamicTest> golden() throws Exception {
double[][] rows = input();
List<DynamicTest> tests = new ArrayList<>();
for (Spec s : SPECS) {
tests.add(dynamicTest(s.canonical(), () -> {
Object ind = construct(s);
Method upd = updateMethod(ind);
double[][] exp = fixture(s.canonical());
assertTrue(exp.length == rows.length, s.canonical() + ": row count " + exp.length + " vs " + rows.length);
for (int i = 0; i < rows.length; i++) {
double[] got = row(s, ind, upd, rows[i], i);
double[] want = exp[i];
assertTrue(got.length == want.length,
s.canonical() + " row " + i + ": arity " + got.length + " vs " + want.length);
for (int k = 0; k < want.length; k++) {
double w = want[k], g = got[k];
if (Double.isNaN(w)) {
assertTrue(Double.isNaN(g), s.canonical() + " row " + i + " col " + k + ": want NaN got " + g);
} else if (Double.isInfinite(w)) {
assertTrue(Double.isInfinite(g) && Math.signum(g) == Math.signum(w),
s.canonical() + " row " + i + " col " + k + ": want " + w + " got " + g);
} else {
double tol = TOL * Math.max(1.0, Math.abs(w));
assertTrue(Math.abs(g - w) <= tol,
s.canonical() + " row " + i + " col " + k + ": got " + g + " want " + w);
}
}
}
}));
}
return tests;
}
}
@@ -157,4 +157,51 @@ class GoldenTests {
}
}
}
// The four de-duplicated indicators, pinned against the Rust reference.
@Test
void adOscillatorMatchesGolden() throws Exception {
double[][] in = input();
List<String[]> e = readCsv("ad_oscillator");
try (AdOscillator ad = new AdOscillator()) {
for (int i = 0; i < in.length; i++) {
close(ad.update(in[i][0], in[i][1], in[i][2], in[i][3], in[i][4], i), cell(e.get(i)[0]), i, "ad_oscillator");
}
}
}
@Test
void intradayIntensityMatchesGolden() throws Exception {
double[][] in = input();
List<String[]> e = readCsv("intraday_intensity");
try (IntradayIntensity ii = new IntradayIntensity()) {
for (int i = 0; i < in.length; i++) {
close(ii.update(in[i][0], in[i][1], in[i][2], in[i][3], in[i][4], i), cell(e.get(i)[0]), i, "intraday_intensity");
}
}
}
@Test
void awesomeOscillatorHistogramMatchesGolden() throws Exception {
double[][] in = input();
List<String[]> e = readCsv("awesome_oscillator_histogram");
try (AwesomeOscillatorHistogram aoh = new AwesomeOscillatorHistogram(5, 34, 1)) {
for (int i = 0; i < in.length; i++) {
close(aoh.update(in[i][0], in[i][1], in[i][2], in[i][3], in[i][4], i), cell(e.get(i)[0]), i, "awesome_oscillator_histogram");
}
}
}
@Test
void averageDrawdownMatchesGolden() throws Exception {
double[][] in = input();
List<String[]> e = readCsv("average_drawdown");
try (AverageDrawdown avg = new AverageDrawdown(20)) {
for (int i = 0; i < in.length; i++) {
// generator fed the close column as the equity-curve sample.
close(avg.update(in[i][3]), cell(e.get(i)[0]), i, "average_drawdown");
}
}
}
}