feat: add 19 indicators for external feature-extractor coverage (377 -> 396) (#175)
Adds 19 streaming indicators so an external trading-bot feature extractor can replace its hand-built features with native, batch/streaming-equivalent ones. Each is a real gap (verified against the existing catalogue), production-only, with full Python/Node/WASM bindings, fuzz drivers, and tests. Five commits, one per family group; counter 377 -> 396. ## What's added **Price Statistics (6)** — `LogReturn`, `RealizedVolatility` (raw quadratic variation, the un-annualised counterpart to `HistoricalVolatility`), `RollingQuantile`, `RollingIqr`, `RollingPercentileRank`, `SpreadAr1Coefficient` (pairwise AR(1) rho of the spread; complements `OuHalfLife`). **Price Action (4)** — `CloseVsOpen`, `BodySizePct`, `WickRatio`, `HighLowRange` (stateless per-bar OHLC transforms). **Regime / Trend / Jump labels (3)** — `TrendLabel` (sign of the rolling OLS slope), `JumpIndicator` (return outliers vs trailing volatility, measured as deviation from the trailing mean so steady drift is not flagged), `RegimeLabel` (volatility-quantile regime split). **Risk / Performance (2)** — `WinRate`, `Expectancy` (R-multiple). **Microstructure (4)** — `OrderFlowImbalance` (Cont-Kukanov-Stoikov OFI), `Vpin`, `AmihudIlliquidity`, `RollMeasure`. These reuse the existing `OrderBook` / `Trade` inputs (no new input type). ## Intentionally NOT added (already present, would be duplicates) - **Population skew / kurtosis** — `skewness.rs` / `kurtosis.rs` are already population moments (divisor n). - **Hurst R/S** — `hurst_exponent.rs` already uses rescaled-range (R/S) analysis. - **Queue Imbalance** — exactly `OrderBookImbalanceTop1` ((bidSize - askSize) / (bidSize + askSize)). ## Verification `cargo test -p wickra-core` (lib 3187 + doc 354), `cargo clippy --workspace --all-targets --all-features -D warnings` clean, node `npm run build && npm test` (471), python `pytest` (784). Counter consistent across `mod.rs`, lib block, README, and docs/README at 396.
This commit is contained in:
@@ -25,6 +25,20 @@ from __future__ import annotations
|
||||
|
||||
from ._wickra import (
|
||||
__version__,
|
||||
Expectancy,
|
||||
WinRate,
|
||||
RegimeLabel,
|
||||
JumpIndicator,
|
||||
TrendLabel,
|
||||
HighLowRange,
|
||||
WickRatio,
|
||||
BodySizePct,
|
||||
CloseVsOpen,
|
||||
RollingQuantile,
|
||||
RollingPercentileRank,
|
||||
RollingIqr,
|
||||
RealizedVolatility,
|
||||
LogReturn,
|
||||
TSF,
|
||||
LINEARREG_INTERCEPT,
|
||||
ROCR100,
|
||||
@@ -189,6 +203,7 @@ from ._wickra import (
|
||||
PearsonCorrelation,
|
||||
Beta,
|
||||
PairwiseBeta,
|
||||
SpreadAr1Coefficient,
|
||||
PairSpreadZScore,
|
||||
LeadLagCrossCorrelation,
|
||||
Cointegration,
|
||||
@@ -351,6 +366,7 @@ from ._wickra import (
|
||||
FibExtension,
|
||||
FibRetracement,
|
||||
# Microstructure: order book
|
||||
OrderFlowImbalance,
|
||||
OrderBookImbalanceTop1,
|
||||
OrderBookImbalanceTopN,
|
||||
OrderBookImbalanceFull,
|
||||
@@ -358,6 +374,9 @@ from ._wickra import (
|
||||
QuotedSpread,
|
||||
DepthSlope,
|
||||
# Microstructure: trade flow
|
||||
RollMeasure,
|
||||
AmihudIlliquidity,
|
||||
Vpin,
|
||||
SignedVolume,
|
||||
CumulativeVolumeDelta,
|
||||
TradeImbalance,
|
||||
@@ -430,6 +449,20 @@ from ._wickra import (
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"Expectancy",
|
||||
"WinRate",
|
||||
"RegimeLabel",
|
||||
"JumpIndicator",
|
||||
"TrendLabel",
|
||||
"HighLowRange",
|
||||
"WickRatio",
|
||||
"BodySizePct",
|
||||
"CloseVsOpen",
|
||||
"RollingQuantile",
|
||||
"RollingPercentileRank",
|
||||
"RollingIqr",
|
||||
"RealizedVolatility",
|
||||
"LogReturn",
|
||||
"TSF",
|
||||
"LINEARREG_INTERCEPT",
|
||||
"ROCR100",
|
||||
@@ -595,6 +628,7 @@ __all__ = [
|
||||
"PearsonCorrelation",
|
||||
"Beta",
|
||||
"PairwiseBeta",
|
||||
"SpreadAr1Coefficient",
|
||||
"PairSpreadZScore",
|
||||
"LeadLagCrossCorrelation",
|
||||
"Cointegration",
|
||||
@@ -757,6 +791,7 @@ __all__ = [
|
||||
"FibExtension",
|
||||
"FibRetracement",
|
||||
# Microstructure: order book
|
||||
"OrderFlowImbalance",
|
||||
"OrderBookImbalanceTop1",
|
||||
"OrderBookImbalanceTopN",
|
||||
"OrderBookImbalanceFull",
|
||||
@@ -764,6 +799,9 @@ __all__ = [
|
||||
"QuotedSpread",
|
||||
"DepthSlope",
|
||||
# Microstructure: trade flow
|
||||
"RollMeasure",
|
||||
"AmihudIlliquidity",
|
||||
"Vpin",
|
||||
"SignedVolume",
|
||||
"CumulativeVolumeDelta",
|
||||
"TradeImbalance",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -45,6 +45,16 @@ def ohlcv() -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
|
||||
# --- Scalar (f64 -> f64) indicators ---------------------------------------
|
||||
|
||||
SCALAR = [
|
||||
(ta.Expectancy, (20,)),
|
||||
(ta.WinRate, (20,)),
|
||||
(ta.RegimeLabel, (5, 20)),
|
||||
(ta.JumpIndicator, (20, 3.0)),
|
||||
(ta.TrendLabel, (10,)),
|
||||
(ta.RollingQuantile, (20, 0.5)),
|
||||
(ta.RollingPercentileRank, (14,)),
|
||||
(ta.RollingIqr, (14,)),
|
||||
(ta.RealizedVolatility, (20,)),
|
||||
(ta.LogReturn, (1,)),
|
||||
(ta.TSF, (14,)),
|
||||
(ta.LINEARREG_INTERCEPT, (14,)),
|
||||
(ta.ROCR100, (10,)),
|
||||
@@ -167,6 +177,7 @@ def test_scalar_streaming_matches_batch(cls, args, sine_prices):
|
||||
# --- Two-series (asset, benchmark) indicators -----------------------------
|
||||
|
||||
PAIR = [
|
||||
(ta.SpreadAr1Coefficient, (40,)),
|
||||
(ta.GrangerCausality, (60, 1)),
|
||||
(ta.VarianceRatio, (60, 2)),
|
||||
(ta.BetaNeutralSpread, (20,)),
|
||||
@@ -330,6 +341,12 @@ def test_relative_strength_streaming_matches_batch():
|
||||
# 6-tuple candle; the batch helper takes only the columns it needs.
|
||||
|
||||
CANDLE_SCALAR = {
|
||||
# Per-bar OHLC transforms (open matters). The streaming harness feeds
|
||||
# open == close, so batch passes the close column in for open to match.
|
||||
"HighLowRange": (lambda: ta.HighLowRange(), lambda ind, h, l, c, v: ind.batch(c, h, l, c)),
|
||||
"WickRatio": (lambda: ta.WickRatio(), lambda ind, h, l, c, v: ind.batch(c, h, l, c)),
|
||||
"BodySizePct": (lambda: ta.BodySizePct(), lambda ind, h, l, c, v: ind.batch(c, h, l, c)),
|
||||
"CloseVsOpen": (lambda: ta.CloseVsOpen(), lambda ind, h, l, c, v: ind.batch(c, h, l, c)),
|
||||
"ThreeDrives": (
|
||||
lambda: ta.ThreeDrives(),
|
||||
lambda ind, h, l, c, v: ind.batch(c, h, l, c),
|
||||
@@ -2707,6 +2724,16 @@ def test_fib_time_zones_reference():
|
||||
assert t.update((151.0, 155.0, 151.0, 151.0, 1.0, 4)) == pytest.approx((0.0, 1.0))
|
||||
assert t.update((151.0, 155.0, 151.0, 151.0, 1.0, 5)) == pytest.approx((1.0, 3.0))
|
||||
|
||||
|
||||
def test_spread_ar1_coefficient_reference():
|
||||
t = ta.SpreadAr1Coefficient(20)
|
||||
assert t.update(1.0, 1.0) is None
|
||||
# Spread a - b grows by exactly 1 each bar (unit root) => rho == 1.
|
||||
a = np.array([2.0 * i for i in range(40)])
|
||||
b = np.array([float(i) for i in range(40)])
|
||||
out = ta.SpreadAr1Coefficient(20).batch(a, b)
|
||||
assert math.isclose(out[-1], 1.0, abs_tol=1e-9)
|
||||
|
||||
# --- Lifecycle ------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -3024,6 +3051,7 @@ def test_orderbook_indicators_streaming_equals_batch():
|
||||
ta.Microprice,
|
||||
ta.QuotedSpread,
|
||||
ta.DepthSlope,
|
||||
lambda: ta.OrderFlowImbalance(10),
|
||||
):
|
||||
batch = make().batch(snaps)
|
||||
streamer = make()
|
||||
@@ -3043,6 +3071,9 @@ def test_tradeflow_indicators_streaming_equals_batch():
|
||||
ta.SignedVolume,
|
||||
ta.CumulativeVolumeDelta,
|
||||
lambda: ta.TradeImbalance(5),
|
||||
lambda: ta.Vpin(8.0, 5),
|
||||
lambda: ta.AmihudIlliquidity(14),
|
||||
lambda: ta.RollMeasure(14),
|
||||
):
|
||||
batch = make().batch(price, size, is_buy)
|
||||
streamer = make()
|
||||
|
||||
Reference in New Issue
Block a user