Bibliothèque

OMSF_Education_LibTo keep the codebase of the OMSF Learning Space indicator cleanly structured, easy to read, and as concise as possible, I have extracted core calculations and logic functions into this reusable library. This keeps the main script lightweight while allowing you to flexibly utilize these individual building blocks for your own custom scripts and quantitative experiments.
Extracted Functions & Modules
1. pivot_fun – Pivot Analytics & Trend State
Derivatives of the classic Pivot High/Low concept to identify key structural highs and lows using configurable confirmation lookback windows.
Provides continuously updated persistent pivot levels, running extreme levels, and a clean trend state machine (1 = Long, -1 = Short) along with standard pivot lag metrics (avg_std_delay).
2. dir_kaufman_eff_ratio – Directional Kaufman Efficiency Ratio (KER)
Computes directional trend efficiency ranging from -1.0 (strong downward efficiency) to +1.0 (strong upward efficiency), featuring built-in protection against division by zero.
3. ker_marketstructure_validation – KER Market Structure Validation
Accumulates and averages KER metrics separately for Long and Short market regimes to evaluate overall structural trend quality.
4. max_excurs_ratio – MFE / MAE Analytics
Tracks ATR-normalized Maximum Favorable Excursion (MFE) and Maximum Adverse Excursion (MAE) values for individual trend segments.
Computes running aggregate ratios and stores historical trade metrics in float arrays—ideal for statistical distribution and percentile analysis.
5. vis_mfe_mae_ratio_long & vis_mfe_mae_ratio_short – Visualization Components
Renders dynamic chart overlays featuring break-even levels, stop-loss excursion bounds, color fills, and informational labels displaying real-time or locked segment performance.
📌 Coming next:
OMSF Learning Space Update: Chapter 5 MFE/MAE. ()
Best regards, arni Bibliothèque

FootprintKitAnalysis toolkit for the native footprint API introduced in Pine v6. It turns a `footprint` object into aggregated row statistics, price-interval measurements, low and high volume node runs, stacked imbalances, unfinished auction reads, absorption reads and multi-bar composite profiles.
It is written for script authors who build their own footprint tools and who would otherwise re-implement the same row loops in every script.
WHY THE LIBRARY NEVER CALLS request.footprint()
Pine allows only one unique footprint request per script. If the library issued that request internally it would consume the caller's single slot, and the importing script could no longer query the footprint on its own terms. So the caller makes the one allowed call and passes the resulting object into every function.
The consequence is that the library performs no requests, draws nothing and keeps no persistent state outside the Profile object you create yourself. There is nothing in it that can repaint.
WHAT IT COMPUTES THAT THE API DOES NOT EXPOSE
- Stacked imbalances. The API flags imbalance per row; the stack of consecutive flagged rows is what carries meaning in footprint reading, and it has to be assembled.
- Unfinished auction at the extremes of a bar, i.e. an extreme row that still shows trade on both sides.
- Volume traded inside an arbitrary price interval, with rows that only partly overlap the interval counted pro rata.
- Runs of thin rows, the price pockets a move passed through without trade, and runs of heavy rows, the shelves inside the bar.
- A composite profile across several bars with its own point of control and value area. request.footprint() returns one bar at a time; feeding successive bars into a Profile builds the multi-bar picture the single call cannot give.
- Distribution shape metrics that read the whole profile rather than only its peak.
FORMULAS
Aggressive volume per row and per interval is derived from total volume V and delta D as buy = (V + D) / 2 and sell = (V - D) / 2. This is exact by the definition of delta and avoids depending on optional per-row accessors.
slice() weights each row by the fraction of its height that falls inside the requested interval, k = overlap / rowHeight, clamped to 1. Counting a boundary row either whole or not at all is the usual source of error in hand-written versions.
concentration() is POC row volume divided by mean row volume. A value near 1 means volume was spread evenly, a high value means one price row absorbed most of the activity.
dispersion() is the Shannon entropy of the row volume distribution, normalised by ln(n) to the 0 to 1 range: H = -sum(p * ln p) / ln(n), where p is a row's share of bar volume. Zero means all volume sat in one row, one means a perfectly even spread. Unlike concentration it distinguishes a bar with two heavy rows from a bar with one.
deltaCentroid() returns the centre of mass of absolute delta as a fraction of the bar range from the low, showing where aggression concentrated regardless of which side was aggressive.
absorption() splits the bar range into thirds, measures each extreme third with slice(), and reports absorption when a third holds at least the requested share of bar volume while its delta points against the direction of the bar. Aggression that meets size and fails to move price is the signature of a passive participant taking the other side.
The composite value area grows outward from the point of control, repeatedly taking the heavier of the two neighbouring buckets until the requested share of total volume is enclosed.
USAGE
import Smart-Day-Trader/FootprintKit/1 as fpk
footprint fp = request.footprint(4, 70, 300)
fpk.RowStats s = fpk.stats(fp)
float conc = fpk.concentration(s)
float pos = fpk.pocPosition(s, high, low)
array voids = fpk.runs(fp, s, 0.4, 3, true)
= fpk.wicks(fp, open, high, low, close)
NOTES
A plan with footprint data access is required for the data itself; the library compiles on any plan because it makes no requests.
Choose ticks per row relative to the instrument's tick size rather than copying a default. On NASDAQ 100 E-mini futures the tick is 0.25 points, so 4 ticks per row equals one point and yields roughly 20 to 30 rows on a 5 minute bar. A value of 20 there would collapse the same bar into 4 rows, at which point concentration, entropy and row runs stop carrying information.
All functions accept a na footprint and return empty or na results rather than failing, so they are safe to call on bars without data.
REFERENCE
stats(fp)
Aggregates every row of a footprint in a single pass: totals, POC, delta,
imbalance counts and the price bounds actually covered by rows. Reading these values
one by one costs several loops over the same array; this does it once.
Parameters:
fp (footprint) : Footprint object returned by request.footprint(). Safe to pass na.
Returns: A RowStats object. When the footprint is na or empty, `n` is 0 and the
float fields are na.
concentration(s)
Concentration of the bar's volume: POC row volume divided by the mean row
volume. A value near 1 means volume was spread evenly across the bar; a high value
means a single price row absorbed most of the activity.
Parameters:
s (RowStats) : RowStats produced by stats().
Returns: The ratio, or na when statistics are empty.
dispersion(fp, s)
Normalised Shannon entropy of the volume distribution across rows, scaled to
0 to 1. Zero means all volume sat in one row, one means perfectly even spread. Unlike
concentration() this reads the whole shape rather than just the peak, so a bar with
two heavy rows is distinguished from a bar with one.
Parameters:
fp (footprint) : Footprint object.
s (RowStats) : RowStats produced by stats() for the same footprint.
Returns: Entropy in the 0 to 1 range, or na when fewer than two rows carry volume.
pocPosition(s, barHigh, barLow)
Where the POC sits inside the bar's range, as a fraction from the low.
0 places the heaviest row at the low of the bar, 1 at the high.
Parameters:
s (RowStats) : RowStats produced by stats().
barHigh (float) : High of the bar.
barLow (float) : Low of the bar.
Returns: Position clamped to 0 to 1, or na when the range is degenerate.
deltaCentroid(fp, barHigh, barLow)
Centre of mass of absolute delta inside the bar, as a fraction from the low.
Shows where aggression was concentrated regardless of which side was aggressive,
which often differs from where total volume sat.
Parameters:
fp (footprint) : Footprint object.
barHigh (float) : High of the bar.
barLow (float) : Low of the bar.
Returns: Position in the 0 to 1 range, or na when there is no delta to weight by.
slice(fp, priceA, priceB)
Volume traded inside an arbitrary price interval. Rows that only partly
overlap the interval are counted in proportion to the overlapped fraction of their
height, so the result is correct even when the interval boundaries fall mid-row.
This is the primitive behind wick, zone and level measurements.
Parameters:
fp (footprint) : Footprint object.
priceA (float) : One boundary of the interval. Order does not matter.
priceB (float) : The other boundary of the interval.
Returns: A Slice object. All fields are 0 and `share` is na when nothing overlaps.
wicks(fp, o, h, l, c)
Splits the bar's volume into upper wick, body and lower wick using slice(),
so partially overlapped rows are handled correctly. An empty upper wick slice on a
bar with a long upper shadow means price travelled there without trading size, which
reads very differently from a wick that carries volume.
Parameters:
fp (footprint) : Footprint object.
o (float) : Open of the bar.
h (float) : High of the bar.
l (float) : Low of the bar.
c (float) : Close of the bar.
Returns: A tuple of Slice objects.
runs(fp, s, ratio, minRun, below)
Finds every group of consecutive rows whose volume is below or above a
multiple of the bar's mean row volume, and returns them as price spans. With
below = true this locates thin rows, the price pockets a move passed through without
trade; with below = false it locates the heavy shelves inside the bar.
Parameters:
fp (footprint) : Footprint object.
s (RowStats) : RowStats produced by stats() for the same footprint.
ratio (float) : Multiplier applied to the mean row volume to form the threshold.
minRun (int) : Minimum number of consecutive rows required to report a span.
below (bool) : When true, keep rows at or below the threshold; when false, at or above.
Returns: An array of Span objects, ordered as the rows are ordered. Empty when nothing
qualifies.
stacks(fp, minRun, buySide)
Finds stacked imbalances: runs of consecutive rows all flagged on the same
side. The native API exposes the flag per row, but the stack is what carries meaning
in footprint reading, and stacks have to be assembled by hand.
Parameters:
fp (footprint) : Footprint object.
minRun (int) : Minimum number of consecutive flagged rows to report, commonly 3.
buySide (bool) : When true, collect buy imbalance stacks; when false, sell imbalance stacks.
Returns: An array of Span objects covering each stack. Empty when none reach minRun.
unfinished(fp, tol)
Tests the extreme rows for an unfinished auction: an extreme that still shows
trade on both sides, meaning the move stopped before either side was cleared out.
A finished extreme has one side at or near zero.
Parameters:
fp (footprint) : Footprint object.
tol (float) : Fraction of the extreme row's own volume below which a side counts as empty.
Use 0 for a strict test, or a small value such as 0.05 to tolerate noisy feeds.
Returns: A tuple of booleans. Both false when the footprint is empty.
absorption(fp, s, o, c, minShare)
Reads absorption at the extremes of the bar: one third of the bar's range
holding a large share of the volume with delta pointing against the bar's direction.
Aggression that meets size and fails to move price is the signature of a passive
participant taking the other side.
Parameters:
fp (footprint) : Footprint object.
s (RowStats) : RowStats produced by stats() for the same footprint.
o (float) : Open of the bar.
c (float) : Close of the bar.
minShare (float) : Minimum share of bar volume the third must hold, for example 0.4.
Returns: 1 when sellers were absorbed at the lows of an up bar, -1 when buyers were
absorbed at the highs of a down bar, 0 otherwise.
newProfile(step)
Creates an empty composite profile. request.footprint() delivers one bar at a
time; feeding successive bars into a profile builds the multi-bar picture the single
call cannot give on its own.
Parameters:
step (float) : Price bucket height. Use the row height from RowStats to keep the composite
at the same resolution as the footprint itself.
Returns: An empty Profile object.
method feed(p, fp)
Folds one bar's rows into the profile. Buckets are keyed by rounded row
midpoint and kept sorted, so repeated calls stay ordered and lookups stay cheap.
Call once per confirmed bar.
Namespace types: Profile
Parameters:
p (Profile) : Profile to update, modified in place.
fp (footprint) : Footprint object for the bar being added.
Returns: Nothing. The profile is mutated.
method sum(p)
Total volume held by the profile.
Namespace types: Profile
Parameters:
p (Profile) : Profile to read.
Returns: Sum of all bucket volumes, or 0 when the profile is empty.
method poc(p)
Point of control of the composite profile.
Namespace types: Profile
Parameters:
p (Profile) : Profile to read.
Returns: Centre price of the heaviest bucket, or na when the profile is empty.
method valueArea(p, pct)
Value area of the composite profile, grown outward from the point of control
by repeatedly taking the heavier neighbouring bucket until the requested share of
total volume is enclosed.
Namespace types: Profile
Parameters:
p (Profile) : Profile to read.
pct (float) : Share of total volume to enclose, expressed 0 to 1, for example 0.7.
Returns: A tuple of prices, both na when the profile is empty.
method reset(p)
Empties the profile while keeping its bucket size, ready for a new window.
Namespace types: Profile
Parameters:
p (Profile) : Profile to clear, modified in place.
Returns: Nothing. The profile is mutated.
RowStats
Aggregated statistics for every row of a single bar's footprint.
Fields:
n (series int) : Number of rows. Zero when the footprint holds no data.
total (series float) : Sum of row volume across the bar.
avg (series float) : Mean volume per row.
maxVol (series float) : Volume of the heaviest row, i.e. the POC row.
minVol (series float) : Volume of the lightest row.
pocVol (series float) : Same as maxVol, kept for readability at call sites.
pocTop (series float) : Upper price bound of the POC row.
pocBot (series float) : Lower price bound of the POC row.
pocMid (series float) : Midpoint of the POC row.
pocIdx (series int) : Index of the POC row inside the rows array, -1 when empty.
buy (series float) : Aggressive buy volume of the bar, derived as (total + delta) / 2.
sell (series float) : Aggressive sell volume of the bar, derived as (total - delta) / 2.
delta (series float) : Net delta of the bar summed across rows.
buyImb (series int) : Count of rows flagged as buy imbalances.
sellImb (series int) : Count of rows flagged as sell imbalances.
top (series float) : Highest price covered by any row.
bot (series float) : Lowest price covered by any row.
rowH (series float) : Height of one row in price units.
Span
A contiguous group of rows inside one bar, reported as a price span.
Fields:
top (series float) : Upper price bound of the span.
bot (series float) : Lower price bound of the span.
idxA (series int) : Index of the first row of the span.
idxB (series int) : Index of the last row of the span.
count (series int) : Number of rows in the span.
vol (series float) : Total volume inside the span.
delta (series float) : Net delta inside the span.
Slice
Volume measured over an arbitrary price interval, with partial rows counted pro rata.
Fields:
total (series float) : Volume inside the interval.
buy (series float) : Aggressive buy volume inside the interval.
sell (series float) : Aggressive sell volume inside the interval.
delta (series float) : Net delta inside the interval.
share (series float) : Interval volume divided by the bar's total volume, 0 to 1.
Profile
Composite volume profile accumulated from several bars' footprints.
Fields:
step (series float) : Price bucket size. Rows are folded into buckets of this height.
price (array) : Bucket centre prices, kept sorted ascending.
vol (array) : Volume per bucket, index-aligned with `price`.
dlt (array) : Delta per bucket, index-aligned with `price`.
Bibliothèque

Bibliothèque

Bibliothèque

Bibliothèque

qqq_momentum_engineLibrary "qqq_momentum_engine"
exitAtrMode(atrTf)
Decide how the exit ATR must be sourced, and refuse the one
case that cannot be sourced honestly.
SPEC v3 Part VII.4, pinned:
atrTf > chart → completed-HTF-bar request (see the note above)
atrTf == chart → local ta.atr() on the confirmed bar, no request at all
atrTf < chart → runtime.error. Unsupported, and deliberately so: a
sub-chart ATR would need request.security_lower_tf
and an intrabar walk to be meaningful, and an exit
distance is not worth that machinery. Failing loudly
beats returning a number that looks like an ATR.
Parameters:
atrTf (simple string) : The exit ATR timeframe, as a Pine timeframe string.
Returns: 0 = compute locally (same timeframe), 1 = request the HTF bar.
exitTicks(ex, atrRef)
Exit distances as WHOLE TICKS. The single source of exit
geometry for both strategy.pine and labeler.pine.
Quantisation lives here on purpose. strategy.exit()'s profit/loss
parameters are tick counts, so the backtest necessarily trades a rounded
distance. If the labeller measured the raw float instead, ATR mode would
diverge: a 0.9274 distance becomes a 0.93 order, and a bar reaching 0.928
would be labelled "target" for a target the strategy never had. Both sides
now derive their levels from the same integers, so that gap cannot exist.
minMoveFloor clamps BOTH legs in atr mode. 0.75 x a 5m QQQ ATR can be a few
cents; a target or a stop narrower than the option spread is not a
measurable event.
Returns when atr mode is selected and atrRef is na (ATR warmup).
Callers must check — an na bracket is silently no bracket at all.
Parameters:
ex (Exits) : Exits configuration.
atrRef (float) : ATR to use in "atr" mode. Ignored in "fixed" mode.
Returns:
ticksToPrices(entry, dir, tgtTicks, stpTicks)
Convert tick distances into absolute prices for one fill.
Used by labeler.pine to build its walk levels and by
strategy.pine only to DRAW the levels it already sent as
tick brackets.
Parameters:
entry (float) : Fill price.
dir (int) : +1 long, -1 short.
tgtTicks (int) : Target distance in ticks, from exitTicks().
stpTicks (int) : Stop distance in ticks, from exitTicks().
Returns:
vwapSideOf(dir, closeV, vwapV)
The VWAP side label for ONE signal, per SPEC v3 Part III.3.
"with" = the signal direction agrees with the VWAP side
(long above VWAP, short below it)
"against" = it does not
A TIE COUNTS AS "against". close == vwap is not agreement,
and the strict comparison keeps the label reproducible
instead of depending on float equality luck.
Parameters:
dir (int) : +1 long, -1 short.
closeV (float) : The signal bar's close.
vwapV (float) : Session VWAP on the signal bar.
Returns: "with" | "against"
evaluate(cfg, bodyLen, volLen, extAtrLen)
Evaluate the QQQ Momentum Trigger for this bar.
One call per bar per script. All state (ring buffer,
cooldown marks) is per call site and mutates only on
confirmed bars.
Parameters:
cfg (Config) : Every parameter of the signal definition.
bodyLen (simple int) : Body baseline window. Must equal cfg.bodyLen.
volLen (simple int) : Volume baseline window. Must equal cfg.volLen.
extAtrLen (simple int) : Extension ATR length. Must equal cfg.extAtrLen.
Returns: Signal for this bar.
tz()
The timezone every minute-of-day calculation in this library uses.
Exposed so consuming scripts format timestamps identically.
Returns: "America/New_York"
Config
Fields:
useSlope (series bool)
useCandle (series bool)
closeLocPct (series float)
bodyMult (series float)
bodyLen (series int)
volumeMode (series string)
volMult (series float)
volLen (series int)
rvolThreshold (series float)
rvolSessions (series int)
rvolMinSessions (series int)
useStructure (series bool)
useExtension (series bool)
extMult (series float)
extAtrLen (series int)
cooldownBars (series int)
requiredTfSeconds (series int)
requireStandardChart (series bool)
requireRegularSessionChart (series bool)
regularSessionOnly (series bool)
sessionStartMin (series int)
sessionMinutes (series int)
blockLastMinutes (series int)
Signal
Fields:
long (series bool)
short (series bool)
ema9 (series float)
slopeUp (series bool)
slopeDn (series bool)
reclaimLong (series bool)
reclaimShort (series bool)
isGreen (series bool)
isRed (series bool)
closeLocLongOk (series bool)
closeLocShortOk (series bool)
bodyOk (series bool)
bodyRatio (series float)
bodyBase (series float)
candleLongOk (series bool)
candleShortOk (series bool)
volRatio (series float)
volBase (series float)
volPass (series bool)
rvol (series float)
rvolBaseline (series float)
rvolFill (series int)
rvolWarm (series bool)
rvolBucketsShort (series int)
rvolBucketsEmpty (series int)
structLongOk (series bool)
structShortOk (series bool)
atrExt (series float)
emaDistAtr (series float)
extLongOk (series bool)
extShortOk (series bool)
vwap (series float)
vwapDist (series float)
vwapAbove (series bool)
cooldownLongOk (series bool)
cooldownShortOk (series bool)
barsSinceLong (series int)
barsSinceShort (series int)
inSession (series bool)
lateBlock (series bool)
warmupOk (series bool)
minuteOfDay (series int)
bucket (series int)
bucketRows (series int)
minutesLeft (series int)
longF1Reclaim (series bool)
longF2Slope (series bool)
longF3Candle (series bool)
longF4Volume (series bool)
longF5Struct (series bool)
longF6Ext (series bool)
longF7Gate (series bool)
shortF1Reclaim (series bool)
shortF2Slope (series bool)
shortF3Candle (series bool)
shortF4Volume (series bool)
shortF5Struct (series bool)
shortF6Ext (series bool)
shortF7Gate (series bool)
Exits
Fields:
mode (series string)
fixedTarget (series float)
fixedStop (series float)
atrTargetMult (series float)
atrStopMult (series float)
minMoveFloor (series float)
maxBarsInTrade (series int) Bibliothèque

OptiPine: High-Performance Caching and Data PipelinesOptiPine is a high performance architecture library for Pine Script™, built for algorithms that push beyond ordinary indicator workloads. It turns caching, sparse updates, reusable storage and workload-aware data structures into practical APIs that stay small at the call site.
In a small indicator, optimization is often optional. In a rendering engine, machine learning library, simulation, dashboard or object system, it can determine whether a feature runs at all. The problem is rarely one slow formula. It is the thousands of unnecessary operations around it: recalculating unchanged results, shifting rolling arrays, scanning large collections for a few changes, and moving stored objects when one disappears.
OptiPine attacks that layer with techniques used in projects such as Pine3D and NeuraLib . The idea is simple: do less work, move less data, and let the representation follow the workload.
Compared with conventional Pine implementations of the same task, OptiPine's optimized paths commonly ran 15% to 40% faster . Sparse updates and indexed lookups exceeded 90% when the alternative scanned or searched the full collection.
Most users can stay entirely within the high-level API. A Memo cache with several dependencies looks like this:
// Pseudocode: trendRegime and volatilityRegime are floats;
// rebuildModel() is a pure calculation.
var op.FloatMemo model = op.floatMemo()
if model.staleOn(trendRegime, volatilityRegime)
model.store(rebuildModel(trendRegime, volatilityRegime))
float result = model.get()
// Output: rebuildModel() runs once, then only when either regime changes.
Memo owns the previous dependencies, first-run state, validity and cached result. The caller only declares what the result depends on.
----------------------------------------------------------------------------------------------------------------
🔷 DO NOT CALCULATE THE SAME THING TWICE
The fastest expensive calculation is the one that never needed to run. Models, simulations and generated geometry often remain valid across many script executions.
Memo is the direct choice when the cached result is an int, float, bool, string or color. staleOn() checks up to four floats, two integers, one Boolean and one string; store() saves a rebuilt value, and get() returns it.
Many models respond to regimes rather than every tiny change in raw data. Round the inputs into meaningful regimes, pass them to staleOn() , and the model runs only when a regime changes.
In practice: Memo is useful for scenario models, parameter sweeps, numerical solvers and other expensive pure calculations that reduce to one primitive result. If its dependencies repeat on nine out of ten executions, it avoids roughly 90% of those model runs.
For collections or a variable dependency list, use Memo's explicit begin() , dependencies.watch*() and miss() lifecycle.
Keep guarded work pure: Stateful ta.* and similar history-dependent calls must remain outside Memo and Watch guards. Compute them every bar, then pass their results into the guarded calculation.
🔸 WATCH: CHANGE DETECTION WITHOUT RESULT STORAGE
Watch is the lighter choice when the caller already owns the result. Several consumers can observe the same producer independently by giving each its own Watch. changed() returns true on the first observation and whenever one scalar, primitive array or OptiPine row ring changes. Row rings expose an internal revision, so checking them is O(1).
For a single source, the dependency check should take less attention than the calculation it protects. Here another component supplies one caller-owned feature array:
// Pseudocode: getFeatureSnapshot() supplies an array.
array features = getFeatureSnapshot()
var op.Watch featureWatch = op.watch()
var float modelScore = na
if featureWatch.changed(features)
modelScore := evaluateModel(features)
// Output: modelScore is rebuilt only when the features array changes.
Because OptiPine does not own features , it compares the array with a retained snapshot and rewrites that snapshot only after a change. Supported row rings use their internal revision instead. The call stays the same, and this compare-first array pattern measured roughly 35% to 60% faster than rewriting the snapshot every time.
The array comparison is still O(N), so use it when the avoided calculation costs more than the comparison. If the producer already provides one reliable change flag, use the flag directly.
For several dependencies, use an explicit pass. begin() starts the comparison, the typed watch*() methods add dependencies, and finish() returns true if the completed set changed. A Watch remembers dependencies; it does not store the result.
// Pseudocode dependencies: int length, float multiplier,
// and array features.
var op.Watch settingsWatch = op.watch()
var float result = na
settingsWatch.begin()
settingsWatch.watchInt(length)
settingsWatch.watchFloat(multiplier)
settingsWatch.watchFloats(features)
bool dependenciesChanged = settingsWatch.finish()
if dependenciesChanged
result := rebuild(length, multiplier, features)
// Output: result is rebuilt when any observed dependency changes.
Construct the Watch once with var , then run begin() and finish() on every comparison pass. For one dependency, changed(source) is the shorter path.
In practice: Watch fits module boundaries: a model can observe a feature array, a renderer can observe a managed ring, or a cache can observe several mixed settings without duplicating the producer's change logic.
CadenceGate limits how often work may run. due() is periodic; dueWhenChanged() also requires a producer revision and remembers changes until the cadence opens. Use it for intentionally delayed work such as periodic model fitting, not results that must update immediately.
----------------------------------------------------------------------------------------------------------------
🔷 ROLLING HISTORY WITHOUT SHIFTING IT
Rolling histories often perform work that adds nothing to the result. If an array keeps the latest 200 events, removing the oldest one and shifting the other 199 entries is unnecessary.
FloatRowRing and IntRowRing keep fixed-width rows in reusable storage. Once full, the next row overwrites the oldest physical slot while reads remain chronological.
var op.FloatRowRing history = op.floatRowRing(200, 3)
float atr14 = ta.atr(14)
if barstate.isconfirmed
history.push(array.from(close, volume, atr14))
float oldestPrice = history.at(0, 0)
float latestPrice = history.newestAt(0, 0)
// Output: after a confirmed push, these are the oldest and newest retained closes.
A push costs O(width), or O(1) through pushValue() for a width-one ring. Rings also provide chronological windows and gathered rows. When several producers can mutate a ring, a separate consumer can detect its revision with Watch.changed(ring) in O(1).
In practice: Row rings fit pivots, completed trades, sampled features and other fixed event histories.
Performance: A full ring overwrites one row instead of shifting every retained row. Its chronological output uses at most two native contiguous copies, which measured 90% faster than rebuilding a 512-cell, width-four output row by row.
Use RingCursor when several caller-owned arrays need the same circular layout. Ordinary series history such as close should remain native Pine.
----------------------------------------------------------------------------------------------------------------
🔷 KEEP DYNAMIC OBJECTS STABLE
Dynamic objects become surprisingly expensive when identity is tied to array position. If one object is removed from several parallel arrays, every later entry shifts, every synchronized payload array needs the same removal, and every external reference to those positions becomes fragile.
StablePool is not the zone storage itself. It keeps one association: an object ID supplied by the script points to a reusable array slot. The ID answers "which zone is this?" while the slot answers "where is this zone's data stored?"
The example has three different logical zones named A, B and C. Their IDs, 1001, 1002 and 1003, are arbitrary unique values chosen for readability. Real IDs may come from a pivot bar, timestamp, order number or incrementing counter.
const int ZONE_A_ID = 1001
const int ZONE_B_ID = 1002
const int ZONE_C_ID = 1003
var op.StablePool zonePool = op.stablePool()
// This example never has more than two active zones.
var array prices = array.new(2, na)
if barstate.isfirst
// A receives slot 0. B receives slot 1.
= zonePool.acquire(ZONE_A_ID)
= zonePool.acquire(ZONE_B_ID)
prices.set(slotA, 100.0)
prices.set(slotB, 200.0)
// Zone A no longer exists. Its slot becomes available.
zonePool.release(ZONE_A_ID)
// C is a new zone with a new identity, but it can reuse A's old slot.
= zonePool.acquire(ZONE_C_ID)
prices.set(slotC, 300.0)
// Output: B keeps slot 1. C has ID 1003 but reuses A's released slot 0.
// prices is .
Why C needs a new ID: C is a different zone, even though it occupies the same array position A once used. Reusing 1001 would describe A returning, not a new zone C. IDs preserve object identity; slots are only reusable storage addresses.
Several fields, one slot: In production, the same slot usually addresses every field belonging to the object. Continuing the A, B and C lifecycle with four parallel arrays:
const int ZONE_A_ID = 1001
const int ZONE_B_ID = 1002
const int ZONE_C_ID = 1003
var op.StablePool zonePool = op.stablePool()
var array zonePrices = array.new()
var array zoneTimes = array.new()
var array zoneStrengths = array.new()
var array zoneColors = array.new()
if barstate.isfirst
= zonePool.acquire(ZONE_A_ID)
= zonePool.acquire(ZONE_B_ID)
// Grow every payload array to cover the allocated slots.
int required = zonePool.slotCount()
op.ensureSizeFloat(zonePrices, required, na)
op.ensureSizeInt(zoneTimes, required, na)
op.ensureSizeFloat(zoneStrengths, required, na)
op.ensureSizeColor(zoneColors, required, na)
zonePrices.set(slotA, 100.0)
zoneTimes.set(slotA, 10)
zoneStrengths.set(slotA, 0.40)
zoneColors.set(slotA, color.blue)
zonePrices.set(slotB, 200.0)
zoneTimes.set(slotB, 20)
zoneStrengths.set(slotB, 0.80)
zoneColors.set(slotB, color.red)
zonePool.release(ZONE_A_ID)
= zonePool.acquire(ZONE_C_ID)
// C reuses A's slot, so every field at that slot must be overwritten.
zonePrices.set(slotC, 300.0)
zoneTimes.set(slotC, 30)
zoneStrengths.set(slotC, 0.60)
zoneColors.set(slotC, color.lime)
// Output: B keeps slot 1 in every array. C owns slot 0 in every array.
// Nothing is removed or shifted.
acquire(id) returns the slot and whether the ID was newly added. Calling it again for an active ID returns the same slot. release(id) frees the slot, but does not erase its array data, so every field must be overwritten when that slot is reused.
The example preallocates two values because it has at most two active zones. A dynamic script can grow its payload arrays with ensureSize*() whenever acquire() reports a new ID. zonePool.slots() returns the currently active slots as a read-only view.
In practice: One zone slot can index its price, time, color, strength and line across several arrays. In the complete example later, the pivot bar and event type form each zone ID. Releasing one zone frees its slot without shifting other zones or breaking saved positions.
Performance: StablePool is independent of payload layout: its slots can index parallel arrays or one array of UDTs. acquire() , release() and find() are O(1), and releasing an object never shifts caller-owned payloads.
For a few fixed objects, manual indices are simpler. StablePool becomes useful when IDs appear and disappear over time, several payload arrays share the same slots, or other parts of the script retain those positions.
SlotCache is the frame-based alternative. Call begin() , acquire every active key, then call finish() ; previously active keys that were not touched are retired automatically.
----------------------------------------------------------------------------------------------------------------
🔷 UPDATE ONLY WHAT CHANGED
Large state does not imply large change. A dashboard may contain 10,000 cells while only a few change on one bar, or a large object system may need to refresh only a handful of entries.
A conventional dirty-flag array must be cleared and scanned in full. DirtySet stores only the changed indices, removes duplicate marks and begins a new cycle without clearing the entire universe. It is a work list, not payload storage or an ID-to-slot map.
Here StablePool resolves zoneId , the arrays store zone data, and DirtySet schedules the slots that need rebuilding. The event values are pseudocode:
int MAX_ZONES = 50000
var op.StablePool zones = op.stablePool()
var op.DirtySet dirtySlots = op.dirtySet(MAX_ZONES)
var array tops = array.new()
var array bottoms = array.new()
var array midpoints = array.new()
// Start this bar's sparse-work cycle.
dirtySlots.begin()
if zoneGeometryChanged
// StablePool converts the logical ID into a reusable physical slot.
= zones.acquire(zoneId)
if created
op.ensureSizeFloat(tops, zoneSlot + 1, na)
op.ensureSizeFloat(bottoms, zoneSlot + 1, na)
op.ensureSizeFloat(midpoints, zoneSlot + 1, na)
tops.set(zoneSlot, newTop)
bottoms.set(zoneSlot, newBottom)
dirtySlots.mark(zoneSlot)
if zoneStyleChanged
int styleSlot = zones.find(zoneId)
if styleSlot >= 0
dirtySlots.mark(styleSlot) // A second mark of the same slot is ignored.
// Process only the distinct physical slots marked during this bar.
for dirtySlot in dirtySlots.values()
float midpoint = (tops.get(dirtySlot) + bottoms.get(dirtySlot)) * 0.5
midpoints.set(dirtySlot, midpoint)
redrawZone(zones.keyAt(dirtySlot), midpoint)
// Output: one zone is rebuilt once even if geometry and style both mark it.
Repeated marks are deduplicated, and unmarked zones are never visited. Work scales with the number of changed slots, not the size of the collection. If the natural address is already a dense index, mark it directly without StablePool.
In practice: Several producers can mark work, then one consumer updates each affected cell, drawing or record once. With 1% of entries changed, this measured 93% faster than clearing and scanning the full universe.
----------------------------------------------------------------------------------------------------------------
🔷 KEYED LOOKUP WITHOUT GUESSWORK
Keyed lookup appears throughout object systems, caches and grouped data, but no structure fits every key set. Distribution, rebuild frequency and query volume change the best choice. OptiPine sees the completed keys at build() , then selects the lookup shape that fits them.
🔸 TYPED STORES: ONE VALUE PER KEY
A typed store maps each integer key to one primitive value. build() pairs entries at matching positions in the key and value arrays. Consecutive IDs allow direct addressing:
var op.IntFloatStore scores = op.intFloatStore()
if barstate.isfirst
// Four entries are shown for readability; both arrays may be much larger.
scores.build(
array.from(410, 411, 412, 413),
array.from(0.80, 0.30, 0.95, 0.50))
float selected = scores.get(412)
// Output: integer key 412 resolves to float value 0.95.
Lookup is one-way: get(412) returns 0.95 , but values may repeat, so get(0.95) has no general meaning.
What automatic mode chooses:
Consecutive ascending keys: Direct arithmetic indexing.
Compact key ranges: A dense lookup table.
Other unordered keys: A native map when within Pine's map limit.
Ascending sparse keys: Binary search, or a map within that limit when expectedQueries justifies its build cost.
Linear lookup remains available for unusual workloads that rebuild far more often than they query. Automatic mode only selects it for non-empty stores when linearMaxEntries is deliberately configured.
The same API avoids hashing when direct addressing fits, uses a map when it pays, and remains usable beyond Pine's map capacity. Automatic mode is the normal default. Use op.indexConfigDynamic() when future query volume is unknown and the store may need to promote itself later.
build(keys, values, expectedQueries) accepts two same-length arrays. The optional hint tells OptiPine how many lookups to expect before the next build. Stores support int, float, bool, string and color values. Use one IntIndex for several payload fields, or IntBuckets when a key owns several integers.
In practice: Batch-build IDs to scores, states or metadata, then query them without committing to a representation. Direct integer addressing measured 21% faster than a map, while a map measured 91% faster than repeated linear lookup with 32 entries.
🔸 INTBUCKETS: ONE KEY TO MANY INTEGER VALUES
A Store returns one value for each key. IntBuckets returns a group of integers, usually object IDs or physical slots. Repeating a key adds another member instead of replacing the previous one.
var op.IntBuckets cellMembers = op.intBuckets()
var array matches = array.new()
if barstate.isfirst
// Six (cell, object slot) pairs. Cell 7 appears three times.
array cellKeys = array.from(7, 2, 7, 5, 2, 7)
array objectSlots = array.from(101, 205, 412, 990, 777, 888)
cellMembers.buildFromPairs(cellKeys, objectSlots)
// Read cell 7's group from flat storage. matches is only demo output.
= cellMembers.rangeByKey(7)
if count > 0
for position = start to start + count - 1
matches.push(cellMembers.valueAt(position))
// Output: matches contains , the object slots assigned to cell 7.
What happens: Each key is paired with the slot at the same array position. Cell 7 appears three times, so its group contains 101, 412 and 888. rangeByKey() returns where that group starts and how many values it contains. A missing key returns a count of 0.
Lifecycle: buildFromPairs() replaces all previous groups. Use buildBegin() , add() and buildFinish() only when pairs arrive one at a time.
In practice: A price cell can own several zone slots, a graph node can own several neighbors, or a category can own several record IDs. One query visits only that group.
Why use it: A native map stores one value per key, and Pine does not allow an array directly as that value. Giving one key several values therefore requires a small wrapper UDT containing an array. IntBuckets provides that relationship directly, packing every group into shared contiguous storage. It suits batch rebuilds followed by repeated traversal, while the wrapper approach is more convenient when individual groups change constantly. In the tested 64-key traversal workload, IntBuckets averaged 19% faster across four runs.
----------------------------------------------------------------------------------------------------------------
🔷 REUSE STATE INSTEAD OF REBUILDING IT
IntDoubleBuffer and FloatDoubleBuffer retain current and previous arrays. swap() exchanges their references in O(1), preserves the old result and clears the new current buffer for reuse. That clear still costs O(N).
This is useful when one pass must remain readable while the next is built. In this small search, node n has children 2n and 2n + 1 . Each pass reads the active level and writes the next one:
var op.IntDoubleBuffer searchFrontier = op.intDoubleBuffer()
if barstate.isfirst
searchFrontier.current.push(1)
for depth = 1 to 3
= searchFrontier.swap()
for nodeId in activeFrontier
nextFrontier.push(nodeId * 2)
nextFrontier.push(nodeId * 2 + 1)
// Output: current contains .
// previous contains .
What happens: swap() makes the completed level available as activeFrontier and returns the other retained array, already empty, as nextFrontier . No level is copied and no replacement array is created. The same pattern supports graph searches, flood fills, iterative clustering and simulations. Use swapSized() when every pass needs a fixed-size output.
A var array can also be reused. The ensureSize*() , resize*() and refill*() families modify existing storage, while sameExact*() compares primitive arrays without Pine's float-comparison rounding.
Revision handles caller-owned state that OptiPine cannot observe. The producer calls bump() after a change; each consumer compares its own saved token with changedSince() instead of keeping a snapshot.
----------------------------------------------------------------------------------------------------------------
🔷 WEIGHTED SELECTION FOR STATIC AND DYNAMIC SYSTEMS
Weighted selection chooses entries in proportion to their weights. It is useful in simulations, randomized search and priority sampling.
WeightedSampler is the high-level interface. Set weights, then supply a fraction to select a slot. The sampler does not generate randomness; use math.random() or a repeatable fraction sequence:
var op.WeightedSampler sampler = op.weightedSampler(512)
if barstate.isfirst
sampler.setWeight(10, 0.25)
sampler.setWeight(11, 0.80)
sampler.setWeight(12, 0.10)
float fraction = 0.50
int selected = sampler.sample(fraction)
// Output: selected is 11 for the supplied fraction of 0.50.
The default cumulative prefix suits stable weights. Pass op.weightConfigSparseUpdates() and the sampler can move to an update-friendly Fenwick tree as the workload changes. sample() stays the same. Use WeightedIndex for circular ranges or explicit policy control.
In practice: Each slot can represent a candidate model, simulation outcome or work item. Update its weight when its score changes, then sample repeatedly through the same interface.
----------------------------------------------------------------------------------------------------------------
🔷 THREE LEVELS OF CONTROL
OptiPine is layered so high-level code describes the problem rather than the mechanism. Start with Tier 1 and move deeper only when the workload requires more control:
Tier 1, Quick: Ready-to-use APIs with automatic defaults, including Watch, Memo, CadenceGate, typed stores, StablePool, DirtySet, row rings, double buffers and WeightedSampler.
Tier 2, Composable: Explicit lifecycles, configuration and representation policies through IntIndex, IntBuckets, SlotCache, RingCursor and Revision.
Tier 3, Expert: Physical addressing, unchecked operations and scoped raw mutation for measured hot paths. Ordinary read-only views are not Tier 3.
Editor warnings: Methods such as get() , set() , push() and clear() intentionally match Pine's collection vocabulary. Any shadowing-method warning is cosmetic; the receiver's type determines which method runs.
----------------------------------------------------------------------------------------------------------------
🔷 COMPLETE, COPY-PASTE EXAMPLES
The fragments above isolate one idea at a time. These two copy-paste indicators combine them in practical workflows, using native Pine where it is simpler and OptiPine where it removes real work.
🔸 Complete example 1: high-level cached stress model
What it does: The indicator plots a probability-weighted downside estimate for the current trend and volatility regime, while exposing both regime values in the Data Window.
The EMA and ATR calculations run normally on every bar. Their rounded regimes change less often, so Memo recalculates the 401-scenario model only when one of those regimes changes and serves the cached result between changes.
//@version=6
indicator("OptiPine - Cached Regime Stress", overlay = false)
import Alien_Algorithms/OptiPine/1 as op
// Test 401 possible moves, giving more weight to common moves.
// This function is pure: its result depends only on its inputs.
estimateDownside(float trendInAtr, float atrPercent) =>
float result = na
if not na(trendInAtr) and not na(atrPercent) and atrPercent > 0
float weightedDownside = 0.0
float totalWeight = 0.0
for scenario = -200 to 200
float standardShock = scenario / 40.0
float weight = math.exp(-0.5 * standardShock * standardShock)
float projectedMove = (trendInAtr + standardShock) * atrPercent
float downside = math.max(-projectedMove, 0.0)
weightedDownside += downside * weight
totalWeight += weight
result := totalWeight > 0 ? weightedDownside / totalWeight : na
result
// Stateful Pine calculations stay outside the Memo guard.
float ema20 = ta.ema(close, 20)
float ema50 = ta.ema(close, 50)
float atr14 = ta.atr(14)
float trendInAtr = atr14 > 0 ? (ema20 - ema50) / atr14 : na
float atrPercent = close > 0 ? atr14 / close * 100.0 : na
// Quantization makes the dependencies describe a regime, not every tick.
float trendRegime = math.round(
math.max(-3.0, math.min(3.0, trendInAtr)) * 10.0) / 10.0
float volatilityRegime = math.round(atrPercent * 4.0) / 4.0
var op.FloatMemo downsideStress = op.floatMemo()
if downsideStress.staleOn(trendRegime, volatilityRegime)
downsideStress.store(
estimateDownside(trendRegime, volatilityRegime))
float stress = downsideStress.get()
plot(stress, "Expected downside (%)", color.orange, linewidth = 2)
plot(trendRegime, "Trend regime (ATR units)", display = display.data_window)
plot(volatilityRegime, "Volatility regime (%)", display = display.data_window)
🔸 Complete example 2: advanced zone-cluster engine
What it does: The indicator draws recent pivot levels, thickens those near the current price, plots the strongest price cluster and reports its key statistics in the Data Window.
StablePool preserves drawing slots, the ring tracks retirement order, DirtySet queues redraws, IntBuckets forms price clusters and IntFloatStore looks up their strength.
Relevant benchmarks: These are component results, not a total for this 32-zone indicator. In larger matching workloads, DirtySet saved 93% at 1% dirty and IntBuckets averaged 19% with 64 keys. For typed lookup, direct addressing saved 21% over a map on compact keys, while a map saved 91% over linear search at 32 entries. Automatic mode selects the representation.
StablePool and the ring manage recycling. The script still scans live zones for proximity changes, then DirtySet avoids unnecessary drawing updates.
//@version=6
indicator("OptiPine - Zone Cluster Engine", overlay = true, max_lines_count = 100)
import Alien_Algorithms/OptiPine/1 as op
int pivotLength = input.int(5, "Pivot length", minval = 1)
int maxZones = input.int(32, "Maximum zones", minval = 4, maxval = 100)
int bucketTicks = input.int(25, "Cluster size in ticks", minval = 1)
float bucketSize = syminfo.mintick * bucketTicks
// Stateful Pine calculations remain outside every conditional rebuild.
float pivotHigh = ta.pivothigh(high, pivotLength, pivotLength)
float pivotLow = ta.pivotlow(low, pivotLength, pivotLength)
float pivotStrength = math.max(nz(volume , 1.0), 1.0)
float highlightDistance = ta.atr(14)
var op.StablePool zones = op.stablePool()
var op.IntRowRing zoneOrder = op.intRowRing(maxZones, 1)
var op.DirtySet dirtyZones = op.dirtySet(maxZones)
var array zonePrices = array.new(maxZones, na)
var array zoneStrengths = array.new(maxZones, 0.0)
var array zoneTimes = array.new(maxZones, na)
var array resistance = array.new(maxZones, false)
var array highlighted = array.new(maxZones, false)
var array zoneLines = array.new(maxZones)
var op.IntBuckets zonesByBucket = op.intBuckets()
var op.IntFloatStore strengthByBucket = op.intFloatStore()
// Retained build storage is resized and overwritten, never cleared and repopulated.
var array bucketKeyByPosition = array.new()
var array aggregateKeys = array.new()
var array aggregateStrengths = array.new()
var int strongestBucketKey = na
var float strongestBucketStrength = na
var int strongestZoneCount = 0
dirtyZones.begin()
bool topologyChanged = barstate.isfirst
// Logical pivot IDs receive stable, reusable physical drawing slots.
for event = 0 to 1
float level = event == 0 ? pivotHigh : pivotLow
if barstate.isconfirmed and not na(level)
int pivotBar = bar_index - pivotLength
int pivotTime = time
int zoneId = pivotBar * 2 + event
int slot = zones.find(zoneId)
// Only a new logical pivot enters the retirement queue.
if slot < 0
if zoneOrder.rowCount() == maxZones
int oldestId = zoneOrder.at(0, 0)
zones.release(oldestId)
= zones.acquire(zoneId)
slot := newSlot
zoneOrder.pushValue(zoneId)
zonePrices.set(slot, level)
zoneStrengths.set(slot, pivotStrength)
zoneTimes.set(slot, pivotTime)
resistance.set(slot, event == 0)
highlighted.set(slot, false)
dirtyZones.mark(slot)
topologyChanged := true
// Proximity can mark a newly created slot again; DirtySet still stores it once.
for slot in zones.slots()
bool isHighlighted = math.abs(close - zonePrices.get(slot)) <= highlightDistance
if isHighlighted != highlighted.get(slot)
highlighted.set(slot, isHighlighted)
dirtyZones.mark(slot)
// Only changed drawings cross the line API boundary.
for slot in dirtyZones.values()
float level = zonePrices.get(slot)
color baseColor = resistance.get(slot) ? color.red : color.lime
line zoneLine = zoneLines.get(slot)
if na(zoneLine)
zoneLine := line.new(zoneTimes.get(slot), level, time, level,
xloc = xloc.bar_time)
zoneLines.set(slot, zoneLine)
line.set_xy1(zoneLine, zoneTimes.get(slot), level)
line.set_xy2(zoneLine, time, level)
line.set_extend(zoneLine, extend.right)
line.set_width(zoneLine, highlighted.get(slot) ? 3 : 1)
line.set_color(zoneLine,
color.new(baseColor, highlighted.get(slot) ? 0 : 55))
// Rebuild grouped lookup only after the explicit creation event.
if topologyChanged
array liveSlots = zones.slots()
int liveCount = liveSlots.size()
op.resizeInt(bucketKeyByPosition, liveCount, 0)
if liveCount > 0
for position = 0 to liveCount - 1
int slot = liveSlots.get(position)
int bucketKey = int(math.round(zonePrices.get(slot) / bucketSize))
bucketKeyByPosition.set(position, bucketKey)
// Repeated bucket keys accumulate several physical zone slots.
zonesByBucket.buildFromPairs(bucketKeyByPosition, liveSlots)
int bucketCount = zonesByBucket.bucketCount()
op.resizeInt(aggregateKeys, bucketCount, 0)
op.resizeFloat(aggregateStrengths, bucketCount, 0.0)
strongestBucketKey := na
strongestBucketStrength := na
strongestZoneCount := 0
if bucketCount > 0
for bucketSlot = 0 to bucketCount - 1
int bucketKey = zonesByBucket.keyAt(bucketSlot)
= zonesByBucket.rangeBySlot(bucketSlot)
float totalStrength = 0.0
if count > 0
for position = start to start + count - 1
int zoneSlot = zonesByBucket.valueAt(position)
totalStrength += zoneStrengths.get(zoneSlot)
aggregateKeys.set(bucketSlot, bucketKey)
aggregateStrengths.set(bucketSlot, totalStrength)
if na(strongestBucketStrength) or totalStrength > strongestBucketStrength
strongestBucketKey := bucketKey
strongestBucketStrength := totalStrength
strongestZoneCount := count
strengthByBucket.build(aggregateKeys, aggregateStrengths)
// Query the current price cluster directly and display the strongest cluster.
int currentBucketKey = int(math.round(close / bucketSize))
float nearbyStrength = strengthByBucket.get(currentBucketKey, 0.0)
float strongestClusterPrice = na(strongestBucketKey) ?
na : strongestBucketKey * bucketSize
plot(strongestClusterPrice, "Strongest zone cluster", color.orange,
linewidth = 2, style = plot.style_stepline)
plot(nearbyStrength, "Strength near current price", display = display.data_window)
plot(strongestBucketStrength, "Strongest cluster strength",
display = display.data_window)
plot(strongestZoneCount, "Zones in strongest cluster",
display = display.data_window)
plot(dirtyZones.size(), "Drawings updated", display = display.data_window)
----------------------------------------------------------------------------------------------------------------
🔷 API REFERENCE
This is a compact index of the main public entry points.
🔸 Watch and Memo: changed(source) handles one scalar, primitive array or row ring. For several dependencies, use begin() , watch*() and finish() . Typed Memos add staleOn() , store() , get() and invalidate() .
🔸 Revision and Cadence: revision() exposes bump() , current() and changedSince() for manual change tracking. cadenceGate() provides due() and change-aware dueWhenChanged() scheduling.
🔸 Row Rings: floatRowRing() and intRowRing() provide push() , width-one pushValue() , at() , setAt() , newestAt() , chronological() and gather() .
🔸 RingCursor: Circular addressing for caller-owned arrays. Use reserve() to advance, physical() and logical() to translate positions, and newest() or oldest() to locate retained rows.
🔸 StablePool: acquire() and release() manage stable key-to-slot assignments. Lookup and traversal use find() , contains() , keyAt() , slots() and size() . Recycled slots retain their caller-owned payload until overwritten.
🔸 SlotCache: Frame-based stable allocation follows begin() , acquire() , finish() . active() , retired() and size() expose its state.
🔸 DirtySet: begin() starts a cycle; mark() , markMany() and markRange() add entries. Read the distinct work list with values() and size() .
🔸 Typed Stores: intIntStore() , intFloatStore() , intBoolStore() , intStringStore() and intColorStore() map integer keys to primitive values. Build with build() , then use get() , set() , contains() or getMany() .
🔸 IntIndex: A shared integer key-to-slot directory for custom payloads and explicit lookup policy. Build with buildBegin() , add() or addMany() and buildFinish() ; query with find() , keyAt() and findMany() . IndexConfig controls representation and duplicate policy.
🔸 IntBuckets: A one-key-to-many-integers index. Build directly with buildFromPairs() , or incrementally with buildBegin() , add() or addMany() and buildFinish() . Read groups with rangeByKey() and valueAt() .
🔸 Double Buffers: intDoubleBuffer() and floatDoubleBuffer() retain current and previous arrays. swap() exchanges them; swapSized() also sizes and refills the new current buffer.
🔸 Weighted Sampling: weightedSampler() provides weight updates, sample() , sampleMany() , probability() and total() . It maps caller-supplied fractions; it does not generate randomness. weightedIndex() adds circular ranges and explicit policy control.
🔸 Storage Utilities: ensureSize*() , resize*() , refill*() and sameExact*() handle primitive arrays. Other helpers cover flat/matrix conversion, transposition and bulk ring reads.
----------------------------------------------------------------------------------------------------------------
🔷 WHY OPTIPINE EXISTS
Pine's limits are real, but standard architecture often reaches them long before the idea itself has to. Repeating unchanged calculations, shifting rolling storage, scanning mostly untouched collections and rebuilding state all consume the same execution budget the feature needs to exist.
OptiPine reclaims that budget. Expensive models can run only when their inputs change. Large dashboards can refresh only what moved. Dynamic object systems can grow and recycle storage without reorganizing everything around them. The APIs stay approachable, while the architecture underneath is built for workloads that would normally force a Pine project to scale back.
At large scale, optimization is no longer simply about feature speed. It is the factor that dictates whether an ambitious idea can ship at all.
----------------------------------------------------------------------------------------------------------------
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
The publication diagram has been rendered natively by Pine3D .
Bibliothèque

ChatgptLibraryLibrary "ChatgptLibrary"
TODO: add library description here
effective_period(high_series, low_series, volume_series, period_length, lookback_length, max_search)
Calculates adaptive effective period.
Parameters:
high_series (float) : High price series.
low_series (float) : Low price series.
volume_series (float) : Volume series.
period_length (simple int) : Base period.
lookback_length (simple int) : EMA lookback multiplier.
max_search (int) : Maximum search distance.
Returns: Adaptive effective period.
adaptive_ema(source, high_series, low_series, volume_series, period_length, lookback_length, max_search)
Adaptive EMA using effective period.
Parameters:
source (float) : Source series.
high_series (float) : High price series.
low_series (float) : Low price series.
volume_series (float) : Volume series.
period_length (simple int) : Base period.
lookback_length (simple int) : EMA lookback multiplier.
max_search (int) : Maximum search distance.
Returns: Adaptive EMA, alpha and effective period.
adaptive_channel(high_series, low_series, volume_series, period_length, lookback_length, smooth_length, max_search)
Adaptive price channel.
Parameters:
high_series (float) : High price series.
low_series (float) : Low price series.
volume_series (float) : Volume series.
period_length (simple int) : Base period.
lookback_length (simple int) : EMA lookback multiplier.
smooth_length (simple int) : EMA smoothing.
max_search (int) : Maximum search distance.
Returns: Effective period, upper, lower, middle and width.
adaptive_rsi(source, high_series, low_series, volume_series, period_length, lookback_length, max_search)
Adaptive RSI.
Parameters:
source (float) : Source series.
high_series (float) : High price series.
low_series (float) : Low price series.
volume_series (float) : Volume series.
period_length (simple int) : Base period.
lookback_length (simple int) : EMA lookback multiplier.
max_search (int) : Maximum search distance.
Returns: Adaptive RSI and effective period.
adaptive_atr(high_series, low_series, close_series, volume_series, period_length, lookback_length, max_search)
Adaptive ATR.
Parameters:
high_series (float) : High price series.
low_series (float) : Low price series.
close_series (float) : Close price series.
volume_series (float) : Volume series.
period_length (simple int) : Base period.
lookback_length (simple int) : EMA lookback multiplier.
max_search (int) : Maximum search distance.
Returns: Adaptive ATR and effective period.
adaptive_macd(source, high_series, low_series, volume_series, fast_period, slow_period, signal_period, lookback_length, max_search)
Adaptive MACD.
Parameters:
source (float) : Source series.
high_series (float) : High price series.
low_series (float) : Low price series.
volume_series (float) : Volume series.
fast_period (simple int) : Fast adaptive period.
slow_period (simple int) : Slow adaptive period.
signal_period (int) : Signal EMA period.
lookback_length (simple int) : EMA lookback multiplier.
max_search (int) : Maximum search distance.
Returns: MACD, Signal, Histogram.
adaptive_bollinger(source, high_series, low_series, volume_series, period_length, deviation, lookback_length, max_search)
Adaptive Bollinger Bands.
Parameters:
source (float) : Source series.
high_series (float) : High price series.
low_series (float) : Low price series.
volume_series (float) : Volume series.
period_length (simple int) : Base period.
deviation (float) : Standard deviation multiplier.
lookback_length (simple int) : EMA lookback multiplier.
max_search (int) : Maximum search distance.
Returns: Upper band, Middle band, Lower band, Band width and Effective period.
adaptive_supertrend(high_series, low_series, close_series, volume_series, period_length, multiplier, lookback_length, max_search)
Adaptive SuperTrend.
Parameters:
high_series (float) : High price series.
low_series (float) : Low price series.
close_series (float) : Close price series.
volume_series (float) : Volume series.
period_length (simple int) : Base period.
multiplier (float) : ATR multiplier.
lookback_length (simple int) : EMA lookback multiplier.
max_search (int) : Maximum search distance.
Returns: SuperTrend, Trend Direction and Effective Period.
adaptive_donchian(high_series, low_series, volume_series, period_length, lookback_length, max_search)
Adaptive Donchian Channel.
Parameters:
high_series (float) : High price series.
low_series (float) : Low price series.
volume_series (float) : Volume series.
period_length (simple int) : Base period.
lookback_length (simple int) : EMA lookback multiplier.
max_search (int) : Maximum search distance.
Returns: Upper band, Lower band, Middle line, Width and Effective period.
adaptive_keltner(source, high_series, low_series, close_series, volume_series, period_length, multiplier, lookback_length, max_search)
Adaptive Keltner Channel.
Parameters:
source (float) : Source series.
high_series (float) : High price series.
low_series (float) : Low price series.
close_series (float) : Close price series.
volume_series (float) : Volume series.
period_length (simple int) : Base period.
multiplier (float) : ATR multiplier.
lookback_length (simple int) : EMA lookback multiplier.
max_search (int) : Maximum search distance.
Returns: Upper band, Middle band, Lower band, Width and Effective period.
adaptive_adx(high_series, low_series, close_series, volume_series, period_length, lookback_length, max_search)
Adaptive ADX.
Parameters:
high_series (float) : High price series.
low_series (float) : Low price series.
close_series (float) : Close price series.
volume_series (float) : Volume series.
period_length (simple int) : Base period.
lookback_length (simple int) : EMA lookback multiplier.
max_search (int) : Maximum search distance.
Returns: ADX, +DI, -DI and Effective Period.
adaptive_stochastic(close_series, high_series, low_series, volume_series, period_length, smooth_k, smooth_d, lookback_length, max_search)
Adaptive Stochastic.
Parameters:
close_series (float) : Close price series.
high_series (float) : High price series.
low_series (float) : Low price series.
volume_series (float) : Volume series.
period_length (simple int) : Base period.
smooth_k (int) : K smoothing.
smooth_d (int) : D smoothing.
lookback_length (simple int) : EMA lookback multiplier.
max_search (int) : Maximum search distance.
Returns: K, D and Effective Period.
adaptive_cci(high_series, low_series, close_series, volume_series, period_length, lookback_length, max_search)
Adaptive Commodity Channel Index.
Parameters:
high_series (float) : High price series.
low_series (float) : Low price series.
close_series (float) : Close price series.
volume_series (float) : Volume series.
period_length (simple int) : Base period.
lookback_length (simple int) : EMA lookback multiplier.
max_search (int) : Maximum search distance.
Returns: CCI and Effective Period.
adaptive_williams_r(high_series, low_series, close_series, volume_series, period_length, lookback_length, max_search)
Adaptive Williams %R.
Parameters:
high_series (float) : High price series.
low_series (float) : Low price series.
close_series (float) : Close price series.
volume_series (float) : Volume series.
period_length (simple int) : Base period.
lookback_length (simple int) : EMA lookback multiplier.
max_search (int) : Maximum search distance.
Returns: Williams %R and Effective Period.
adaptive_roc(source, high_series, low_series, volume_series, period_length, lookback_length, max_search)
Adaptive Rate of Change.
Parameters:
source (float) : Source series.
high_series (float) : High price series.
low_series (float) : Low price series.
volume_series (float) : Volume series.
period_length (simple int) : Base period.
lookback_length (simple int) : EMA lookback multiplier.
max_search (int) : Maximum search distance.
Returns: ROC and Effective Period.
adaptive_pivot(source, left_bars, right_bars)
Adaptive Pivot Detector.
Parameters:
source (float) : Source series.
left_bars (int) : Left pivot bars.
right_bars (int) : Right pivot bars.
Returns: Pivot High, Pivot Low, Pivot High Price, Pivot Low Price.
adaptive_divergence(price_source, indicator_source, pivot_length)
Adaptive Divergence Detector.
Parameters:
price_source (float) : Price series.
indicator_source (float) : Indicator series.
pivot_length (int) : Pivot length.
Returns: Bullish divergence, Bearish divergence and Divergence strength.
adaptive_pivot_divergence(price_source, signal_source, pivot_length)
Adaptive Pivot Divergence Detector.
Parameters:
price_source (float) : Price series.
signal_source (float) : Indicator series.
pivot_length (int) : Pivot length.
Returns: Bullish divergence, Bearish divergence and Divergence strength.
adaptive_flat_channel(upper_channel, lower_channel, flat_length, tolerance)
Adaptive Flat Channel Detector.
Parameters:
upper_channel (float) : Upper channel.
lower_channel (float) : Lower channel.
flat_length (int) : Number of bars to evaluate.
tolerance (float) : Maximum allowed movement.
Returns: Flat upper, Flat lower and Flat channel.
adaptive_breakout_strength(close_series, upper_channel, lower_channel, channel_width, volume_series, volume_length)
Adaptive Breakout Strength.
Parameters:
close_series (float) : Close price.
upper_channel (float) : Upper channel.
lower_channel (float) : Lower channel.
channel_width (float) : Channel width.
volume_series (float) : Volume.
volume_length (simple int) : Volume EMA length.
Returns: Breakout direction and Breakout strength.
adaptive_channel_rejection(open_series, high_series, low_series, close_series, upper_channel, lower_channel)
Adaptive Channel Rejection.
Parameters:
open_series (float) : Open price.
high_series (float) : High price.
low_series (float) : Low price.
close_series (float) : Close price.
upper_channel (float) : Upper channel.
lower_channel (float) : Lower channel.
Returns: Rejection direction and Rejection strength.
adaptive_channel_compression(channel_width, compression_length)
Adaptive Channel Compression.
Parameters:
channel_width (float) : Width of the channel.
compression_length (simple int) : Number of bars.
Returns: Compression ratio, Is compressing, Is expanding.
adaptive_market_energy(channel_width, volume_series, volume_length)
Adaptive Market Energy.
Parameters:
channel_width (float) : Width of channel.
volume_series (float) : Volume series.
volume_length (simple int) : Volume EMA length.
Returns: Energy score.
adaptive_market_phase(adx, rsi, compression_ratio, breakout_strength)
Adaptive Market Phase.
Parameters:
adx (float) : Adaptive ADX.
rsi (float) : Adaptive RSI.
compression_ratio (float) : Channel compression ratio.
breakout_strength (float) : Breakout strength.
Returns: Market phase.
adaptive_rsi_zigzag(rsi_series, center_level, lookback_length)
Adaptive RSI Zigzag Detector.
Parameters:
rsi_series (float) : RSI series.
center_level (float) : Center level.
lookback_length (int) : Number of bars.
Returns: Zigzag count and Zigzag detected.
adaptive_flat_level(level_series, flat_length, tolerance)
Adaptive Flat Level Detector.
Parameters:
level_series (float) : Channel upper or lower series.
flat_length (int) : Number of bars.
tolerance (float) : Maximum allowed movement.
Returns: Flat state and Flat strength.
adaptive_level_strength(level_series, high_series, low_series, tolerance, lookback_length)
Adaptive Level Strength.
Parameters:
level_series (float) : Support or resistance level.
high_series (float) : High price series.
low_series (float) : Low price series.
tolerance (float) : Touch tolerance.
lookback_length (int) : Number of bars.
Returns: Touch count and Level strength.
adaptive_breakout_probability(breakout_strength, level_strength, compression_ratio, volume_ratio)
Adaptive Breakout Probability.
Parameters:
breakout_strength (float) : Breakout strength.
level_strength (float) : Level strength.
compression_ratio (float) : Channel compression ratio.
volume_ratio (float) : Volume ratio.
Returns: Breakout probability.
adaptive_reversal_probability(rsi, divergence_strength, rejection_strength, flat_strength, channel_width_percent)
Adaptive Reversal Probability.
Parameters:
rsi (float) : Relative Strength Index.
divergence_strength (float) : Divergence strength.
rejection_strength (float) : Rejection strength.
flat_strength (float) : Flat level strength.
channel_width_percent (float) : Channel width percentage.
Returns: Reversal probability.
adaptive_trend_exhaustion(rsi, adx, momentum, roc)
Adaptive Trend Exhaustion.
Parameters:
rsi (float) : Relative Strength Index.
adx (float) : Average Directional Index.
momentum (float) : Momentum.
roc (float) : Rate of Change.
Returns: Trend exhaustion score.
adaptive_channel_memory(upper_channel, lower_channel, tolerance, lookback_length)
Adaptive Channel Memory.
Parameters:
upper_channel (float) : Upper channel.
lower_channel (float) : Lower channel.
tolerance (float) : Maximum channel difference.
lookback_length (int) : Number of bars.
Returns: Memory score.
adaptive_false_breakout(breakout_strength, rejection_strength, volume_ratio)
Adaptive False Breakout Detector.
Parameters:
breakout_strength (float) : Breakout strength.
rejection_strength (float) : Rejection strength.
volume_ratio (float) : Current volume divided by average volume.
Returns: False breakout probability.
adaptive_trap_detector(breakout_direction, breakout_strength, rejection_strength, rsi)
Adaptive Trap Detector.
Parameters:
breakout_direction (int) : Breakout direction.
breakout_strength (float) : Breakout strength.
rejection_strength (float) : Rejection strength.
rsi (float) : Relative Strength Index.
Returns: Trap direction and Trap probability.
adaptive_rsi_behavior(rsi, zigzag_count, divergence_strength, rejection_strength)
Adaptive RSI Behavior.
Parameters:
rsi (float) : Relative Strength Index.
zigzag_count (int) : RSI zigzag count.
divergence_strength (float) : Divergence strength.
rejection_strength (float) : Rejection strength.
Returns: RSI behavior score.
adaptive_market_behavior(trend_strength, reversal_probability, breakout_probability, exhaustion, energy, rsi_behavior)
Adaptive Market Behavior.
Parameters:
trend_strength (float) : Trend strength.
reversal_probability (float) : Reversal probability.
breakout_probability (float) : Breakout probability.
exhaustion (float) : Trend exhaustion.
energy (float) : Market energy.
rsi_behavior (float) : RSI behavior.
Returns: Market behavior score. Bibliothèque

Bibliothèque

Arbor_Gradient_Boosting_GainzAlgoGainzAlgo is excited to bring the ability to perform gradient boosting and feature importance selection to Pine Script. Currently, there are no native capabilities within Pine Script for gradient boosting or feature importance selection. Arbor fills this significant gap by introducing a from-scratch Gradient Boosting Machine (GBM) engineered with XGBoost-style mechanics.
Designed to support both classification and regression tasks, and building on our Random Forest approach to Pinescript, Arbor utilizes depth-1 stumps, meaning it performs one split per round without column subsampling.
Because TradingView automatically lists the exported types and function parameters, the following outlines the core mechanics and capabilities you unlock by importing Arbor.
Core Mechanics
Arbor brings advanced machine-learning concepts directly into your Pine Script workflows:Advanced Training: Utilizes Newton leaf steps (second-order hessian weighting) and the exact XGBoost gain formula.
Regularization & Pruning: Integrates L2 regularization (lambda), minimum gain pruning (gamma), and minimum child weight checks to manage model complexity and prevent overfitting.
Stochasticity: Implements Fisher-Yates row subsampling to provide genuine round-to-round stochasticity matching XGBoost's subsample behavior.
Reproducibility: You can pass an optional seed to any fit function to ensure reproducible training runs across reloads.
Model Tiers
The library supports models scaled across three specific feature tiers:
GBM (1 Feature): Built for rapid classification or regression implementations.
GBM3 (3 Features): Purpose-built specifically for classification tasks.
GBM4 (4 Features): Supports both classification and regression, and uniquely offers XGBoost-style, gain-based feature importance evaluation.
Library "Arbor_Gradient_Boosting_GainzAlgo"
Arbor — gradient boosting for Pine Script. From-scratch GBM v2
with XGBoost-style mechanics: Fisher-Yates row subsampling, Newton leaf steps
(second-order hessian weighting), exact XGBoost gain formula with L2
regularization (lambda), minimum gain pruning (gamma), and minimum child
weight. Trees are depth-1 stumps (one split per round) and there is no
column (feature) subsampling — this is an XGBoost-style boosting scheme,
not a full XGBoost reimplementation. Supports classification and regression
across three feature tiers:
- GBM (1 feature) : gbm_fit / gbm_predict
classification or regression via is_classifier
- GBM3 (3 features) : gbm3_fit / gbm3_predict
classification only
- GBM4 (4 features) : gbm4_fit / gbm4_predict / gbm4_importance_pct
classification or regression with XGBoost-style
gain-based feature importance
All variants use Newton leaf steps, exact gain formula, L2 regularization,
Fisher-Yates shuffle subsampling, and gamma/min_child_weight pruning. Pass
an optional seed to any fit function for reproducible training runs.
gbm_fit(feat, target, n_rounds, lr, n_thresh, is_classifier, lambda, gamma, min_child_w, subsample, seed)
Fits a single-feature gradient-boosted stump ensemble using
XGBoost-style mechanics: Newton leaf steps (second-order hessian weighting),
exact gain formula with L2 regularization, gamma pruning, minimum child
weight, and Fisher-Yates row subsampling. Each round fits one depth-1 stump
(this is not a full multi-level tree, and there is no column subsampling).
Supports both binary classification (log-odds + sigmoid) and regression (MSE).
Parameters:
feat (array) : Array of feature values, one per training row
target (array) : Array of targets — 0.0/1.0 for classification, continuous for regression
n_rounds (int) : Number of boosting rounds / stumps to fit
lr (float) : Learning rate / shrinkage applied to each round's leaf contribution
n_thresh (int) : Candidate split thresholds to scan per round
is_classifier (bool) : True = binary classification, False = squared-error regression
lambda (float) : L2 leaf regularization — Ridge-style shrinkage toward zero (XGBoost default: 1.0)
gamma (float) : Minimum gain required to accept a split — prunes weak splits (XGBoost default: 0.0)
min_child_w (float) : Minimum hessian sum per child node — prevents tiny noisy splits (XGBoost default: 1.0)
subsample (float) : Fraction of rows randomly sampled per round via Fisher-Yates (default: 1.0 = all rows)
seed (int) : Optional seed for the row-subsampling shuffle — pass a fixed value for reproducible fits across reloads (default: na = unseeded/random each time)
Returns: Fitted GBM object ready for gbm_predict()
gbm_predict(model, x)
Scores a single feature value against a fitted GBM ensemble.
Parameters:
model (GBM) : A GBM object previously returned by gbm_fit()
x (float) : Feature value to score (same feature definition used in training)
Returns: Predicted probability if classifier, raw predicted value if regressor
gbm3_fit(feat1, feat2, feat3, target, n_rounds, lr, n_thresh, lambda, gamma, min_child_w, subsample, seed)
Fits a 3-feature gradient-boosted classifier using XGBoost-style
mechanics: Newton leaf steps, exact gain formula, L2 regularization, gamma
pruning, minimum child weight, and Fisher-Yates row subsampling. Selects the
best (feature, threshold) pair each round and boosts in log-odds space.
Each round fits a single depth-1 stump; there is no column subsampling.
Parameters:
feat1 (array) : Array of feature 1 values, one per training row
feat2 (array) : Array of feature 2 values, one per training row
feat3 (array) : Array of feature 3 values, one per training row
target (array) : Array of binary targets (0.0 or 1.0), one per training row
n_rounds (int) : Number of boosting rounds
lr (float) : Learning rate / shrinkage
n_thresh (int) : Candidate thresholds scanned per feature per round
lambda (float) : L2 leaf regularization (Ridge shrinkage, XGBoost default: 1.0)
gamma (float) : Minimum gain to accept a split (XGBoost default: 0.0)
min_child_w (float) : Minimum hessian sum per child node (XGBoost default: 1.0)
subsample (float) : Row sampling fraction per round via Fisher-Yates (default: 1.0)
seed (int) : Optional seed for the row-subsampling shuffle — pass a fixed value for reproducible fits across reloads (default: na = unseeded/random each time)
Returns: Fitted GBM3 object ready for gbm3_predict()
gbm3_predict(model, x1, x2, x3)
Scores 3 feature values against a fitted GBM3 classifier.
Parameters:
model (GBM3) : GBM3 object from gbm3_fit()
x1 (float) : Current value of feature 1
x2 (float) : Current value of feature 2
x3 (float) : Current value of feature 3
Returns: Predicted probability
gbm4_fit(feat1, feat2, feat3, feat4, target, n_rounds, lr, n_thresh, is_classifier, lambda, gamma, min_child_w, subsample, seed)
Fits a 4-feature gradient-boosted ensemble with Newton steps, exact gain
formula, L2 regularization, gamma pruning, minimum child weight, Fisher-Yates
row subsampling, and gain-based feature importance tracking.
Supports both binary classification and regression. Each round fits a single
depth-1 stump; there is no column subsampling.
Parameters:
feat1 (array) : Array of feature 1 values, one per training row
feat2 (array) : Array of feature 2 values, one per training row
feat3 (array) : Array of feature 3 values, one per training row
feat4 (array) : Array of feature 4 values, one per training row
target (array) : Array of targets — 0.0/1.0 for classification, continuous for regression
n_rounds (int) : Number of boosting rounds
lr (float) : Learning rate / shrinkage
n_thresh (int) : Candidate thresholds scanned per feature per round
is_classifier (bool) : True = binary classification, False = regression
lambda (float) : L2 leaf regularization (Ridge shrinkage, XGBoost default: 1.0)
gamma (float) : Minimum gain to accept a split (XGBoost default: 0.0)
min_child_w (float) : Minimum hessian sum per child node (XGBoost default: 1.0)
subsample (float) : Row sampling fraction per round via Fisher-Yates (default: 1.0)
seed (int) : Optional seed for the row-subsampling shuffle — pass a fixed value for reproducible fits across reloads (default: na = unseeded/random each time)
Returns: Fitted GBM4 object with importance scores, ready for gbm4_predict() / gbm4_importance_pct()
gbm4_predict(model, x1, x2, x3, x4)
Scores 4 feature values against a fitted GBM4 ensemble.
Parameters:
model (GBM4) : GBM4 object from gbm4_fit()
x1 (float) : Current value of feature 1
x2 (float) : Current value of feature 2
x3 (float) : Current value of feature 3
x4 (float) : Current value of feature 4
Returns: Predicted probability if classifier, raw predicted value if regressor
gbm4_importance_pct(model, feat_idx)
Returns normalized feature importance as % of total gain for one feature.
Importance = accumulated gain credited to this feature across all boosting rounds,
matching XGBoost's xgb.importance() Gain column definition.
Parameters:
model (GBM4) : GBM4 object from gbm4_fit()
feat_idx (int) : Feature index to query (0-3)
Returns: Percentage of total ensemble gain attributed to this feature (0.0–100.0)
GBM
Holds a fitted gradient-boosted stump ensemble (1 feature).
Fields:
thresh (array) : Split threshold for each round's stump
left_val (array) : Newton leaf value when feature < threshold
right_val (array) : Newton leaf value when feature >= threshold
base_score (series float) : Log-odds of training mean (classifier) or mean (regressor)
lr (series float) : Learning rate stored for inference
is_classifier (series bool) : True = sigmoid probability output, False = raw regression output
GBM3
Holds a fitted 3-feature gradient-boosted stump ensemble (classification only).
Fields:
stump_feat (array) : Which feature index (0-2) each round's stump split on
thresh (array) : Split threshold for each round's stump
left_val (array) : Newton leaf value when feature < threshold
right_val (array) : Newton leaf value when feature >= threshold
base_score (series float) : Log-odds of training mean
lr (series float) : Learning rate stored for inference
GBM4
Holds a fitted 4-feature gradient-boosted ensemble with gain-based importance.
Fields:
stump_feat (array) : Which feature index (0-3) each round's stump split on
thresh (array) : Split threshold for each round's stump
left_val (array) : Newton leaf value when feature < threshold
right_val (array) : Newton leaf value when feature >= threshold
importance (array) : Accumulated gain per feature (indices 0-3), raw — normalize via gbm4_importance_pct()
base_score (series float) : Log-odds (classifier) or mean (regressor)
lr (series float) : Learning rate stored for inference
is_classifier (series bool) : True = sigmoid probability output, False = raw regression output Bibliothèque

CircularArraysLibrary "CircularArrays"
This library shows how to implement circular arrays. Native arrays in Pine are simple, resizable data structures. If you add or insert another element, the array extends its size. Arrays can grow to 100,000 elements.
🟩 WHY USE A CIRCULAR ARRAY?
The built-in methods that add or remove elements at the beginning of an array can create a performance problem. When `array.shift()` removes and returns the first element, every remaining element moves down one place and receives a new index. The operation is O(n), meaning that its cost scales linearly with the number of elements. For example, shifting an array of 200 elements is roughly twice as expensive as shifting one of 100 elements. Similar considerations apply to `array.unshift()`, which inserts an element at the beginning.
By contrast, `array.push()` and `array.pop()` operate at the end of the array and are generally O(1).
A common requirement is to keep an array at a fixed size. The usual approach is to remove an element from the beginning whenever the script adds one to the end after the array reaches its maximum size. Because removing the first element is O(n), maintaining the fixed-size array this way is also O(n).
A circular array offers an alternative. It has a fixed size and can be imagined as a ring. This implementation uses a normal backing array together with an integer pointer that identifies the element containing the first (oldest) element. The pointer lets us change the apparent order of the elements without actually moving them all.
🟩 EXAMPLE: ADD `Z` TO THE BEGINNING WHEN THE CIRCULAR ARRAY IS NOT FULL
Let's take a "string" array with `A` as element 0. To put `Z` before it, we move the oldest-element pointer back one slot. Because it was at index 0, it wraps around to index 4, the final slot in the backing array. We write `Z` there - to the end of the backing array. The existing values do not move, but reading from the new oldest-element pointer makes the logical order `Z, A, B, C`.
Index: 0 1 2 3 4
Before insertion: A B C - -
^
oldest
int head = 0
Index: 0 1 2 3 4
After insertion: A B C - Z
^
oldest
int head = 4
Logical order: Z A B C
The other beginning-of-array operations use the same pointer:
Remove when not full: Read and clear the value at the oldest-element pointer, move the pointer forward one slot, and decrease the stored size. The next value becomes the oldest without any remaining values moving.
Add when full: Move the oldest-element pointer back one slot. Because every slot is occupied, this is the slot containing the previous newest value. Replace it with the new value. The new value becomes the oldest, the previous newest value is evicted, and the stored size stays the same.
Remove when full: Read and clear the slot identified by the oldest-element pointer, move the pointer forward one slot, and decrease the stored size. The next value becomes the oldest and the cleared slot becomes empty.
🟩 LIMITATIONS
Circular arrays can potentially improve performance when you need to keep an array at a fixed size (see below), but they have some drawbacks:
You cannot use the built-in array methods to alter them. You must use custom methods that manipulate the circular-array object rather than only its backing array.
They require more setup than native arrays.
Their capacity must be chosen in advance. Changing it requires rebuilding the backing array.
Although operations at either end are O(1), inserting or removing elements in the middle remains O(n).
Eviction methods return the displaced value so that your script can react to it if you need. One common use is a ring of drawing objects, where the script additionally deletes the line, box, or label returned as a separate cleanup step.
Pine does not support arrays of a generic type, so this public library stores floats only and serves as a template . Copy it and replace the element type to create a ring of integers, strings, or user-defined objects. Alternatively, for a slight performance increase, you can inline the types and methods you need.
🟩 FUNCTIONAL DEMONSTRATION
The functional demo shows how to keep the last n `close` prices in a fixed-size circular array. It displays the contents of the array in a table on the chart.
🟩 PROFILER DEMONSTRATION
The library also includes a Pine Profiler demonstration of the common fixed-window operation: add one new value and evict the oldest.
It compares four implementations:
The exported circular-array `pushValue()` method.
The same ring operation written directly against a normal backing array, to remove the exported-method overhead.
Native `array.shift()` followed by `array.push()`, which is the usual Pine implementation.
A manual Pine-level linear shift wrapped in a UDT method. This moves every value down one index and is there to compare the circular and linear algorithms when both are written in Pine. It does not reproduce TradingView's much more optimised native `array.shift()` implementation. It's there so we can compare not the implementations but the principles of both methods.
The main benchmarks run over several bars so that the measured work is much bigger than the Profiler's overhead. The manual linear shift performs 1000x fewer top-level replacements because it is so much sloooower.
Of these options, the emulated Pine linear shift is so much slower it's not even funny.
The native Pine `shift()` and `push()` does the same work but in an optimsed way. You can see that it is ~1000x faster than the emulator.
Our own circular array starts off being much slower than the native version, but as you increase the array size, the native one gets significantly slower, and at some point they cross over and our version "wins". In my tests using this library's demo function the crossover point landed somewhere around ~5,000 elements.
Performance profiling is a tricky business. It depends very much on how your script is written, and even for the same script, it also changes from run to run. YMMV.
My personal conclusions from testing this library are:
Circular arrays *as an operating principle* are great for fixed-size array operations where you are adding new values and removing old ones.
Native optimisations are huge.
This Pine library offers potential performance advantages over the built-in methods only for very large arrays (thousands of elements).
If Pine made native circular arrays they would massively outperform linear arrays for these kinds of tasks.
Find out more about the Pine Profiler: www.tradingview.com
The functions:
new(_capacity)
Creates an empty circular array with a fixed capacity. The backing array starts at its full length, filled with `na`, so normal operations never need to resize it.
Parameters:
_capacity (int) : The maximum number of elements. Values below 1 or `na` are changed to 1 so the circular array remains usable without a runtime error.
Returns: A new, empty `o_circularFloat` object. Empty in the sense that there are no values in it.
method elementCount(_this)
Gets the number of elements currently stored. This is different from capacity: the backing array always has `capacity` slots, but only `elementCount` of them currently hold logical values. We have kind of split array "size" into two concepts of elementCount and capacity, so to avoid confusion we do not expose a .size() method.
Namespace types: o_circularFloat
Parameters:
_this (o_circularFloat) : The circular buffer.
Returns: The number of stored elements (0..capacity).
method capacity(_this)
Gets the maximum number of elements the circular array can hold. We have kind of split array "size" into two concepts of elementCount and capacity, so to avoid confusion we do not expose a .size() method.
Namespace types: o_circularFloat
Parameters:
_this (o_circularFloat) : The circular buffer.
Returns: The fixed capacity.
method isFull(_this)
Checks whether the circular array is full. When it is, pushValue() or unshiftValue() must evict an element (they always return the element).
Namespace types: o_circularFloat
Parameters:
_this (o_circularFloat) : The circular buffer.
Returns: `true` when elementCount == capacity.
method isEmpty(_this)
Checks whether the circular array is empty.
Namespace types: o_circularFloat
Parameters:
_this (o_circularFloat) : The circular buffer.
Returns: `true` when elementCount == 0.
method getValue(_this, _index)
Gets the element at a logical index, where 0 is the oldest and elementCount-1 is the newest.
Namespace types: o_circularFloat
Parameters:
_this (o_circularFloat) : The circular buffer.
_index (int) : The logical index.
Returns: The element, or `na` when the index is out of range.
method setValue(_this, _index, _value)
Replaces the element at a logical index without changing the circular array's element count or order. This can update a stored value, or a field when the same pattern is adapted for objects.
Namespace types: o_circularFloat
Parameters:
_this (o_circularFloat) : The circular buffer.
_index (int) : The logical index (0..elementCount-1). An out-of-range index does nothing.
_value (float) : The new value.
method firstValue(_this)
Gets the oldest element (logical index 0) without removing it.
Namespace types: o_circularFloat
Parameters:
_this (o_circularFloat) : The circular buffer.
Returns: The oldest element, or `na` when empty.
method lastValue(_this)
Gets the newest element (logical index elementCount-1) without removing it.
Namespace types: o_circularFloat
Parameters:
_this (o_circularFloat) : The circular buffer.
Returns: The newest element, or `na` when empty.
method pushValue(_this, _value)
Adds a value at the end as the new newest element. If the circular array is full, this replces and returns the oldest element so you can react to it, for example by deleting an evicted drawing.
Namespace types: o_circularFloat
Parameters:
_this (o_circularFloat) : The circular buffer.
_value (float) : The value to add.
Returns: The evicted oldest value when the circular array was full, otherwise `na`.
method unshiftValue(_this, _value)
Adds a value at the beginning as the new oldest element. If the circular array is full, this replaces and returns the newest element. This gives the same result as using array.unshift() with a size limit, but is O(1).
Namespace types: o_circularFloat
Parameters:
_this (o_circularFloat) : The circular buffer.
_value (float) : The value to add.
Returns: The evicted newest value when the circular array was full, otherwise `na`.
method shiftValue(_this)
Removes and returns the oldest element. Unlike the O(n) array.shift(), this is O(1) because we only clear one slot and move the head pointer. This is therefore the big payoff for all this fussing about.
Namespace types: o_circularFloat
Parameters:
_this (o_circularFloat) : The circular buffer.
Returns: The removed oldest element, or `na` when the circular array was empty.
method popValue(_this)
Removes and returns the newest element.
Namespace types: o_circularFloat
Parameters:
_this (o_circularFloat) : The circular buffer.
Returns: The removed newest element, or `na` when the circular array was empty.
method removeAt(_this, _index)
Removes and returns the element at a logical index. Later elements move down to close the gap, so this is O(n).
Namespace types: o_circularFloat
Parameters:
_this (o_circularFloat) : The circular buffer.
_index (int) : The logical index to remove (0..elementCount-1). An out-of-range index returns `na` and changes nothing.
Returns: The removed element, or `na` when the index was out of range.
method insertAt(_this, _index, _value)
Inserts a value at a logical index. Elements at and after that index move towards the end, so this is O(n). If the circular array is full, the newest element is removed and returned.
Namespace types: o_circularFloat
Parameters:
_this (o_circularFloat) : The circular buffer.
_index (int) : The logical index at which to insert (0..elementCount). Numeric values outside this range are clamped into it, except that an index past the newest element on a full buffer has nowhere to go, so the value is returned unstored. An `na` index changes nothing and returns `_value`.
_value (float) : The value to insert.
Returns: The evicted newest value when the circular array was full, the supplied value when an `na` index prevented insertion, otherwise `na`.
method containsValue(_this, _value)
Checks whether the circular array contains an exact match, treating a stored `na` as matching an `na` search value. This is O(n). If you adapt this template to an object type, `==` compares references, not contents, so you will likely want to replace the comparison with a field-by-field check.
Namespace types: o_circularFloat
Parameters:
_this (o_circularFloat) : The circular buffer.
_value (float) : The value to look for.
Returns: `true` when found.
method clearValues(_this)
Empties the circular array without changing its capacity. All backing-array slots are reset to `na`, and head and elementCount return to zero.
Namespace types: o_circularFloat
Parameters:
_this (o_circularFloat) : The circular buffer.
method toArray(_this)
Copies the circular array's contents into a normal array, from oldest to newest. This can be useful for iteration or debugging. Changes to the returned array do not affect the circular array.
Namespace types: o_circularFloat
Parameters:
_this (o_circularFloat) : The circular buffer.
Returns: A new array containing the stored values from oldest to newest.
method deepCopy(_this)
Copies the circular array, including a separate backing array, so either copy can be changed without affecting the other.
Namespace types: o_circularFloat
Parameters:
_this (o_circularFloat) : The circular buffer.
Returns: A new o_circularFloat with the same contents and capacity.
method resize(_this, _newCapacity)
Changes the capacity. Increasing it keeps every element. Decreasing it below the current element count keeps the newest elements and returns the dropped oldest elements, oldest first, so you can react to them as with pushValue().
Namespace types: o_circularFloat
Parameters:
_this (o_circularFloat) : The circular buffer.
_newCapacity (int) : The new capacity. Values below 1 are changed to 1. An `na` value leaves the capacity unchanged.
Returns: An array containing the dropped oldest elements, or an empty array when nothing was dropped.
o_circularFloat
A fixed-capacity circular buffer of floats. Elements are addressed by a LOGICAL index where 0 is the oldest retained element and elementCount-1 is the newest, matching normal array indexing. Internally the data lives in a recycled backing array of length `capacity`; `head` marks where the oldest element physically sits, and the buffer wraps around the end of the backing array as elements are added and removed.
Fields:
a_data (array) : The backing array. Always actually `capacity` elements long; unused slots hold na. Never index this directly - use getValue()/setValue(), which translate logical indices to physical ones.
head (series int) : The physical index in `a_data` of the oldest logical element (logical index 0). Advances on shiftValue(), retreats on unshiftValue(), wrapping modulo capacity. Always kept in 0..capacity-1; the index maths in f_physicalIndex() relies on this.
elementCount (series int) : The number of elements currently stored (0..capacity). This differs from the backing array's size, which always equals capacity.
capacity (series int) : The maximum number of elements the buffer can hold. Fixed at construction; change it only via resize(). Bibliothèque

ImportantLevelsLinesLabels_UtilitiesLevelsLinesLabels_Utilities is a shared Pine v6 utility library for scripts that already resolve their own level values, source candles, session logic, and visibility conditions, but want a reusable level-output layer.
It centralizes the pieces that tend to get rewritten across level-based scripts:
• line-style and label-size resolvers
• EM-space right-label padding
• compact price / $ difference / % difference formatting
• standardized right-side level label text
• above/below-current-price color routing
• bar-time horizontal level line management
• transparent right-side text label management
• synchronized line / label slot-array helpers
• float / int / line / label array pruning helpers
• newest-first history lookup helpers
• Active Period / Source Window / Source Candle start-time routing
• newest-first rolling highest / lowest helpers
• newest-first rolling highest / lowest helpers with matching source time
The example chart demonstrates how a calling script can use the library to render live close-style levels, previous-day high/low levels, rolling completed-window high/low levels, right-side label stacks, source-aware line starts, and reusable object slots.
This library is intentionally focused on output, formatting, object lifecycle, and history-array utilities.
It does not:
• request higher-timeframe data
• decide regular-session versus extended-session sources
• detect sessions, opens, closes, highs, lows, or pivots
• calculate candle levels, VWAPs, pivots, trendlines, or envelopes
• decide which levels should be shown
• own script inputs, tooltips, colors, or final visibility logic
• provide trading signals or directional recommendations
Calling scripts remain responsible for:
• the level engine
• the source engine
• session logic
• request.security() calls
• user inputs and tooltips
• final show/hide conditions
• color choices
• interpretation
How to use
Import the library near the top of your script in global scope, before calling its helpers.
Typical placement:
//@version=6
indicator(...)
import MYNAMEISBRANDON/LevelsLinesLabels_Utilities/1 as LVL
Replace /1 with the latest published version if a newer version is available.
This library expects the calling script to already know the level value, source time, window time, active period start, display state, colors, and label text it wants to use. The library then handles the reusable formatting, line, label, object-slot, pruning, lookup, and rolling-window utility layer.
➖Style Helpers➖
These helpers convert simple user-facing strings into Pine style enums and route colors based on whether price is above or below a level.
levelLineStyle(styleIn)
Converts user-facing line-style text into a Pine line-style enum.
Parameters:
styleIn (simple string): Solid, Dashed, or Dotted
Returns:
Pine line style
levelLabelSize(sizeIn)
Converts user-facing label-size text into a Pine label-size enum.
Parameters:
sizeIn (simple string): Tiny, Small, Normal, Large, or Huge
Returns:
Pine label size
levelColor(level, currentPrice, aboveColor, belowColor)
Routes a level to the above-color or below-color based on the current/reference price.
Parameters:
level (float): Level price
currentPrice (float): Current/reference price
aboveColor (color): Color used when currentPrice is greater than or equal to level
belowColor (color): Color used when currentPrice is below level
Returns:
Resolved color
➖Text Formatting Helpers➖
These helpers keep level labels compact and readable across high-priced stocks, low-priced stocks, crypto pairs, futures-style symbols, and other price scales.
levelSpacer(pad)
Builds EM-space padding for right-side text labels.
levelTrimTrailingZeros(txt)
Removes unnecessary trailing zeros and trailing decimal points.
levelStripLeadingZero(txt)
Removes leading decimal zeroes such as 0.42 → .42 and -0.42 → -.42.
levelSigFig(value, figs)
Rounds a number to a requested number of significant figures.
levelNumberText(value, pattern)
Formats a number with a Pine pattern and then trims unnecessary zeros.
levelPriceText(value, sigFigs)
Formats a level price using significant figures and compact decimal trimming.
levelAbsMoneyText(absValue)
Formats an absolute money value rounded to two decimals.
levelAbsPctText(absValue)
Formats an absolute percent value rounded to two decimals.
levelMoneyChangeText(currentPrice, level)
Formats current price minus level as a signed $ difference.
levelPctChangeText(currentPrice, level)
Formats current price minus level as a signed % difference.
➖Level Label Text Helpers➖
levelLabelText(tag, level, currentPrice, pad, showPrice, showMoneyDiff, showPctDiff, sigFigs)
Builds a standardized right-side level label block.
The label model is:
• optional price row
• optional $ difference row
• optional % difference row
• required level tag row supplied by the calling script
Example output:
741.82
-$22.16
-2.90%
D Hi
EM-space padding is applied to every row. This lets scripts visually stagger labels to the right while keeping the actual label pinned to the current bar_index.
➖Object Sync Helpers➖
These helpers create an object when enabled, update it in place while enabled, and delete it when the caller’s condition turns false.
syncTextLabel(lbl, show, y, txt, txtColor, sizeIn)
Creates, updates, or deletes a transparent right-side text label at the current bar_index.
syncBarTimeLevelLine(ln, show, t1, t2, y, lineColor, lineWidth, styleIn)
Creates, updates, or deletes a horizontal bar-time level line using xloc.bar_time.
This is useful for level scripts that want line starts based on a real timestamp instead of deep bar-index offsets.
➖Slot Array Helpers➖
These helpers let scripts store many repeated level lines and labels in fixed array slots instead of declaring one separate variable per object.
syncLineSlot(lines, slot, show, t1, t2, y, lineColor, lineWidth, styleIn)
Creates, updates, or deletes a bar-time level line stored in a fixed array slot.
syncLabelSlot(labels, slot, show, y, txt, txtColor, sizeIn)
Creates, updates, or deletes a transparent right-side text label stored in a fixed array slot.
Typical use:
const int SLOT_HI = 0
const int SLOT_LO = 1
const int SLOT_CL = 2
var array rowLines = array.new_line(3, na)
var array rowLabels = array.new_label(3, na)
LVL.syncLineSlot(rowLines, SLOT_HI, showHi, hiStartTime, time, hiLevel, hiColor, 2, "Dotted")
LVL.syncLabelSlot(rowLabels, SLOT_HI, showHiLabel, hiLevel, hiText, hiColor, "Normal")
This is especially useful for scripts with repeated rows such as:
• Previous Day High / Low / Close
• Weekly High / Low / Close
• Monthly High / Low / Close
• VWAP bands
• ATR levels
• rolling window levels
• trendline or envelope companion levels
➖History Array Helpers➖
These helpers support scripts that store completed records in arrays, especially newest-first arrays populated with array.unshift().
pruneFloat(arr, maxKeep)
Prunes a float array by popping old records from the end.
pruneInt(arr, maxKeep)
Prunes an int array by popping old records from the end.
pruneLineObjects(arr, maxKeep)
Prunes a line array and deletes removed line objects.
pruneLabelObjects(arr, maxKeep)
Prunes a label array and deletes removed label objects.
pruneHiLoHistory(highs, lows, highTimes, lowTimes, windowTimes, maxKeep)
Prunes synchronized high / low / high-time / low-time / window-time arrays.
pruneHlcHistory(highs, lows, closes, highTimes, lowTimes, closeTimes, windowTimes, maxKeep)
Prunes synchronized high / low / close / source-time / window-time arrays.
histFloat(arr, idx)
Returns a float history value at an array index, or na if unavailable.
histInt(arr, idx)
Returns an int history value at an array index, or na if unavailable.
requestOrManual(requestValue, manualValue)
Returns a requested value when available, otherwise the manual value.
manualOrRequest(manualValue, requestValue)
Returns a manual value when available, otherwise the requested value.
manualSourceTime(manualValue, times, idx)
Returns a matching manual source time only when the matching manual value exists.
➖Source Start-Time Helpers➖
These helpers route line-start timestamps using a common level-script model.
sourceLineStartTime(mode, sourceTime, activeTime)
Resolves Active Period versus Source Candle / Source Close Candle starts.
windowSourceLineStartTime(mode, windowTime, sourceTime, activeTime)
Resolves Active Period, Source Window, Source Candle, or Source Close Candle starts.
Start-time model:
Active Period:
Uses the active period start supplied by the calling script.
Source Window:
Uses the completed source window start supplied by the calling script.
Source Candle / Source Close Candle:
Uses the exact source candle time supplied by the calling script when available. If the exact source candle time is not available, it falls back to Source Window when available, then Active Period.
This keeps the library generic while allowing calling scripts to decide what a “source candle” means in their own context.
➖Newest-First Rolling Extreme Helpers➖
These helpers are built for arrays where index 0 is the most recent completed record.
Newest-first history model:
• index 0 = most recent completed record
• index 1 = one completed record back
• index 2 = two completed records back
• index 3 = three completed records back
• index 4 = four completed records back
A 5-record rolling high scans indexes 0 through 4 when available.
highestNewestFirst(values, lookback)
Returns the highest value and matching array index from a newest-first array window.
lowestNewestFirst(values, lookback)
Returns the lowest value and matching array index from a newest-first array window.
highestNewestFirstWithTime(values, times, lookback)
Returns the highest value, matching array index, and matching source time from synchronized newest-first arrays.
lowestNewestFirstWithTime(values, times, lookback)
Returns the lowest value, matching array index, and matching source time from synchronized newest-first arrays.
Important note:
The returned index is an array index, not a bar offset. If the calling script stores synchronized time arrays, the “with time” helpers can also return the matching source timestamp.
Example:
// Newest-first arrays populated with array.unshift().
= LVL.highestNewestFirstWithTime(
dailyHighHistory,
dailyHighTimeHistory,
5)
= LVL.lowestNewestFirstWithTime(
dailyLowHistory,
dailyLowTimeHistory,
5)
➖Recommended Usage➖
This library works best when the calling script follows this workflow:
1. Resolve the level value in the script.
2. Resolve the source candle time or source window time in the script.
3. Resolve the final visibility condition in the script.
4. Use this library to format the label, route color, choose start time, and manage the line/label object.
This keeps source logic and interpretation script-level while making the reusable output layer cleaner and easier to maintain.
➖Important Notes➖
This library is a utility layer only.
It does not:
• request data
• detect sessions
• choose RTH or EXT behavior
• calculate previous-day levels
• calculate VWAP
• calculate pivots
• calculate trendlines
• decide trade direction
• generate signals
Calling scripts remain responsible for their own engine logic and interpretation.
The included demo script is meant to show how the library can be used to manage live levels, previous-day levels, rolling completed-record levels, label padding, object slots, and start-time routing.
Bibliothèque

MarketPokerEnginev2
MarketPokerEnginev2
: Advanced Hand Evaluation Library
Overview
MarketPokerEngine is an institutional-grade, highly optimized library designed to evaluate poker hand combinatorics within Pine Script v6. It is specifically engineered to offload heavy logical processing and Abstract Syntax Tree (AST) node consumption from your main indicator script, ensuring rapid execution speeds even during live tick, multi-state simulations.
Core Architecture
The engine operates on a strict 5-card subset evaluation model. By feeding it exact 5-element arrays, the library mathematically guarantees zero false-positive evaluations (such as cross-suit straight flushes) without requiring excessive loop iterations.
Key Features & Functions
evaluate_hand(int ranks, int suits): The primary evaluation engine. It takes two 5-element arrays (ranks and suits) and returns a comprehensive tuple of 10 boolean/integer flags representing every possible hand hierarchy (from Royal Flush down to High Card), including Joker counts.
get_card_vertical(int r, int s): A streamlined string formatting utility. It converts raw integer IDs into clean, vertical Unicode representations (e.g., "♠️ A") optimized for box.new() or label.new() UI rendering.
Implementation Note
This library assumes 0 is reserved for Jokers, 1-13 for standard ranks (A-K), and 1-4 for standard suits. It is highly recommended to pair this library with a master script that generates combinatoric 5-card subsets (e.g., 21 combinations for a 7-card Texas Hold'em board) to determine the absolute best hand score.
日本語公開文
MarketPokerEngine: 高度なポーカー役判定コアライブラリ
概要
MarketPokerEngine は、Pine Script v6においてポーカーの役(組み合わせ)評価を処理するための、高度に最適化された専用ライブラリです。メインのインジケータースクリプトから複雑な論理演算を切り離し、抽象構文木(AST)ノードの枯渇を回避することで、ライブティック更新時や多状態シミュレーションにおいても極めて軽量な実行速度を担保します。
コア・アーキテクチャ
本エンジンは、厳密な「5要素部分集合(Subset)」の評価モデルを採用しています。7枚などの複合状態から5要素の配列を抽出して本ライブラリに渡すことで、「スートが異なるストレートフラッシュ」などの誤判定を数学的かつ構造的に排除し、無駄な計算ループを必要としない洗練された判定を実現しています。
主要機能
evaluate_hand(int ranks, int suits): 判定エンジンの心臓部です。ランク(数字)とスート(マーク)の5要素配列を受け取り、ロイヤルフラッシュからワンペアまでの全役のフラグ、およびジョーカーの枚数を含む10要素のタプル(戻り値のまとまり)を高速で返します。
get_card_vertical(int r, int s): UI描画のための文字列フォーマット機能です。内部の整数IDを、box.new() や label.new() での表示に最適化されたクリーンな縦型のUnicodeテキスト(例: "♠️ A")に即座に変換します。
実装上の注意事項
本ライブラリは、整数 0 をジョーカー、1-13 をランク(A-K)、1-4 をスートとして処理します。テキサスホールデムのような7枚のカードを扱うシステムに組み込む場合は、メインスクリプト側で7枚から5枚を選ぶ全21通りの組み合わせループを構築し、本ライブラリの評価を通過させることで、最もスコアの高い役を正確に抽出することが推奨されます。
Bibliothèque

Bibliothèque

ZT_Dashboard_LibCompanion library for the Alpha Flow Zone Trader (AFZT) invite-only indicator. Provides the table-rendering helpers for the AFZT on-chart dashboard — splits the rendering code out of Core so the indicator stays under TradingView's per-script token limit.
Exports:
• renderFlow(table, ...) — fills the FLOW tab: grade, zone, entry/stop/TPs, unrealized R, optional stats, optional Ichimoku, optional local rec, optional TV-Pack row.
• renderOps(table, ...) — fills the OPS tab: engine state, vol regime, zones, tick health, signal status, last trade.
All exports are pure table.cell() writers — they receive a pre-created table object and color palette from the Core, write rows into it, and return. No plots, no alerts, no series state. Bibliothèque

ZT_Telemetry_LibCompanion library for the Alpha Flow Zone Trader (AFZT) invite-only indicator. Provides telemetry-encoding helpers used by the AFZT Core script when emitting webhook payloads.
Exports:
• tfCode(tfStr) — maps a TradingView timeframe string ("1", "5", "15", "60", "D", etc.) to a stable integer code.
• buildMasks_v22(scoreNorm, atrMult, entryAtr, riskTicks, zoneWidthTicks, hasCisd) — returns bitmask triples encoding which stop/BE/TP policies are eligible for the current setup.
• zoneCodeDemand / zoneCodeSupply — maps a zone name + auto-zone index to a stable integer code (1100+ for auto-detected, 101-103 / 201-203 for manual DZ/SZ slots).
All exports are pure functions — no plots, no alerts, no series state. Bibliothèque

Bibliothèque

ZT_Webhook_LibCompanion library for the Alpha Flow Zone Trader (AFZT) invite-only indicator. Provides the AFZT webhook payload encoders — formats the AFZT|... pipe-delimited strings the Core script sends in its alert() messages on entry, breakeven, close, and S-event signals.
Exports:
• encode_entry_v1038 — entry-event payload (zone code, base type, confidence, touch count, zone age, entry & stop prices).
• encode_be_v1038 — breakeven-event payload.
• encode_close_v1038 / encode_close_v1041 / encode_close_v1043_v2 — close-event payloads with progressively richer telemetry (R-multiple, MFE/MAE, zone metadata, stop/BE/TP masks, ATR/risk/zone-width).
• encode_signal_v1 / encode_signal_v2 — S-Event signal payload with filter masks (v2 adds 4 upstream ML features: sweep flag, trend bias, HTF direction, liquidity distance).
All exports are pure string-formatting functions — no plots, no alerts, no state mutation. Bibliothèque

Bibliothèque

Bibliothèque

Bibliothèque

KLP_Telemetry_LibLibrary "KLP_Telemetry_Lib"
build_payload(sym, tf, dir, fam, sub, bt, loc, ichi_st, ichi_wr, vwap_c, lvn_p, ilvn, hvn_p, sb, wick_p, body_p, close_l, dist_kl, dist_vw, entry, stop, tp1, tp2, rticks, qtag, ts_ms, acct, zdz, zsz, qlvl, shlb, spmx, a5tk, sess_src, active_sess, family_name, manual_sess, auto_switched, stop_meth, atr15tk, atr2tk, atr25tk, sw_stop_tk, vol_reg, tp_cnt, be_rule, raw_conf, kl_match_count, kl_match_names_json, vwap_aligned, poc_magnet, poc_dist_r_x100, ichi_at_break, hvn_break_thru, cont_path, zone_exempt_used)
Parameters:
sym (string)
tf (string)
dir (int)
fam (string)
sub (string)
bt (int)
loc (string)
ichi_st (string)
ichi_wr (float)
vwap_c (bool)
lvn_p (bool)
ilvn (bool)
hvn_p (bool)
sb (bool)
wick_p (float)
body_p (float)
close_l (float)
dist_kl (float)
dist_vw (float)
entry (float)
stop (float)
tp1 (float)
tp2 (float)
rticks (int)
qtag (string)
ts_ms (int)
acct (string)
zdz (bool)
zsz (bool)
qlvl (int)
shlb (int)
spmx (int)
a5tk (int)
sess_src (string)
active_sess (string)
family_name (string)
manual_sess (string)
auto_switched (int)
stop_meth (string)
atr15tk (int)
atr2tk (int)
atr25tk (int)
sw_stop_tk (int)
vol_reg (int)
tp_cnt (int)
be_rule (string)
raw_conf (int)
kl_match_count (int)
kl_match_names_json (string)
vwap_aligned (bool)
poc_magnet (bool)
poc_dist_r_x100 (int)
ichi_at_break (bool)
hvn_break_thru (bool)
cont_path (string)
zone_exempt_used (bool) Bibliothèque
