SimTradeIndicatorsLibrary "SimTradeIndicators"
SimTrade indicator library — exact parity with Python pipeline (TA-Lib + pandas_ta).
Each function replicates the formula used in base.py / signals.py so that
PulseWire charts match the GPU hunt / validator / live engine outputs.
Formula sources:
TA-Lib → RSI, ATR, EMA, MACD, CCI, Stoch, WILLR, MFI, ADX, PSAR, OBV, BBANDS, AROON, PPO, AD
pandas_ta → SuperTrend, Vortex, Ichimoku, Donchian, HMA, TSI, CMF, EFI, CHOP, Heikin-Ashi
Manual → Keltner (EMA+ATR Wilder), TTM Squeeze, Chandelier Exit, VWAP reset, Pivot Points
Known intentional deviations (documented):
- Stoch trigger 11 uses Full %D (double-smoothed), not single %K
- OBV filter 206 uses windowed 800-bar OBV (GPU-aligned, not cumulative)
- Pivot Points use rolling window, not session-based (see pivot_pp notes)
- EMA has longer warmup in TA-Lib (~50 bars unstable period) vs TW (from bar 1); steady-state identical
smma(src, length)
SMMA / Wilder RMA. alpha = 1/length. Matches talib "RMA" used for ATR/RSI internally.
Parameters:
src (float) : Source series
length (simple int) : Period
Returns: RMA value
hma(src, length)
HMA (Hull Moving Average). HMA = WMA(2·WMA(N/2) − WMA(N), √N). Matches pandas_ta.hma.
Parameters:
src (float) : Source series
length (simple int) : Period
Returns: HMA value
dema(src, length)
DEMA (Double EMA) = 2·EMA − EMA(EMA). Matches talib.DEMA.
Parameters:
src (float) : Source series
length (simple int) : Period
Returns: DEMA value
tema(src, length)
TEMA (Triple EMA) = 3·EMA − 3·EMA² + EMA³. Matches talib.TEMA.
Parameters:
src (float) : Source series
length (simple int) : Period
Returns: TEMA value
atr_wilder(length)
ATR using Wilder RMA. Identical to talib.ATR and TW ta.atr.
Parameters:
length (simple int) : Period (default 14)
Returns: ATR value
atr_percentile_pct(length, lookback)
ATR percentile rank over a rolling window. Matches Python vol_filter 201.
Logic: for each bar count how many ATR values in are <= current ATR,
return that fraction as 0..100. Warmup bars (< lookback + length) return 50.0.
Parameters:
length (simple int) : ATR period / Wilder RMA (default 14). Matches params .
lookback (simple int) : Rolling window for percentile rank (default 30). Matches params .
Returns: Percentile rank 0..100 (pass filter when >= pct_min / params )
bbands(src, length, mult)
Bollinger Bands. Returns .
mid = SMA. Matches talib.BBANDS (matype=0 = SMA).
Parameters:
src (float) : Source series (typically close)
length (simple int) : Period (default 20)
mult (float) : Standard deviation multiplier (default 2.0)
Returns:
bb_pctb(src, length, mult)
Bollinger Bands %B = (close − lower) / (upper − lower).
Returns 0.5 during warmup (matches Python _nan50 fallback in base.bb_pctb).
Parameters:
src (float) : Source series
length (simple int) : Period (default 20)
mult (float) : Multiplier (default 2.0)
Returns: %B value
bb_width_x1000(src, length, mult)
BB bandwidth × 1000 / mid. Used by vol filter 202 (bb_width).
Parameters:
src (float) : Source series
length (simple int) : Period (default 20)
mult (float) : Multiplier (default 2.0)
Returns: (upper − lower) / |mid| × 1000
keltner(ema_period, atr_period, mult)
Keltner Channel. mid = EMA(close, ema_period), band = ATR(atr_period) Wilder RMA.
IMPORTANT: this is the TW-standard formula. NOT pandas_ta kc(mamode="ema") which uses EMA(TR).
That version produces ~40% narrower bands than TW. This library uses the correct RMA(ATR) band.
Parameters:
ema_period (simple int) : EMA period for midline (default 20)
atr_period (simple int) : ATR period for band width (default 10)
mult (float) : ATR multiplier (default 1.5)
Returns:
keltner_width_x1000(period, mult)
Keltner Channel bandwidth × 1000 / mid. Used by vol filter 204 (keltner_width).
Parameters:
period (simple int) : Period for both EMA and ATR (default 20)
mult (float) : ATR multiplier (default 1.5)
Returns: (upper − lower) / |mid| × 1000
choppiness(length)
Choppiness Index. CHOP = 100·log10(Σ ATR1 / (HH − LL)) / log10(N).
Matches pandas_ta.chop and TW built-in CHOP. Returns 50.0 during warmup.
Parameters:
length (simple int) : Period (default 14)
Returns: CHOP value
rsi_val(src, length)
RSI using Wilder RMA. Identical to talib.RSI and TW ta.rsi.
Returns 50.0 during warmup (matches Python _nan50 fallback).
Parameters:
src (float) : Source series (typically close)
length (simple int) : Period (default 14)
Returns: RSI value
cci_val(length)
CCI = (typical − SMA(typical)) / (0.015 · mean_deviation). Matches talib.CCI.
Returns 0.0 during warmup (matches Python _nan0 fallback).
Parameters:
length (simple int) : Period (default 20)
Returns: CCI value
stoch_raw_k(k_period)
Stochastic raw %K (no smoothing). Matches base.stoch_k (talib slowk_period=1).
NOTE: TW ta.stoch default smooths %K with SMA(3). This is the unsmoothed fast %K.
Used by filter 103 (stoch_k_below).
Parameters:
k_period (simple int) : Lookback period (default 14)
Returns: Raw %K (50.0 during warmup)
stoch_full_d(k_period, d_period)
Full Stochastic %D = SMA(SMA(raw%K, d_period), d_period). Matches talib.STOCH output.
Used by trigger 11 (stoch_cross). NOT single-smoothed %K — lag is +2-3 bars vs TW default.
Parameters:
k_period (simple int) : Raw %K lookback (default 14)
d_period (simple int) : Smoothing applied twice (default 3)
Returns: Full Stochastic %D (50.0 during warmup)
williams_r(length)
Williams %R = −100 · (HH − close) / (HH − LL). Matches talib.WILLR.
Range: −100 to 0. Returns −50.0 during warmup.
Parameters:
length (simple int) : Period (default 14)
Returns: Williams %R value
mfi_val(length)
MFI (Money Flow Index). Matches talib.MFI.
Returns 50.0 during warmup.
Parameters:
length (simple int) : Period (default 14)
Returns: MFI value
macd_val(src, fast, slow, signal_period)
MACD. Returns . Identical to talib.MACD.
All NaN values replaced with 0.0 (matches Python _nan0).
Parameters:
src (float) : Source series
fast (simple int) : Fast EMA period (default 12)
slow (simple int) : Slow EMA period (default 26)
signal_period (simple int) : Signal EMA period (default 9)
Returns:
ppo_val(src, fast, slow)
PPO = (EMA(fast) − EMA(slow)) / EMA(slow) × 100. Matches talib.PPO.
Returns 0.0 during warmup.
Parameters:
src (float) : Source series
fast (simple int) : Fast period (default 12)
slow (simple int) : Slow period (default 26)
Returns: PPO value
tsi_val(src, long_period, short_period)
TSI (True Strength Index). Matches pandas_ta.tsi parameter order.
TSI = 100 · EMA(EMA(Δclose, slow), fast) / EMA(EMA(|Δclose|, slow), fast)
slow is the OUTER (first) smoothing, fast is the INNER (second). Same as TW.
Parameters:
src (float) : Source series
long_period (simple int) : Outer (slow) EMA period (default 25)
short_period (simple int) : Inner (fast) EMA period (default 13)
Returns: TSI value (0.0 during warmup)
adx_di(length)
ADX + DI lines. Returns . Matches talib.ADX/PLUS_DI/MINUS_DI.
Uses Wilder RMA (identical to TW ta.dmi / ta.adx).
Parameters:
length (simple int) : Period (default 14)
Returns: — 0.0 during warmup
supertrend_val(length, mult)
SuperTrend direction and value. Matches pandas_ta.supertrend (RMA ATR).
Returns : direction = 1 (bull) or −1 (bear).
Parameters:
length (simple int) : ATR period (default 10)
mult (float) : ATR multiplier (default 3.0)
Returns:
psar_val(start, inc, max_af)
Parabolic SAR. Returns . Matches talib.SAR.
direction = 1 if close > SAR (bull), −1 bear.
Parameters:
start (simple float) : Initial AF / step (default 0.02)
inc (simple float) : AF increment per bar (default 0.02)
max_af (simple float) : Maximum AF cap (default 0.2)
Returns:
aroon_val(length)
Aroon Up and Down. Returns . Matches talib.AROON.
ta.aroon does not exist in Pine v6 — computed manually:
Aroon Up = (length − bars since highest high over length+1 bars) / length × 100
Aroon Down = (length − bars since lowest low over length+1 bars) / length × 100
This is identical to talib.AROON and PulseWire's built-in Aroon indicator.
Returns 50.0 during warmup (matches Python _nan50).
Parameters:
length (simple int) : Period (default 25)
Returns:
vortex_diff(length)
Vortex Indicator difference (VI+ − VI−). Matches base.vortex.
Positive = bullish regime, negative = bearish. Returns 0.0 during warmup.
Parameters:
length (simple int) : Period (default 14)
Returns: VI+ minus VI−
linreg_slope(src, length)
Linear Regression Slope. Matches talib.LINEARREG_SLOPE exactly.
Computes OLS slope for x = 0..N-1 (oldest=0, newest=N-1).
Positive = uptrend, negative = downtrend. Returns 0.0 during warmup.
Parameters:
src (float) : Source series
length (simple int) : Period (default 20)
Returns: Slope value
obv_val()
OBV (On-Balance Volume). Cumulative. Matches talib.OBV.
Returns: Cumulative OBV
vwap_reset(reset_bars)
VWAP with periodic session reset. Matches base.vwap(reset_bars).
reset_bars=24 on H1 ≈ daily VWAP (crypto 24/7). reset_bars=6 on H4 ≈ daily.
reset_bars=0 uses TW built-in ta.vwap (session anchor).
Parameters:
reset_bars (simple int) : Bars per session (0 = TW session anchor, 24 = H1 daily, 6 = H4 daily)
Returns: VWAP value
cmf_val(length)
CMF (Chaikin Money Flow) = Σ(CLV·vol) / Σvol. Matches pandas_ta.cmf.
CLV = ((close − low) − (high − close)) / (high − low). Returns 0.0 during warmup.
Parameters:
length (simple int) : Period (default 20)
Returns: CMF value (−1 to 1)
ad_val()
Accumulation/Distribution Line. Matches talib.AD.
Returns: Cumulative A/D value
efi_val(length)
EFI (Elder Force Index). EFI = EMA((close − close ) · volume, length).
Matches pandas_ta.efi. Returns 0.0 during warmup.
Parameters:
length (simple int) : EMA period (default 13)
Returns: EFI value
ichimoku_val(tenkan_period, kijun_period, senkou_b_period)
Ichimoku lines. Returns .
Matches pandas_ta.ichimoku with CORRECTED column mapping (ISA, ISB, ITS, IKS, ICS).
senkou_a/b are plotted 26 bars AHEAD in TW — values here are for current bar alignment.
Parameters:
tenkan_period (simple int) : Tenkan-sen period (default 9)
kijun_period (simple int) : Kijun-sen period (default 26)
senkou_b_period (simple int) : Senkou B period (default 52)
Returns:
donchian_val(length)
Donchian Channel. Returns .
upper = highest(high, N), lower = lowest(low, N). Matches pandas_ta.donchian.
NOTE: lookback may differ ±1 bar from TA-Lib; consistent across all pipeline stages.
Trigger 37 (donchian_break) compares close > upper — use upper in Pine.
Parameters:
length (simple int) : Period (default 20)
Returns:
ttm_squeeze_val(bb_period, bb_mult, kc_period, kc_mult)
TTM Squeeze. Returns .
squeeze_on: BB inside KC (volatility compression).
momentum: ta.linreg(close − (donchian_mid + SMA) / 2, bb_period)
EXACT match with John Carter formula and base.ttm_squeeze after fix.
Parameters:
bb_period (simple int) : BB period (default 20)
bb_mult (float) : BB multiplier (default 2.0)
kc_period (simple int) : KC period — same for EMA midline and ATR band (default 20)
kc_mult (float) : KC ATR multiplier (default 1.5)
Returns:
chandelier_val(period, mult)
Chandelier Exit. Returns .
long_exit = highest(high, period) − mult · ATR(period)
short_exit = lowest(low, period) + mult · ATR(period)
Exact match with base.chandelier_exit. Both include current bar in rolling max/min.
Trigger 45 fires when close crosses long_exit or short_exit (up = bull, down = bear).
Parameters:
period (simple int) : Lookback and ATR period (default 22)
mult (float) : ATR multiplier (default 3.0)
Returns:
ha_close()
Heikin Ashi close. HA_close = (open + high + low + close) / 4. Matches pandas_ta.ha.
Returns: HA close value
ha_open()
Heikin Ashi open. HA_open = (HA_open + HA_close ) / 2.
Trigger 48 fires on HA candle color flip: HA_close vs HA_open.
Returns: HA open value
pivot_pp(period)
Rolling Pivot Point (floor method). Matches base.pivot_points.
pp = (max(high, period bars ago) + min(low, period bars ago) + close ) / 3
NOTE: Rolling window, NOT session-based. H1 default period=24 ≈ 1 day (crypto 24/7).
For H4 set period=6 (6 × 4h = 1 day). TW Pivot Points use session H/L/C — differs.
Parameters:
period (simple int) : Rolling lookback (default 24)
Returns: Pivot point value
pivot_r1_s1(period)
Rolling R1 and S1 levels. Matches base.pivot_points r1/s1.
r1 = 2·pp − lowest_low, s1 = 2·pp − highest_high
Parameters:
period (simple int) : Rolling lookback (default 24)
Returns: Library

NeuraLib Expansion: Advanced Model LayersNeuraLib_Models is the companion model expansion for NeuraLib .
NeuraLib provides the runtime: tensors, graph execution, datasets, scalers, losses, optimizers, training, inference, and validation tools. NeuraLib_Models builds on that foundation with higher-level neural architectures that are difficult and repetitive to write by hand.
The purpose of this expansion is to keep the main NeuraLib runtime clean, compact, and general, while giving researchers ready-to-use model families for sequence learning, attention, temporal pattern extraction, and Reinforcement Learning workflows.
----------------------------------------------------------------------------------------------------------------
🔷 HOW IT FITS INTO NEURALIB
NeuraLib_Models is built entirely on top of the public NeuraLib API. It does not replace the main runtime and it does not introduce a separate training engine.
After importing NeuraLib_Models, its fluent methods become available directly on NeuraLib `Sequential` models. The expansion alias can remain unused in the layer chain.
//@version=6
indicator("NeuraLib Models Quick Start", overlay = false, calc_bars_count = 600)
import Alien_Algorithms/NeuraLib/1 as nl
import Alien_Algorithms/NeuraLib_Models/1 as models
var nl.Sequential model = nl.sequential("advanced_model")
var float qLong = na
var float qFlat = na
var float qShort = na
if barstate.isfirst
model := model
.input(array.from(8), "sequence")
.temporalConvStack(4, 2, 2, 2, 1, 1, nl.ActivationKind.relu, 0.0, "temporal")
.globalAvgPool1d(3, 2, "pool")
.duelingQHead(4, 3, nl.ActivationKind.relu, "dueling_head")
.build(nl.rng(7))
float ret0 = na(close ) or close == 0.0 ? 0.0 : close / close - 1.0
float ret1 = na(close ) or close == 0.0 ? 0.0 : close / close - 1.0
float ret2 = na(close ) or close == 0.0 ? 0.0 : close / close - 1.0
float ret3 = na(close ) or close == 0.0 ? 0.0 : close / close - 1.0
float atrValue = ta.atr(14)
float atr0 = close == 0.0 ? 0.0 : atrValue / close
float atr1 = close == 0.0 ? 0.0 : atrValue / close
float atr2 = close == 0.0 ? 0.0 : atrValue / close
float atr3 = close == 0.0 ? 0.0 : atrValue / close
bool ready = not na(ret3) and not na(atr3)
if ready
nl.Tensor state = nl.vector(array.from(ret3, atr3, ret2, atr2, ret1, atr1, ret0, atr0), "state_window")
nl.Tensor qValues = model.predict(state)
qLong := qValues.get1d(0)
qFlat := qValues.get1d(1)
qShort := qValues.get1d(2)
plot(qLong, "Q long", color = color.lime, linewidth = 2)
plot(qFlat, "Q flat", color = color.gray)
plot(qShort, "Q short", color = color.red, linewidth = 2)
hline(0.0, "Zero", color = color.new(color.gray, 70))
The model is still a normal NeuraLib model. You still call `.compile()`, `.trainOnBatch()`, `.predict()`, `.evaluate()`, `.getWeightsArray()`, and `.softUpdateFrom()` from the main library.
----------------------------------------------------------------------------------------------------------------
🔷 WHY THIS EXPANSION EXISTS
The main NeuraLib library is the foundation. It exposes a graph engine powerful enough to create custom architectures, but repeatedly building LSTM gates, attention projections, residual blocks, Conv1D stacks, or Transformer paths from raw graph operations would be too verbose for everyday research.
NeuraLib_Models packages those patterns into readable blocks:
Temporal models : Conv1D blocks, temporal convolution stacks, global average pooling, and global max pooling for flattened sequence inputs.
Recurrent models : LSTM and GRU blocks for compact sequence memory.
Attention models : Self-attention, multi-head self-attention, cross-attention, Transformer encoder blocks, Transformer encoder stacks, and Transformer decoder blocks.
Residual models : Residual dense blocks for deeper feedforward paths.
Reinforcement Learning heads : Q-head blocks and dueling Q-heads for action-value style outputs.
Replay utilities : Deterministic Prioritized Experience Replay for reproducible Pine research.
Sequence helpers : Positional encoding for token, sequence, and attention workflows.
----------------------------------------------------------------------------------------------------------------
🔷 PRACTICAL EXAMPLES
🔸 Temporal Conv Model With Dueling Q-Head
This pattern is useful when a flattened sequence contains recent market states and the output represents action values.
//@version=6
indicator("NeuraLib Models Temporal Q Example", overlay = false, calc_bars_count = 600)
import Alien_Algorithms/NeuraLib/1 as nl
import Alien_Algorithms/NeuraLib_Models/1 as models
var nl.Sequential qModel = nl.sequential("temporal_q_model")
var nl.WindowDataset qDataset = nl.windowDataset(8, 3, 400, "q_rows")
var float qDown = na
var float qNeutral = na
var float qUp = na
var float qLoss = na
if barstate.isfirst
nl.CompileConfig cfg = nl.compileConfig()
cfg := cfg
.presetQValues()
.optimizer(nl.adamW(0.001))
.withTrainingGate(true)
qModel := qModel
.input(array.from(8), "state_window")
.temporalConvStack(4, 2, 2, 2, 1, 1, nl.ActivationKind.relu, 0.0, "temporal")
.globalAvgPool1d(3, 2, "pool")
.duelingQHead(4, 3, nl.ActivationKind.relu, "dueling_head")
.compile(cfg)
qDataset := qDataset
.setInputScaler(nl.ScalerKind.zScore)
.setTargetScaler(nl.ScalerKind.none)
float ret0 = na(close ) or close == 0.0 ? 0.0 : close / close - 1.0
float ret1 = na(close ) or close == 0.0 ? 0.0 : close / close - 1.0
float ret2 = na(close ) or close == 0.0 ? 0.0 : close / close - 1.0
float ret3 = na(close ) or close == 0.0 ? 0.0 : close / close - 1.0
float ret4 = na(close ) or close == 0.0 ? 0.0 : close / close - 1.0
float atrValue = ta.atr(14)
float atr0 = close == 0.0 ? 0.0 : atrValue / close
float atr1 = close == 0.0 ? 0.0 : atrValue / close
float atr2 = close == 0.0 ? 0.0 : atrValue / close
float atr3 = close == 0.0 ? 0.0 : atrValue / close
float atr4 = close == 0.0 ? 0.0 : atrValue / close
bool rowReady = not na(ret4) and not na(atr4)
if rowReady
array features = array.from(ret4, atr4, ret3, atr3, ret2, atr2, ret1, atr1)
float downTarget = math.max(-ret0, 0.0)
float neutralTarget = math.max(0.002 - math.abs(ret0), 0.0)
float upTarget = math.max(ret0, 0.0)
qDataset := qDataset.pushRow(features, array.from(downTarget, neutralTarget, upTarget))
if qDataset.ready(48)
if barstate.islastconfirmedhistory
nl.Batch train = qDataset.trainBatch(12)
qModel := qModel.trainOnBatch(train.inputTensor, train.targetTensor)
qLoss := qModel.trainStats.lastLoss
nl.Tensor liveState = nl.vector(array.from(ret3, atr3, ret2, atr2, ret1, atr1, ret0, atr0), "live_state")
nl.Tensor scaledState = qDataset.scaleInput(liveState)
nl.Tensor qValues = qModel.predict(scaledState)
qDown := qValues.get1d(0)
qNeutral := qValues.get1d(1)
qUp := qValues.get1d(2)
plot(qDown, "Q down", color = color.red, linewidth = 2)
plot(qNeutral, "Q neutral", color = color.gray)
plot(qUp, "Q up", color = color.lime, linewidth = 2)
plot(qLoss, "Training loss", color = color.orange)
hline(0.0, "Zero", color = color.new(color.gray, 70))
Input shape `array.from(8)` represents a flattened 4 step by 2 feature sequence. The temporal stack extracts short sequence structure, pooling compresses the sequence, and the dueling head separates value and advantage paths before producing action scores. The example trains only on the last confirmed historical bar so it remains safe to paste onto long charts.
🔸 Transformer Encoder For Token Rows
Attention models are useful when each row is a token or time step, and each column is a feature dimension.
//@version=6
indicator("NeuraLib Models Transformer Encoder Example", overlay = false, calc_bars_count = 600)
import Alien_Algorithms/NeuraLib/1 as nl
import Alien_Algorithms/NeuraLib_Models/1 as models
var nl.Sequential encoder = nl.sequential("encoder_model")
var float tokenSignal = na
var float tokenContext = na
var float tokenVolatility = na
if barstate.isfirst
encoder := encoder
.input(array.from(4), "tokens")
.multiHeadSelfAttention(4, 2, true, "mha")
.transformerEncoder(4, true, 2, nl.ActivationKind.geluApprox, "encoder", 0.05, 2)
.build(nl.rng(11))
float emaValue = ta.ema(close, 21)
float atrValue = ta.atr(14)
float ret0 = na(close ) or close == 0.0 ? 0.0 : close / close - 1.0
float ret1 = na(close ) or close == 0.0 ? 0.0 : close / close - 1.0
float ret2 = na(close ) or close == 0.0 ? 0.0 : close / close - 1.0
float ret3 = na(close ) or close == 0.0 ? 0.0 : close / close - 1.0
float emaGap0 = emaValue == 0.0 ? 0.0 : close / emaValue - 1.0
float emaGap1 = emaValue == 0.0 ? 0.0 : close / emaValue - 1.0
float emaGap2 = emaValue == 0.0 ? 0.0 : close / emaValue - 1.0
float emaGap3 = emaValue == 0.0 ? 0.0 : close / emaValue - 1.0
float atr0 = close == 0.0 ? 0.0 : atrValue / close
float atr1 = close == 0.0 ? 0.0 : atrValue / close
float atr2 = close == 0.0 ? 0.0 : atrValue / close
float atr3 = close == 0.0 ? 0.0 : atrValue / close
bool ready = not na(ret3) and not na(emaGap3) and not na(atr3)
if ready
nl.Tensor tokens = nl.vector(array.from(
ret3, emaGap3, atr3, -1.0,
ret2, emaGap2, atr2, -0.33,
ret1, emaGap1, atr1, 0.33,
ret0, emaGap0, atr0, 1.0), "tokens").reshape(array.from(4, 4))
nl.Tensor encoded = encoder.predict(tokens)
tokenSignal := encoded.get1d(12)
tokenContext := encoded.get1d(13)
tokenVolatility := encoded.get1d(14)
plot(tokenSignal, "Latest token signal", color = color.aqua, linewidth = 2)
plot(tokenContext, "Latest token context", color = color.purple)
plot(tokenVolatility, "Latest token volatility", color = color.orange)
hline(0.0, "Zero", color = color.new(color.gray, 70))
In this example, each input row has 4 features. `headCount` is 2, so the model dimension is split into two attention heads.
Attention rule: `modelDim` must be divisible by `headCount`, and the current implementation supports up to 8 heads.
🔸 Prioritized Experience Replay
Prioritized Experience Replay stores examples with priorities, then returns reproducible weighted samples. This is especially useful for Reinforcement Learning experiments where high-error transitions should be revisited more often.
//@version=6
indicator("NeuraLib Models PER Example", overlay = false, calc_bars_count = 1200)
import Alien_Algorithms/NeuraLib/1 as nl
import Alien_Algorithms/NeuraLib_Models/1 as models
var models.PrioritizedReplayBuffer replay = models.prioritizedReplayBuffer(4, 2, 300, "replay")
var nl.Sequential replayModel = nl.sequential("replay_q_model")
var float replayLoss = na
var float firstImportanceWeight = na
var float replayRows = na
if barstate.isfirst
nl.CompileConfig cfg = nl.compileConfig()
cfg := cfg
.presetQValues()
.optimizer(nl.adamW(0.001))
.trainEveryCall()
replayModel := replayModel
.input(array.from(4), "state")
.dense(8, nl.ActivationKind.relu, "hidden")
.qHead(2, nl.ActivationKind.linear, "q_values")
.compile(cfg)
float rsiValue = ta.rsi(close, 14)
float emaValue = ta.ema(close, 21)
float atrValue = ta.atr(14)
float atrPct = close == 0.0 ? 0.0 : atrValue / close
float momentum = na(close ) or close == 0.0 ? 0.0 : close / close - 1.0
float nextReturn = na(close ) ? 0.0 : nl.nextReturnValue(close , close)
bool rowReady = not na(rsiValue ) and not na(emaValue ) and not na(atrPct ) and not na(momentum )
if rowReady
float prevEma = emaValue
float priceVsEma = prevEma == 0.0 ? 0.0 : close / prevEma - 1.0
array stateFeatures = array.from(rsiValue / 100.0, priceVsEma, atrPct , momentum )
array targetValues = array.from(math.max(-nextReturn, 0.0), math.max(nextReturn, 0.0))
float priority = math.abs(nextReturn) + 0.0001
replay := replay.pushExperience(stateFeatures, targetValues, priority)
replayRows := float(replay.size())
if replay.ready(32)
models.PrioritizedReplaySample sample = replay.sampleBatch(32, 0.6, 0.4, 17)
replayModel := replayModel.trainOnBatch(sample.batch.inputTensor, sample.batch.targetTensor)
replayLoss := replayModel.trainStats.lastLoss
firstImportanceWeight := sample.weightArray.size() > 0 ? sample.weightArray.get(0) : na
if sample.indexArray.size() > 0
replay := replay.updatePriority(sample.indexArray.get(0), replayLoss + 0.0001)
plot(replayLoss, "Replay training loss", color = color.orange, linewidth = 2)
plot(firstImportanceWeight, "First sample weight", color = color.aqua)
The returned sample includes:
batch : A normal NeuraLib `Batch` containing sampled inputs and targets.
indexArray : Logical replay indices that can be passed back to `updatePriority()`.
weightArray : Normalized importance weights for custom loss weighting or diagnostics.
sampleRows : Number of sampled rows.
PER sampling is deterministic for a given buffer, `batchSize`, and `seed`. That makes Pine tests and live research easier to reproduce.
----------------------------------------------------------------------------------------------------------------
🔷 MODEL FAMILIES
🔸 Residual Dense Blocks
`residualDense()` adds a feedforward residual block. Residual paths help preserve information through deeper models and reduce the chance that a dense stack destroys useful features too early.
🔸 Conv1D And Temporal Convolution Stacks
`conv1d()` and `temporalConvStack()` operate on flattened sequence inputs. A sequence with `timeSteps = 4` and `featureCount = 2` is represented as 8 input features. These blocks are useful for local temporal structure, short rolling windows, feature rhythm, and compact pattern extraction.
🔸 Global Pooling
`globalAvgPool1d()` and `globalMaxPool1d()` compress flattened sequence outputs into feature-level summaries. Average pooling captures broad sequence behavior, while max pooling emphasizes the strongest activation per feature.
🔸 LSTM And GRU Blocks
`lstm()` and `gru()` provide recurrent sequence memory over flattened time-series inputs. They are useful when the order of recent states matters more than a single snapshot.
🔸 Attention And Transformers
`selfAttention()`, `multiHeadSelfAttention()`, `crossAttention()`, `transformerEncoder()`, `transformerEncoderStack()`, and `transformerDecoder()` bring attention-style modeling into Pine. They are designed for compact token matrices, packed target-memory layouts, and small Transformer-style research models that fit PulseWire limits.
🔸 Q-Heads And Dueling Q-Heads
`qHeadBlock()` creates action-value style outputs. `duelingQHead()` splits the model into value and advantage branches, then recombines them into Q-values. This is useful when you want the model to estimate both the overall state value and the relative value of each action.
🔸 Positional Encoding
`pushPositionalEncoding()` adds sinusoidal position features to a NeuraLib `FeatureBuilder`. This helps attention-style models distinguish where a token or time step sits in a sequence.
----------------------------------------------------------------------------------------------------------------
🔷 FEATURE QUICK REFERENCE
Built on NeuraLib : Uses the main NeuraLib graph, tensor, training, optimizer, dataset, and inference runtime.
Fluent API : Adds methods directly to NeuraLib `Sequential` models after import.
Block factories : Provides standalone `GraphBlock` factories for users who want lower-level composition.
Temporal modeling : Conv1D, temporal convolution stacks, and 1D pooling.
Recurrent modeling : LSTM and GRU sequence blocks.
Attention modeling : Self-attention, multi-head self-attention, cross-attention, encoders, encoder stacks, and decoders.
Reinforcement Learning support : Q-heads, dueling Q-heads, target-model soft updates through NeuraLib, and Prioritized Experience Replay.
Reproducible replay : PER sampling is deterministic for a given seed.
Shape guardrails : Advanced builders validate expected model feature counts and attention head compatibility.
----------------------------------------------------------------------------------------------------------------
🔷 IMPORTANT USAGE NOTES
Import order matters : Import `NeuraLib` first, then `NeuraLib_Models`.
The alias can be unused : The imported expansion registers methods on NeuraLib types, so `.lstm()`, `.gru()`, `.transformerEncoder()`, and similar methods can be called in the model chain.
Keep models compact : Pine Script has execution limits. Start with small hidden sizes, short sequences, and low head counts.
Control chart history : Use `calc_bars_count = 600` in `indicator()` when needed to balance available training history against model size and execution time.
Respect sequence shapes : Conv1D, temporal stacks, LSTM, and GRU methods expect flattened sequence sizes of `timeSteps * featureCount`.
Respect attention shapes : Attention methods expect each input row to have `modelDim` columns. Cross-attention and decoder blocks use packed rows.
Use NeuraLib guardrails : Train/validation splits, scalers, EarlyStopper, training gates, and gradient clipping remain part of the main NeuraLib workflow.
----------------------------------------------------------------------------------------------------------------
🔷 API REFERENCE
🔸 Sequential Methods
residualDense(hiddenUnits, activationKind, dropoutRate, name) : Adds a residual dense block.
duelingQHead(hiddenUnits, actionCount, activationKind, name) : Adds a dueling value/advantage Q-head.
conv1d(timeSteps, featureCount, filters, kernelSize, stride, activationKind, name) : Adds a Conv1D block for flattened sequences.
temporalConvStack(timeSteps, featureCount, filters, kernelSize, layers, stride, activationKind, dropoutRate, name) : Adds stacked temporal Conv1D layers.
globalAvgPool1d(timeSteps, featureCount, name) : Adds global average pooling over a flattened 1D sequence.
globalMaxPool1d(timeSteps, featureCount, name) : Adds global max pooling over a flattened 1D sequence.
lstm(timeSteps, featureCount, units, activationKind, name) : Adds an LSTM scan block.
gru(timeSteps, featureCount, units, activationKind, name) : Adds a GRU scan block.
selfAttention(modelDim, causal, name) : Adds row-wise self-attention.
multiHeadSelfAttention(modelDim, headCount, causal, name) : Adds multi-head self-attention.
crossAttention(queryRows, memoryRows, modelDim, headCount, name) : Adds packed query-memory cross-attention.
transformerEncoder(modelDim, causal, ffMultiplier, activationKind, name, dropoutRate, headCount) : Adds one Transformer encoder block.
transformerEncoderStack(modelDim, layers, causal, ffMultiplier, activationKind, dropoutRate, headCount, name) : Adds repeated Transformer encoder blocks.
transformerDecoder(targetRows, memoryRows, modelDim, headCount, ffMultiplier, activationKind, dropoutRate, name) : Adds a packed target-memory Transformer decoder.
🔸 GraphBlock Factories
qHeadBlock(inputFeatures, actionCount, activationKind, name) : Creates a Q-head block.
duelingQHeadBlock(inputFeatures, hiddenUnits, actionCount, activationKind, name) : Creates a dueling Q-head block.
residualDenseBlock(inputFeatures, hiddenUnits, activationKind, dropoutRate, name) : Creates a residual dense block.
conv1dBlock(timeSteps, featureCount, filters, kernelSize, stride, activationKind, name) : Creates a Conv1D block.
temporalConvStackBlock(timeSteps, featureCount, filters, kernelSize, layers, stride, activationKind, dropoutRate, name) : Creates a temporal convolution stack.
globalAvgPool1dBlock(timeSteps, featureCount, name) and globalMaxPool1dBlock(timeSteps, featureCount, name) : Create pooling blocks.
lstmBlock(timeSteps, featureCount, units, activationKind, name) and gruBlock(timeSteps, featureCount, units, activationKind, name) : Create recurrent blocks.
selfAttentionBlock(modelDim, causal, name) , multiHeadSelfAttentionBlock(modelDim, headCount, causal, name) , and crossAttentionBlock(queryRows, memoryRows, modelDim, headCount, name) : Create attention blocks.
transformerEncoderBlock(modelDim, causal, ffMultiplier, activationKind, name, dropoutRate, headCount) and transformerDecoderBlock(targetRows, memoryRows, modelDim, headCount, ffMultiplier, activationKind, dropoutRate, name) : Create Transformer blocks.
🔸 Prioritized Experience Replay
prioritizedReplayBuffer(featureCount, targetCount, maxRows, name) : Creates a replay buffer.
pushExperience(featureRowArray, targetRowArray, priority) : Adds or overwrites one replay row.
sampleBatch(batchSize, alpha, beta, seed) : Returns a deterministic weighted sample.
updatePriority(index, priority) : Updates a sampled row priority.
toBatch() : Returns all replay rows in chronological order.
ready(minRows) , size() , and clear() : Replay buffer utilities.
🔸 Feature Helpers
pushPositionalEncoding(position, dimensions, maxPeriod, featurePrefix) : Appends sinusoidal positional encoding values to a NeuraLib `FeatureBuilder`.
NeuraLib_Models is for Pine Script developers who want higher-level neural architecture blocks without leaving the NeuraLib runtime. It is built for compact research models inside PulseWire's execution limits, not for oversized GPU-style networks.
All the diagrams in this publication are rendered natively on PulseWire using Pine3D
----------------------------------------------------------------------------------------------------------------
This work is licensed under (CC BY-NC-SA 4.0) , meaning usage is free for non-commercial purposes given that Alien_Algorithms is credited in the description for the underlying software. For commercial use licensing, contact Alien_Algorithms
Library

NeuraLib: A Native AI and Deep Learning RuntimeNeuraLib is a tensor-based, auto-differentiating Machine Learning runtime built natively for Pine Script™.
It brings real Deep Learning mechanisms that power modern Artificial Intelligence systems into PulseWire. Instead of relying on fixed formulas, static regressions, or rigid structures, NeuraLib gives Pine developers a different tool: a compact neural runtime that can learn from the features you feed it, using the architecture you define.
This means users are no longer limited to classical methods like Linear Regression, Logistic Regression, KNN, Naive Bayes, Kalman Filters, or Markov Chains. One can build adaptive architectures perfectly suited for custom indicators, strategies, regime detection, directional prediction, price transforms, and AI-assisted signal generation.
Using NeuraLib, one can build a model, collect market data, normalize it, run predictions, train through backpropagation, track validation behavior, and update weights directly inside PulseWire.
Furthermore, it is not necessary to directly display trained variables. The process can be a part of a larger script functionality, where AI-powered decision making changes how an indicator behaves.
The goal is to make real neural network workflows usable in Pine Script without hiding the important controls, being scalable with evolving market dynamics, and abstracting away the complexity that comes with such software. The provided API is highly modular and intuitive, using chained object-oriented programming for easy readability and use. The backend is engineered with fault-tolerance in mind, providing users with sanity checks and preventing common pitfalls by default.
Think of NeuraLib as a comprehensive machine learning ecosystem, containing:
A Model Builder : Define neural networks with readable chained calls like `.input()`, `.dense()`, and `.dropout()`.
An In-Pine Training Engine : Models calculate losses, backpropagate gradients, update weights, and produce predictions directly on chart data.
Automated Data Pipelines : Built-in datasets handle feature collection, robust scaling (Z-Score, Min-Max), validation holdout splits, and time-series rolling windows.
Finance-Native Loss Functions : Beyond standard error metrics, the engine includes Directional, Quantile, Multi-Horizon Weighted, and Sharpe-style losses tailored for trading.
Practical Training Controls : Layer Normalization, AdamW weight decay, gradient clipping, gradient accumulation, and early stopping are built in to prevent overfitting.
Advanced Optimizers : Train networks using RMSProp, Adam, or AdamW, paired with learning rate schedules like Warmup Cosine and Step Decay.
For newer users, this means you can start with a simple dense model. For advanced users, the same runtime exposes graph operations, custom blocks, tensors, matrix operations, optimizers, schedules, losses, and extension hooks.
In plain terms, a model receives a row of numbers called features, compares its output against a target, measures the error with a loss function, and then adjusts its internal weights to reduce that error next time.
----------------------------------------------------------------------------------------------------------------
🔷 WHAT MAKES IT DIFFERENT
🔸 Parity-tested neural math
NeuraLib’s core operations have been tested against established Machine Learning Runtimes outside of PulseWire (Such as Keras / TensorFlow / PyTorch).
The goal was not to imitate the appearance of Machine Learning, but to reproduce the math that is proven to work. Standard forward passes, gradients, losses, and optimizer behavior were checked for 1:1 algorithmic parity, with negligible differences coming from normal floating-point behavior.
That means the matrix math, backpropagation, and gradient updates running on your chart follow the same underlying logic expected from professional Machine Learning environments.
🔸 Matrix-first computation
NeuraLib uses tensor and matrix abstractions as the foundation of the runtime. Under the hood, it supports the operations needed for neural computation, including matrix multiplication, broadcasting, activation functions, softmax, slicing, concatenation, reductions, normalization, attention scoring, convolution-style operations, and recurrent scan blocks.
🔸 Auto-differentiating graph engine
NeuraLib makes the computational graph a first-class object.
You can use high-level Sequential models, or build custom GraphBlocks from lower-level operations. Once a custom block is connected to a model, the same runtime handles the backward pass. That means your custom architecture can be trained with the same `.trainOnBatch()` workflow as standard layers.
----------------------------------------------------------------------------------------------------------------
🔷 CUSTOM GRAPHS
The Sequential API is the easiest way to start, but NeuraLib is not just a list of built-in layers.
You can create a `GraphBlock`, add operations, set an output node, and plug that block into a model. Once connected, the runtime handles the backward pass and parameter updates.
Useful graph operations include:
Matrix multiplication, transpose, add, subtract, multiply, divide, and scale.
Activation functions and softmax.
Layer Normalization and Dropout.
Causal masking, slicing, concatenation, row reduction, and column reduction.
Global average pooling and global max pooling for 1D sequences.
Attention score and attention apply operations.
Conv1D, LSTM scan, and GRU scan primitives.
This is the foundation that allows companion model libraries to add advanced AI and Machine Learning architectures without changing the main NeuraLib runtime.
----------------------------------------------------------------------------------------------------------------
🔷 BUILT-IN DATA GUARDRAILS
NeuraLib is not only a training mechanism. It also includes guardrails for cleaner research:
Invalid rows are rejected : Dataset rows must match the configured feature and target counts, and rows containing `na` values are not inserted.
Shape checks protect model calls : Forward, training, backward, and evaluation paths validate input and target shapes before running expensive graph code.
Train and validation splits are separated : `trainBatch()` and `validationBatch()` use holdout rows instead of blending all rows into one batch.
Scaler leakage is controlled : Validation batches are scaled from the training-side profile where the dataset split requires it, so validation normalization does not learn from the holdout slice.
Rolling windows respect time order : `RollingDataset` supports target offsets and wrapped ring buffers while preserving chronological reads.
These checks help reduce common data poisoning and data leakage mistakes: wrong row widths, missing values, validation contamination, target-offset leakage, and accidental overtraining across every historical bar.
----------------------------------------------------------------------------------------------------------------
🔷 A FIRST MODEL
The basic API is intentionally readable. This creates a small model with dropout, one hidden layer, Huber loss, AdamW optimization, and MAE tracking.
//@version=6
indicator("NeuraLib Basic Model", overlay = false, calc_bars_count = 600)
import Alien_Algorithms/NeuraLib/1 as nl
var nl.Sequential model = nl.sequential("basic_model")
var float modelOutput = na
if barstate.isfirst
nl.CompileConfig cfg = nl.compileConfig()
cfg := cfg
.optimizer(nl.adamW(0.001))
.loss(nl.LossKind.huber)
.metric(nl.MetricKind.mae)
.withTrainingGate(true)
model := model
.input(array.from(4), "features")
.dropout(0.15)
.dense(8, nl.ActivationKind.relu, "hidden")
.dense(1, nl.ActivationKind.linear, "output")
.compile(cfg)
float rsiValue = ta.rsi(close, 14)
float emaValue = ta.ema(close, 21)
float atrValue = ta.atr(14)
float atrPct = close == 0.0 ? 0.0 : atrValue / close
float momentum = na(close ) or close == 0.0 ? 0.0 : close / close - 1.0
bool ready = not na(rsiValue) and not na(emaValue) and not na(atrPct) and not na(momentum)
if ready
float priceVsEma = emaValue == 0.0 ? 0.0 : close / emaValue - 1.0
nl.Tensor inputTensor = nl.vector(array.from(rsiValue, priceVsEma, atrPct, momentum), "features")
nl.Tensor outputTensor = model.predict(inputTensor)
modelOutput := outputTensor.get1d(0)
plot(modelOutput, "Untrained model output", color = color.aqua, linewidth = 2)
hline(0.0, "Zero", color = color.new(color.gray, 70))
The same model can then receive scaled batches from a dataset and train with `.trainOnBatch()`. The plot in this first example is the untrained forward output, included so the block can be pasted directly into an indicator.
----------------------------------------------------------------------------------------------------------------
🔷 A PRACTICAL DATA FLOW
Machine Learning models usually fail when the data pipeline is careless. Price, volume, volatility, and oscillators often live on very different scales. NeuraLib includes dataset and scaling helpers so the common workflow stays explicit:
Build a feature row.
Build a target row.
Push the row into a dataset.
Request a training batch.
Request a validation batch when needed.
Train, evaluate, predict, and inverse-scale targets when appropriate.
//@version=6
indicator("NeuraLib Return Validation Example", overlay = false, calc_bars_count = 600)
import Alien_Algorithms/NeuraLib/1 as nl
var nl.Sequential model = nl.sequential("returns_model")
var nl.WindowDataset dataset = nl.windowDataset(4, 1, 500, "returns_dataset")
var float predictedReturn = na
var float validationLossValue = na
var float trainingLossValue = na
if barstate.isfirst
nl.CompileConfig cfg = nl.compileConfig()
cfg := cfg
.optimizer(nl.adamW(0.003))
.loss(nl.LossKind.huber)
.metric(nl.MetricKind.mae)
.trainEveryCall()
model := model
.input(array.from(4), "features")
.dense(8, nl.ActivationKind.relu, "hidden")
.dropout(0.10, "dropout")
.dense(1, nl.ActivationKind.linear, "next_return")
.compile(cfg)
dataset := dataset
.setInputScaler(nl.ScalerKind.zScore)
.setTargetScaler(nl.ScalerKind.zScore)
float rsiValue = ta.rsi(close, 14)
float emaValue = ta.ema(close, 21)
float atrValue = ta.atr(14)
float atrPct = close == 0.0 ? 0.0 : atrValue / close
float momentum = na(close ) or close == 0.0 ? 0.0 : close / close - 1.0
float realizedReturn = na(close ) ? na : nl.nextReturnValue(close , close)
bool rowReady = not na(rsiValue ) and not na(emaValue ) and not na(atrPct ) and not na(momentum ) and not na(close )
if rowReady
float prevEma = emaValue
float priceVsEma = prevEma == 0.0 ? 0.0 : close / prevEma - 1.0
array features = array.from(
rsiValue ,
priceVsEma,
atrPct ,
momentum )
array target = array.from(nl.nextReturnValue(close , close))
dataset := dataset.pushRow(features, target)
if dataset.ready(64)
nl.Batch train = dataset.trainBatch(16)
nl.Batch validation = dataset.validationBatch(16)
model := model.trainOnBatch(train.inputTensor, train.targetTensor)
trainingLossValue := model.trainStats.lastLoss
nl.LossResult validationLoss = model.evaluate(validation.inputTensor, validation.targetTensor)
validationLossValue := validationLoss.value
bool liveReady = not na(rsiValue) and not na(emaValue) and not na(atrPct) and not na(momentum)
if liveReady
float livePriceVsEma = emaValue == 0.0 ? 0.0 : close / emaValue - 1.0
array liveFeatures = array.from(rsiValue, livePriceVsEma, atrPct, momentum)
nl.Tensor liveInput = nl.vector(liveFeatures, "live_features")
nl.Tensor scaledInput = dataset.scaleInput(liveInput)
nl.Tensor scaledPrediction = model.predict(scaledInput)
nl.Tensor rawPrediction = dataset.inverseScaleTarget(scaledPrediction)
predictedReturn := rawPrediction.get1d(0)
plot(realizedReturn, "Last realized return", color = color.gray)
plot(predictedReturn, "Predicted next return", color = color.aqua, linewidth = 2)
plot(validationLossValue, "Validation loss", color = color.orange)
plot(trainingLossValue, "Training loss", color = color.new(color.blue, 35))
hline(0.0, "Zero", color = color.new(color.gray, 70))
This example trains from completed historical pairs. The feature row comes from the previous bar, and the target is the return from that previous bar to the current bar. That keeps the example easy to inspect and avoids using future information in the feature row. When pasted into an indicator, it plots the last realized return, the model's predicted next return, training loss, and validation loss.
----------------------------------------------------------------------------------------------------------------
🔷 TWO PRACTICAL EXECUTION MODES
Deep Learning in Pine requires careful execution control. NeuraLib supports two main workflows.
🔸 1. Live-edge training
Use this when you want safer execution for larger models.
The dataset can collect rows across the chart, while the expensive training step only runs on the last confirmed historical bar. This helps avoid timeouts while still allowing the model to learn from recent prepared data.
cfg := cfg.withTrainingGate(true)
Use this for:
Larger models
More features
Rolling sequence inputs
Heavier architectures
Safer live-edge updates
🔸 2. Full-history training and inference
Use this when the model is intentionally small.
The model can train and infer across historical bars, which makes it possible to create lightweight adaptive indicators, such as an AI Moving Average that learns from recent local structure instead of using a fixed smoothing formula.
cfg := cfg.trainEveryCall()
Use this for:
Tiny dense models
Small batches
Fast adaptive filters
AI-assisted moving averages
Lightweight feature transforms
For full-history workflows, start small. A shallow model with 4 to 8 hidden units and a batch size of 8 or 16 is usually a better starting point than a deep architecture.
----------------------------------------------------------------------------------------------------------------
🔷 ADVANCED MODEL EXPANSION
NeuraLib is designed to act as the foundation for larger model libraries and community-built extensions.
To demonstrate this, NeuraLib Expansion: Advanced Model Layers is built entirely on top of the public NeuraLib API and is launched in parallel on day one. The expansion library is published as NeuraLib_Models . It extends the runtime with higher-level builders for LSTMs, GRUs, temporal convolution stacks, residual dense blocks, dueling Q-heads for Reinforcement Learning, Transformer-style attention blocks, and Prioritized Experience Replay utilities.
The important part is architectural: advanced models plug into the same runtime. NeuraLib remains the foundation for tensors, graph execution, optimization, training, inference, datasets, and scaling. After importing `NeuraLib_Models`, its fluent methods become available on NeuraLib `Sequential` models, so the expansion alias does not need to be referenced directly in the layer chain.
//@version=6
indicator("NeuraLib Models Extension Demo", overlay = false, calc_bars_count = 600)
import Alien_Algorithms/NeuraLib/1 as nl
import Alien_Algorithms/NeuraLib_Models/1 as models
var nl.Sequential model = nl.sequential("advanced_demo")
if barstate.isfirst
model := model
.input(array.from(8), "sequence")
.temporalConvStack(4, 2, 3, 2, 2, 1, nl.ActivationKind.relu, 0.0, "temporal")
.globalAvgPool1d(2, 3, "pool")
.duelingQHead(4, 2, nl.ActivationKind.relu, "q_head")
.build(nl.rng(7))
----------------------------------------------------------------------------------------------------------------
🔷 FEATURE QUICK REFERENCE
Runtime : Matrix-first auto-differentiating neural graph runtime for Pine Script.
Model API : Chainable `Sequential` builder with `input`, `dense`, `dropout`, `layerNorm`, `activation`, `flatten`, `reshape`, and custom `block` support.
Training : Forward pass, loss calculation, backpropagation, gradient accumulation, optimizer steps, train stats, and history buffers.
Inference : `.predict()` for deterministic inference and `.predictMC()` for dropout-based uncertainty sampling.
Datasets : `WindowDataset` for flat rows and `RollingDataset` for time-series windows.
Scaling : None, Z-Score, Min-Max, Running Z-Score scalers, dataset input scaling, target scaling, and inverse target scaling.
Optimizers : SGD, Momentum, RMSProp, Adam, and AdamW.
Schedulers : Constant, Step Decay, Cosine Decay, and Warmup Cosine.
Activations : Linear, ReLU, Leaky ReLU, ELU, GELU Approx, Sigmoid, Tanh, Softplus, Swish, and Softmax.
Losses : MSE, MAE, Huber, LogCosh, Binary Cross Entropy, Binary Cross Entropy From Logits, Categorical Cross Entropy, Softmax Cross Entropy From Logits, Directional, Quantile, Multi-Horizon Weighted, and Sharpe.
Metrics : MAE, RMSE, Directional Accuracy, Binary Accuracy, Binary Accuracy From Logits, Categorical Accuracy, and Cosine Similarity.
Guardrails : Shape validation, invalid-row rejection, train/validation split helpers, leakage-aware scaler profiles, training gates, gradient clipping, and EarlyStopper.
Advanced expansion : Conv1D, temporal stacks, recurrent blocks, attention, Transformers, dueling Q-heads, positional encodings, and Prioritized Experience Replay.
----------------------------------------------------------------------------------------------------------------
🔷 IMPORTANT CONSIDERATIONS
Start small : Pine Script is not a GPU training environment. Compact models are the right starting point.
Control chart history : Use `calc_bars_count = 600` in `indicator()` when needed to balance available training history against model size and execution time.
Use the training gate : For heavier models, use `.withTrainingGate(true)` so backpropagation runs only at the confirmed historical edge.
Scale your inputs : Raw market features often differ by orders of magnitude. Use dataset scalers unless you have a deliberate reason not to.
Validate separately : Use `trainBatch()` and `validationBatch()` to monitor generalization instead of only watching training loss.
Avoid lookahead : Build feature rows only from information available at the time of the row. Use completed target rows for training.
Treat outputs as research signals : NeuraLib provides model mechanics. Strategy design, risk management, and market assumptions remain the user's responsibility.
----------------------------------------------------------------------------------------------------------------
🔷 API REFERENCE
🔸 Model Setup
sequential(name) : Creates an empty `Sequential` model.
compileConfig() : Creates a model configuration object.
build(rng) : Builds model parameters with a deterministic random stream.
compile(config) : Builds the model when needed and applies the training configuration.
rng(seed, streamId) : Creates a deterministic random stream.
🔸 Sequential Methods
input(dimsArray, name) : Defines the input shape.
dense(units, activation, name) : Adds a fully connected layer.
qHead(actionCount, activation, name) : Adds a Q-value output head.
activation(activationKind, alpha, name) : Adds an activation block.
dropout(rate, name) : Adds dropout regularization.
layerNorm(name) : Adds layer normalization.
flatten(name) and reshape(outputDimsArray, name) : Adjust model shape metadata.
block(graphBlock) : Adds a custom `GraphBlock`.
trainOnBatch(inputTensor, targetTensor) : Runs training when the active gate allows it.
backward(targetTensor) : Accumulates gradients from the last forward pass without stepping.
step() : Applies the optimizer step to accumulated gradients.
predict(inputTensor) : Runs inference.
predictMC(inputTensor, samples) : Runs dropout-enabled Monte Carlo prediction and returns mean and variance.
evaluate(inputTensor, targetTensor) : Calculates loss without updating weights.
fitDataset(dataset) and fitRollingDataset(dataset, targetOffset) : Train through dataset adapters.
getWeightsArray() and setWeightsArray(weightsArray) : Export and import flat model weights.
softUpdateFrom(sourceModel, tau) : Soft-update parameters from another model.
🔸 CompileConfig Methods
optimizer(optimizerState) : Sets the optimizer.
schedule(scheduleState) : Sets the learning-rate schedule.
loss(lossKind) : Sets the training loss.
reduction(reductionKind) : Sets loss reduction behavior.
metric(metricKind) : Adds a metric.
batchSize(size) , epochsPerBar(count) , evalStride(stride) , and historyLength(length) : Store batch and cadence preferences, and set the metric history length.
clipNorm(value) and clipValue(value) : Apply gradient clipping.
gradAccumSteps(steps) : Accumulates gradients before stepping.
withTrainingGate(enabled) : Restricts training to the last confirmed historical bar when enabled.
trainEveryCall() : Allows training whenever `.trainOnBatch()` is called.
presetPriceRegression() , presetReturnRegression() , presetBinaryDirection() , presetBinaryDirectionLogits() , presetQValues() , and presetSharpe() : Apply common loss and metric presets.
🔸 Datasets
windowDataset(featureCount, targetCount, maxRows, name) : Stores flat feature and target rows.
rollingDataset(timeSteps, featureCount, targetCount, maxRows, name) : Stores time-series windows.
pushRow(featureArray, targetArray) : Adds one validated row.
pushBuilderRow(featureBuilder, targetArray) : Adds a row from a `FeatureBuilder`.
pushNextReturnRow(featureBuilder, currentValue, futureValue) : Adds a next-return target.
pushNextDirectionRow(featureBuilder, currentValue, futureValue, threshold, zeroOne) : Adds a direction target.
ready(minRows or minWindows, targetOffset) and size() : Check dataset readiness.
lastBatch(batchSize) : Returns the most recent scaled rows from a `WindowDataset`.
toBatch() : Returns all rows from a `WindowDataset`.
unrollBatch(targetOffset) : Returns all rolling windows from a `RollingDataset`.
trainBatch(validationRows or validationWindows, targetOffset) : Returns the training side of the split.
validationBatch(validationRows or validationWindows, targetOffset) : Returns the validation side of the split.
setInputScaler(kind) , setTargetScaler(kind) , scaleInput(tensor) , scaleTarget(tensor) , and inverseScaleTarget(tensor) : Configure and apply scaling.
clear() : Clears stored rows.
🔸 Tensor, Matrix, and Feature Helpers
scalar(value) , vector(valuesArray) , matrix2d(rows, cols, fillValue) , zeros(shape) , ones(shape) , and full(shape, fillValue) : Create tensors.
shapeFromDims(dimsArray) : Creates a shape.
matrixTensor(tensor) , matrixTensor2d(rows, cols, fillValue) , and matrixTensorFromMatrix(sourceMatrix) : Create matrix tensors.
reshape(dimsArray) , flatten() , row(rowIndex) , get1d(index) , sum() , mean() , variance() , normL2() , argmax() , and dot(other) : Tensor methods.
matmul() , transpose() , add() , subtract() , multiply() , divide() , scale() , activate() , softmax() , sliceRows() , sliceCols() , concatRows() , concatCols() , globalAvgPool1d() , and globalMaxPool1d() : MatrixTensor methods.
featureBuilder(name) , push(value, featureName) , addFeature(value, featureName) , toTensor(tensorName) , toArray() , size() , and clear() : Feature row helpers.
🔸 Scalers, Optimizers, and Schedules
zScoreScaler() , minMaxScaler() , runningZScoreScaler() , and noneScaler() : Standalone scaler states.
fit(tensor) , partialFit(tensor) , transform(tensor) , and inverseTransform(tensor) : Scaler methods.
sgd(learningRate) , momentum(learningRate, momentum) , rmsprop(learningRate, rho, epsilon) , adam(learningRate, beta1, beta2, epsilon) , and adamW(learningRate, beta1, beta2, epsilon, weightDecay) : Optimizers.
constantSchedule(learningRate) , stepDecay(baseLearningRate, decaySteps, gamma) , cosineDecay(baseLearningRate, minLearningRate, decaySteps) , and warmupCosine(baseLearningRate, minLearningRate, warmupSteps, decaySteps) : Schedules.
currentRate(stepCount) : Reads a schedule's learning rate at a step.
paramBank() , append() , zeroGrad() , globalGradNorm() , step(optimizerState) , and softUpdateFrom(sourceBank, tau) : Low-level parameter bank utilities.
🔸 Losses and Metrics
mse() , mae() , huber() , logCosh() , binaryCrossEntropy() , binaryCrossEntropyFromLogits() , categoricalCrossEntropy() , softmaxCrossEntropyFromLogits() , directionalLoss() , quantileLoss() , multiHorizonWeighted() , and sharpeLoss() : Direct loss helpers.
metricValue(metricKind, predictionTensor, targetTensor) : Direct metric helper.
earlyStopper(patience, minDelta) , update(validationLoss) , and reset() : Validation stopping helper.
nextReturnValue(currentValue, futureValue) and nextDirectionValue(currentValue, futureValue, threshold, zeroOne) : Common target helpers.
🔸 GraphBlock Operations
graphBlock(name) : Creates a custom trainable graph block.
input() , param() , constScalar() , constMatrix() , and output() : Define graph inputs, parameters, constants, and output metadata.
matmul() , add() , subtract() , multiply() , divide() , scale() , activate() , softmax() , transpose() , layerNorm() , and dropout() : NeuraLib graph math.
causalMask() , sliceRows() , concatRows() , sliceCols() , concatCols() , reduceRows() , and reduceCols() : Structural graph operations.
globalAvgPool1d() , globalMaxPool1d() , attentionScore() , attentionApply() , conv1d() , scanLstm() , and scanGru() : Sequence and architecture primitives.
🔸 NeuraLib_Models API
prioritizedReplayBuffer(featureCount, targetCount, maxRows, name) : Creates a replay buffer.
pushExperience(featureRowArray, targetRowArray, priority) , sampleBatch(batchSize, alpha, beta, seed) , updatePriority(index, priority) , toBatch() , ready(minRows) , size() , and clear() : Prioritized Experience Replay helpers.
pushPositionalEncoding(position, dimensions, maxPeriod, featurePrefix) : Adds positional encoding values to a `FeatureBuilder`.
residualDense() , duelingQHead() , conv1d() , temporalConvStack() , globalAvgPool1d() , globalMaxPool1d() , lstm() , gru() , selfAttention() , multiHeadSelfAttention() , crossAttention() , transformerEncoder() , transformerEncoderStack() , and transformerDecoder() : NeuraLib_Models `Sequential` methods.
NeuraLib is for Pine Script developers who want to move beyond fixed formulas and experiment with real neural network workflows directly inside PulseWire. It is a research framework, not a guarantee of market performance. Use validation, avoid lookahead, control risk, and keep models small enough for Pine's execution limits.
All the diagrams in this publication are rendered natively on PulseWire using Pine3D
----------------------------------------------------------------------------------------------------------------
This work is licensed under (CC BY-NC-SA 4.0) , meaning usage is free for non-commercial purposes given that Alien_Algorithms is credited in the description for the underlying software. For commercial use licensing, contact Alien_Algorithms
Library

TP_Ephem_LibLibrary "TP_Ephem_Lib"
Cowan-tailored heliocentric ephemeris (VSOP87D) for PulseWire.
@description Returns heliocentric ecliptic longitudes for Mercury through Neptune
@description (tropical or sidereal/Lahiri), synodic phases between any two planets,
@description and Cowan-canon helpers: 3-Step Astro cumulative advance (V4:L5079),
@description pentagram vertex dates (V4:L449), and cube-face boundary dates
@description (V1:L2106-2114, V1:L2962-2997). All math validated to <0.01 deg vs
@description Swiss Ephemeris (DE441) across 1899-2026. Frame: ecliptic of date.
jd_to_t_millennia(jd)
Parameters:
jd (float)
jd_from_timestamp(unix_ms)
Parameters:
unix_ms (int)
timestamp_from_jd(jd)
Parameters:
jd (float)
normalize_longitude(deg)
Parameters:
deg (float)
get_ayanamsa(jd)
Parameters:
jd (float)
get_helio_longitude(planet, jd, useSidereal)
Parameters:
planet (simple Planet)
jd (float)
useSidereal (simple bool)
get_sun_geo_longitude(jd, useSidereal)
Parameters:
jd (float)
useSidereal (simple bool)
get_synodic_phase(p1, p2, jd)
Parameters:
p1 (simple Planet)
p2 (simple Planet)
jd (float)
get_aspect_angle(p1, p2, jd)
Parameters:
p1 (simple Planet)
p2 (simple Planet)
jd (float)
average_speed_deg_per_day(planet)
Parameters:
planet (simple Planet)
speed_deg_per_day(planet, jd)
Parameters:
planet (simple Planet)
jd (float)
is_retrograde(planet, jd)
Parameters:
planet (simple Planet)
jd (float)
cumulative_advance_deg(planet, origin_jd, target_jd, useSidereal)
Parameters:
planet (simple Planet)
origin_jd (float)
target_jd (float)
useSidereal (simple bool)
cumulative_synodic_advance_deg(p1, p2, origin_jd, target_jd)
Parameters:
p1 (simple Planet)
p2 (simple Planet)
origin_jd (float)
target_jd (float)
find_advance_jd(planet, origin_jd, target_advance_deg, useSidereal)
Parameters:
planet (simple Planet)
origin_jd (float)
target_advance_deg (float)
useSidereal (simple bool)
find_synodic_advance_jd(p1, p2, origin_jd, target_synodic_advance_deg)
Parameters:
p1 (simple Planet)
p2 (simple Planet)
origin_jd (float)
target_synodic_advance_deg (float)
pentagram_vertex_jd(planet, origin_jd, n, useSidereal)
Parameters:
planet (simple Planet)
origin_jd (float)
n (simple int)
useSidereal (simple bool)
cube_face_boundary_jd(origin_jd, face_n)
Parameters:
origin_jd (float)
face_n (simple int) Library

RandomForestLibraryRandomForestLibrary is a self-contained Random Forest library for Pine Script v6 that other Pine developers can import and use to build their own machine learning indicators and strategies.
What Makes This Different
Random Forest is one of the most widely used ensemble methods in applied machine learning. Until now, Pine Script developers wanting to use it had only two choices: call out to an external Python / ONNX pipeline, or hand-roll a single decision tree inline. This library closes that gap by providing a complete Random Forest implementation — CART trees, bootstrap aggregation, Gini / MSE splitting, out-of-bag scoring, weighted feature importance — reachable with a few lines of import code.
The API intentionally mirrors scikit-learn's RandomForestClassifier and RandomForestRegressor (init → fit → predict → evaluate), so practitioners already familiar with scikit-learn can translate existing logic directly.
What This Library Provides
Binary classification: fit(X, y) , predict , predict_proba , predict_batch , oob_score
Multi-output regression: fit_regressor(X, Y) , predict_multi , predict_multi_per_tree , oob_r2 , oob_residual_std
Weighted Gini / MSE feature importance: feature_importance()
Deterministic Park-Miller RNG for reproducible forests
Exported Types
Forest — the ensemble model. Holds all trees, hyperparameters, training data references, OOB accumulators, and feature importances.
Tree — a single decision tree with its node array, max depth, leaf count, and split-failure count.
Node — a single node storing feature index, threshold, children indices, leaf label / probability, Gini impurity (or MSE in regressor mode), sample count, and a per-horizon output array for regression.
RNG — a Park-Miller linear congruential generator with a=48271, m=2^31-1. Deterministic given the same seed.
Exported Methods
Initialization and training
init(n_estimators, max_depth, max_features, min_samples_leaf, n_threshold_candidates, seed) — configure hyperparameters. max_features=0 auto-selects ceil(sqrt(n_features)) for classification and ceil(n_features/3) for regression.
fit(X, y) — train classifier on a feature matrix X (rows = samples, columns = features) and integer label array y (values 0 or 1).
fit_regressor(X, Y) — train multi-output regressor. Y is a matrix whose columns are separate regression horizons / targets.
Inference
predict(sample) — classify a single sample via soft voting (threshold 0.5).
predict_proba(sample) — average class-1 probability across all trees.
predict_batch(X) — classify every row of a matrix.
predict_multi(sample) — regressor output: averaged per-horizon predictions.
predict_multi_per_tree(sample) — per-tree, per-horizon predictions for custom uncertainty analysis.
tree_predict , tree_predict_proba , tree_predict_multi — single-tree inference for advanced use.
Evaluation
oob_score() — classification out-of-bag accuracy (0.0 to 1.0), computed by soft voting on samples not selected in each tree's bootstrap.
oob_r2() — regression out-of-bag R^2, averaged across horizons.
oob_residual_std() — per-horizon standard deviation of OOB residuals. Useful for prediction interval construction (Wager, Hastie, and Efron 2014).
feature_importance() — normalized weighted Gini (or MSE) decrease per feature, averaged across trees. Sums to approximately 1.0.
How It Works
Tree construction (CART, iterative, level-by-level)
Each tree is built top-down, one depth level at a time, using complete binary tree indexing ( left = 2i+1 , right = 2i+2 ). At every internal node:
A random subset of features of size max_features is drawn without replacement.
For each feature, n_threshold_candidates thresholds are sampled uniformly between the feature's min and max on the samples at that node.
For classification, the split minimizing weighted Gini impurity is chosen. For regression, the split minimizing weighted MSE (summed over all horizons) is chosen.
A node becomes a leaf when it is pure (classification), too small ( n < 2 * min_samples_leaf ), at max depth, or when no valid split exists.
Bootstrap aggregation and OOB
Each tree is trained on a bootstrap sample (same size as the training set, sampled with replacement). Samples that were not drawn for a given tree become its out-of-bag set and are used to compute unbiased performance estimates ( oob_score / oob_r2 ) and residual variance ( oob_residual_std ), avoiding the need for a separate holdout.
Feature importance
Each split records its weighted impurity decrease ( n_node * impurity_node - n_left * impurity_left - n_right * impurity_right ). Per-tree importances are normalized to sum to 1, then averaged across trees — matching scikit-learn's definition.
Quick Start
//@version=6
indicator("My RF Indicator")
import ShigemiQuant/RandomForestLibrary/2 as RF
// 1. Build feature matrix X and label array y over recent bars
// (not shown: accumulate features into a matrix)
// 2. Initialize and train
var RF.Forest model = RF.Forest.new().init(
n_estimators = 10,
max_depth = 4,
seed = 42)
if barstate.islast
model.fit(X, y)
// 3. Predict on current bar
array sample = array.from(rsi_val, atr_pct, cci_val, adx_val)
float prob = model.predict_proba(sample)
// 4. Evaluate
float oob = model.oob_score()
label.new(bar_index, close, "prob=" + str.tostring(prob, "#.##") + " oob=" + str.tostring(oob, "#.##"))
Compatibility Notes
scikit-learn parity : same init → fit → predict / predict_proba workflow, same default for max_features , OOB uses soft voting, importances use weighted Gini decrease.
Determinism : given identical seed , training set, and hyperparameters, the resulting forest and all predictions are bit-identical across reruns.
Binary classification only in fit() : labels must be 0 or 1. Multi-class is not yet supported.
Numeric features only : all columns of X must be float .
Limitations
This is a machine-learning library , not a trading signal. Indicators built with it make no guarantee of profit, do not predict the future, and depend entirely on the quality of the features, labels, and hyperparameters that the caller supplies.
Binary classification only in fit() (labels must be 0 or 1); multi-class is not supported. Regression via fit_regressor() supports multi-output targets but assumes they are numeric float values.
PulseWire runtime budget limits tree size. A reasonable starting point is n_estimators between 5 and 20 with max_depth between 3 and 6. Total node budget per tree is 2^(max_depth+1) - 1 — depth 6 allows up to 127 nodes per tree, and 15 trees means up to roughly 1,905 nodes total.
Large training sets combined with deep trees (thousands of bars × depth 6) can hit Pine Script's loop iteration caps. Start small and scale up while watching compile / runtime warnings.
OOB metrics ( oob_score , oob_r2 , oob_residual_std ) are valid only when each sample is out-of-bag in at least one tree. For very small training sets or very few estimators, some samples may never be OOB and those metrics will be biased or undefined.
Overfitting is the caller's responsibility. The library exposes standard controls ( max_depth , min_samples_leaf , max_features , n_estimators ) but applies no automatic regularization. Trees that are too deep on a noisy training window will memorize noise.
Features must be stationary enough to generalize. Raw price levels or unnormalized indicators that drift with the market will cause training-test distribution shift. Prefer bounded or ratio-based features (RSI, ATR%, percentile ranks).
Training happens on the chart's own bar history. There is no external data upload; the library cannot import pre-trained models, and the forest must be rebuilt whenever the script recomputes. Designs that rely on very large historical context may conflict with Pine Script's bar-history window.
References
Breiman, L. (2001). Random Forests. Machine Learning, 45(1), 5–32.
Wager, S., Hastie, T., and Efron, B. (2014). Confidence Intervals for Random Forests: The Jackknife and the Infinitesimal Jackknife. Journal of Machine Learning Research, 15, 1625–1651.
Disclaimer
This library is an educational and research tool. It does not constitute financial advice. All trading decisions based on code built with this library are the sole responsibility of the user. Past model performance does not guarantee future results. Library

Pine3D: A Native 3D Graphical Rendering EnginePine3D is a full 3D rendering engine for PulseWire, powered by Pine Script™ v6.
Pine3D pushes forward the frontier of PulseWire 3D rendering capabilities, providing a fully fledged graphical engine under an intuitive, chainable, object oriented API. Build meshes, transform them in world space, light them, cast shadows, project them through a perspective camera, and render the result directly on your chart, all without ever bothering about trigonometry synchronization or optimization.
The library brings forth a streamlined process for anyone that wishes to visualize data in 3D, without needing to know anything about the complex math that has previously gatekept such indicators. Pine3D does all the heavy lifting, including extreme optimization techniques designed for production ready indicators.
The entire API is chainable and tag addressable, so spawning a mesh, registering it, pointing the camera at it, and rendering the frame is a four line affair:
Mesh mybox = cube(40.0, color.orange).setTag("hero").rotateBy(0.0, 45.0, 0.0)
scene.add(mybox)
scene.lookAt("hero")
render(scene)
🔷 SURFACES: CONTOUR BAND RENDERING
Pine Script imposes a hard ceiling of 100 polylines and 500 lines per indicator . On the surface this looks fatal for dense 3D meshes: every triangle drawn naively burns one of those 100 slots, or two of the 500, and the budget evaporates within a few hundred faces.
The conventional escape hatch is strip stitching , tracing a polyline forward along one row of a grid and back along the next, packing a ribbon of quads into a single drawing slot. It buys a meaningful multiplier, but it pays for that multiplier with two structural constraints baked into the geometry itself:
One color per strip. A polyline carries a single stroke and fill color, so every cell along the ribbon must share the same shade. The moment you want per cell lighting, contour banding, or value driven gradients, every color change forces a new polyline and the budget collapses.
One contiguous ribbon per slot. Strips can only describe topologically connected runs of cells. Disjoint regions, holes, islands, and value clustered fragments scattered across the surface each demand their own polyline.
Pine3D breaks both constraints at once.
At the core of the engine sits an innovation that redefines the limits for visual fidelity: contour band rendering using degenerate bridge stitching . The technique quantizes a surface's elevation into colored bands, then collapses every cell that falls inside the same band, no matter where it sits on the screen , into one continuous, hole aware polyline path per band, threading invisible zero width bridges between disjoint islands so that a single polyline can carry thousands of polygon equivalent fragments scattered across the geometry.
The result:
A single polyline can render up to 2,000 disconnected triangle equivalents , spread across arbitrarily separated regions of the surface.
Theoretical ceiling of around 200,000 disconnected faces inside the 100 polyline budget, a regime that strip based stitching cannot enter at any color count above one.
A 40 x 40 heightmap (around 3,000 triangles) renders inside the budget with full per band contour coloring and room to spare. Stress harnesses have run 40 x 80 grids .
Each band's path is depth sorted and near plane culled, and cached between bars , so once geometry is built only the screen space projection runs per frame.
This algorithm enables scenes with extreme detail relative to the 100 polyline limit, and shifts the optimization focus from "drawing limits" to "CPU limits", which Pine3D natively handles with aggressive caching at every layer of the pipeline. The contour technique is currently integrated into the surface() function, with the same compression strategy generalizable to any mesh class and ultimately full scene rendering in future versions.
Non-uniform grids out of the box. surface() accepts optional axisX and axisZ arrays that override the default uniform spacing with custom column and row positions. This means logarithmic strike spacing on an option volatility surface, irregular timestamp spacing on a market depth heatmap, or any other non-evenly-sampled grid renders correctly without resampling the data first. The contour band engine, axis ticks, and gridBox cage all snap to the custom positions automatically.
A full contour surface is just a handful of lines; the damped ripple below builds once and never needs updating:
//@version=6
indicator("Pine3D - Contour Surface", overlay = false, max_polylines_count = 100, max_lines_count = 500, max_labels_count = 500)
import Alien_Algorithms/Pine3D/1 as p3d
var p3d.Scene scene = p3d.newScene()
var p3d.Mesh heatmap = na
if barstate.isfirst
// Damped cosine ripple
int N = 20
matrix data = matrix.new(N, N, 0.0)
for r = 0 to N - 1
for c = 0 to N - 1
float dx = c - (N - 1) / 2.0
float dz = r - (N - 1) / 2.0
float d = math.sqrt(dx * dx + dz * dz) * 0.7
data.set(r, c, math.cos(d) * math.exp(-d * 0.12) * 50.0)
heatmap := p3d.surface(data, 200.0, color.blue, color.red, 24)
.gridBox()
.gridLabels(color.white, "X", "Amplitude", "Z")
scene.add(heatmap)
scene.camera.orbit(35.0, 25.0, 380.0)
if barstate.islast
p3d.render(scene, lighting = true)
🔷 TRAIL3D: STREAMED OSCILLATOR PATHS
Trail3D is a first class streaming primitive built for visualizing two correlated time series as a 3D ribbon evolving through time. You give it a rolling buffer capacity and push (u, v) samples bar by bar; the primitive maintains the buffer, builds the ribbon geometry, and renders it inside a normalized bounding cube so the path always fits cleanly in view regardless of the underlying data range.
Under the hood, Trail3D is a coordinated bundle of polylines: one for the main ribbon, two for optional shadow projections onto the back wall and floor, and one for the wireframe cage. All four are depth sorted and occlusion clipped against the rest of the scene, and the primitive auto normalizes incoming samples against the rolling window's min/max so streaming data always fills the cube without manual scaling.
This enables a class of visualizations that would otherwise require dozens of polylines and manual buffer management: phase space portraits, Lissajous figures, oscillator pair correlations, attractor trajectories, and any "two indicators evolving together over time" study. The demo above shows a sine and cosine pair pushing samples each bar to trace a clean spiral inside the cage, the same pattern you would use to plot RSI vs MFI, momentum vs volatility, or any custom (u, v) signal pair.
A full streamed scene is a handful of lines:
//@version=6
indicator("Pine3D - Trail3D", overlay = false, max_polylines_count = 100, max_lines_count = 500, max_labels_count = 500)
import Alien_Algorithms/Pine3D/1 as p3d
var p3d.Scene scene = p3d.newScene()
var p3d.Trail3D trail = na
if barstate.isfirst
trail := p3d.trail3D(220.0, 200, color.yellow)
.cage(true)
.axisLabels("sin", "cos", color.white)
trail._uProj.col := #00ffff69
trail._vProj.col := #ff00ff71
scene.add(trail)
scene.camera.orbit(215.0, 20.0, 360.0)
float phase = bar_index * 0.15
float sinX = math.sin(phase) * 100.0
float cosY = math.cos(phase) * 100.0
if barstate.isconfirmed
trail.pushSample(sinX, cosY)
p3d.render(scene)
🔷 BARS3D: CATEGORICAL 3D BAR CHARTS
bars3D() turns any series of values into a fully lit, depth sorted 3D bar chart in a single call. Each bar is height mapped to its value, color graded between a low and high color, and packed into one combined mesh with per bar depth grouping so individual bars sort correctly even inside the merged geometry. The companion updateBars() mutator refreshes heights, colors, and labels in place every bar without rebuilding geometry, making it suitable for live rankings, rolling windows, and animated comparisons.
The chainable barLabels(catNames, valNames) helper attaches category labels at the base of each bar and value labels at the top, both depth sorted with the rest of the scene. Category labels are set once at build time, while value labels can be passed to updateBars(values, valLabels = ...) each frame to reflect live data. Combined with wireGrid() for the floor and a contour surface() in the background, bars3D() becomes the centerpiece of dashboards comparing assets, sectors, timeframes, or any categorical metric.
Negative values are handled automatically: bars below zero extrude downward from the base plane with reversed face winding, so signed series like PnL, delta, or momentum histograms render correctly without any extra setup.
A complete labeled bar chart is just a few lines:
//@version=6
indicator("Pine3D - Bars3D", overlay = false, max_polylines_count = 100, max_lines_count = 500, max_labels_count = 500)
import Alien_Algorithms/Pine3D/1 as p3d
var p3d.Scene scene = p3d.newScene()
var p3d.Mesh bars = na
array values = array.from(volume - volume , volume - volume , volume - volume , volume - volume , volume - volume , volume - volume )
array names = array.from("ΔV0", "ΔV-1", "ΔV-2", "ΔV-3", "ΔV-4", "ΔV-5")
if barstate.isfirst
bars := p3d.bars3D(values, 30.0, 30.0, 10.0, color.blue, color.red, 200.0)
.barLabels(names)
scene.add(bars)
p3d.wireGrid(scene, 300.0, 300.0, 6, 6, color.new(color.gray, 80))
scene.camera.orbit(215.0, 25.0, 360.0)
if barstate.islast
bars.updateBars(values)
p3d.render(scene, lighting = true)
Omitting valLabels in updateBars() tells the engine to auto format each numeric value via str.tostring() . Pass valLabels only when you need custom strings.
🔷 SCATTER CLOUDS: POINTS IN 3D SPACE
Pine3D treats scatter clouds as a first class use case without needing a dedicated scatter API. Because Label3D is the primitive and scene.add(array) is a single batch operation, you can scatter up to 500 points anywhere in 3D space, each with independent color, symbol, size, and tooltip , and have them depth sorted and occlusion clipped against the rest of the scene automatically.
Each point is a fully addressable Label3D with mutable fields. You can change position , textColor , bgColor , labelStyle (any label.style_* glyph including circles, squares, diamonds, triangles, crosses, arrows, flags), labelSize (any size.* preset), and text per point per bar. The renderer reads these mutations every frame, so animation is just direct field assignment.
This unlocks a wide class of visualizations: clustered data scatter, K means visualizations, particle systems, parametric surfaces sampled as point clouds, gradient colored attractors, multi class classification overlays, and structured curves like the demo above. The double helix demo plots two intertwined parametric strands as ~500 points with alternating colors and per point sizing, all inside the standard scene.add(array) pipeline.
The pattern is straightforward: build the array once in barstate.isfirst , add it to the scene, then mutate point fields per bar to animate.
//@version=6
indicator("Pine3D - Scatter Cloud", overlay = false, max_polylines_count = 100, max_lines_count = 500, max_labels_count = 500)
import Alien_Algorithms/Pine3D/1 as p3d
var p3d.Scene scene = p3d.newScene()
var array points = array.new()
if barstate.isfirst
for i = 0 to 499
p3d.Vec3 pos = p3d.vec3(0.0, 0.0, 0.0)
points.push(p3d.Label3D.new(position = pos, txt = "•"))
scene.add(points)
scene.camera.orbit(35.0, 20.0, 400.0)
if barstate.islast
for i = 0 to points.size() - 1
float t = i * 0.05 + bar_index * 0.01
p3d.Label3D pt = points.get(i)
pt.position := p3d.vec3(80.0 * math.cos(t), i * 0.4 - 100.0, 80.0 * math.sin(t))
pt.textColor := i % 2 == 0 ? color.aqua : color.fuchsia
p3d.render(scene)
----------------------------------------------------------------------------------------------------------------
🔷 TWO LAYER ARCHITECTURE
Pine3D ships as a clean, two layer library:
🔸 Layer 1 - DIY API. First principle building blocks ( Vec3 , Mesh , Camera , Light , Scene , plus world space overlay primitives) for total creative control. Author your own geometry, camera behavior, lighting setup, and scene graph from scratch.
🔸 Layer 2 - High Level Helpers. Production ready wrappers like surface() , bars3D() , trail3D() , updateBars() , updateSurface() , sphere() , torus() , cylinder() , and wireGrid() , plus chainable contour helpers gridBox() and gridLabels() that wrap the primitives into a few lines of code. Scatter clouds use the standard Label3D primitive directly.
The object model is chainable and scene oriented, so complex setups still read cleanly.
🔷 FEATURE LIST
Contour Surface Rendering - The most powerful 3D surface engine ever released for Pine Script. Render tens of thousands of polygon equivalent faces using a single polyline per contour band, delivering smooth, continuous terrain with natural ridges and valleys.
Adaptive Rail Sharing - Solid meshes drawn with the default linefill backend reuse one edge line between adjacent coplanar faces, averaging roughly 1.6 lines per face instead of the naive two, pushing practical mesh capacity up to ~360 faces depending on topology.
Interior Face Culling on Merge - mergeMeshes(meshes, removeInterior = true) detects coincident faces with opposing normals and strips them, so voxel style scenes (stacked cubes, block walls, lattice geometry) ship only their exterior shell and spend no budget on hidden interior faces.
True Perspective Camera System - Full 3D camera with position, target, fov, and orbit() controls. Supports cinematic camera movement, lookAt by mesh tag, and realistic depth.
Real Time Lighting and Shadows - Directional and point lights with configurable ambient, shadow strength, self shadowing, and a spatial grid acceleration structure for fast shadow queries.
High Performance Update System - updateSurface() and updateBars() let you animate massive datasets bar by bar without rebuilding geometry, keeping CPU usage minimal.
Rich Primitive Library - Cubes, cuboids, spheres, cylinders, tori, pyramids, planes, discs, circles, custom meshes, and the groundbreaking bars3D() with automatic labels.
Streamed Trail Primitive - trail3D() maintains a rolling buffer of (u, v) samples and renders them as a 3D ribbon inside a bounding cube, with optional projections onto the back wall and floor and a wireframe cage.
Depth Sorted Overlays - 3D labels, lines, polylines, wire grids, and trails, all correctly occluded and painter sorted against the rest of the scene.
Professional Contour Helpers - gridBox() and gridLabels() automatically add clean bounding boxes and axis titles, ticks, and series names that refresh on every updateSurface() call.
Tag Based Scene Graph - Every Mesh , Label3D , Line3D , and Polyline3D can carry a string tag. Scene exposes getMesh() , getLabel() , getLine() , getPolyline() , lookAt() , and remove() by tag, turning your scene into a lookup by name graph instead of an index juggling exercise.
Chainable, Intuitive API - Everything is designed for maximum readability and speed of development. Build complex scenes in just a few lines.
Production Ready Optimizations - World vertex caching, view projection caching, face preprocessing cache, shadow grid cache, and contour geometry cache, all managed automatically.
----------------------------------------------------------------------------------------------------------------
🔷 THE RENDERER
Every frame is produced by a single call to render(scene, ...) . The renderer runs the full pipeline: world transform, camera transform, back face culling, occlusion culling, depth sort, directional or point lighting with shadows, and perspective projection.
⚠ render() clears the entire chart drawing pool at the start of every call - every polyline , line , label , and linefill on the chart is deleted before Pine3D redraws, not just the ones it created. If you mix Pine3D with manual label.new() , line.new() , or similar calls, those drawings must be emitted after render() or they will be wiped every frame.
🔸 Setup Requirements. Pine3D consumes polylines, lines, and labels simultaneously, so your indicator() declaration must raise all three budgets, and the library must be imported under an alias:
indicator("My 3D Scene", overlay = false,
max_polylines_count = 100,
max_lines_count = 500,
max_labels_count = 500)
import Alien_Algorithms/Pine3D/1 as p3d
🔸 render() parameters.
maxFaces (int, default 100). Hard cap on solid faces drawn per frame. Contour bands, wireframe edges, labels, lines, and overlay polylines are not counted against this cap, and are bounded only by PulseWire's global 100 polyline / 500 line / 500 label budgets.
culling (bool, default true). Enable back face culling.
lighting (bool, default false). Enable diffuse shading. Reads scene.light if set; otherwise falls back to the render() args.
lightDir (Vec3). Overrides scene.light.direction when provided. Points toward the light.
ambient (float, default 0.3). Minimum brightness for shadowed faces (0.0-1.0).
wireframe (bool, default false). Force outline only output for the entire scene.
occlusion (bool, default true). Sparse raster pass that drops hidden faces before drawing. Major perf win on dense scenes.
occlusionRaster (int, default 768). Raster resolution of the occlusion buffer. Lower = faster but coarser; higher = stricter hidden face rejection.
Explicit render() args always win over scene.light , which makes render() the right place for ad hoc, per frame lighting tweaks.
----------------------------------------------------------------------------------------------------------------
🔷 MESH DRAWING MODES
Two independent axes control how a mesh appears on the chart:
🔸 Style (via mesh.setStyle(...) ) - what gets drawn:
"solid" . Filled faces. Default.
"wireframe" . All edges, no fill. Shows interior geometry.
"wireframe_front" . Only front facing edges. Cleaner silhouette for convex meshes.
🔸 Draw Mode (via mesh.drawMode ) - which PulseWire primitive carries the solid faces:
"linefill" (default). Uses the line and linefill budgets. An adaptive rail sharing optimization reuses one edge line between adjacent coplanar faces, pushing practical capacity up to ~360 faces per mesh depending on topology. Supports in place updates via updateSurface() and updateBars() . Rails are drawn transparent, so solid faces in this mode have no visible outline - use a wireframe style or "poly" drawMode if you need stroked edges. Recommended for all new code.
"poly" . Legacy polyline backend. Capacity ~100 faces, no in place updates, but renders the face outline using mesh.lineStyle and mesh.lineWidth . Use only when you need styled solid face outlines.
Wireframe styles always render with line primitives regardless of drawMode. Stroke width and style on edges (and on poly mode face outlines) come from mesh.lineWidth and mesh.lineStyle , which you mutate by direct field assignment.
----------------------------------------------------------------------------------------------------------------
🔷 QUICK START
The best practice lifecycle is simple:
Create one persistent Scene with newScene() .
Build meshes and helper overlays once in barstate.isfirst .
On later bars, mutate objects in place with transforms or helper mutators like updateBars() and updateSurface() .
Call render(scene, ...) once per frame. It automatically clears the previous chart drawings.
A complete, lit, animated 3D scene is still a handful of lines:
//@version=6
indicator("My First 3D Scene", overlay = false, max_polylines_count = 100, max_lines_count = 500, max_labels_count = 500)
import Alien_Algorithms/Pine3D/1 as p3d
var p3d.Scene scene = p3d.newScene()
var p3d.Mesh sun = na
if barstate.isfirst
scene.setLightDir(1.0, -1.0, 0.5).setAmbient(0.3)
sun := p3d.sphere(50.0, 16, 12, color.orange).setTag("sun")
scene.add(sun)
p3d.wireGrid(scene, 300.0, 300.0, 6, 6, color.new(color.gray, 80))
scene.camera.orbit(35.0, 25.0, 220.0)
if barstate.islast
sun.rotateBy(0.0, 1.5, 0.0)
p3d.render(scene, lighting = true)
----------------------------------------------------------------------------------------------------------------
🔷 RECOMMENDED USAGE PATTERN
Use your Scene and major meshes in var .
Build geometry once in barstate.isfirst .
Use updateSurface() and updateBars() on later bars instead of rebuilding meshes.
Use scene level helpers like wireGrid() when you want overlays added immediately.
Use trail3D() when you want a streamed oscillator style path with built in wall projections and cage geometry.
For scatter clouds, build an array once, hand it to scene.add(pts) , then mutate pt.position , pt.textColor , etc. each bar to animate.
Use mesh level gridBox() and gridLabels() (contour) and barLabels() (bars) to attach overlays to the mesh setup chain. They are drained into the scene by scene.add(mesh) .
🔷 CONSIDERATIONS
scene.clear() vs render(). scene.clear() removes objects from the scene graph (meshes, labels, lines, polylines). render() only clears the previous frame's PulseWire drawings and redraws from the current scene graph. You almost never need scene.clear() in the build once and update pattern.
Global scope series for updateSurface() / updateBars(). If your data uses Pine's history operator ( ) or calls functions like ta.rsi() , ta.atr() , request.security() , those must be declared at global scope so Pine tracks their bar by bar history. Calling them inside barstate.islast produces inconsistent results or compiler errors.
gridLabels() tick values auto refresh. When you call updateSurface() , any tick value labels created by gridLabels() are automatically updated to reflect the new data range. Axis titles and positions stay constant. You don't need to rebuild them.
barLabels() value labels via updateBars(). Create category labels once with mesh.barLabels(catNames) at build time, then pass valLabels to updateBars() on each frame. Value labels are refreshed automatically. Don't call barLabels() again.
Lighting convenience methods are chainable. scene.setLightDir() , setLightPos() , setLightMode() , setAmbient() , setShadowStrength() , and showLightSource() all return Scene and can be chained: scene.setLightMode("point").setLightPos(0, 200, 150).setAmbient(0.25) .
Mesh transforms return Mesh. moveTo() , moveBy() , rotateTo() , rotateBy() , scaleTo() , scaleUniform() , setTag() , setStyle() , setColor() , show() , hide() all return Mesh for chaining: mesh.moveTo(0, -20, 0).rotateTo(0, 45, 0).setStyle("solid") .
Degrees vs radians. rotateTo() and rotateBy() on Mesh expect degrees. The low level Vec3.rotateX/Y/Z() methods expect radians.
scene.lookAt() is tag only. scene.lookAt(t) accepts a string tag and points the camera at that mesh. To aim the camera at an arbitrary Vec3 , call scene.camera.lookAt(vec) directly.
remove(tag) removes one object. The search order is meshes, then labels, then lines, then polylines, and the first hit wins. Avoid reusing tags across primitive types if you intend to delete by tag.
Shadow grid acceleration is directional light only. The spatial shadow grid is only built when lightMode == "directional" . Point lights fall back to a linear O(M) scan, so heavy shadow scenes are fastest in directional mode.
guiShift and yOffset. scene.guiShift and scene.yOffset position the 3D viewport on the chart without consuming historical bar slots. Increase guiShift to push the scene rightward into future bar space; adjust yOffset to slide it vertically in price units.
bar_time projection. All chart drawings are emitted with xloc.bar_time , so the scene can sit arbitrarily far left or right of bar_index without forcing Pine to extend its history buffer. This is what keeps the engine stable on long charts and future projected scenes.
barLabels() without values. When you call mesh.barLabels(catNames) and omit value labels, every later updateBars(values) auto formats the numeric values via str.tostring() . Pass valLabels only when you need custom strings.
Direct mesh.vertices mutation requires invalidateCache(). Transform mutators ( moveTo , rotateBy , scaleTo , etc.) invalidate the world vertex cache on their own. Only raw index writes like mesh.vertices.set(i, newVec) need a manual mesh.invalidateCache() call to force re-projection. Skipping it will make the renderer draw stale geometry.
Drawing budgets fail silently. If a scene emits more than 100 polylines, 500 lines, or 500 labels in a single frame, PulseWire silently drops the overflow without raising a runtime error. Missing geometry almost always means a budget overrun - lower maxFaces , drop a contour level, or simplify overlay primitives to bring the frame back inside the caps.
render() deletes non Pine3D drawings too. Every render() call clears polyline.all , line.all , label.all , and linefill.all before redrawing. Any manual label.new() , line.new() , etc. issued before render() in the same frame will be wiped. Issue custom drawings after the render call if you need them to persist.
mergeMeshes() preserves depth grouping. When every source mesh passed into mergeMeshes() has the same vertex and face count (e.g. identical primitives in a voxel grid), the merged mesh auto derives depth group boundaries so the combined geometry still sorts correctly per original instance. Mixing primitives with different topologies disables the grouping.
CPU timeouts: knobs to turn. Pine Script enforces a per bar execution budget, and dense scenes can trip it before the drawing budget ever does. If a scene compiles but times out at runtime, reach for these levers in order: lower occlusionRaster (e.g. 768 -> 384) for the biggest single perf win, reduce maxFaces to cap the solid face pool, drop levels on contour surfaces, simplify sphere/torus segment counts, and gate heavy work behind barstate.islast so history bars only build geometry rather than render it.
----------------------------------------------------------------------------------------------------------------
🔷 MORE EXAMPLES
The following scenes were all built entirely in Pine Script™ v6 using Pine3D as the rendering layer. They exist to demonstrate that the library is a real engine capable of complex, production grade visualizations.
🔸 4D Hypercube (Tesseract). A rotating tesseract, projected from 4D to 3D to 2D in real time using a custom 4D rotation matrix layered on top of Pine3D's standard projection pipeline.
🔸 Solar System. Following the publication of my 3D Solar System back in 2024, which introduced new graphical rendering concepts into Pine Script, we have seen a wave of various interpretations of the underlying vector classes, ranging from tutorials to niche specific integrations using hardcoded math. It became clear that a unified architecture was needed, one that would lower the barrier to entry while simultaneously handling the optimization process, which is both complex and error prone to do manually.
That architecture is what Pine3D delivers. Below is a re-creation of the classic 3D Solar System rebuilt entirely on top of the library. It uses a fraction of the original code , renders roughly 5x faster , and adds real lighting cast directly from the Sun , all while consuming only a third of the available drawing budget thanks to the occlusion and culling mechanisms Pine3D handles out of the box.
----------------------------------------------------------------------------------------------------------------
🔷 API REFERENCE
🔸 Top Level Entry Points. newScene() creates a ready to use Scene with a default camera and light. render(scene, ...) draws the current frame and auto clears the previous frame's chart drawings; see the Renderer section above for the full parameter list. vec3(x, y, z) creates a Vec3. colorBrightness() is an exported color utility helper.
🔸 Mesh Factories.
Primitives - cube() , cuboid() , pyramid() , plane() , sphere() , cylinder() , torus() , grid() , disc() , circle() for ready made geometry.
customMesh(verts, faces) - Low level escape hatch for authoring your own topology.
mergeMeshes(meshes, tag, removeInterior) - Bakes transforms and combines many meshes into one. With removeInterior = true , coincident faces with opposing normals (e.g. shared walls between adjacent cubes in a grid) are culled so only the exterior shell survives, a major optimization for dense voxel style scenes.
surface(heights, size, lowCol, highCol, levels, axisX, axisZ) - Creates a contour surface mesh.
bars3D(values, barWidth, barDepth, spacing, lowCol, highCol, maxHeight) - Creates a combined 3D bar chart mesh; add labels with the chainable barLabels(names, values) method.
🔸 UDT Constructors. Overlay primitives and face descriptors are plain UDTs. Because these types have many fields, always instantiate them with named arguments rather than positional, e.g. Label3D.new(position = pos, txt = "•") :
Face - fields: vi (array of vertex indices into the parent mesh), col . Used when authoring customMesh() topology; every face must have at least 3 indices and should be planar.
Label3D - fields: position , txt , textColor , bgColor , labelStyle , labelSize , fontFamily , tooltip , visible , tag . Only position is required.
Line3D - fields: start , end , col , width , visible , tag , lineStyle .
Polyline3D - fields: points , col , fillColor , width , closed , visible , tag , lineStyle .
Vec3.new(x, y, z) or the vec3(x, y, z) shorthand.
🔸 Trail Primitive. trail3D(size, capacity, trailCol, minSamples) creates a streamed Trail3D primitive with a main trail, two projection polylines, and a cage polyline. capacity is internally clamped to 300 samples to keep the rolling buffer inside Pine's execution budget; passing a larger value silently resolves to 300. minSamples (default 60) is the sample count at which the cage reaches its full cube width: below that the cage stays cube shaped and samples stretch across it; above that the cage grows rightward at a fixed step until capacity is hit. scene.add(trail) registers the sub primitives into the scene. Trail3D methods: pushSample() , axisLabels() , cage() , moveTo() , show() , hide() .
🔸 Mesh Methods.
Transform - moveTo() , moveBy() , rotateTo() , rotateBy() , scaleTo() , scaleUniform() .
Appearance - setColor() , setFaceColor() , setStyle() , show() , hide() , setTag() .
Stroke styling (direct) - mesh.lineWidth := 3 and mesh.lineStyle := line.style_dashed control width and style of every visible mesh edge in wireframe modes and the outline of solid faces in drawMode = "poly" .
Shadow opt out (direct) - mesh.castShadow := false excludes the mesh from shadow casting while still receiving light. Useful for ghost overlays, debug geometry, or semi transparent meshes you do not want occluding the scene.
Lifecycle - clone() , faceCount() , invalidateCache() .
Data mutation - updateSurface() and updateBars() refresh persistent meshes in place. updateBars() refreshes any bar label positions automatically; pass catLabels / valLabels to also update the text.
Contour helpers - gridBox() and gridLabels() queue overlays on the mesh and hand them to the scene when you call scene.add(mesh) .
Bar helpers - barLabels() is chainable on a bars3D() mesh and queues its category and value labels for the next scene.add(mesh) .
Note: rotateTo() and rotateBy() expect degrees. The low level Vec3.rotateX/Y/Z() methods work in radians.
🔸 Scene Methods.
Lighting - setLightDir() , setLightPos() , setLightMode() , setAmbient() , setShadowStrength() , showLightSource() .
Scene graph - add(mesh) , add(label) , add(array) , add(line) , add(polyline) , add(trail) , remove(index) , remove(tag) , clear() .
Lookup and navigation - getMesh() , getLabel() , getLine() , getPolyline() , lookAt() , totalFaces() .
Cache control - invalidateLightCache() after mutating light direction or scene bounds externally; invalidateAllCaches() to also invalidate every mesh's world vertex cache (use after directly mutating mesh.vertices ).
Note: scene.clear() clears the scene graph itself. render() only clears the previous frame's PulseWire drawings.
🔸 Camera Methods. setPosition(x, y, z) moves the camera. lookAt(x, y, z) / lookAt(vec3) points at a world space target. orbit(angleX, angleY, distance) does a spherical orbit around the current target. setFov(val) sets the perspective scale factor. Camera fields ( position , target , fov ) are also directly mutable via assignment when you need to tune them outside the provided setters, e.g. scene.camera.fov := 1200.0 .
🔸 Light Field Mutation. In addition to the scene level convenience setters, every field on scene.light is directly mutable for fine grained tuning: scene.light.selfShadow := true enables self shadowing, scene.light.shadowBias := 0.2 adjusts the shadow acne offset, scene.light.shadowStrength and scene.light.ambient are also exposed. Mutate them after newScene() or between frames; the renderer reads them every call.
🔸 Vec3 Methods. Core math: add() , sub() , scale() , negate() , dot() , cross() , length() , normalize() , distanceTo() , lerp() . Rotation and helpers: rotateX() , rotateY() , rotateZ() , copy() , toString() .
🔸 Overlay Primitive Methods.
Label3D - moveTo() , moveBy() , setText() , setTextColor() , setTooltip() , show() , hide() , setTag() .
Line3D - setStart() , setEnd() , setPoints() , setColor() , show() , hide() , setTag() .
Polyline3D - setColor() , show() , hide() , setTag() .
Every UDT field is mutable via direct assignment for properties without a chainable setter:
Label3D - bgColor , labelStyle (label.style_*), labelSize (size.*), fontFamily (font.family_*), visible .
Line3D - width , lineStyle (line.style_solid / _dashed / _dotted / _arrow_left / _arrow_right / _arrow_both), visible .
Polyline3D - width , lineStyle (line.style_solid / _dashed / _dotted only; arrow styles are not supported by PulseWire's polyline primitive), fillColor , closed , visible .
Mutations are read per frame by the renderer, so they animate freely.
🔸 High Level Scene Helpers. wireGrid(scene, w, d, divX, divZ, col) adds a depth sorted ground grid. scene.add(array) adds a batch of labels in one call - the idiomatic way to push a scatter cloud into the scene.
🔸 Mesh Level Chainable Overlays. mesh.barLabels(names, values, ...) adds category and value labels on a bars3D() mesh. mesh.gridBox(col, divs) adds a wireframe bounding box cage on a surface() mesh. mesh.gridLabels(col, xName, yName, zName, ticks, fmt) adds axis titles and tick value labels on a surface() mesh; tick values auto refresh on updateSurface() . All three are queued on the mesh and drained into the scene by scene.add(mesh) .
----------------------------------------------------------------------------------------------------------------
This work is licensed under (CC BY-NC-SA 4.0) , meaning usage is free for non-commercial purposes given that Alien_Algorithms is credited in the description for the underlying software. For commercial use licensing, contact Alien_Algorithms
Library

KalmanEngineLibKalmanEngineLib
A Pine Script v6 library that provides a reusable engine for multi-state Kalman filtering, symmetric covariance packing, sequential scalar measurement updates, Mahalanobis gating, adaptive noise estimation, online coupling estimation, multi-scale trajectory storage, covariance-derived confidence bands, and k-step covariance propagation.
What it does
Implements a generic N-state Kalman filter where the posterior covariance P is stored as a packed upper triangle (n*(n+1)/2 elements), saving ~47% memory vs a full matrix.new(n,n) at n=14.
Supports block-diagonal transition matrices via separate sub-blocks (3×3 kinematics, 6×6 z-score dynamics, 5×5 Mahalanobis, 3×5 cross-coupling Γ_lag) instead of a single n×n F matrix.
Provides a sequential scalar measurement update in Joseph form for numerical stability; calling it once per observation is equivalent to a batch update but avoids allocating an m×n H matrix.
Core components
UDTs: KalmanState_N, TransitionConfig, TrajectoryStore, ConceptConfig — callers own all persistent state; library functions are stateless transforms.
Triangle primitives: f_tri_idx, f_tri_get, f_tri_set, f_tri_new, f_tri_diag, f_tri_add_outer_product for packed symmetric matrix arithmetic.
Prediction: f_predict_block, f_predict_identity, f_P_predict_diag, f_P_predict_cross.
Update: f_sequential_update returning for external diagnostics.
Gating: f_mahalanobis_3d (analytic 3×3 inverse with Ledoit-Wolf shrinkage and diagonal fallback near singularity), f_nis_test, f_gating_gain_mod.
Adaptive noise: f_adaptive_Q_scalar (windowed MLE), f_adaptive_R_scalar (innovation z-score ratchet).
Coupling: f_gamma_lag_update (scalar 1-D Kalman β-tracker for Γ_lag elements).
Trajectory: f_traj_init, f_traj_update, f_traj_xcorr — circular buffers at Δ={3,5,7} bar skips with Pearson cross-correlation for lag calibration.
Bands and projection: f_covariance_band_width, f_confidence_envelope, f_k_step_cov_propagation, f_z_spread.
Derived outputs: KMEMA (adaptive EMA modulated by innovation shock, TE confidence, velocity), online OLS beta update, execution-score helpers.
Architecture notes
All functions are stateless transforms operating on UDTs passed by the caller; no var declarations inside library functions.
Element budget: ~3,600 for the core engine; ~1,800 for TrajectoryStore at depth=100, n_feat=6. Total ~22K elements under Pine's 100K limit.
Self-healing: f_state_sanitize resets na or overflow entries in x and P diagonals to caller-supplied defaults.
Usage pattern
Declare a var KalmanState_N state = f_init_regression(n, P0, Q0, R0) in the indicator.
Each bar: call f_predict_block → f_P_predict_diag/f_P_predict_cross → one or more f_sequential_update per scalar observation → optional f_mahalanobis_3d/f_adaptive_Q_scalar/f_adaptive_R_scalar → read outputs via f_z_spread, f_confidence_envelope, f_k_step_cov_propagation.
Scope
General-purpose Kalman infrastructure; no market-specific logic, no signals, no thresholds embedded. Intended as a dependency for indicators and strategies that need rigorous multi-state filtering with adaptive noise and regime-aware gating.
License
Mozilla Public License 2.0. Library

HeikinAshiTrendUtilities
Library HeikinAshiTrendUtilities
This library contains reusable Heikin Ashi helpers for building Pine scripts that use Heikin Ashi as more than a candle style.
It centralizes the Heikin Ashi foundation, HA-based oscillator engines, streak and confirmed-trend logic, Pressure Meter helpers, max-move scanning, Fib Backbone structure helpers, and Primary Trend helpers so those parts do not need to be rewritten across multiple scripts.
Everything on the example chart is materially driven by the library, whether through the Heikin Ashi calculations themselves, the HA-based oscillator and pressure engine, the predictive close logic, the smoothed HA overlay, the Fib Backbone context window, or the structure geometry used to project key analytical visuals.
How to use
Import the library near the top of your script in global scope, alongside any other imports, before you start calling its helpers.
Typical placement:
• //@version=6
• indicator(...) or strategy(...)
• import MYNAMEISBRANDON/HeikinAshiTrendUtilities/1 as haUtils
For more information on libraries and incorporating them into your scripts, see the Libraries section of the Pine Script User Manual: www.pulsewire.com
➖Heikin Ashi Core Helpers➖
These helpers handle the basic building blocks of Heikin Ashi. They let a script create standard HA candles, estimate the price needed to flip the current HA candle, and generate a smoothed HA version for a cleaner trend view. In other words, this region provides the core HA math used to build the rest of the library’s trend, engine, and structure tools.
heikinAshi(openValue, closeValue, highValue, lowValue, haOpenPrev, haClosePrev)
Builds one Heikin Ashi candle from real OHLC and prior HA state
Parameters:
openValue (float): Real open
closeValue (float): Real close
highValue (float): Real high
lowValue (float): Real low
haOpenPrev (float): Prior Heikin Ashi open
haClosePrev (float): Prior Heikin Ashi close
Returns: HA open, HA close, HA high, HA low, is HA up, is HA down
haPredictClose(haOpen, openValue, highValue, lowValue)
Estimates the real close price needed to flip the current HA candle
Parameters:
haOpen (float): Current Heikin Ashi open
openValue (float): Real open
highValue (float): Real high
lowValue (float): Real low
Returns: Predicted real close needed to flip the HA candle
smoothedHeikinAshi(openValue, highValue, lowValue, closeValue, len1, len2)
Builds double-smoothed Heikin Ashi values from real OHLC inputs
Parameters:
openValue (float): Real open
highValue (float): Real high
lowValue (float): Real low
closeValue (float): Real close
len1 (simple int): First EMA smoothing length applied to real OHLC
len2 (simple int): Second EMA smoothing length applied to HA OHLC
Returns: Smoothed HA open, smoothed HA high, smoothed HA low, smoothed HA close, is smoothed HA up, is smoothed HA down
➖HA Oscillator Foundation Helpers➖
These helpers turn raw Heikin Ashi candle movement into a usable oscillator foundation. They measure the HA candle’s bullish or bearish range, normalize that movement so it can be compared more consistently across bars, and build upper/lower guide levels that help a script judge when that oscillator is stretching into stronger trend pressure. In other words, this region creates the base signal that the HA Blend, HA Range Base, color engine, and Pressure Meter can build from.
haSignedRangePct(haHigh, haLow, haClose, haIsBull, haIsBear)
Returns the signed HA range-percent foundation used by the oscillator engine
Parameters:
haHigh (float): Heikin Ashi high
haLow (float): Heikin Ashi low
haClose (float): Heikin Ashi close
haIsBull (bool): True when the current HA candle is bullish
haIsBear (bool): True when the current HA candle is bearish
Returns: Signed HA range-percent foundation
haPreparedOscSource(signedSrc, normLen, useClamp, clampRange)
Returns the normalized / optionally clamped HA oscillator source
Parameters:
signedSrc (float): Signed HA foundation
normLen (simple int): Normalization lookback length
useClamp (simple bool): Whether the normalized result should be clamped
clampRange (float): Absolute clamp boundary when useClamp is true
Returns: Prepared HA oscillator source
haOscGuides(src, lookback, guideFactor)
Returns upper and lower threshold guides from an oscillator series
Parameters:
src (float): Oscillator series
lookback (simple int): Guide lookback window
guideFactor (float): Scaling factor applied to the highest/lowest values
Returns: Upper guide, lower guide
➖HA Blend Engine Helpers➖
These helpers take the prepared HA oscillator source and turn it into a smoother trend engine by blending multiple EMA pairs together. They let the script choose a faster, more balanced, or slower blend profile, then optionally smooth that final output one more time. In other words, this region builds the more layered, trend-following version of the HA oscillator engine.
haBlendPairStackText(pairSet)
Returns the active EMA pair-stack text for the selected HA Blend pair set
Parameters:
pairSet (simple string): Pair-set label. Expected values: "Fast", "Balanced", or "Slow"
Returns: Pair-stack text
haBlendEngineCore(src, pairSet)
Returns the raw HA Blend engine core before final smoothing
Parameters:
src (float): Prepared HA signed source used by the blend engine
pairSet (simple string): Pair-set label. Expected values: "Fast", "Balanced", or "Slow"
Returns: Raw HA Blend engine core
haBlendEngine(src, pairSet, useFinalSmooth, finalSmoothLen, finalSmoothType)
Returns the final HA Blend engine with optional final smoothing
Parameters:
src (float): Prepared HA signed source used by the blend engine
pairSet (simple string): Pair-set label. Expected values: "Fast", "Balanced", or "Slow"
useFinalSmooth (simple bool): Whether final smoothing should be applied
finalSmoothLen (simple int): Final smoothing length
finalSmoothType (simple string): Final smoothing type. Expected values: "EMA" or "SMA"
Returns: Final HA Blend engine
➖HA Range Base Engine Helpers➖
These helpers take the prepared HA oscillator source and smooth it in a more direct way than the Blend engine. Instead of combining multiple EMA pairs, they use one selected smoothing length and MA type to create a cleaner base trend signal, with the option to smooth that result one more time. In other words, this region builds the simpler, more straightforward version of the HA oscillator engine.
haRangeEngineCore(src, rangeLen, rangeMaType)
Returns the raw HA Range Base engine core before final smoothing
Parameters:
src (float): Prepared HA signed source used by the Range Base engine
rangeLen (simple int): Core smoothing length used by the Range Base engine
rangeMaType (simple string): Core smoothing type. Expected values: "EMA" or "SMA"
Returns: Raw HA Range Base engine core
haRangeEngine(src, rangeLen, rangeMaType, useFinalSmooth, finalSmoothLen, finalSmoothType)
Returns the final HA Range Base engine with optional final smoothing
Parameters:
src (float): Prepared HA signed source used by the Range Base engine
rangeLen (simple int): Core smoothing length used by the Range Base engine
rangeMaType (simple string): Core smoothing type. Expected values: "EMA" or "SMA"
useFinalSmooth (simple bool): Whether final smoothing should be applied
finalSmoothLen (simple int): Final smoothing length
finalSmoothType (simple string): Final smoothing type. Expected values: "EMA" or "SMA"
Returns: Final HA Range Base engine
➖HA Threshold Color Helpers➖
This helper takes centered oscillator behavior and turns it into a usable visual color state. It helps a script decide when the HA-based oscillator is rising or falling above or below its guide levels so candles, rows, or other visuals can reflect stronger or weaker trend pressure.
haThresholdStateColor(src, upperGuide, lowerGuide, aboveUpperRiseColor, aboveZeroRiseColor, aboveZeroFallColor, belowZeroFallColor, belowLowerFallColor, belowZeroRiseColor)
Resolves a visual color from centered-oscillator threshold state
Parameters:
src (float): Source series
upperGuide (float): Upper threshold guide
lowerGuide (float): Lower threshold guide
aboveUpperRiseColor (color): Color used when src is above the upper guide and rising
aboveZeroRiseColor (color): Color used when src is above zero and rising
aboveZeroFallColor (color): Color used when src is above zero and falling
belowZeroFallColor (color): Color used when src is below zero and falling
belowLowerFallColor (color): Color used when src is below the lower guide and falling
belowZeroRiseColor (color): Color used when src is below zero and rising
Returns: Resolved visual color
➖HA Structure Scan Helpers➖
This helper scans a chosen lookback window and finds the strongest completed move inside it. It compares bullish and bearish candidates in the same scan, then returns whichever move was stronger along with the start and end anchors. In other words, this region gives a script a reusable way to locate the dominant move that can later be used for Fib Backbone structure, Primary Max Move logic, or other trend-structure work. :contentReference {index=0} :contentReference {index=1}
haScanMaxMove(lookback, includeCurrentBar, highSeries, lowSeries)
Scans a lookback window for the strongest upward or downward percentage move
Parameters:
lookback (simple int): Number of bars to scan
includeCurrentBar (simple bool): Whether bar 0 should be included in the scan
highSeries (float): High series used for upward and downward move detection
lowSeries (float): Low series used for upward and downward move detection
Returns: Winning direction, winning percent move, winning start bars-ago, winning end bars-ago, winning span bars
➖HA Streak Helpers➖
These helpers let a script keep track of active Heikin Ashi streaks. They determine whether the current HA sequence is bullish or bearish, count how long that streak has been running, assign a tier color based on streak length, and measure how far price has moved from the streak’s starting point. In other words, this region helps turn raw HA trend runs into usable streak state, color, and percent-move data for candles, rows, labels, and trend readouts. :contentReference {index=0}
haStreakState(haOpen, haClose, bullCountPrev, bearCountPrev)
Resolves raw HA bull/bear state, streak counts, and streak start offset
Parameters:
haOpen (float): Current Heikin Ashi open
haClose (float): Current Heikin Ashi close
bullCountPrev (int): Prior bullish streak count
bearCountPrev (int): Prior bearish streak count
Returns: is HA bullish, is HA bearish, bullish streak count, bearish streak count, current streak length, streak start bars-ago
haStreakTierColor(isHaBull, isHaBear, bullCount, bearCount, streakTierBars, bullTier1, bullTier2, bullTier3, bullTier4, bearTier1, bearTier2, bearTier3, bearTier4, neutralColor)
Returns the active streak-tier color from bull/bear streak counts
Parameters:
isHaBull (bool): True when the current HA streak is bullish
isHaBear (bool): True when the current HA streak is bearish
bullCount (int): Current bullish streak count
bearCount (int): Current bearish streak count
streakTierBars (simple int): Number of bars required before advancing to the next tier
bullTier1 (color): Bullish tier 1 color
bullTier2 (color): Bullish tier 2 color
bullTier3 (color): Bullish tier 3 color
bullTier4 (color): Bullish tier 4 color
bearTier1 (color): Bearish tier 1 color
bearTier2 (color): Bearish tier 2 color
bearTier3 (color): Bearish tier 3 color
bearTier4 (color): Bearish tier 4 color
neutralColor (color): Fallback color when no active streak is available
Returns: Active streak-tier color
haStreakPct(isHaBull, isHaBear, streakBars, highSeries, lowSeries)
Returns the wick-based percent move from the streak start to the current bar
Parameters:
isHaBull (bool): True when the current HA streak is bullish
isHaBear (bool): True when the current HA streak is bearish
streakBars (int): Current active streak length
highSeries (float): High series used for streak measurement
lowSeries (float): Low series used for streak measurement
Returns: Wick-based streak percent move
➖Confirmed HA Trend Helpers➖
These helpers let a script work with a slower, confirmation-based HA trend instead of flipping immediately on the first opposite HA candle. They track the currently confirmed direction, count how many opposite candles are building toward the next possible flip, project the confirmed trend using regular-price body or wick anchors, and measure how far that confirmed trend has moved from its confirmed start. In other words, this region helps scripts build a more stable HA trend model that filters out some of the noise of raw HA flips. :contentReference {index=0} :contentReference {index=1}
haConfirmedTrendState(enabled, rawDir, confirmBars, dirPrev, oppCountPrev, startBarPrev, firstOppBarPrev)
Resolves confirmed trend direction, build count, and confirmed start bar
Parameters:
enabled (simple bool): Whether the confirmed-trend engine is active
rawDir (int): Current raw HA direction: +1 bull, -1 bear, 0 neutral
confirmBars (simple int): Consecutive opposite raw HA bars required to confirm a flip
dirPrev (int): Prior confirmed direction
oppCountPrev (int): Prior opposite-side build count
startBarPrev (int): Prior confirmed trend start bar index
firstOppBarPrev (int): Prior first opposite raw HA bar index
Returns: Confirmed direction, opposite-side build count, confirmed start bar index, first opposite raw HA bar index, confirmed leg bars, confirmed start bars-ago
haConfirmedTrendProjection(confirmedDir, startOffset, openValue, highValue, lowValue, closeValue, anchorMode, pathMode, forwardBars)
Returns confirmed trend projection geometry from body/wick anchor rules
Parameters:
confirmedDir (int): Confirmed direction: +1 bull, -1 bear, 0 neutral
startOffset (int): Confirmed start bars-ago offset
openValue (float): Regular-price open
highValue (float): Regular-price high
lowValue (float): Regular-price low
closeValue (float): Regular-price close
anchorMode (simple string): Projection anchor mode: "Body" or "Wick"
pathMode (simple string): Projection path mode: "Same Side" or "Opposite Side"
forwardBars (simple int): Number of bars forward for projection
Returns: Has valid projection, start Y, current Y, future Y, slope
haConfirmedTrendPct(confirmedDir, startOffset, highSeries, lowSeries)
Returns confirmed streak percent movement from the confirmed start bar
Parameters:
confirmedDir (int): Confirmed direction: +1 bull, -1 bear, 0 neutral
startOffset (int): Confirmed start bars-ago offset
highSeries (float): HA high series used for confirmed move measurement
lowSeries (float): HA low series used for confirmed move measurement
Returns: Confirmed streak percent move
➖HA Pressure Meter Helpers➖
These helpers take the HA-based oscillator engine and convert it into an easier 0–100 pressure reading. They help a script decide when bullish or bearish pressure is becoming active, assign matching tier colors for rows or other visuals, and return the color state for a pressure strip or similar chart-edge signal. In other words, this region turns the HA oscillator into a simpler pressure model that is easier to read at a glance.
haPressureMeter(rawOsc, bullAnchor, bearAnchor)
Normalizes a raw oscillator value into a 0-100 Pressure Meter
Parameters:
rawOsc (float): Raw oscillator value
bullAnchor (float): Raw oscillator value that should map to 100
bearAnchor (float): Raw oscillator value that should map to 0
Returns: Pressure Meter value in the 0-100 range
haPressureState(pressureMeter, bullThreshold, bearThreshold)
Resolves bullish, bearish, and neutral threshold state from the Pressure Meter
Parameters:
pressureMeter (float): Normalized Pressure Meter value
bullThreshold (float): Meter level where bullish pressure becomes active
bearThreshold (float): Meter level where bearish pressure becomes active
Returns: Bull-active, bear-active, neutral-between
haPressureTierColors(pressureMeter, bullTier1, bullTier2, bullTier3, bullTier4, bearTier1, bearTier2, bearTier3, bearTier4, fallbackBg)
Returns tier-based pressure-row background and readable text color
Parameters:
pressureMeter (float): Normalized Pressure Meter value
bullTier1 (color): Bull tier 1 color
bullTier2 (color): Bull tier 2 color
bullTier3 (color): Bull tier 3 color
bullTier4 (color): Bull tier 4 color
bearTier1 (color): Bear tier 1 color
bearTier2 (color): Bear tier 2 color
bearTier3 (color): Bear tier 3 color
bearTier4 (color): Bear tier 4 color
fallbackBg (color): Fallback background when the meter is na
Returns: Row background color, row text color
haPressureStripColor(pressureMeter, bullThreshold, bearThreshold, bullTier1, bullTier2, bullTier3, bullTier4, bearTier1, bearTier2, bearTier3, bearTier4, neutralColor, activeTransp, neutralTransp)
Returns active or neutral strip color from the Pressure Meter state
Parameters:
pressureMeter (float): Normalized Pressure Meter value
bullThreshold (float): Meter level where bullish pressure becomes active
bearThreshold (float): Meter level where bearish pressure becomes active
bullTier1 (color): Bull tier 1 color
bullTier2 (color): Bull tier 2 color
bullTier3 (color): Bull tier 3 color
bullTier4 (color): Bull tier 4 color
bearTier1 (color): Bear tier 1 color
bearTier2 (color): Bear tier 2 color
bearTier3 (color): Bear tier 3 color
bearTier4 (color): Bear tier 4 color
neutralColor (color): Neutral-zone base color
activeTransp (int): Transparency used when bull or bear pressure is active
neutralTransp (int): Transparency used inside the neutral zone
Returns: Strip color
➖Fib Backbone Structure Helpers➖
These helpers take a winning max-move scan and turn it into the structure a script can use for Fib Backbone analysis. They define the backbone’s start and end anchors, determine the related support/resistance anchor geometry, calculate Fib level prices between those anchors, and measure how far current price is from those levels. In other words, this region helps convert a dominant move into a reusable backbone structure that can support diagonals, S/R anchors, boxes, and Fib-based readouts.
haFibBackboneStructure(dir, startBA, endBA, openValue, highValue, lowValue, closeValue)
Returns backbone coordinates, S/R anchors, and anchor-box geometry
Parameters:
dir (int): Winning move direction: +1 bull, -1 bear, 0 none
startBA (int): Winning move start bars-ago
endBA (int): Winning move end bars-ago
openValue (float): Regular-price open
highValue (float): Regular-price high
lowValue (float): Regular-price low
closeValue (float): Regular-price close
Returns: ok, xStart, xEnd, yStart, yEnd, startIsRes, endIsRes, anchorTopS, anchorBotS, anchorTopE, anchorBotE, isTopS, isTopE
haFibLevelPrice(yStart, yEnd, fibLevel)
Returns the price of one fib level between the backbone anchors
Parameters:
yStart (float): Backbone start anchor price
yEnd (float): Backbone end anchor price
fibLevel (float): Fib level such as 0.236, 0.382, 0.50, 0.618, 0.786
Returns: Fib level price
haFibPctFromClose(closeValue, fibPrice)
Returns percent distance from close to a fib level
Parameters:
closeValue (float): Current close
fibPrice (float): Fib level price
Returns: Percent from close to fib level
➖Fib Backbone Context Window Helpers➖
These helpers build the larger context window around the active Fib Backbone lookback. They let a script define the left/right range of that window, calculate its current high and low bounds, and find the midpoint of the same structure. In other words, this region helps frame the broader area that the active backbone move is being selected from, so the move can be viewed in context rather than in isolation.
haFibContextWindow(lookback, includeCurrentBar, sourceMode, highValue, lowValue, closeValue)
Returns the active Fib Backbone context window geometry
Parameters:
lookback (simple int): Context-window lookback length
includeCurrentBar (simple bool): Whether the current bar participates in the active window
sourceMode (simple string): Source selection. Expected values: "Wicks" or "Closes"
highValue (float): Regular-price high
lowValue (float): Regular-price low
closeValue (float): Regular-price close
Returns: ok, leftX, rightX, windowBars, windowHigh, windowLow, leftHigh, leftLow
haFibContextMidpoint(ok, windowHigh, windowLow)
Returns the midpoint of the active Fib Backbone context window
Parameters:
ok (bool): Whether the context window is valid
windowHigh (float): Active context-window high
windowLow (float): Active context-window low
Returns: Context-window midpoint
➖Primary Trend Window Helpers➖
These helpers scan a lookback window to find the strongest completed HA streak and turn that winner into usable trend information. They identify the winning streak, assign it the correct tier color, and return the anchor coordinates needed to project that streak as a chart-side diagonal. In other words, this region helps a script reduce a larger HA trend window down to its most important completed streak structure.
haPrimaryTrendWinner(lookback, haBull, haBear, bullCount, bearCount, haHigh, haLow)
Returns the strongest completed HA streak inside the lookback window
Parameters:
lookback (simple int): Number of bars to scan
haBull (bool): Bullish HA state series
haBear (bool): Bearish HA state series
bullCount (int): Bullish HA streak-count series
bearCount (int): Bearish HA streak-count series
haHigh (float): HA high series used for wick-based streak measurement
haLow (float): HA low series used for wick-based streak measurement
Returns: Winning streak length, winning direction, winning percent move, winning start bars-ago, winning end bars-ago, winning validity state
haPrimaryTrendTierColor(dir, streakLen, streakTierBars, bullTier1, bullTier2, bullTier3, bullTier4, bearTier1, bearTier2, bearTier3, bearTier4, fallbackBg)
Returns the winning Primary Trend tier color
Parameters:
dir (int): Winning streak direction: +1 bull, -1 bear, 0 none
streakLen (int): Winning streak length
streakTierBars (simple int): Number of bars required before advancing to the next tier
bullTier1 (color): Bullish tier 1 color
bullTier2 (color): Bullish tier 2 color
bullTier3 (color): Bullish tier 3 color
bullTier4 (color): Bullish tier 4 color
bearTier1 (color): Bearish tier 1 color
bearTier2 (color): Bearish tier 2 color
bearTier3 (color): Bearish tier 3 color
bearTier4 (color): Bearish tier 4 color
fallbackBg (color): Fallback background when no valid winner exists
Returns: Winning tier color
haPrimaryTrendCoords(dir, startBA, endBA, highSeries, lowSeries)
Returns diagonal coordinates from the winning streak anchors
Parameters:
dir (int): Winning streak direction: +1 bull, -1 bear, 0 none
startBA (int): Winning start bars-ago
endBA (int): Winning end bars-ago
highSeries (float): High series used for line anchors
lowSeries (float): Low series used for line anchors
Returns: ok, x1, x2, y1, y2
NOTES
This is a Heikin-Ashi-specific utility library. It is meant to provide the reusable HA math, state, and structure layer. Final rendering choices such as plot style, line objects, boxes, labels, tables, and overall UI layout are expected to remain script-level decisions.
Thanks to SimpleCryptoLife for the open-source HA core functions heikinAshi() & haPredictClose() and thus the inspiration that they've given me to create HA-based indicators for the HA trader enthusiast.
Library

Vantage_PairedSizingVantage_PairedSizing — Position sizing for strategies that pair a primary trade with a recovery trade under a daily loss budget.
─────────────────────────────────────────
WHAT IT DOES
Answers the question "how many contracts can I take on the primary trade so that, if it stops out, a correctly-sized recovery trade still fits within my daily loss limit?" The library scans candidate quantities top-down and returns the largest one whose worst case (primary stop + recovery stop) stays inside the budget, subject to one of four risk-reward modes.
─────────────────────────────────────────
WHAT IT PROVIDES
A single entry-point auto-sizer that takes the primary leg's entry/stop/target, the recovery leg's entry/stop/target, a daily loss budget, and a sizing mode — and returns a result record with primary quantity, recovery quantity, per-leg dollar risk, worst-case dollar exposure, and net-if-recovery-wins. The scan falls through a mode ladder (MatchPrimaryProfit → MatchPct → NetGreen) and a no-recovery fallback before giving up, so callers get a usable answer in marginal cases instead of a flat rejection.
Four named sizing modes covering the common risk-reward shapes: largest size under a drawdown cap, largest size where the recovery win leaves the session net-green, largest size where the recovery win matches the primary's target profit, and a percentage variant of the match mode.
Two utility functions for the dollar math — risk and profit for a given quantity between two prices — so strategies don't have to redo the tick-size / point-value arithmetic themselves.
─────────────────────────────────────────
HOW TO USE
A complete example call is in the comment block at the top of the source file — import the library, copy the pattern, plug in your primary and recovery price levels. Hover any exported type, enum, or function in the Pine Editor for per-parameter documentation. Library

KernelLens🟦 KernelLens is a professional kernel regression library for Pine Script v6, providing eight mathematically rigorous Nadaraya–Watson estimators, a three-mode filter layer, a unified string dispatcher, and a suite of trading utilities — all built from the ground up on correct non-parametric statistics. Unlike existing Pine smoothing libraries — which inherit a decade-old loop-bound bug that silently reduces every kernel window to a handful of bars, regardless of the bandwidth parameter — KernelLens is built with auditable math, NA-safe iteration, input validation at every entry point, and academic references cited inline next to the formulas they describe.
The library integrates eight independent kernel families — Rational Quadratic, Gaussian, Periodic, Locally Periodic, Epanechnikov, Tricube, Triangular, and Cosine — behind a consistent API, with every raw estimator wrapped in a filter layer (None / Smooth / Zero Lag), a unified dispatcher for dropdown-driven kernel selection, and five utility exports covering slope detection, trend state, crossover signaling, residual confidence bands, and Silverman's rule-of-thumb bandwidth recommendation. Every public function validates its inputs, raises descriptive runtime errors on misuse, and returns `na` only when there is genuinely no data — never as a silent fallback.
🟦 MATHEMATICAL FOUNDATION
**The Nadaraya–Watson Estimator**
Given a source series `y_t` and a symmetric kernel `K` with scale parameter `ℓ` (the "bandwidth"), the Nadaraya–Watson estimator of the regression function `m(x) = E ` evaluated at the current bar is:
```
Σᵢ K(dᵢ / ℓ) · y_{t−i}
ŷ(t) = ───────────────────────
Σᵢ K(dᵢ / ℓ)
```
where `dᵢ` is the bar-distance from the kernel center and the sum runs over a finite window determined by the effective support of `K`.
The estimator is a locally weighted average: bars close to the kernel center contribute heavily, distant bars contribute proportionally less, and bars outside the support contribute nothing. It is asymptotically unbiased up to `O(ℓ²)` for twice-differentiable `m`, with variance of order `(n·ℓ)⁻¹` — the classical bias–variance trade-off that defines all non-parametric smoothers.
**Why Kernel Regression Beats Rolling Means**
A simple moving average gives every bar in the window the same weight. Kernel regression gives each bar a weight that decays smoothly with distance, producing:
- **Smoother output** — no step artifacts when bars enter / leave the window
- **Better bias control** — the peak of the kernel sits exactly on the point being estimated
- **Kernel-specific behavior** — compact-support kernels eliminate tail contamination entirely; Rational Quadratic's `α` parameter exposes multi-scale mixing; Periodic kernels resonate with known cycle lengths
The math has been the academic standard for non-parametric regression since Nadaraya (1964) and Watson (1964). KernelLens brings it to Pine Script v6 in its correct, bug-free form.
🟦 THE EIGHT KERNELS
All eight kernels implement the Nadaraya–Watson weighting scheme. They differ in support (compact versus infinite), smoothness (how many times differentiable), and how weight decays with distance.
| # | Kernel | Formula | Support | Smoothness | Character |
|---|---|---|---|---|---|
| 1 | **Rational Quadratic** | `(1 + d² / (2·α·ℓ²))^(−α)` | ℝ | C∞ | Multi-scale mixer — `α` controls stretch versus wiggle |
| 2 | **Gaussian (RBF)** | `exp(−d² / (2·ℓ²))` | ℝ | C∞ | The canonical smoother — smoothest possible with L² optimality |
| 3 | **Periodic** | `exp(−2·sin²(π·d/p) / ℓ²)` | ℝ | C∞ | Resonates with repetition distance `p` — ideal for cycles |
| 4 | **Locally Periodic** | Periodic · Gaussian | ℝ | C∞ | Seasonal patterns that slowly drift with trend |
| 5 | **Epanechnikov** | `(3/4)(1 − u²) · 𝟙{|u|≤1}` | | C⁰ | Asymptotically MSE-optimal (Watson 1964) — no tail contamination |
| 6 | **Tricube** | `(70/81)(1 − \|u\|³)³ · 𝟙{|u|≤1}` | | C² | The LOWESS standard — near-Gaussian with compact support |
| 7 | **Triangular** | `(1 − \|u\|) · 𝟙{|u|≤1}` | | C⁰ | Simplest non-uniform kernel — fastest to compute |
| 8 | **Cosine** | `(π/4)·cos(π·u/2) · 𝟙{|u|≤1}` | | C¹ | Raised-cosine taper — smoother boundary than Epanechnikov |
where `u = d/ℓ` and `𝟙` is the indicator function.
**Infinite-Support vs Compact-Support — Why Both Matter**
| | Infinite Support (RQ, Gauss, Periodic, LocPeriodic) | Compact Support (Epa, Tricube, Triangular, Cosine) |
|---|---|---|
| **Tail weight** | Never exactly zero | Exactly zero beyond ±ℓ |
| **Loop depth** | `3·ℓ` (3-σ cutoff, ≈99.7% mass) | Exactly `ℓ` |
| **Bar contamination** | Distant bars still pull the estimate a tiny amount | Distant bars cannot affect the estimate at all |
| **Best for** | Smooth trends, Gaussian-process intuition | Robust regression, outlier resistance |
KernelLens picks the correct loop depth automatically based on kernel family: `_depthInfinite` for Gaussian-family kernels, `_depthCompact` for bounded kernels, `_depthPeriodic` for Periodic (which must span enough cycles to reach stable weights).
**Why Eight, Not Four**
Most Pine kernel libraries ship only the four kernels from MacKay's Gaussian process tutorial. KernelLens adds the four compact-support classical kernels because:
- **Epanechnikov** minimises asymptotic mean squared error among all non-negative kernels of bounded support (Watson 1964) — it is the MSE-optimal baseline against which all other kernels are measured
- **Tricube** is the kernel used by LOWESS (Cleveland 1979), the de-facto standard for robust locally weighted scatterplot smoothing
- **Triangular** is the cheapest non-uniform compact kernel — useful when loop-budget matters on intraday charts with huge dataset size
- **Cosine** is C¹-continuous at the support boundary, unlike Epanechnikov's C⁰ discontinuity, producing visibly smoother transitions at kernel edges
Adding them makes the library an academically complete toolkit, not just a Pine port of one tutorial.
🟦 FILTER LAYER — NONE / SMOOTH / ZERO LAG
Every kernel export accepts a `_filter` parameter with three valid values. The filter layer is implemented identically across all eight kernels, so switching kernel families does not change filter behavior.
**"No Filter" — Single-Pass Raw Estimate**
```
ŷ = K(y)
```
One Nadaraya–Watson pass over the source. Cheapest mode, most reactive, fully represents the underlying kernel. Use this when you want the kernel's raw behavior with no additional smoothing or lag correction.
**"Smooth" — Double-Pass Estimate**
```
ŷ = K(K(y))
```
The kernel is applied once to the source, then applied again to its own output using the same bandwidth and the same parameters. The result is a more strongly smoothed curve at the cost of one extra loop pass per bar.
This is mathematically equivalent to convolving the kernel with itself — the effective kernel is wider and flatter, pulling longer-range context into each estimate without requiring the user to double the bandwidth.
**"Zero Lag" — Ehlers De-Lagged Estimate**
```
ŷ = 2·K(y) − K(K(y))
```
The ZLEMA identity from Ehlers (*Rocket Science for Traders*, 2000): subtract the smoothing lag from the raw estimate, effectively shifting the output back in time to match the source more closely.
The intuition: `K(y)` lags `y` by some amount; `K(K(y))` lags `K(y)` by the same amount; so `K(y) − K(K(y))` is an estimate of the lag itself, and adding it back to `K(y)` cancels out. The result tracks the source more tightly than either pass alone, at the cost of slightly noisier turning points.
**Lazy Evaluation — No Wasted Cycles**
In `"No Filter"` mode, the second pass is skipped entirely — it never runs. The filter branch uses an `if` block (not a ternary), so Pine's short-circuit semantics prevent the unused computation. A single kernel call costs one pass; `"Smooth"` or `"Zero Lag"` costs two. You only pay for what you use.
🟦 KERNEL CENTER OFFSET — THE `_phase` PARAMETER
Every KernelLens kernel takes a `_phase` parameter that shifts the kernel center into the past by `_phase` bars. It is the library's non-repainting knob.
**_phase = 0 — Live Estimate**
The kernel is centered on the current bar. The most recent price has maximum weight, and the estimate is as fresh as possible. Suitable for live signal generation, but the most recent bar can re-evaluate as it develops within its interval — standard Pine real-time behavior.
**_phase > 0 — Non-Repainting Historical Estimate**
The kernel center is moved `_phase` bars into the past. The estimate becomes the smoothed value *at that historical bar*, not the current bar. Once the bar at `bar_index − _phase` is fully confirmed (`barstate.isconfirmed`), its estimate cannot change again.
This is the standard trick for publishing kernel indicators that do not repaint: you get a stable, historically accurate curve at the cost of shifting the entire output `_phase` bars to the right on the chart. A `_phase = 25` call gives a curve that lags live price by 25 bars but is guaranteed stable for every past bar.
**Why It Belongs in the Library, Not the Caller**
Pushing `_phase` into the kernel's own loop is not the same as evaluating the kernel at a shifted source (`K(src )`). Shifting the source just uses a stale input with a current-bar-centered kernel, which still produces a fresh estimate of a stale series. KernelLens's `_phase` genuinely moves the kernel center, producing a historical-bar estimate that computes over the correct surrounding window.
🟦 NON-REPAINTING BEHAVIOR
Repainting is the single most-asked question about any Pine indicator, and the single most common source of silent failure when a retail trader moves from backtest to live. A strategy that looks flawless on historical bars and then bleeds money the moment it is deployed is almost always suffering from some form of repainting. KernelLens is engineered from first principles to eliminate every class of repainting by construction — not by patching symptoms, but by removing the dependencies that cause repainting in the first place.
**The Two Forms of Repainting**
| Form | Symptom | Typical Cause |
|---|---|---|
| **Historical repainting** | A bar that was closed days or weeks ago silently changes its plotted value when the chart is refreshed or scrolled | `request.security()` with `lookahead = barmerge.lookahead_on`, un-gated higher-timeframe data, or incorrect array rotation that reads into future bars |
| **Real-time repainting** | The plotted value on the live (current developing) bar flickers tick-by-tick as new price ticks arrive, then freezes at a final value when the bar closes | The indicator reads `close ` (or any current-bar value) inside a weighted sum — the current-bar weight changes every tick |
KernelLens avoids the first kind **entirely and unconditionally**: the library contains no `request.security` calls, no higher-timeframe lookups, no `lookahead_on` usage, and no array rotation that could leak future bars into the window. Every historical bar plotted by any KernelLens kernel is computed exclusively from bars that existed at the time that bar was closed. The plotted history is immutable.
Real-time repainting is controlled explicitly by the `_phase` parameter — it is the user's choice whether to accept tick-by-tick flicker on the live bar in exchange for zero lag (`_phase = 0`) or to eliminate the flicker entirely at the cost of a small fixed lag (`_phase ≥ 1`).
**Why Kernel Regression Normally Repaints (And How KernelLens Stops It)**
A traditional Nadaraya–Watson call centered on the current bar evaluates:
```
ŷ(t) = Σᵢ K(dᵢ/ℓ) · y_{t−i} for i = 0 … depth
```
On the live bar, the term `y_{t−0} = close ` is the current real-time price — which changes on every tick. Every tick moves the weighted sum, every tick moves the estimate, and the trader watching the chart sees the kernel plot flicker as the bar develops. The historical bars (where `close ` for that past bar is now fixed) are stable, but the live plot is unstable.
KernelLens's `_phase` parameter shifts the loop so the kernel runs over `i = _phase … _phase + depth`. With `_phase = 2`:
```
ŷ(t) = Σᵢ K((i−2)/ℓ) · y_{t−i} for i = 2 … 2 + depth
```
The sum no longer touches `close ` or `close ` — every bar it reads is already confirmed and cannot change. The live-bar kernel output is therefore identical from the first tick of the bar to the last tick of the bar, and identical again when the bar finally closes. There is no flicker and nothing to repaint.
**The Lag / Stability Trade-Off**
| `_phase` | Lag on Live Bar | Live-Bar Flicker | Historical Repainting | Best For |
|---|---|---|---|---|
| **0** | 0 bars | Yes (real-time only; history is stable) | None | Scalping, academic research, calibration |
| **1** | 1 bar | None | None | Fast day-trading; minimum acceptable lag for a live trading desk |
| **2** | 2 bars | None | None | Default for most users — the sweet spot between freshness and stability |
| **3** | 3 bars | None | None | Swing trading — extra margin against false flickers from erratic ticks |
| **5+** | 5+ bars | None | None | Position trading, long-term chart analysis, published signal marks |
Even at `_phase = 0`, **historical repainting never occurs** — only the live bar flickers during its own development. Once a bar closes, its plotted value is final; scrolling away and back, refreshing the chart, or re-opening PulseWire will never change that historical plot. The flicker is exclusively a live-bar tick-by-tick phenomenon.
**KernelLens as a Non-Repainting Primitive**
KernelLens exposes real-time flicker as an explicit, user-controlled trade-off rather than a hidden behavior. The caller picks any point on the spectrum from "fully live" (`_phase = 0`, maximum reactivity with tick-by-tick flicker) to "fully confirmed" (`_phase ≥ 1`, one or more bars of lag in exchange for a curve that never redraws) with a single integer parameter. Historical repainting — the dangerous form that silently rewrites past plots — is eliminated unconditionally regardless of `_phase`.
**How to Verify Non-Repainting Yourself**
Do not trust the word "non-repainting" from any library — always verify. KernelLens can be verified in about thirty seconds:
1. Load a chart with KernelLens on it using `_phase = 2` (or any value > 0).
2. Take a screenshot at any specific historical bar.
3. Scroll far to the left, refresh the chart, or reload the indicator.
4. Return to the same bar. The plotted value at that bar must be pixel-identical to the screenshot — because the computation on that bar used only the bars before it, which have not changed.
5. Repeat with `_phase = 0`. The historical bars must still be pixel-identical — only the live bar's plot can differ between observations, and only because the live bar's `close` is now a different number than it was when you took the screenshot.
For a stricter test, use PulseWire's **Bar Replay** mode. Enable Bar Replay, step forward one bar at a time, and watch the kernel plot on each newly-closed bar. With `_phase ≥ 1`, the value plotted on each newly-closed bar will exactly match what the indicator shows after you exit replay mode and view the same bar normally. This is the gold-standard test — Bar Replay reproduces live-bar tick arrival in a controlled way.
**Common Misconceptions**
> *"Any Pine indicator that uses `close` repaints."*
False. Using `close` on a confirmed bar does not repaint — the confirmed bar's close is locked. What can repaint is using `close` on the live bar, and only within that live bar's interval. KernelLens with `_phase > 0` never reads the live-bar close at all.
> *"`lookahead = barmerge.lookahead_on` is always wrong."*
Context-dependent. `lookahead_on` is used correctly in some multi-timeframe indicators to request a higher-TF value that is already settled on the lower TF. KernelLens does not use `request.security` at all, so this question does not apply — but for libraries that do, `lookahead_on` is only problematic when it leaks values from bars that were not yet closed at the lower-TF time of evaluation.
> *"Non-repainting means zero lag."*
False. Zero lag and non-repainting are orthogonal properties. KernelLens `_phase = 0` is zero lag with real-time flicker; `_phase = 2` is two-bar lag with no flicker. You can have any combination of the two, and the right choice depends on the trading style.
> *"The `FILTER_ZEROLAG` mode makes the indicator non-repainting."*
False. `FILTER_ZEROLAG` is an Ehlers-style de-lagging filter applied to the kernel output; it reduces the perceived lag of the estimate, but it does not affect whether the live bar flickers. Non-repainting is controlled exclusively by `_phase`. Choose `_phase` for repainting behavior, and `_filter` for smoothness / lag shape — they are independent knobs.
**When to Accept Real-Time Flicker (`_phase = 0`)**
Despite everything above, there are legitimate reasons to deliberately use `_phase = 0`:
- **Academic research and backtesting** — you want the kernel mathematics in its classical form, centered on the point being estimated, with no phase adjustment
- **Scalping on very short timeframes** — a 2-bar lag on a 1-minute chart is a 2-minute delay, which can matter when you are exiting within a 4-minute window
- **Visual calibration** — when you are choosing a bandwidth by eye, the live-bar flicker actually helps: you see how sensitive the curve is to each incoming tick, which is diagnostic information
- **Indicators that read the kernel output only on `barstate.isconfirmed`** — if your signal logic is gated by `if barstate.isconfirmed`, then live-bar flicker is invisible to your signal (it sees only the frozen close-of-bar value), and you can safely use `_phase = 0` with no practical consequence
For every other case — and especially for any live alert or automated trading system — use `_phase ≥ 1`. Two bars of lag on a clean, stable curve is almost always worth more than zero lag on a curve that redraws itself several times per bar.
🟦 UNIFIED DISPATCHER — `estimate()`
For indicators where the user picks a kernel from a dropdown, writing eight separate ternary branches is tedious and error-prone. KernelLens ships with a unified dispatcher that routes to the correct kernel based on a string argument:
```pine
import a_jabbaroff/KernelLens/1 as kl
line = kl.estimate(
kernelType = kl.KERNEL_GAUSS,
src = close,
bandwidth = 32,
shapeAlpha = 1.0,
period = 1,
phase = 2,
filter = kl.FILTER_SMOOTH)
```
The dispatcher forwards to the matching typed export, so there is no performance penalty versus calling the kernel directly — it is a compile-time routing pass. Unknown kernel names raise a descriptive `runtime.error` naming every valid alternative, so typos fail loudly instead of silently returning `na`.
**Public Constants**
KernelLens exposes its string constants so callers never type the magic values by hand:
| Constant | Value |
|---|---|
| `FILTER_NONE` | `"No Filter"` |
| `FILTER_SMOOTH` | `"Smooth"` |
| `FILTER_ZEROLAG` | `"Zero Lag"` |
| `KERNEL_RQ` | `"Rational Quadratic"` |
| `KERNEL_GAUSS` | `"Gaussian"` |
| `KERNEL_PERIODIC` | `"Periodic"` |
| `KERNEL_LOCPER` | `"Locally Periodic"` |
| `KERNEL_EPA` | `"Epanechnikov"` |
| `KERNEL_TRICUBE` | `"Tricube"` |
| `KERNEL_TRIANG` | `"Triangular"` |
| `KERNEL_COSINE` | `"Cosine"` |
Using the constants in your caller code means the Pine compiler — not a runtime string compare — catches typos at edit time.
🟦 UTILITY LAYER — FIVE PROFESSIONAL HELPERS
KernelLens ships with five utility exports that complement the core estimators. They are the functions you almost always write immediately after getting a smoothed line, factored out so you don't rewrite them in every indicator.
**`slope(estimate, step)` — Discrete First Derivative**
Returns `(y_t − y_{t−step}) / step`, the normalized rate of change over `step` bars. Use it to detect whether a kernel output is trending up, flat, or down — the foundation for any trend-following signal built on top of KernelLens.
```pine
rising = kl.slope(line, 3) > 0.0
```
**`trendState(estimate, step)` — Ternary Trend Indicator**
Returns `+1` if the estimate is rising, `−1` if falling, `0` if exactly flat over the window. A single-call replacement for hand-rolled `line > line ? 1 : line < line ? -1 : 0` ladders.
**`crossSignal(fast, slow)` — Bi-directional Crossover**
Returns `+1` on the bar where `fast` crosses above `slow` (bullish), `−1` on a bearish cross, and `0` otherwise. Built on `ta.crossover` / `ta.crossunder`, so the signal is non-repainting once the bar is confirmed.
**`confidenceBand(src, estimate, window)` — Residual Standard Deviation**
Computes the rolling standard deviation of `(src − estimate)` over a user-defined window. Use the return value as the half-width of a confidence band around the estimate:
```pine
est = kl.gaussian(close, 32, 2, kl.FILTER_SMOOTH)
sigma = kl.confidenceBand(close, est, 50)
upper = est + 1.96 * sigma
lower = est - 1.96 * sigma
```
This is a computationally cheap proxy for the full kernel-weighted local variance — ideal when you need visual bands without paying for a second weighted pass.
**`silvermanBandwidth(src, window)` — Optimal ℓ Suggestion**
Returns the Silverman rule-of-thumb bandwidth:
```
h ≈ 1.06 · σ · n^(−1/5)
```
where `σ` is the rolling standard deviation of the source and `n` is the window size. This is the classical starting point for Gaussian-family bandwidths in academic texts (Silverman 1986). Because Pine requires `simple int` for kernel bandwidth, the returned value is intended for diagnostic display — plot it, read it off the chart, then hard-code the rounded integer into the kernel call.
🟦 INPUT VALIDATION — FAIL LOUDLY, FAIL EARLY
Every public function in KernelLens validates its inputs through a set of internal `_assert*` helpers. Invalid arguments never produce silent `na` fallbacks or buried zero-divisions — they raise `runtime.error` with a descriptive message identifying the function, the parameter, and the expected range.
| Helper | Checks | Raises On |
|---|---|---|
| `_assertFilter` | Filter string is `FILTER_NONE`, `FILTER_SMOOTH`, or `FILTER_ZEROLAG` | Typos like `"No FIlter"` (capital I) — a bug that exists in at least one published kernel indicator |
| `_assertBandwidth` | Bandwidth is a strictly positive integer | Negative or zero bandwidth, which would cause division by zero or infinite loops |
| `_assertPeriod` | Period is a strictly positive integer | Zero period, which would cause `sin(π·d/0)` in Periodic kernels |
| `_assertAlpha` | Rational Quadratic shape parameter is strictly positive | Zero or negative `α`, which would invert the RQ formula |
Error messages are prefixed `KernelLens:` (or `KernelLens.:`) so they are easy to spot in the PulseWire runtime log. Every message names the parameter that failed, the value that was passed, and the set of valid alternatives — so a misconfigured chart tells you exactly what to fix.
🟦 LOOP DEPTH — THE BUG FIX THAT MOTIVATED KERNELLENS
The two most popular Pine kernel libraries on PulseWire share the same fatal bug: both compute their loop depth as
```pine
_size = array.size(array.from(_src))
```
where `array.from(_src)` creates a **one-element array containing the current value of `_src`**, so `_size` is always `1`. The loop then runs `for i = 0 to 1 + startAtBar`, effectively using only `startAtBar + 2` bars — completely ignoring the user's bandwidth. Every published kernel indicator built on those libraries inherits this silent miscalculation.
KernelLens replaces the broken helper with three explicit depth selectors:
| Helper | Depth | Used By |
|---|---|---|
| `_depthInfinite(bw)` | `max(bw · 3, 4)` | Gaussian, Rational Quadratic, Locally Periodic |
| `_depthCompact(bw)` | `max(bw, 4)` | Epanechnikov, Tricube, Triangular, Cosine |
| `_depthPeriodic(bw, p)` | `max(bw · 3, p · 10, 4)` | Periodic |
For Gaussian-family kernels, the `3·ℓ` cutoff captures approximately 99.7% of the kernel mass (the three-sigma rule). For compact-support kernels, the depth equals the bandwidth exactly — the loop terminates at the kernel's natural zero point. For Periodic kernels, the depth is the larger of the scale-based and cycle-based minima, so the loop always spans enough periods to produce a stable weighted average.
The loop counter `i` runs over bar offsets starting at `_phase`, every bar lookup is NA-checked before being incorporated into the sum, and the final `num / den` division is guarded against zero denominators. On a fresh chart, the kernel gracefully returns `na` for bars where the window extends past available history, rather than producing poisoned sums from implicit NA arithmetic.
🟦 API REFERENCE
**Core Kernel Estimators — Eight Exports**
| Export | Signature |
|---|---|
| `rationalQuadratic` | `(src, bandwidth, shapeAlpha, phase, filter) → float` |
| `gaussian` | `(src, bandwidth, phase, filter) → float` |
| `periodic` | `(src, bandwidth, period, phase, filter) → float` |
| `locallyPeriodic` | `(src, bandwidth, period, phase, filter) → float` |
| `epanechnikov` | `(src, bandwidth, phase, filter) → float` |
| `tricube` | `(src, bandwidth, phase, filter) → float` |
| `triangular` | `(src, bandwidth, phase, filter) → float` |
| `cosineKernel` | `(src, bandwidth, phase, filter) → float` |
**Unified Dispatcher**
| Export | Signature |
|---|---|
| `estimate` | `(kernelType, src, bandwidth, shapeAlpha, period, phase, filter) → float` |
**Utility Layer — Five Exports**
| Export | Signature |
|---|---|
| `slope` | `(estimate, step) → float` |
| `trendState` | `(estimate, step) → int` |
| `crossSignal` | `(fast, slow) → int` |
| `confidenceBand` | `(src, estimate, window) → float` |
| `silvermanBandwidth` | `(src, window) → float` |
**Parameter Types**
| Name | Pine Type | Description |
|---|---|---|
| `src` | `series float` | Source series (close, hl2, ohlc4, or any other price-derived series) |
| `bandwidth` | `simple int` | Kernel scale `ℓ`, must be `> 0` |
| `shapeAlpha` | `simple float` | Rational Quadratic shape parameter, must be `> 0` |
| `period` | `simple int` | Periodic repetition distance, must be `> 0` |
| `phase` | `simple int` | Kernel center offset in bars, must be `≥ 0` |
| `filter` | `simple string` | One of `FILTER_NONE`, `FILTER_SMOOTH`, `FILTER_ZEROLAG` |
| `kernelType` | `simple string` | One of the eight `KERNEL_*` constants |
| `step` | `simple int` | Finite-difference step for `slope` / `trendState`, must be `≥ 1` |
| `window` | `simple int` | Rolling window for `confidenceBand` / `silvermanBandwidth`, must be `≥ 2` |
🟦 USAGE EXAMPLES
**Minimal — One Gaussian Curve**
```pine
//@version=6
indicator("KernelLens — Gaussian Demo", overlay = true)
import a_jabbaroff/KernelLens/1 as kl
line = kl.gaussian(close, 32, 2, kl.FILTER_SMOOTH)
plot(line, "Gaussian", color = color.orange, linewidth = 2)
```
**Fast / Slow Crossover System**
```pine
//@version=6
indicator("KernelLens — RQ Crossover", overlay = true)
import a_jabbaroff/KernelLens/1 as kl
fast = kl.rationalQuadratic(close, 8, 1.0, 2, kl.FILTER_NONE)
slow = kl.rationalQuadratic(close, 32, 1.0, 2, kl.FILTER_SMOOTH)
cross = kl.crossSignal(fast, slow)
plot(fast, "Fast", color = color.aqua, linewidth = 2)
plot(slow, "Slow", color = color.orange, linewidth = 2)
plotshape(cross == 1, "Bull", location = location.belowbar,
color = color.lime, style = shape.triangleup, size = size.tiny)
plotshape(cross == -1, "Bear", location = location.abovebar,
color = color.red, style = shape.triangledown, size = size.tiny)
```
**Confidence Band Envelope**
```pine
//@version=6
indicator("KernelLens — Confidence Band", overlay = true)
import a_jabbaroff/KernelLens/1 as kl
est = kl.tricube(close, 48, 2, kl.FILTER_SMOOTH)
sigma = kl.confidenceBand(close, est, 50)
k = 1.96
upper = est + k * sigma
lower = est - k * sigma
plot(est, "Estimate", color = color.orange, linewidth = 2)
p1 = plot(upper, "+1.96σ", color = color.new(color.aqua, 70))
p2 = plot(lower, "−1.96σ", color = color.new(color.aqua, 70))
fill(p1, p2, color = color.new(color.aqua, 92))
```
**Dropdown-Driven Kernel Selection**
```pine
//@version=6
indicator("KernelLens — Dropdown", overlay = true)
import a_jabbaroff/KernelLens/1 as kl
kernelType = input.string(kl.KERNEL_GAUSS, "Kernel",
options = )
bandwidth = input.int(32, "Bandwidth", minval = 2)
alphaRQ = input.float(1.0,"RQ Alpha", minval = 0.01, step = 0.25)
period = input.int(20, "Period", minval = 1)
phase = input.int(2, "Phase", minval = 0)
filter = input.string(kl.FILTER_SMOOTH, "Filter",
options = )
line = kl.estimate(kernelType, close, bandwidth, alphaRQ, period, phase, filter)
plot(line, "KernelLens", color = color.orange, linewidth = 2)
```
🟦 TIMEFRAME PRESETS — BANDWIDTH BY STYLE
Kernel bandwidth is the single most important parameter. It controls the trade-off between reactivity (small `ℓ`, tight fit, noisier) and stability (large `ℓ`, smooth curve, slower to react). The presets below are tested starting points — adjust by ±25 % to taste.
---
**SCALPER — 1m / 3m / 5m**
| Parameter | Value |
|---|---|
| Bandwidth (ℓ) | 8 |
| Phase | 1 |
| Filter | `FILTER_NONE` |
| Best Kernel | Rational Quadratic or Gaussian |
| RQ shapeAlpha | 1.0 |
**Why:** Short bandwidth means the kernel reacts within a handful of bars. `FILTER_NONE` removes the double-pass lag, so the estimate tracks price as tightly as possible. Phase 1 keeps the estimate nearly live while still avoiding the current-bar tick noise.
---
**DAY TRADER — 15m / 30m / 1H**
| Parameter | Value |
|---|---|
| Bandwidth (ℓ) | 16 |
| Phase | 2 |
| Filter | `FILTER_SMOOTH` |
| Best Kernel | Gaussian or Tricube |
| RQ shapeAlpha | 1.0 |
**Why:** Balanced reactivity — the 16-bar Gaussian is the default Silverman range for intraday price data, and `FILTER_SMOOTH` removes most of the bar-to-bar chop without significantly increasing lag. Tricube provides near-identical behaviour with strict compact support and is preferred on noisy assets where outlier bars should not influence the curve.
---
**SWING TRADER — 4H / 1D**
| Parameter | Value |
|---|---|
| Bandwidth (ℓ) | 32 |
| Phase | 3 |
| Filter | `FILTER_SMOOTH` |
| Best Kernel | Rational Quadratic |
| RQ shapeAlpha | 2.0 |
**Why:** Swing trades need structural signals, not intraday noise. Rational Quadratic with `α = 2.0` mixes medium and long length scales, producing a curve that ignores transient spikes but catches genuine regime shifts. Phase 3 shifts the estimate three bars back so each swing decision is made against a fully confirmed kernel output.
---
**POSITION / LONG-TERM — 1D / 1W / 1M**
| Parameter | Value |
|---|---|
| Bandwidth (ℓ) | 64 |
| Phase | 5 |
| Filter | `FILTER_SMOOTH` or `FILTER_ZEROLAG` |
| Best Kernel | Gaussian or Locally Periodic |
| Period (if LP) | 52 (weekly cycle) |
**Why:** Position traders care about the macro trajectory. A Gaussian with ℓ = 64 produces a curve that only turns on genuine multi-month inflections. Locally Periodic with `period = 52` is the ideal choice when a clear seasonal cycle is present — it uses both the long-range Gaussian envelope and the 52-bar periodicity to highlight cycle turns that align with trend.
---
**RESEARCH — Academic / Backtest**
| Parameter | Value |
|---|---|
| Bandwidth (ℓ) | Compute via `silvermanBandwidth(src, 200)` |
| Phase | 0 |
| Filter | `FILTER_NONE` |
| Best Kernel | Epanechnikov |
**Why:** Epanechnikov is the MSE-optimal kernel; `FILTER_NONE` keeps the estimator in its classical single-pass form; `phase = 0` centers the kernel on the bar being evaluated. This is the configuration that matches the statistical literature exactly — use it when publishing research, running Monte-Carlo studies, or calibrating against reference implementations.
🟦 BANDWIDTH SELECTION
Bandwidth `ℓ` is the single most consequential choice in kernel regression. Too small and the estimate overfits local noise; too large and it flattens real structure. KernelLens exposes two helpers to support both manual and semi-automated bandwidth selection.
**Manual — Start with ℓ ≈ √n**
A practical starting point for financial time series: set `ℓ ≈ √window_of_interest`. If you care about 100-bar structure, try `ℓ = 10`. If you care about 400-bar structure, try `ℓ = 20`. Adjust by ±25 % based on how noisy the result looks.
**Silverman's Rule of Thumb**
The closed-form optimal bandwidth for Gaussian-family kernels under Gaussian source assumptions:
```
h ≈ 1.06 · σ · n^(−1/5)
```
Call `silvermanBandwidth(src, window)` to compute this value live. Because Pine requires `simple int` bandwidth at compile time, the returned value is for diagnostic use — plot it, read the stable value off the chart, then hard-code the rounded integer into your kernel calls.
**Leave-One-Out Cross-Validation (Manual)**
For academic rigor, compute the leave-one-out mean squared error for a range of bandwidths and pick the minimum. KernelLens does not automate this (it would require `series int` bandwidth, which Pine does not support inside kernel loops), but the formula is straightforward:
```
LOOCV(ℓ) = (1/n) · Σᵢ (yᵢ − ŷᵢ⁻ⁱ(ℓ))²
```
where `ŷᵢ⁻ⁱ` is the kernel estimate at bar `i` computed without including bar `i` in the sum. Evaluate offline, pick the minimum, hard-code the result.
🟦 FILTER SELECTION — WHEN TO USE EACH
| Filter | Best For | Avoid When |
|---|---|---|
| `FILTER_NONE` | Live signal generation, research / calibration, compact-support kernels on noisy data | Choppy markets where you need extra smoothing |
| `FILTER_SMOOTH` | Swing and position trades, confidence band midlines, most day-trading setups | Scalping — the double pass adds measurable lag |
| `FILTER_ZEROLAG` | Regime detection, crossover systems that need the curve to track price tightly | Low-volume assets — Zero Lag amplifies high-frequency noise |
The three filters use the same underlying kernel with the same bandwidth, so switching between them does not require re-tuning. Default to `FILTER_SMOOTH` when in doubt — it is the best-behaved option across the widest range of assets and timeframes.
🟦 COMPATIBILITY
KernelLens targets Pine Script v6 and runs on every PulseWire chart — no exchange, asset class, or timeframe restriction.
- **Crypto** — Spot, futures, perpetual contracts
- **Forex** — All majors, minors, and exotics
- **Equities** — Stocks, ETFs, indices
- **Commodities** — Metals, energy, agriculture
- **Timeframes** — 1 minute through Monthly
The library is deterministic — given the same source and parameters, every bar of every symbol produces the same estimate. No calibration is needed across assets; the bandwidth parameter alone controls smoothness, and the kernel formulas are scale-free in the source dimension. Silverman's bandwidth helper automatically adapts to each asset's volatility.
🟦 TECHNICAL NOTES
- **Pine Script v6** — uses the modern type system, strict type checking, and the `switch` expression in the unified dispatcher
- **Non-repainting** — kernel outputs for any confirmed bar depend only on that bar's history; there is no look-ahead, no `request.security` with lookahead, and no dependency on the unconfirmed current bar unless `_phase = 0` is deliberately chosen
- **NA-safe iteration** — every bar lookup inside a kernel loop is guarded by `if not na(y)`, so chart history gaps and warm-up bars cannot poison the weighted sum
- **Division-by-zero protection** — every kernel's final division checks `den > 0.0` and returns `na` if the denominator collapses (which can only happen on truly empty windows)
- **Input validation** — every public function asserts its preconditions up front via `_assertFilter`, `_assertBandwidth`, `_assertPeriod`, `_assertAlpha`, and raises `runtime.error` with a descriptive message on misuse — no silent `na` fallbacks
- **Lazy filter evaluation** — the `"No Filter"` path never executes the second kernel pass; the `if`-branch check short-circuits, so single-pass mode is as cheap as a raw kernel call
- **Correct loop bounds** — `_depthInfinite`, `_depthCompact`, and `_depthPeriodic` compute the correct window size per kernel family, fixing the silent `_size = 1` bug that plagues every other published Pine kernel library
- **No persistent state** — the library is purely functional: no `var`, no arrays, no history buffers that grow over time; every export is a pure expression of `(inputs) → output`, so Pine's `max_*_count` limits cannot be exceeded and the library cannot leak memory
- **O(bandwidth) per bar per kernel call** — the loop depth is bounded by the constants in Section 0; there is no hidden quadratic behavior and the cost scales linearly with the user-chosen bandwidth
- **Unicode-safe comments** — the source uses academic notation (`σ`, `ℓ`, `α`, `ŷ`, `ℝ`) where it improves readability; all strings are plain ASCII for runtime compatibility
🟦 ACADEMIC REFERENCES
Every kernel and every formula in KernelLens is cited inline in the source. The combined bibliography:
- **Nadaraya, E. A. (1964).** On estimating regression. *Theory of Probability & Its Applications*, 9(1), 141–142.
- **Watson, G. S. (1964).** Smooth regression analysis. *Sankhyā: The Indian Journal of Statistics, Series A*, 26(4), 359–372.
- **Cleveland, W. S. (1979).** Robust locally weighted regression and smoothing scatterplots. *Journal of the American Statistical Association*, 74(368), 829–836. *(Tricube kernel, LOWESS.)*
- **Silverman, B. W. (1986).** *Density Estimation for Statistics and Data Analysis*. Chapman & Hall, London. *(Bandwidth rule of thumb.)*
- **Wand, M. P. & Jones, M. C. (1995).** *Kernel Smoothing*. Chapman & Hall. *(Unified treatment of all eight kernels.)*
- **MacKay, D. J. C. (1998).** Introduction to Gaussian Processes. *NIPS Tutorial*. *(Periodic and Rational Quadratic kernels.)*
- **Ehlers, J. F. (2000).** *Rocket Science for Traders*. John Wiley & Sons. *(Zero-lag smoothing trick.)*
- **Rasmussen, C. E. & Williams, C. K. I. (2006).** *Gaussian Processes for Machine Learning*. MIT Press. *(Locally Periodic and Rational Quadratic kernels.)*
🟦 VERSIONING & LICENSE
- **Version** — 1.0.0
- **Pine Script** — v6
- **License** — Mozilla Public License 2.0
- **Status** — Production-ready
KernelLens follows semantic versioning. Minor versions add new exports without breaking existing ones; patch versions fix bugs; major versions may change function signatures and will be announced in the changelog.
🟦 DISCLAIMER
KernelLens is a mathematical library for non-parametric regression on financial time series using the Nadaraya–Watson method. The library is provided solely for educational and research purposes and does not constitute financial, investment, or trading advice.
Kernel regression is a local smoothing technique. It estimates the mean of a source series in the neighborhood of the current bar based on historical data, but it does not predict future prices, does not generate trading signals on its own, and does not guarantee the profitability of any strategy built on top of its output.
Past performance of any model does not guarantee future results. Markets contain systemic risks that cannot be eliminated by any amount of mathematical rigor in the kernel itself. Responsibility for any trading decisions made using this library rests entirely with the user. Always apply sound capital management, conduct your own independent analysis, and never risk capital you are not prepared to lose.
The author assumes no liability for direct or indirect losses incurred through the use of KernelLens or any indicator built on top of it. Library

Library

Library

DafeRCMLibRolling Confidence Matrix Library (RCM)
A Structural Evidence Accumulation Engine for Pine Script Developers
What This Library Does
The Rolling Confidence Matrix (RCM) is a developer library that provides a stateful structural analysis engine for Pine Script indicators. It maintains rolling evidence buckets that accumulate and decay observations about market structure on every bar, then synthesizes those observations into confidence scores, a directional state classification, and a set of modulation outputs that downstream indicators can consume.
The library is designed to be algorithm-agnostic. It does not generate signals, draw lines, or produce visual output. It computes structural context that other indicators use to make better decisions — whether that indicator is a Supertrend, a moving average crossover, a Bollinger Band system, or a machine learning model.
The Problem This Solves
Traditional indicators are structurally blind. A Supertrend calculates band width from ATR alone. A moving average crossover fires regardless of whether the cross happens during a structural breakout or inside exhaustion chop. A Bollinger Band squeeze looks identical mathematically whether it precedes a genuine expansion or a false breakout.
These indicators lack the ability to evaluate what kind of price action is producing their signals. The RCM addresses this by maintaining a persistent, per-bar structural memory that any indicator can query.
How It Works: The Five Evidence Buckets
The RCM tracks five categories of structural evidence, accumulated separately for bull and bear sides (10 buckets total). Each bucket decays by a configurable rate every bar, accumulates when its specific conditions are detected, and is hard-capped to prevent runaway values.
Impulse — Detects directional thrust bars. Criteria: body exceeds the 10-bar average body by 15%, close position is in the upper 30% (bull) or lower 30% (bear) of the bar's range, and the bar's range exceeds the 10-bar average range by 5%. Volume confirmation adds additional evidence when the volume ratio exceeds 1.2x the 20-bar average.
Structure — Detects swing-level events. Criteria: price closes above the 5-bar highest high (swing break), price sweeps below a swing low and reclaims it on a bullish close (reclaim), or price wicks through a swing level but closes back inside (sweep absorption). Each event type contributes a different evidence weight, reflecting its structural significance.
Exhaustion — Detects reversal pressure. Criteria: a bearish-body bar with a lower wick exceeding 45% of the total range on volume above 1.2x average contributes bull exhaustion evidence (potential buying absorption). The inverse applies for bear exhaustion. This bucket represents counter-trend pressure building within the current move.
Continuation — Detects trend persistence. Criteria: the EMA(21) of HLC3 has a positive slope, price is above the anchor, and the current close exceeds the previous close. This bucket also has asymmetric decay: when price moves to the wrong side of the anchor, continuation evidence decays at 62% per bar instead of the standard rate. Anchor crosses trigger a 50% immediate reduction.
Compression — Detects range contraction. Criteria: the current bar's range is below 80% of the 10-bar average range, and the ATR(14) to SMA(ATR,30) ratio is below 0.95. Compression evidence accumulates on the side of the anchor (bull compression above, bear compression below), representing potential energy buildup before expansion.
Bucket Caps
Each bucket has a defined maximum to prevent any single evidence type from dominating the confidence calculation:
Impulse: 25
Structure: 30
Exhaustion: 20
Continuation: 20
Compression: 15
Confidence Computation
Bull and bear confidence scores are computed as weighted sums of their respective five buckets:
bullConf = bullImpulse × wImpulse + bullStructure × wStructure +
bullExhaustion × wExhaustion + bullContinuation × wContinuation +
bullCompression × wCompression
Default weights are: Impulse 1.20, Structure 1.35, Exhaustion 1.15, Continuation 1.00, Compression 0.90. Structure carries the highest default weight because swing-level events are the most structurally significant observations.
From these scores, the library derives:
Net Confidence: bullConf − bearConf
Activity: bullConf + bearConf (total evidence in the system)
Dominance: netConf / activity (how one-sided the evidence is, range −1 to +1)
Bull/Bear Pressure: each side's share of total activity (range 0 to 1)
The Three-State Engine
The state engine uses hysteresis to prevent flickering between states. Entering a state requires strong evidence; holding a state requires only moderate evidence.
Entry Conditions (Transition → Bull/Bear):
Net confidence exceeds the entry threshold (default: 12.0)
AND dominance exceeds the dominance threshold (default: 0.18)
Hold Conditions (Bull/Bear → Transition):
A state is lost when ANY of:
Net confidence drops below the hold threshold (default: 5.0)
Opposing pressure exceeds the flip pressure threshold (default: 0.58)
Erosion (peak confidence minus current) exceeds 35% of current confidence
This hysteresis design means the engine requires conviction to enter a directional state but gives the trend room to breathe once established.
Substates
When the engine is in Transition (state = 0), it internally classifies the type of transition based on which evidence buckets are dominant:
Early (substate 1): Within 3 bars of losing a directional state. Evidence is collapsing.
Contested (substate 2): Both bull and bear confidence exceed 50% of the entry threshold. Both sides have material evidence.
Rotational (substate 3): Exhaustion buckets represent more than 35% of total non-compression evidence. The market is churning.
Compression (substate 4): Compression buckets exceed 35% of total evidence while impulse is below 15%. Energy is building.
The external state remains 0 for all substates. Consumers who need granularity can query st.substate.
Damage Detection
When the engine is in a directional state, it evaluates structural compromise on every bar by accumulating a damage score from seven independent checks:
For a Bull state, damage accumulates from:
Price below the anchor (+1.5)
Bear impulse condition detected (+1.0)
Upper wick ratio exceeds 35% (+0.75)
Anchor slope is negative (+1.0)
Bear pressure exceeds 45% (+1.0)
Close and high are both lower than previous bar (+0.75)
Price crossed below the anchor this bar (+1.25)
Maximum possible damage score per bar: 7.25. When the score exceeds the damage threshold (default: 4.0), the trend is flagged as damaged.
Damage Response
When damage is detected, the engine modifies the active side's evidence buckets:
Continuation is reduced by (0.25 × damageDecayMult) — default removes ~44%
Impulse is multiplied by damageImpulseCut — default retains 88%
Structure is multiplied by 0.92
Opposing exhaustion receives +1.5
Opposing impulse receives +1.0
This creates a natural degradation cycle: damage weakens the active trend's evidence while strengthening the opposing side's, making a transition more likely without forcing it.
Integrity Score
The library computes a continuous structural integrity measure from 0.0 (broken) to 1.0 (fully intact), derived from four components:
Erosion component (max −0.30): How far current confidence has fallen from its peak
Damage component (max −0.30): Current damage score relative to threshold
Opposing pressure (max −0.20): Counter-trend pressure magnitude
Transition duration (max −0.15): How long the engine has been in transition state
External evidence modifiers can also adjust integrity by ±0.1.
Directional Permissions
Rather than a simple pass/fail gate, the library outputs four permission values:
allowLong (bool) : Structural permission to take long positions
allowShort (bool): Structural permission to take short positions
preferLong (float, 0−1): Strength of structural preference for longs
preferShort (float, 0−1): Strength of structural preference for shorts
In Bull state: longs are allowed, shorts are blocked unless the trend is damaged (allowing counter-trend fades). preferLong equals the confidence strength. In Bear state: the inverse. In Transition: both sides are allowed, preference leans toward whichever side has more evidence.
External Evidence Sockets
The library accepts additive evidence injection from external systems through the ExternalEvidence type. External evidence is applied after internal bucket computation but before state transitions, meaning it can influence confidence but cannot directly set state.
ev = rcm.ExternalEvidence.new(bullEvidence=3.0, source="dreamer")
st := rcm.inject(st, ev)
External evidence is reset to zero after each update() call. This prevents stale external data from persisting.
Modulation Outputs
The library provides purpose-built modulation functions for different indicator types:
Band Modulation (get_band_mod): Returns a float multiplier for band/envelope width. Bands tighten during high-confidence directional states and widen when damage is detected. Used by Supertrend, Bollinger Band, and PSAR-type indicators.
Score Modulation (get_score_mod): Returns an additive modifier for directional scores. When a score's direction aligns with the RCM state, it receives a confidence-proportional boost. When it opposes, it receives a penalty. Used by signal-scoring systems.
Signal Gate (get_gate): Returns a boolean indicating whether signals should be permitted. When transition blocking is enabled, all signals are suppressed during Transition state.
Full Package (get_modulation): Returns all outputs in a single RCMModulation struct including band mod, score mod, gate, permissions, integrity, state color, and regime label.
Configuration
The library ships with three preset configurations:
default_config(): Balanced settings suitable for 15m−1H timeframes
scalp_config(): Faster decay (0.75), lower thresholds, higher impulse weight — optimized for 1m−5m
swing_config(): Slower decay (0.88), higher thresholds, higher structure weight — optimized for 4H−Daily
All 16 configuration parameters can also be set individually through the RCMConfig constructor.
Developer Integration Guide
Step 1: Import and Initialize
import DskyzInvestments/DafeRCMLib/1 as rcm
var rcm.RCMState st = rcm.RCMState.new()
var rcm.RCMConfig cfg = rcm.default_config()
Both objects must be declared with var for state persistence across bars.
Step 2: Update Every Bar
st := rcm.update(st, cfg)
Call update() exactly once per bar. It handles all evidence detection, decay, confidence computation, damage detection, state transitions, substate classification, and integrity scoring.
Step 3: Query Modulation Outputs
For band-based indicators (Supertrend, BB, PSAR):
band := band * rcm.get_band_mod(st, 0.45, 1.10)
For score-based systems (signal scoring, ML models):
fusedScore = rawScore + rcm.get_score_mod(st, rawScore, 0.25)
For signal gating:
buySignal := buySignal and rcm.get_gate(st, true)
For directional permissions:
rcm.RCMPermissions perms = rcm.get_permissions(st)
if perms.allowLong and perms.preferLong > 0.3
// High structural preference for longs
Step 4: Optional — Inject External Evidence
if myDreamerScore > 2.0
ev = rcm.ExternalEvidence.new(bullEvidence=2.0, source="dreamer")
st := rcm.inject(st, ev)
// inject() must be called BEFORE update()
Step 5: Optional — Use Dashboard Helpers
rcm.conf_bar(st.bullConf, 130, 8) // Returns "████░░░░"
rcm.state_text(st) // Returns "▲ BULL"
rcm.damage_text(st) // Returns "Intact"
rcm.integrity_text(st) // Returns "87.3%"
rcm.state_color(st, bullCol, bearCol, transCol)
Step 6: Optional — Narrative Text
= rcm.narrative_regime(st, bullCol, bearCol, transCol, dimCol)
= rcm.narrative_kinetics(st, accentCol, dimCol)
= rcm.narrative_structure(st, bullCol, bearCol, transCol, dimCol)
What This Library Does Not Do
It does not generate buy/sell signals
It does not draw on the chart
It does not use request.security or access external timeframes
It does not use request.footprint (consumers can inject footprint-derived evidence through the external socket)
It does not persist data beyond the current chart's bar history
It does not adapt its own parameters automatically
The library computes structural context. What the consuming indicator does with that context is entirely the developer's decision.
Companion Demo
The DafeRCMLibDEMO indicator demonstrates every function and output of this library
using a simple EMA crossover system as the base indicator. It includes:
Modulated ATR bands showing get_band_mod() in action
Trade signals gated by get_permissions() and get_gate()
State shift and damage markers
Substate classification labels
Evidence bucket subplots for all 10 buckets
Confidence, integrity, and modulation output subplots
Full quantitative dashboard and narrative panel
— Dskyz, Trade with insight. Trade with anticipation. Library

kNNLib with turboQuant encodingLibrary "kNNLib"
f_quantize_3bit(value, bounds)
Quantize a single feature to 3-bit index (0-7) using percentile boundaries
Parameters:
value (float) : Feature value to quantize
bounds (array) : Array of 9 percentile boundaries
Returns: Integer index 0-7
f_compute_percentile_bounds(feature_array, history_len)
Compute percentile boundaries for a feature over a rolling window
Parameters:
feature_array (array) : Array of feature values (size = history_len)
history_len (int) : Number of bars to use for percentile calculation
Returns: Array of 9 boundaries
f_turboquant_encode(f1, f2, f3, f4, f5, f6)
Encode 6 features into 18-bit TurboQuant state ID
Parameters:
f1 (int) : Feature 1 (VectorOsc) quantized index 0-7
f2 (int) : Feature 2 (BasketCorr) quantized index 0-7
f3 (int) : Feature 3 (InnovZ) quantized index 0-7
f4 (int) : Feature 4 (TE_Osc) quantized index 0-7
f5 (int) : Feature 5 (OrthoZcvb) quantized index 0-7
f6 (int) : Feature 6 (PhiDiv) quantized index 0-7
Returns: 18-bit state ID (0 to 262143)
f_normalize_basket_corr(rho, rho_history, norm_len)
Normalize BasketCorr using Fisher transform then z-score
Parameters:
rho (float) : Raw correlation value
rho_history (array) : Array of historical rho values
norm_len (int) : Normalization window length
Returns: Normalized correlation
f_normalize_te_osc(te_osc, te_history, norm_len)
Normalize TE_Osc using rolling z-score
Parameters:
te_osc (float) : Raw TE oscillator value
te_history (array) : Array of historical TE values
norm_len (int) : Normalization window length
Returns: Normalized TE
f_normalize_phi_div(phi_div, phi_history, norm_len)
Normalize PhiDiv using rolling z-score
Parameters:
phi_div (float) : Raw PhiDiv value
phi_history (array) : Array of historical PhiDiv values
norm_len (int) : Normalization window length
Returns: Normalized PhiDiv
f_euclidean_distance(f1_current, f2_current, f3_current, f4_current, f5_current, f6_current, f1_hist, f2_hist, f3_hist, f4_hist, f5_hist, f6_hist)
Calculate Euclidean distance between two 6D feature vectors
Parameters:
f1_current (float) : Current bar feature 1
f2_current (float) : Current bar feature 2
f3_current (float) : Current bar feature 3
f4_current (float) : Current bar feature 4
f5_current (float) : Current bar feature 5
f6_current (float) : Current bar feature 6
f1_hist (float) : Historical bar feature 1
f2_hist (float) : Historical bar feature 2
f3_hist (float) : Historical bar feature 3
f4_hist (float) : Historical bar feature 4
f5_hist (float) : Historical bar feature 5
f6_hist (float) : Historical bar feature 6
Returns: Euclidean distance
f_find_k_nearest(f1_current, f2_current, f3_current, f4_current, f5_current, f6_current, f1_history, f2_history, f3_history, f4_history, f5_history, f6_history, k, max_history)
Find K nearest neighbors using linear scan
Parameters:
f1_current (float) : Current bar feature 1
f2_current (float) : Current bar feature 2
f3_current (float) : Current bar feature 3
f4_current (float) : Current bar feature 4
f5_current (float) : Current bar feature 5
f6_current (float) : Current bar feature 6
f1_history (array) : Array of historical feature 1 values
f2_history (array) : Array of historical feature 2 values
f3_history (array) : Array of historical feature 3 values
f4_history (array) : Array of historical feature 4 values
f5_history (array) : Array of historical feature 5 values
f6_history (array) : Array of historical feature 6 values
k (int) : Number of neighbors to find
max_history (int) : Maximum bars to search
Returns: Array of K nearest neighbor indices
f_calculate_confidence(f1_current, f2_current, f3_current, f4_current, f5_current, f6_current, f1_history, f2_history, f3_history, f4_history, f5_history, f6_history, k_nearest_indices)
Calculate epistemic confidence score from K-nearest distances
Parameters:
f1_current (float) : Current bar feature 1
f2_current (float) : Current bar feature 2
f3_current (float) : Current bar feature 3
f4_current (float) : Current bar feature 4
f5_current (float) : Current bar feature 5
f6_current (float) : Current bar feature 6
f1_history (array) : Array of historical feature 1 values
f2_history (array) : Array of historical feature 2 values
f3_history (array) : Array of historical feature 3 values
f4_history (array) : Array of historical feature 4 values
f5_history (array) : Array of historical feature 5 values
f6_history (array) : Array of historical feature 6 values
k_nearest_indices (array) : Array of K nearest neighbor indices
Returns: Confidence score Library

BOT_1_0_LIB_TEXTLibrary "BOT_1_0_LIB_TEXT"
f_b01(b)
Parameters:
b (bool)
f_na_raw(x)
Parameters:
x (float)
f_na2(x)
Parameters:
x (float)
f_na4(x)
Parameters:
x (float)
f_panel_m01_detail(tBaseL, tBaseS, tDeltaL, tDeltaS, tSlopeL, tSlopeS, tDeltaUsed)
Parameters:
tBaseL (bool)
tBaseS (bool)
tDeltaL (bool)
tDeltaS (bool)
tSlopeL (bool)
tSlopeS (bool)
tDeltaUsed (float)
f_panel_m02_detail(cHtfUp, cHtfDown, cAdxOk, cAtrOk)
Parameters:
cHtfUp (bool)
cHtfDown (bool)
cAdxOk (bool)
cAtrOk (bool)
f_panel_m05_detail(m05DetView, m05_state, m05_cnt, m05ConfirmPulse, useConf)
Parameters:
m05DetView (string)
m05_state (int)
m05_cnt (int)
m05ConfirmPulse (bool)
useConf (bool)
f_panel_m06_detail(m06_mode, m06_trigger, m06pBarsLeftTxt, m06pMinLeftTxt, useCD)
Parameters:
m06_mode (string)
m06_trigger (string)
m06pBarsLeftTxt (string)
m06pMinLeftTxt (string)
useCD (bool)
f_panel_m07_det1(cycleTxt, entry, sl, tp, gate, gateEvent, killReason, killAge, useM07)
Parameters:
cycleTxt (string)
entry (float)
sl (float)
tp (float)
gate (bool)
gateEvent (bool)
killReason (string)
killAge (float)
useM07 (bool)
f_panel_m07_det2(evScoreL, evScoreS, evThrFinalL, evThrFinalS, evThrRegL, evThrRegS, uqNovelL, uqNovelS, evtNovelL, evtNovelS, m06_canTrade, m07_sigPulse, useM07)
Parameters:
evScoreL (float)
evScoreS (float)
evThrFinalL (float)
evThrFinalS (float)
evThrRegL (float)
evThrRegS (float)
uqNovelL (bool)
uqNovelS (bool)
evtNovelL (bool)
evtNovelS (bool)
m06_canTrade (bool)
m07_sigPulse (bool)
useM07 (bool)
f_panel_m03_detail(stateTxt, sideTxt, regraAtiva, reqAntesArm)
Parameters:
stateTxt (string)
sideTxt (string)
regraAtiva (string)
reqAntesArm (int)
f_panel_perf_detail(perfCumR, perfAvgR, perfAvgWin, perfAvgLoss, perfPayoff, perfExpectancyPanel, perfBestTradeR, perfWorstTradeR, perfWinStreak, perfBestWinStreak, perfLossStreak, perfBestLossStreak)
Parameters:
perfCumR (float)
perfAvgR (float)
perfAvgWin (float)
perfAvgLoss (float)
perfPayoff (float)
perfExpectancyPanel (float)
perfBestTradeR (float)
perfWorstTradeR (float)
perfWinStreak (int)
perfBestWinStreak (int)
perfLossStreak (int)
perfBestLossStreak (int)
f_panel_perf_last_detail(perfLastTradeR, perfLastTradeReason, perfActiveTradeId, perfCycleCumR)
Parameters:
perfLastTradeR (float)
perfLastTradeReason (string)
perfActiveTradeId (int)
perfCycleCumR (float)
f_panel_flow_state_detail(flowDiag_win, flowDiag_followBars, flowDiag_hostileX)
Parameters:
flowDiag_win (int)
flowDiag_followBars (int)
flowDiag_hostileX (int)
f_panel_flow_cnt_long(flowDiag_cCount, flowDiag_gCount)
Parameters:
flowDiag_cCount (int)
flowDiag_gCount (int)
f_panel_flow_cnt_short(flowDiag_pCount, flowDiag_xCount)
Parameters:
flowDiag_pCount (int)
flowDiag_xCount (int)
f_panel_flow_cnt_detail(flowDiag_friction, flowDiag_gFailedCount)
Parameters:
flowDiag_friction (float)
flowDiag_gFailedCount (int)
f_panel_flow_cont_detail(m07_cfSlopeNow, ac_widthPct)
Parameters:
m07_cfSlopeNow (float)
ac_widthPct (float)
f_panel_edge_sig_detail(edgeGlobalClass, edgeFollowRate, edgeTotal)
Parameters:
edgeGlobalClass (string)
edgeFollowRate (float)
edgeTotal (int)
f_panel_edge_cnt_long(edgeFollowCount, edgeNeutralCount, edgeFailCount)
Parameters:
edgeFollowCount (int)
edgeNeutralCount (int)
edgeFailCount (int)
f_panel_edge_cnt_short(traceEdgeMinSamples, traceEdgeGoodRate)
Parameters:
traceEdgeMinSamples (int)
traceEdgeGoodRate (float)
f_panel_edge_cnt_detail(traceEdgeWindowBars, traceEdgeStrongR, traceEdgeFailR)
Parameters:
traceEdgeWindowBars (int)
traceEdgeStrongR (float)
traceEdgeFailR (float)
f_panel_edge_reg_short(follow, neutral, fail)
Parameters:
follow (int)
neutral (int)
fail (int)
f_panel_edge_rate_detail(rate)
Parameters:
rate (float)
f_panel_edge_acc_detail(traceEdgeAccelMode, edgeAccelRate)
Parameters:
traceEdgeAccelMode (string)
edgeAccelRate (float) Library

MLLibLibrary "MLLib"
Machine Learning Library - Adaptive learning algorithms for parameter optimization
f_kirschenbaum_sgd(feature_z, baseline_ma, baseline_dev, pivot_high, pivot_low, sensitivity_current, learning_rate, sensitivity_min, sensitivity_max, coupling_strength, coupling_gate, pivot_lookback, proximity_pct, survival_prob)
Pivot-based SGD for adaptive sensitivity tuning (Kirschenbaum method)
Parameters:
feature_z (float) : Z-score of the predictive feature (e.g., basket vector)
baseline_ma (float) : Baseline moving average (center line)
baseline_dev (float) : Standard deviation for band calculation
pivot_high (float) : Recent pivot high price (na if none)
pivot_low (float) : Recent pivot low price (na if none)
sensitivity_current (float) : Current sensitivity parameter value
learning_rate (float) : Learning rate for SGD updates
sensitivity_min (float) : Minimum allowed sensitivity value
sensitivity_max (float) : Maximum allowed sensitivity value
coupling_strength (float) : Coupling strength for gating updates (0-1)
coupling_gate (float) : Minimum coupling threshold for updates
pivot_lookback (int) : Lookback period to historical feature/price at pivot
proximity_pct (float) : Proximity threshold (0-1) for pivot to be "near band"
survival_prob (float) : Survival probability for band calculation (e.g., 0.68 for 1-sigma)
Returns: Updated sensitivity and number of updates performed
f_sgd_update(param_current, gradient, learning_rate, param_min, param_max, gate_strength, gate_threshold)
Generic SGD parameter update with optional gating
Parameters:
param_current (float) : Current parameter value
gradient (float) : Gradient (error * feature)
learning_rate (float) : Learning rate
param_min (float) : Minimum parameter value
param_max (float) : Maximum parameter value
gate_strength (float) : Gating strength (0-1, optional)
gate_threshold (float) : Minimum gate strength to allow full update
Returns: float Updated parameter value
SGDState
SGD learning state for tracking parameter updates
Fields:
param (series float) : Current parameter value
updates (series int) : Number of updates performed
last_error (series float) : Last prediction error Library

MCLibLibrary "MCLib"
f_eval_validator_path(sim_buffer, run, setup_horizon, setup_entry_price, setup_direction, setup_tp1, setup_sl, fv_base, fv_drift_step, max_hold_bars)
Parameters:
sim_buffer (array)
run (int)
setup_horizon (int)
setup_entry_price (float)
setup_direction (string)
setup_tp1 (float)
setup_sl (float)
fv_base (float)
fv_drift_step (float)
max_hold_bars (int)
f_calc_validator_rr(entry_price, eval_direction, mae_price, mfe_price)
Parameters:
entry_price (float)
eval_direction (string)
mae_price (float)
mfe_price (float)
f_run_lite_antithetic_mc(sim_buffer_A, sim_buffer_B, lite_runs, setup_horizon, setup_entry_price, mc_pool_idx, mc_master_pool, mc_current_state, mc_pool_size, per_bar_vol, mc_squeeze_intensity, fv_cyclic_kalman, fv_drift, c0, is_stretched, is_coupled, shadow_price)
Parameters:
sim_buffer_A (array)
sim_buffer_B (array)
lite_runs (int)
setup_horizon (int)
setup_entry_price (float)
mc_pool_idx (array)
mc_master_pool (array)
mc_current_state (int)
mc_pool_size (int)
per_bar_vol (float)
mc_squeeze_intensity (float)
fv_cyclic_kalman (float)
fv_drift (float)
c0 (float)
is_stretched (bool)
is_coupled (bool)
shadow_price (float)
f_run_realtime_mc_chunk(mc_sim_buffer_A, mc_sim_buffer_B, mc_runs_done, mc_target_runs, mc_chunk_size, mc_horizon, mc_pool_idx, mc_master_pool, mc_current_state, mc_pool_size, per_bar_vol, mc_vol_scalar_base, mc_squeeze_intensity, mc_breakout_multiplier, fv_cyclic_kalman, fv_drift, c0, is_stretched, is_coupled, shadow_price)
Parameters:
mc_sim_buffer_A (array)
mc_sim_buffer_B (array)
mc_runs_done (int)
mc_target_runs (int)
mc_chunk_size (int)
mc_horizon (int)
mc_pool_idx (array)
mc_master_pool (array)
mc_current_state (int)
mc_pool_size (int)
per_bar_vol (float)
mc_vol_scalar_base (float)
mc_squeeze_intensity (float)
mc_breakout_multiplier (float)
fv_cyclic_kalman (float)
fv_drift (float)
c0 (float)
is_stretched (bool)
is_coupled (bool)
shadow_price (float)
f_update_progressive_percentiles(mc_sim_buffer_A, mc_sim_buffer_B, mc_horizon, mc_runs_done, mc_progressive_p10, mc_progressive_p50, mc_progressive_p90)
Parameters:
mc_sim_buffer_A (array)
mc_sim_buffer_B (array)
mc_horizon (int)
mc_runs_done (int)
mc_progressive_p10 (array)
mc_progressive_p50 (array)
mc_progressive_p90 (array) Library

Library

Synapse_VSync_LibV-Sync (Volume Synchronization) is a multi-dimensional macro-confluence engine. It aggregates four objective market truths into a single synchronized bias (0.0 to 1.0) to filter signals and define market regime.
The Four Pillars of V-Sync
1. Base Volume (Temporal Flux)
Engine: Exponentially weighted volume flow.
Logic: up_volume / total_volume with a math.exp(-i/lookback) decay.
Utility: Capturing sustained momentum in raw participation. It filters out low-volume "fakeout" moves that lack broad participation.
2. Footprint (Order Flow Delta)
Engine: Micro-delta tracking (Institutional Tape).
Logic: Normalized ratio of aggressive buy orders vs sell orders, sourced from LTF footprint or synthetic body-to-wick estimation.
Utility: Identifying where "Smart Money" is actively committing capital in real-time.
3. TICK Data (Market Internals)
Engine: Exchange-wide breadth internals.
Index Mapping:
SPX/ES: NYSE:TICK
NQ/NDX: NASDAQ:TICKQ
Fidelity: Processes intrabar HT/LT extremes to capture high-speed institutional sweeps.
Commitment Levels: Benchmarked at 800 (MOO alignment), 1000 (Extreme), and 1200 (Climax).
4. Thermal Map (Structural Binning)
Engine: Range-based volume distribution (Heatmap).
Logic: 30-bin price-range analysis. Identifies if the current price is supported by "Buy Liquidity" below or capped by "Sell Liquidity" above.
Utility: Visualizing structural depth and identifying high-probability zones where price is likely to stick or bounce.
Interaction & Intelligence Modules
5. Interaction Tooltips
Engine: Dynamic string generator.
Logic: Aggregates pillars (V-Sync, TICK, Heatmap) and local interaction (Delta, OB Bias) into a human-readable forensic report.
Utility: Provides instant clarity on why a level is reacting (e.g., "Institutional Defense" vs "Passive Absorption").
6. Delta Aggregation (Defense vs Aggression)
Engine: Decaying session delta sum.
Logic: Tracks footprint delta at discrete price levels. Categorizes bias as:
Aggressive (A): Delta moves in the direction of the break (Push).
Defensive (D): Delta moves against the local price interaction (Absorption/Soaking).
Utility: Standardizing the interpretation of footprint across all Synapse indicators.
7. Universal Plot Auditing
Engine: Kinetic flux interaction logic.
Logic: Allows auditing of any technical plot line (Moving Averages, VWAP, Anchored Levels) for touches, cross-overs, and structural fidelity.
Utility: Enables the entire Synapse forensic suite to be applied to any existing indicator's data lines.
Library Architecture: Synapse_VSync_Lib
Key Functions
f_get_tick_source(): Auto-detects SPX vs NQ for correct internal sourcing.
f_calc_tick_extreme(): High-fidelity internal pressure tracking.
f_vsync_stack(): Blends all pillars into a weighted consensus.
HUD Representation
Indicators utilizing the full stack display V-STACK (instead of V-SYNC), signifying that Market Internals and Structural structural depth are being calculated alongside volume flow.
License: Open Source (MIT License) Library

HighClassCalculationsLibrary "HighClassCalculations"
Advanced Pine Script v6 calculation library for statistical, normalization, trend, and risk metrics.
safeDiv(numerator, denominator, fallback)
Safe division helper that prevents division-by-zero errors.
Parameters:
numerator (float) : Value on the top of the fraction.
denominator (float) : Value on the bottom of the fraction.
fallback (float) : Value returned when denominator is zero.
Returns: Result of the division or fallback.
clamp(value, minValue, maxValue)
Clamps a value into a fixed range.
Parameters:
value (float) : Source value.
minValue (float) : Minimum allowed value.
maxValue (float) : Maximum allowed value.
Returns: Clamped value.
rescale(value, oldMin, oldMax, newMin, newMax)
Rescales a value from one range into another range.
Parameters:
value (float) : Source value.
oldMin (float) : Source range minimum.
oldMax (float) : Source range maximum.
newMin (float) : Target range minimum.
newMax (float) : Target range maximum.
Returns: Rescaled value.
normalize(src, len)
Returns the min-max normalized position of a series within a rolling window.
Parameters:
src (float) : Source series.
len (int) : Rolling lookback window.
Returns: Value between 0 and 1 when the range is valid.
rangePercent(src, len)
Returns the source position inside its rolling range as a percentage.
Parameters:
src (float) : Source series.
len (int) : Rolling lookback window.
Returns: Value between 0 and 100 when the range is valid.
zScore(src, len)
Calculates the z-score of a series.
Parameters:
src (float) : Source series.
len (int) : Rolling lookback window.
Returns: Standardized z-score.
robustZScore(src, len)
Calculates a robust z-score using median absolute deviation.
Parameters:
src (float) : Source series.
len (int) : Rolling lookback window.
Returns: Robust z-score less sensitive to outliers.
percentileRank(src, len)
Calculates percentile rank for the latest value inside a rolling window.
Parameters:
src (float) : Source series.
len (int) : Rolling lookback window.
Returns: Percentile rank from 0 to 100.
percentileValue(src, len, percentile)
Calculates the value at a requested percentile inside a rolling window.
Parameters:
src (float) : Source series.
len (int) : Rolling lookback window.
percentile (float) : Requested percentile from 0 to 100.
Returns: Percentile value.
simpleReturn(src)
Calculates simple arithmetic return versus the previous bar.
Parameters:
src (float) : Price or equity series.
Returns: One-bar simple return.
logReturn(src)
Calculates log return versus the previous bar.
Parameters:
src (float) : Price or equity series.
Returns: One-bar log return.
compoundedReturn(src, len)
Calculates cumulative return over a fixed lookback.
Parameters:
src (float) : Price or equity series.
len (int) : Lookback window.
Returns: Return from src to current src.
realizedVolatility(src, len, annualization)
Calculates realized volatility from log returns and annualizes it.
Parameters:
src (float) : Price or equity series.
len (int) : Rolling lookback window.
annualization (float) : Number of bars used for annualization.
Returns: Annualized volatility.
downsideDeviation(returnSeries, len, mar, annualization)
Calculates downside deviation from a return series.
Parameters:
returnSeries (float) : Series of returns, not raw price.
len (int) : Rolling lookback window.
mar (float) : Minimum acceptable return.
annualization (float) : Number of bars used for annualization.
Returns: Annualized downside deviation.
rollingSharpe(returnSeries, len, riskFreeRate, annualization)
Calculates a rolling Sharpe ratio from a return series.
Parameters:
returnSeries (float) : Series of returns, not raw price.
len (int) : Rolling lookback window.
riskFreeRate (float) : Per-bar risk free rate.
annualization (float) : Number of bars used for annualization.
Returns: Annualized Sharpe ratio.
rollingSortino(returnSeries, len, mar, annualization)
Calculates a rolling Sortino ratio from a return series.
Parameters:
returnSeries (float) : Series of returns, not raw price.
len (int) : Rolling lookback window.
mar (float) : Minimum acceptable return.
annualization (float) : Number of bars used for annualization.
Returns: Annualized Sortino ratio.
efficiencyRatio(src, len)
Calculates Kaufman's efficiency ratio.
Parameters:
src (float) : Source series.
len (int) : Rolling lookback window.
Returns: Efficiency ratio from 0 to 1.
regressionSlope(src, len)
Calculates rolling linear regression slope.
Parameters:
src (float) : Source series.
len (int) : Rolling lookback window.
Returns: Slope per bar.
regressionAngle(src, len)
Converts rolling regression slope into an angle.
Parameters:
src (float) : Source series.
len (int) : Rolling lookback window.
Returns: Slope angle in degrees.
beta(asset, benchmark, len)
Calculates rolling beta versus a benchmark series.
Parameters:
asset (float) : Asset series.
benchmark (float) : Benchmark series.
len (int) : Rolling lookback window.
Returns: Beta coefficient.
alpha(asset, benchmark, len, riskFreeRate)
Calculates Jensen-style alpha versus a benchmark series.
Parameters:
asset (float) : Asset return series.
benchmark (float) : Benchmark return series.
len (int) : Rolling lookback window.
riskFreeRate (float) : Per-bar risk free rate.
Returns: Alpha over the rolling window.
ulcerIndex(src, len)
Calculates the Ulcer Index for a series.
Parameters:
src (float) : Price or equity series.
len (int) : Rolling lookback window.
Returns: Ulcer Index value.
maxDrawdown(src, len)
Calculates the maximum drawdown over a rolling window.
Parameters:
src (float) : Price or equity series.
len (int) : Rolling lookback window.
Returns: Maximum drawdown as a negative decimal.
atrPercent(len, src)
Calculates ATR as a percentage of price.
Parameters:
len (simple int) : ATR lookback window.
src (float) : Reference price used for the percentage denominator.
Returns: ATR percent.
relativeVolume(len)
Calculates relative volume versus its rolling average.
Parameters:
len (simple int) : Rolling lookback window.
Returns: Volume divided by average volume.
getAllFunctions()
Returns a comma-separated list of all exported calculation helpers.
Returns: Function catalog for quick reference. Library

Library

Vantage_LO1_Sizing**Overview**
Position-sizing library for the LO1 breakout box day-trading strategy. Provides a unified recoup (opposite-add) sizing pipeline and dollar-risk/profit helpers. Extracting these functions into a library avoids Pine Script's function inlining, reducing compiled token count in the main strategy.
**Exported Type: RecoupSizingResult**
Holds the output of the 8-step sizing pipeline:
• Micro-level quantities (raw, DLL-capped, final)
• YM-level quantities (pre/post min-1-YM policy, proxy-capped)
• Risk gate values (projected loss, worst-case drawdown, pass/fail)
• Recoup scenario P&L (net outcome if T1 stops and recoup wins)
**Exported Functions**
`f_calcTradeRiskDollars(entry, stop, qty)` → float
Projected risk in dollars. Converts price distance to ticks via syminfo.mintick, then to dollars via syminfo.pointvalue.
`f_calcTradeProfitDollars(entry, tp, qty)` → float
Projected profit in dollars. Same tick-to-dollar conversion applied to the take-profit distance.
`f_computeRecoupSizing(...)` → RecoupSizingResult
8-step recoup sizing pipeline:
1. Raw micro qty from risk multiplier (CEILING)
2. Daily loss limit cap at micro level
3. Micro-to-YM conversion (FLOOR)
4. Minimum-1-YM policy
5. Proxy capacity cap
6. Micro-equivalent for risk gating
7. Worst-case projection (base stop + opposing flatten + recoup stop)
8. Risk gate pass/fail
Plus scenario P&L: net recoup outcome and T1-win profit.
**Usage**
import Vantage-Stack/Vantage_LO1_Sizing/1 as sz
float risk = sz.f_calcTradeRiskDollars(entry, stop, qty)
sz.RecoupSizingResult r = sz.f_computeRecoupSizing(baseQty, baseEntry, baseStop, recoupEntry, recoupStop,
recoupTP, multiplier, proxyMul, minOneYM, curLoss, maxLoss, maxProxy, t1TP, oppTP, oppFrac, oppRemoval)
======================
Library "Vantage_LO1_Sizing"
Position sizing library for LO1 breakout box strategy.
Extracts pure-computation functions to reduce compiled token count in the main script.
f_calcTradeRiskDollars(_entry, _stop, _qty)
Calculates projected risk in dollars for a position (qty × |entry−stop| in ticks × pointvalue).
Parameters:
_entry (float) : Entry price
_stop (float) : Stop-loss price
_qty (float) : Position quantity (contracts)
Returns: Risk in dollars
f_calcTradeProfitDollars(_entry, _tp, _qty)
Calculates projected profit in dollars for a position (qty × |tp−entry| in ticks × pointvalue).
Parameters:
_entry (float) : Entry price
_tp (float) : Take-profit price
_qty (float) : Position quantity (contracts)
Returns: Profit in dollars
f_computeRecoupSizing(baseQtyMicro, baseEntry, baseStop, recoupEntry, recoupStop, recoupTP, oppositeMultiplier, proxyQtyMul, minOneYMEnabled, currentLossDollars, maxDailyLossLimit, maxProxyCap, t1TPPrice, oppTPPrice, oppAddStopFrac, opposingRemovalEnabled)
Computes recoup (opposite-add) position sizing through an 8-step pipeline: raw micro qty, DLL cap, micro-to-YM conversion, min-1-YM policy, proxy cap, risk gating, worst-case projection, and recoup scenario P&L.
Parameters:
baseQtyMicro (int) : T1 micro-contract quantity
baseEntry (float) : T1 entry price
baseStop (float) : T1 stop-loss price
recoupEntry (float) : Recoup entry price
recoupStop (float) : Recoup stop-loss price
recoupTP (float) : Recoup take-profit price
oppositeMultiplier (float) : Target risk multiplier for recoup vs T1 (e.g., 4.0)
proxyQtyMul (float) : Micro-to-YM conversion factor (0 = no proxy)
minOneYMEnabled (bool) : Force minimum 1 YM contract when proxy is active
currentLossDollars (float) : Running session loss in dollars (0 for estimate mode)
maxDailyLossLimit (float) : Daily loss limit in dollars (-1 = disabled)
maxProxyCap (int) : Maximum proxy contracts cap (0 = unlimited)
t1TPPrice (float) : T1 take-profit price (for scenario P&L calculation)
oppTPPrice (float) : Opposing MYM TP price when T1 stops (for scenario profit calc)
oppAddStopFrac (float) : Fraction of base risk for opposite MYM emergency flatten
opposingRemovalEnabled (bool) : Whether opposing removal entry mode is active
Returns: RecoupSizingResult with quantities, risk values, and scenario P&L
RecoupSizingResult
Holds the complete output of the 8-step recoup sizing pipeline: micro/YM quantities, risk gate results, worst-case projections, and recoup-scenario P&L.
Fields:
microQtyRaw (series int)
microCapByDLL (series int)
microQtyCapped (series int)
ymQtyPre (series int)
ymQtyFinal (series int)
microEqForGate (series int)
projectedLossRecoup (series float)
dllRemaining (series float)
totalWorstCase (series float)
riskOK (series bool)
oppMYMLoss (series float)
didMinOneOverride (series bool)
dllForcedZero (series bool)
recoupScenarioNet (series float)
t1WinProfit (series float) Library

biasHelperbiasHelpers: Core Engine for Bias Analytics against a set of Benchmarks
Overview
The ` biasHelper ` library is a highly optimized backend engine designed specifically for evaluating, tracking, and aggregating bias analytics across timeframes.
Built on strict Model-View-Controller (MVC) software architecture principles, this library encapsulates all complex mathematical processing, state caching, and string formatting. By offloading these responsibilities, it allows front-end indicators to remain exceptionally lightweight, mathematically pure, and dedicated entirely to UI visualization.
This library features built-in anti-repainting guardrails, deterministic pseudo-random evaluations, and dynamic memory allocation through User-Defined Types (UDTs).
---
Core Architecture & Design Philosophy
This library operates using two primary User-Defined Types (UDTs) that track the lifecycle of a trading session:
1. ` SessionInfo `: The primary memory matrix. It tracks OHLC data, prevailing directional bias, Heikin-Ashi extensions, and cumulative hit/close counts.
2. ` SessionLines `: The visual array tracker. It orchestrates the projection of structural support/resistance levels and evaluates live price touches against them.
By maintaining state inside these objects rather than utilizing global arrays, the library ensures O(1) time complexity during bar evaluation, resulting in lightning-fast execution even on dense chart histories.
---
Exported Types (UDTs)
`SessionInfo`
The core data structure for tracking session metrics and bias execution.
* **Price Tracking**: `prevHigh`, `prevLow`, `currentHigh`, `currentLow`, `currentOpen`.
* **Heikin-Ashi Anchors**: `prevHaOpen`, `prevHaClose`.
* **State Trackers**: `pushedUp` (bool), `currentBias` (int: 1 = Bullish, -1 = Bearish, 0 = Neutral).
* **Metric Accumulators**: `bullishCount`, `bearishCount`, `hitHighCount`, `hitLowCount`, `closeHighCount`, `closeLowCount`.
`SessionLines`
The spatial data structure for tracking visual levels and live touches.
* Lines : `highLine`, `lowLine`.
* Touch Booleans : `hitHighLine`, `hitLowLine`.
---
Data & State Management
`updateData(infoObj, isNew, pClose, cOpen, cHigh, cLow)`
The standard market structure logic handler. It evaluates historical structural breaks against the previous close to determine the prevailing directional bias without repainting.
* Parameters : Evaluates state using standard OHLC inputs and strictly anchors to `close ` (`pClose`) to guarantee real-time parity with historical states.
`updateBenchmarkData(infoObj, isNew, pClose, cOpen, cHigh, cLow, bType, tfSeconds)`
A dynamic routing matrix for alternative bias benchmarks.
* Supported `bType` Routing :
* `"MOM"`: Pure Candlestick Momentum (Close vs. Open).
* `"HA"`: Synthetic Heikin-Ashi Momentum evaluated securely within standard chart data.
* `"ABULL"` / `"ABEAR"`: Static directional evaluations (Always Bullish / Always Bearish).
* `"COIN"`: Deterministic pseudo-random generation. Generates a perfect 50/50 randomized output anchored mathematically to the timestamp and previous close, guaranteeing it never repaints or flickers on live ticks.
* `"DTD"`: Day-to-Day alternating parity based on absolute time intervals.
`processLines(linesObj, infoObj, isNew, tRight, lStyle, currHigh, currLow)`
Draws historical support/resistance barriers and evaluates current price action (`currHigh`, `currLow`) to register touches and successful close-throughs.
---
Math & Aggregation
`calcTrueRate(biasCount, hitCount, closeCount)`
Calculates the *True Close-Through Rate* (Success Rate × Close-Through Rate) while safely handling zero-division scenarios. Returns a standardized float coefficient.
`getTotals(infoObj)`
Extracts aggregated cross-directional totals for condensed dashboard views.
* Returns : A tuple ` `.
---
UI Formatting Utilities
`getTablePos(pos)` & `getTableSize(size)`
Translates string-based user inputs (e.g., `"Top Right"`, `"Normal"`) into native Pine Script structural variables (`position.top_right`, `size.normal`).
`formatResult(hit, biasCount)`
Converts raw integers into a cleanly formatted fractional percentage string (e.g., `"45.2%"`).
`formatPercent(value)`
Converts a raw floating-point coefficient into a polished percentage string.
---
Implementation Example
To utilize this library in your indicator, instantiate the UDTs and pass them through the update loops securely:
import TRSTNGLRD/biasHelpers/4 as lib
// 1. Initialize Objects
var lib.SessionInfo sessionData = lib.SessionInfo.new()
var lib.SessionLines sessionLines = lib.SessionLines.new()
// 2. Evaluate State
if isEligibleTimeframe
sessionData.updateData(isNewSession, close , open, high, low)
= sessionLines.processLines(sessionData, isNewSession, rightTime, lineStyle, high, low)
// 3. Extract Formatted Data
= sessionData.getTotals()
string trueRate = lib.formatPercent(lib.calcTrueRate(tBias, tHit, tClose))
Note to Developers
This library adheres strictly to PulseWire's anti-repainting guidelines. When feeding inputs into `updateData` or `updateBenchmarkData`, always utilize the previous bar's close (`close `) for the `pClose` parameter. Live data (`high`, `low`) should exclusively be passed into the `processLines` method to allow real-time touch detection. Library
