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 .
Library

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. Library

Library

Library

Library

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 PulseWire 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 Library

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 PulseWire'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.pulsewire.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(). Library

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.
Library

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通りの組み合わせループを構築し、本ライブラリの評価を通過させることで、最もスコアの高い役を正確に抽出することが推奨されます。
Library

Library

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 PulseWire'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. Library

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 PulseWire 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. Library

Library

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. Library

Library

Library

Library

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) Library

EKGapEngineLibrary "EKGapEngine"
Gap engine library: NWOG (New Week Opening Gaps), NDOG (New Day Opening Gaps),
9:30 NY Opening Range Gap, and PXH/L (Previous X High/Low) levels.
Extracted from qt-pro-enigma.pine to avoid PulseWire compiler timeouts.
GAP_VOID_BULL()
Gap type constant: Void Bullish
Returns: 1
GAP_VOID_BEAR()
Gap type constant: Void Bearish
Returns: 2
GAP_OVERLAP_BULL()
Gap type constant: Overlap Bullish
Returns: 3
GAP_OVERLAP_BEAR()
Gap type constant: Overlap Bearish
Returns: 4
getLineStyle(styleStr)
Convert a style string to a line style constant
Parameters:
styleStr (string) : Style string ("Solid", "Dashed", "Dotted")
Returns: line.style_solid, line.style_dashed, or line.style_dotted
getLabelSize(labelSize)
Convert a label size string to a size constant
Parameters:
labelSize (string) : Label size string ("Tiny", "Small", "Normal", "Large")
Returns: size.tiny, size.small, size.normal, or size.large
calcTradingDaysAgo(creationTimestamp)
Calculate approximate trading days since a timestamp
Parameters:
creationTimestamp (int) : The creation timestamp to measure from
Returns: Approximate number of trading days elapsed
getNwogSubtypeName(gapType)
Get the human-readable name for an NWOG gap subtype
Parameters:
gapType (int) : Gap type constant (1-4)
Returns: String name of the gap subtype
buildNwogLabelText(gapType, daysAgo, labelStyle)
Build label text for an NWOG
Parameters:
gapType (int) : Gap type constant (1-4)
daysAgo (int) : Number of trading days ago the gap was created
labelStyle (string) : Label style ("Simple" or "Descriptive")
Returns: Formatted label text string
buildNdogLabelText(isBullish, daysAgo, labelStyle)
Build label text for an NDOG
Parameters:
isBullish (bool) : Whether the gap is bullish
daysAgo (int) : Number of trading days ago the gap was created
labelStyle (string) : Label style ("Simple" or "Descriptive")
Returns: Formatted label text string
getGapColors(nwogColor, boxFillTransp, boxBorderTransp, midLineColor, quadLineColor)
Compute fill, border, mid, and quad colors for an NWOG
Parameters:
nwogColor (color) : Base NWOG color
boxFillTransp (int) : Box fill transparency (0-100)
boxBorderTransp (int) : Box border transparency (0-100)
midLineColor (color) : Midline color
quadLineColor (color) : Quadrant line color
Returns: Tuple of
getNdogColors(ndogColor, boxFillTransp, boxBorderTransp, midLineColor, quadLineColor)
Compute fill, border, mid, and quad colors for an NDOG
Parameters:
ndogColor (color) : Base NDOG color
boxFillTransp (int) : Box fill transparency (0-100)
boxBorderTransp (int) : Box border transparency (0-100)
midLineColor (color) : Midline color
quadLineColor (color) : Quadrant line color
Returns: Tuple of
classifyGap(friClose, friOpen, monOpen)
Classify a weekend gap as Void Bull/Bear or Overlap Bull/Bear
Parameters:
friClose (float) : Friday close price
friOpen (float) : Friday open price
monOpen (float) : Monday open price
Returns: Gap type constant (0 if no gap)
passesAtrFilter(gapSize, atr, multiplier)
Check if a gap passes the ATR size filter
Parameters:
gapSize (float) : Absolute size of the gap
atr (float) : Current ATR value
multiplier (float) : ATR multiplier threshold
Returns: true if the gap passes the filter
isWithinPriceDistance(topPrice, bottomPrice, currentPrice, atr, onlyShowNearPrice, maxDistanceAtr)
Check if a gap is within the max price distance
Parameters:
topPrice (float) : Top of gap zone
bottomPrice (float) : Bottom of gap zone
currentPrice (float) : Current price (close)
atr (float) : Current ATR value
onlyShowNearPrice (bool) : Whether the near-price filter is enabled
maxDistanceAtr (float) : Maximum distance in ATR multiples
Returns: true if within distance (or filter is disabled)
shouldDisplayGapType(gapType, showVoidGaps, showOverlapGaps)
Check if a gap type should be displayed based on settings
Parameters:
gapType (int) : Gap type constant (1-4)
showVoidGaps (bool) : Whether void gaps are enabled
showOverlapGaps (bool) : Whether overlap gaps are enabled
Returns: true if this gap type should be shown
createNwog(nwogArray, cfg, friClose, friOpen, monOpen, atrVal, fridayBarIdx)
Create a new NWOG and add it to the array, removing oldest if over max count
Parameters:
nwogArray (array) : Array of NWOGBox objects
cfg (GapConfig) : GapConfig with all settings
friClose (float) : Friday close price
friOpen (float) : Friday open price
monOpen (float) : Monday open price
atrVal (float) : Current ATR value for size filtering
fridayBarIdx (int) : Bar index of Friday (for left edge of box)
createNdog(ndogArray, cfg, prevClose, todayOpn, atrVal, prevDayBarIdx)
Create a new NDOG and add it to the array, removing oldest if over max count
Parameters:
ndogArray (array) : Array of NDOGBox objects
cfg (GapConfig) : GapConfig with all settings
prevClose (float) : Previous day close price
todayOpn (float) : Today open price
atrVal (float) : Current ATR value for size filtering
prevDayBarIdx (int) : Bar index of previous day (for left edge of box)
updateNwogExtensions(nwogArray, cfg, dailyAtr, shouldRunCleanup)
Update all NWOG box/line/label extensions and handle cleanup
Parameters:
nwogArray (array) : Array of NWOGBox objects
cfg (GapConfig) : GapConfig with all settings
dailyAtr (float) : Current daily ATR value for distance filtering
shouldRunCleanup (bool) : Whether cleanup should run this bar (based on interval)
updateNdogExtensions(ndogArray, cfg, dailyAtr, shouldRunCleanup)
Update all NDOG box/line/label extensions and handle cleanup
Parameters:
ndogArray (array) : Array of NDOGBox objects
cfg (GapConfig) : GapConfig with all settings
dailyAtr (float) : Current daily ATR value for distance filtering
shouldRunCleanup (bool) : Whether cleanup should run this bar (based on interval)
markWeekEndForNwogs(nwogArray, isWeekCloseRisingEdge)
Mark week end for all active NWOGs when week close rising edge detected
Parameters:
nwogArray (array) : Array of NWOGBox objects
isWeekCloseRisingEdge (bool) : Whether the week close rising edge was detected this bar
markDayEndForNdogs(ndogArray, isNewTradingDay)
Mark day end for all active NDOGs when new trading day detected
Parameters:
ndogArray (array) : Array of NDOGBox objects
isNewTradingDay (bool) : Whether a new trading day started this bar
f_get_size(s)
Convert a size string to a size constant
Parameters:
s (string) : Size string ("Auto", "Tiny", "Small", "Normal", "Large")
Returns: Corresponding size constant
f_get_pxhl_style(str)
Convert a style string to a line style constant (PXH/L version)
Parameters:
str (string) : Style string ("Solid", "Dotted", "Dashed")
Returns: Corresponding line style constant
f_is_within_distance(target_price, filter_enabled, daily_atr, filter_preset)
Check if a target price is within distance filter range
Parameters:
target_price (float) : The price to check
filter_enabled (bool) : Whether the distance filter is enabled
daily_atr (float) : Current daily ATR value
filter_preset (string) : Filter preset string ("Really Close", "Balanced", "Slightly Far")
Returns: true if within distance (or filter disabled)
f_find_exact_in_window(target_price, p_start, p_end, is_high)
Find the exact bar time where a high/low occurred within a period window
Parameters:
target_price (float) : The price to find
p_start (int) : Period start time
p_end (int) : Period end time
is_high (bool) : Whether looking for a high (true) or low (false)
Returns: Timestamp of the exact bar where the price was found
f_add_level_smart(arr, h, l, t_start, t_end, count)
Smart-add high/low levels to an array, maintaining count limit
Parameters:
arr (array) : Array of Level objects
h (float) : High price to add
l (float) : Low price to add
t_start (int) : Period start time
t_end (int) : Period end time
count (int) : Maximum number of level pairs
f_check_mitigation(arr)
Check mitigation of levels in an array (price broke through)
Parameters:
arr (array) : Array of Level objects to check
f_draw_single_level(draw_lines, draw_labels, t_start, t_end, price, txt, col, sty, show_lbl, size_val, font_val, filter_enabled, daily_atr, filter_preset)
Draw a single PXH/L level line and optional label
Parameters:
draw_lines (array) : Array to store created line objects
draw_labels (array) : Array to store created label objects
t_start (int) : Start time for the line
t_end (int) : End time for the line
price (float) : Price level
txt (string) : Label text
col (color) : Line/label color
sty (string) : Line style constant
show_lbl (bool) : Whether to show the label
size_val (string) : Label size constant
font_val (string) : Font family constant
filter_enabled (bool) : Whether distance filter is enabled
daily_atr (float) : Current daily ATR
filter_preset (string) : Distance filter preset string
f_clear_drawings(draw_lines, draw_labels)
Clear all PXH/L drawing objects (lines and labels)
Parameters:
draw_lines (array) : Array of line objects to delete
draw_labels (array) : Array of label objects to delete
f_draw_levels(arr, show, mode, show_lbl, col, sty_global, txt_base, txt_size_val, font_val, draw_lines, draw_labels, filter_enabled, daily_atr, filter_preset)
Draw all levels from a PXH/L level array with EQ/Quadrant support
Parameters:
arr (array) : Array of Level objects
show (bool) : Whether to show this timeframe's levels
mode (string) : Midline mode ("None", "EQ", "Quadrants")
show_lbl (bool) : Whether to show labels
col (color) : Level color
sty_global (string) : Global style string ("Solid", "Dotted", "Dashed")
txt_base (string) : Base text for labels (e.g. "pD", "pW", "pM")
txt_size_val (string) : Label size string
font_val (string) : Font family constant
draw_lines (array) : Array to store created line objects
draw_labels (array) : Array to store created label objects
filter_enabled (bool) : Whether distance filter is enabled
daily_atr (float) : Current daily ATR
filter_preset (string) : Distance filter preset string
process_930_gap(cfg930, m1_time, m1_open, m1_close, g930_prevClosePrice, g930_boxArray, g930_lineArray, isStock, isValidTimeframe930)
Process 9:30 NY opening range gap from 1-minute lower timeframe data.
Handles the entire gap creation logic: detects the 16:14 (or 15:59 for stocks) close,
then at 9:30 creates box/midline/quadrant drawings.
Parameters:
cfg930 (Gap930Config) : Gap930Config with all 9:30 gap settings
m1_time (array) : Array of 1-minute bar timestamps from request.security_lower_tf
m1_open (array) : Array of 1-minute bar open prices from request.security_lower_tf
m1_close (array) : Array of 1-minute bar close prices from request.security_lower_tf
g930_prevClosePrice (float) : Previous close price state (pass in, returns updated value)
g930_boxArray (array) : Array to store created box objects
g930_lineArray (array) : Array to store created line objects
isStock (bool) : Whether the symbol is a stock/fund/dr
isValidTimeframe930 (bool) : Whether the current timeframe is valid for 9:30 gaps (intraday <= 15min)
Returns: Updated g930_prevClosePrice value
NWOGBox
New Week Opening Gap box with all price levels and drawing references
Fields:
gapType (series int) : Gap classification (1=Void Bull, 2=Void Bear, 3=Overlap Bull, 4=Overlap Bear)
fridayClose (series float) : Friday close price
fridayOpen (series float) : Friday open price
mondayOpen (series float) : Monday open price
topPrice (series float) : Top of gap zone
bottomPrice (series float) : Bottom of gap zone
midPrice (series float) : Midpoint of gap zone
upperQuad (series float) : Upper quadrant (75%) of gap zone
lowerQuad (series float) : Lower quadrant (25%) of gap zone
gapSize (series float) : Absolute size of gap
startBar (series int) : Bar index where gap starts
creationTime (series int) : Timestamp of gap creation
weekEndBar (series int) : Bar index where the week ended
weekEnded (series bool) : Whether the week has ended for this NWOG
nwogBox (series box) : Box drawing object
midLine (series line) : Midline drawing object
q3Line (series line) : Upper quadrant line drawing object
q1Line (series line) : Lower quadrant line drawing object
infoLabel (series label) : Label drawing object
isActive (series bool) : Whether this NWOG is still active
NDOGBox
New Day Opening Gap box with all price levels and drawing references
Fields:
isBullish (series bool) : Whether the gap opened bullish (today open > prev close)
prevClose (series float) : Previous day close price
todayOpen (series float) : Today open price
topPrice (series float) : Top of gap zone
bottomPrice (series float) : Bottom of gap zone
midPrice (series float) : Midpoint of gap zone
upperQuad (series float) : Upper quadrant (75%) of gap zone
lowerQuad (series float) : Lower quadrant (25%) of gap zone
gapSize (series float) : Absolute size of gap
startBar (series int) : Bar index where gap starts
creationTime (series int) : Timestamp of gap creation
dayEndBar (series int) : Bar index where the day ended
dayEnded (series bool) : Whether the day has ended for this NDOG
ndogBox (series box) : Box drawing object
midLine (series line) : Midline drawing object
q3Line (series line) : Upper quadrant line drawing object
q1Line (series line) : Lower quadrant line drawing object
infoLabel (series label) : Label drawing object
isActive (series bool) : Whether this NDOG is still active
Level
PXH/L level with price, time range, and mitigation state
Fields:
price (series float) : The price level
start_time (series int) : Timestamp where the level starts
is_high (series bool) : Whether this is a high (true) or low (false)
broken (series bool) : Whether the level has been mitigated
stop_time (series int) : Timestamp where the level was mitigated
GapConfig
Configuration for NWOG/NDOG gap display and filtering
Fields:
showNwogs (series bool) : Enable NWOGs
showVoidGaps (series bool) : Show void gap types
showOverlapGaps (series bool) : Show overlap gap types
showNwogLabels (series bool) : Show NWOG labels
extensionMode (series string) : NWOG extension mode ("Extend Live" or "Extend to Week Close")
maxNwogCount (series int) : Maximum number of NWOGs to display
showNdogs (series bool) : Enable NDOGs
showNdogLabels (series bool) : Show NDOG labels
ndogExtensionMode (series string) : NDOG extension mode ("Extend Live" or "Extend to Day Close")
maxNdogCount (series int) : Maximum number of NDOGs to display
force1800 (series bool) : Force 18:00 open for NDOG calculation
customOpenSession (series string) : Session string for custom open time
nwogColor (series color) : Base color for NWOGs
ndogColor (series color) : Base color for NDOGs
boxFillTransp (series int) : Box fill transparency
boxBorderTransp (series int) : Box border transparency
boxBorderWidth (series int) : Box border width
showMidline (series bool) : Show midline on gaps
showQuadrants (series bool) : Show quadrant lines on gaps
midLineColor (series color) : Midline color
quadLineColor (series color) : Quadrant line color
midLineStyle (series string) : Midline style string ("Solid", "Dotted", "Dashed")
quadLineStyle (series string) : Quadrant line style string ("Solid", "Dotted", "Dashed")
labelSize (series string) : Label size string ("Tiny", "Small", "Normal", "Large")
nwogTextColor (series color) : NWOG label text color
labelStyle (series string) : Label style ("Simple" or "Descriptive")
hideHistoryLabels (series bool) : Hide labels on historical gaps
atrMultiplier (series float) : Pre-computed ATR multiplier for NWOG size filter
ndogAtrMultiplier (series float) : Pre-computed ATR multiplier for NDOG size filter
maxDistanceAtr (series float) : Pre-computed max distance in ATR multiples
onlyShowNearPrice (series bool) : Only show gaps near current price
cleanupIntervalMs (series int) : Cleanup interval in milliseconds (0 = immediate)
barOffset (series int) : Bar offset for right edge of drawings
PXHLConfig
Configuration for PXH/L (Previous X High/Low) display
Fields:
global_sty (series string) : Global line style ("Solid", "Dotted", "Dashed")
lbl_size_str (series string) : Label size string ("Auto", "Tiny", "Small", "Normal", "Large")
use_mono (series bool) : Use monospace font for labels
filter_enabled (series bool) : Enable distance filter
filter_preset (series string) : Distance filter preset ("Really Close", "Balanced", "Slightly Far")
filter_atr_len (series int) : ATR length for distance filter
show_d (series bool) : Show daily levels
show_w (series bool) : Show weekly levels
show_m (series bool) : Show monthly levels
col_d (series color) : Daily level color
col_w (series color) : Weekly level color
col_m (series color) : Monthly level color
cnt_d (series int) : Daily level count
cnt_w (series int) : Weekly level count
cnt_m (series int) : Monthly level count
mode_d (series string) : Daily midline mode ("None", "EQ", "Quadrants")
mode_w (series string) : Weekly midline mode ("None", "EQ", "Quadrants")
mode_m (series string) : Monthly midline mode ("None", "EQ", "Quadrants")
show_lbl_d (series bool) : Show daily labels
show_lbl_w (series bool) : Show weekly labels
show_lbl_m (series bool) : Show monthly labels
Gap930Config
Configuration for 9:30 NY Opening Range Gap
Fields:
g930_historyCount (series int) : Number of historical 9:30 gaps to show
g930_projEndH (series int) : End hour for gap projection (NY time)
g930_projEndM (series int) : End minute for gap projection (NY time)
g930_showBox (series bool) : Show gap box
g930_boxColor (series color) : Gap box color
g930_showMid (series bool) : Show midline
g930_midColor (series color) : Midline color
g930_midStyleStr (series string) : Midline style string ("Solid", "Dashed", "Dotted")
g930_showQuad (series bool) : Show quadrant lines
g930_quadColor (series color) : Quadrant line color
g930_quadStyleStr (series string) : Quadrant line style string ("Solid", "Dashed", "Dotted") Library

EKTrueOpensLibrary "EKTrueOpens"
True Opens library: calculation, drawing, history management, and price tracking table
for opening prices at multiple timeframes (yearly, quarterly, monthly, weekly, daily, session, 90-minute).
Extracted from qt-ultimate-enigma.pine lines 1951-2597.
to_tf_to_min(tf)
Converts a timeframe string to minutes
Parameters:
tf (string) : Timeframe string (e.g. "1", "60", "D", "W")
Returns: Float value in minutes
to_is_visible(min_s, max_s)
Checks if current chart timeframe is within the given min/max range
Parameters:
min_s (string) : Minimum timeframe string
max_s (string) : Maximum timeframe string
Returns: True if current timeframe is within range
to_get_style_const(s)
Converts a style name string to a line style constant
Parameters:
s (string) : Style name: "Solid", "Dashed", or "Dotted"
Returns: line.style_* constant
to_get_active_style(cfg)
Returns the line style for active (current) True Open lines
Parameters:
cfg (TOConfig) : TOConfig with style_mode and custom_style settings
Returns: line.style_* constant
to_get_sess_end(start_time)
Calculates the hard session end time for a given start time
Parameters:
start_time (int) : Bar time of the session start
Returns: Timestamp of the session end (6-hour boundaries in NY time)
to_enforce_limit(arr, allow_hist, hist_count)
Enforces the history limit on a TO_Level array, deleting excess old lines/labels
Parameters:
arr (array) : The array of TO_Level objects
allow_hist (bool) : Whether history is enabled for this timeframe
hist_count (int) : Maximum number of historical levels to keep
to_force_close_period(arr, allow_hist, cfg)
Force-closes the current active period in an array (used at timeframe boundaries)
Parameters:
arr (array) : The array of TO_Level objects
allow_hist (bool) : Whether history is enabled for this timeframe
cfg (TOConfig) : TOConfig with style settings
to_manage_history(arr, p, start_time, txt, col, allow_hist, use_hard_end, custom_end, cfg)
Closes the previous active level and creates a new one for the new period
Parameters:
arr (array) : The array of TO_Level objects
p (float) : The opening price for the new period
start_time (int) : Bar time of the new period start
txt (string) : Label text for the new level
col (color) : Color for the new level
allow_hist (bool) : Whether history is enabled for this timeframe
use_hard_end (bool) : Whether to calculate a hard session end time
custom_end (int) : Custom end time (0 = none, overrides hard end calc)
cfg (TOConfig) : TOConfig with style, font, and text size settings
to_maintain_lines(arr, is_vis, is_time_label_mode, allow_hist, cfg)
Updates line extensions, visibility, colors, and label text for all levels in an array
Parameters:
arr (array) : The array of TO_Level objects
is_vis (bool) : Whether this timeframe is visible at current chart resolution
is_time_label_mode (bool) : Whether labels should show time instead of name (for sessions/90m)
allow_hist (bool) : Whether history is enabled for this timeframe
cfg (TOConfig) : TOConfig with offset, session label mode, and font settings
to_calc_yr_func()
Calculates the True Yearly Open (April open per ICT/Daye methodology)
Returns: Tuple
to_calc_qt_func()
Calculates the True Quarterly Open (4th Sunday of quarter-start month)
Returns: Tuple
to_calc_mn_func(t_hour)
Calculates the True Monthly Open (2nd week Sunday at trigger hour)
Parameters:
t_hour (int) : The trigger hour in NY time (typically 18)
Returns: Tuple
to_calc_wk_func(t_hour)
Calculates the True Weekly Open (Monday at trigger hour)
Parameters:
t_hour (int) : The trigger hour in NY time (typically 18)
Returns: Tuple
to_calc_dy_func()
Calculates the True Daily Open (midnight NY time)
Returns: Tuple
to_calc_session_func(t_h, t_m)
Calculates a True Session Open at the given hour:minute in NY time
Parameters:
t_h (int) : Session start hour (NY time)
t_m (int) : Session start minute (NY time)
Returns: Tuple
to_calc_90m_func()
Calculates the True 90-Minute Open (:23/:53 cycle boundaries)
Returns: Tuple
to_is_current_period(trigger_time, type)
Checks if a trigger time falls within the current period of the given type
Parameters:
trigger_time (int) : The bar time when the True Open was triggered
type (string) : Period type: "D", "W", "M", "Q", "Y", "SESS", or "90M"
Returns: True if the trigger time is within the current period
to_get_status(lvl_price, trig_t, period_type, c_tbl_none, c_tbl_above, c_tbl_below)
Gets the status text and color for a single True Open level vs current close
Parameters:
lvl_price (float) : The True Open price
trig_t (int) : The trigger time for the True Open
period_type (string) : Period type string for is_current_period check
c_tbl_none (color) : Color for "Not Open" state
c_tbl_above (color) : Color for "Above" state
c_tbl_below (color) : Color for "Below" state
Returns: Tuple
to_get_array_status(arr, label_name, c_tbl_none, c_tbl_above, c_tbl_below)
Gets the status for an array-based True Open (sessions, 90m) including label text
Parameters:
arr (array) : The array of TO_Level objects
label_name (string) : Display name for the table row (e.g. "Session", "90-Min")
c_tbl_none (color) : Color for "Not Open" state
c_tbl_above (color) : Color for "Above" state
c_tbl_below (color) : Color for "Below" state
Returns: Tuple
to_render_table(tbl_cfg, to_tbl, to_arr_sess, to_arr_90m, to_yr_p, to_yr_t, to_qt_p, to_qt_t, to_mn_p, to_mn_t, to_wk_p, to_wk_t, to_dy_p, to_dy_t)
Renders the True Opens price tracking table with all timeframe rows
Parameters:
tbl_cfg (TOTableConfig) : TOTableConfig with all table display settings
to_tbl (table) : The pre-created table object
to_arr_sess (array) : Session TO_Level array
to_arr_90m (array) : 90-minute TO_Level array
to_yr_p (float) : Yearly True Open price (from request.security)
to_yr_t (int) : Yearly trigger time
to_qt_p (float) : Quarterly True Open price
to_qt_t (int) : Quarterly trigger time
to_mn_p (float) : Monthly True Open price
to_mn_t (int) : Monthly trigger time
to_wk_p (float) : Weekly True Open price
to_wk_t (int) : Weekly trigger time
to_dy_p (float) : Daily True Open price
to_dy_t (int) : Daily trigger time
TO_Level
Core level structure for a single True Open line/label pair
Fields:
l (series line) : The line object drawn on chart
lbl (series label) : The label object drawn on chart
price (series float) : The opening price value
active (series bool) : Whether this level is the current (still-extending) period
name (series string) : Display name for the label (e.g. "TYO", "AO", "90m")
c (series color) : The color assigned to this level
start_t (series int) : Bar time when this level started
end_t (series int) : Hard end time (0 = no hard end, extends until next period)
TOConfig
Configuration for True Opens general settings (replaces input.*() calls)
Fields:
to_style_mode (series string) : "Auto" or "Custom" line style mode
to_custom_style (series string) : "Solid", "Dashed", or "Dotted" when mode is Custom
to_hist_count_global (series int) : How many past lines to keep for selected timeframes
to_offset_val (series int) : Label offset in bars
to_textSize (series string) : Text size for chart labels
to_font_fam (series string) : Font family string (font.family_monospace or font.family_default)
to_sess_lbl_mode (series string) : "Name" or "Time" for session label display
TOTableConfig
Configuration for the True Opens price tracking table (replaces input.*() calls)
Fields:
to_show_table (series bool) : Whether to show the table at all
to_table_pos (series string) : Table position string (e.g. position.bottom_right)
to_table_txt_size (series string) : Table text size
to_tbl_show_90m (series bool) : Show 90-Min row
to_tbl_show_sess (series bool) : Show Session row
to_tbl_show_dy (series bool) : Show Daily row
to_tbl_show_wk (series bool) : Show Weekly row
to_tbl_show_mn (series bool) : Show Monthly row
to_tbl_show_qt (series bool) : Show Quarterly row
to_tbl_show_yr (series bool) : Show Yearly row
to_c_tbl_above (series color) : Color when price is above True Open
to_c_tbl_below (series color) : Color when price is below True Open
to_c_tbl_none (series color) : Color when period is not open
to_font_fam (series string) : Font family string for table cells Library

EKSSMTLibrary "EKSSMT"
SSMT (Smart Money Tool) detection engine library. Handles normal and hidden SMT detection,
QCISD level creation/management, PSP (Price Sync Pattern) calculation, multi-condition alert logic,
status bar color computation, and all session/quarter math.
Extracted from qt-ultimate-enigma.pine to avoid PulseWire compiler timeout.
init_qcisd_state()
Creates and returns an initialized QCISDState with empty arrays.
Returns: QCISDState with all arrays initialized
add_to_line_array(arr, value, limit)
Adds a line to a managed array, deleting the oldest if over the limit.
Parameters:
arr (array) : The line array to manage
value (line) : The new line to add
limit (int) : Maximum array size
add_to_label_array(arr, value, limit)
Adds a label to a managed array, deleting the oldest if over the limit.
Parameters:
arr (array) : The label array to manage
value (label) : The new label to add
limit (int) : Maximum array size
compute_sessions(t, tz, dayStartHour)
Computes all session and quarter values for the current bar.
Calculates micro, 90m, daily, weekly, monthly, quarterly, and yearly session indices,
plus change flags for each.
Parameters:
t (int) : The current bar time
tz (string) : Timezone string (e.g. "UTC-4")
dayStartHour (int) : Hour of day start in the given timezone (e.g. 18)
Returns: SessionState with all computed session/quarter values
f_main_process(_tf_ok, _val_ses, op, cls, hi, lo, ti)
Tracks H/L/close extremes per session period. Core price tracking for SSMT detection.
Parameters:
_tf_ok (bool) : Whether the current timeframe is within the valid range
_val_ses (int) : The current session index value
op (float) : Open price (chart or correlated asset)
cls (float) : Close price
hi (float) : High price
lo (float) : Low price
ti (int) : Time value
Returns: Tuple of 18 values:
f_ssmt(cfg, qcisd_state, ah2, ah1, ah0, al2, al1, al0, bh2, bh1, bh0, bl2, bl1, bl0, _hmaxt, _lmint, _hmaxt1, _lmint1, _tfok, _val_ses, showon, _smt_clr, _smt_txt_clr, _tf_ok_V, lbl, name, pair, _inv, _time_limit, ssmt_pair, ssmt_pair2)
Normal SMT detection with line/label drawing and QCISD level creation.
Parameters:
cfg (SSMTConfig) : SSMTConfig settings
qcisd_state (QCISDState) : QCISDState arrays for QCISD level management
ah2 (float) : Chart asset high 2 periods ago
ah1 (float) : Chart asset high 1 period ago
ah0 (float) : Chart asset current high
al2 (float) : Chart asset low 2 periods ago
al1 (float) : Chart asset low 1 period ago
al0 (float) : Chart asset current low
bh2 (float) : Correlated asset high 2 periods ago
bh1 (float) : Correlated asset high 1 period ago
bh0 (float) : Correlated asset current high
bl2 (float) : Correlated asset low 2 periods ago
bl1 (float) : Correlated asset low 1 period ago
bl0 (float) : Correlated asset current low
_hmaxt (int) : Time of current high maximum
_lmint (int) : Time of current low minimum
_hmaxt1 (int) : Time of previous high maximum
_lmint1 (int) : Time of previous low minimum
_tfok (bool) : Whether timeframe is valid for this SSMT level
_val_ses (int) : Current session index
showon (bool) : Whether to show this SSMT level on chart
_smt_clr (color) : Color for SSMT lines
_smt_txt_clr (color) : Color for SSMT label text
_tf_ok_V (bool) : Whether timeframe is valid for visibility
lbl (string) : Label text string
name (string) : SSMT timeframe name ("90m", "Daily", "Weekly", etc.)
pair (int) : Pair number (2=secondary, 3=tertiary)
_inv (bool) : Whether this pair is inverse correlated
_time_limit (int) : Time limit for active status checking (dayStart or 0)
ssmt_pair (string) : Name string of secondary pair
ssmt_pair2 (string) : Name string of tertiary pair
Returns: Tuple:
f_hidden_ssmt(cfg, _clsmax, _clsmax1, _clsmin, _clsmin1, _clsmaxt, _clsmaxt1, _clsmint, _clsmint1, _cclsmax, _cclsmax1, _cclsmin, _cclsmin1, _tfok, showon, _smt_clr, _smt_txt_clr, _val_ses, _tf_ok_V, lbl, name, pair, _inv, _time_limit, ssmt_pair, ssmt_pair2)
Hidden SMT detection with line/label drawing.
Parameters:
cfg (SSMTConfig) : SSMTConfig settings
_clsmax (float) : Current close max
_clsmax1 (float) : Previous close max
_clsmin (float) : Current close min
_clsmin1 (float) : Previous close min
_clsmaxt (int) : Time of current close max
_clsmaxt1 (int) : Time of previous close max
_clsmint (int) : Time of current close min
_clsmint1 (int) : Time of previous close min
_cclsmax (float) : Correlated asset current close max
_cclsmax1 (float) : Correlated asset previous close max
_cclsmin (float) : Correlated asset current close min
_cclsmin1 (float) : Correlated asset previous close min
_tfok (bool) : Whether timeframe is valid
showon (bool) : Whether to show on chart
_smt_clr (color) : Color for hidden SSMT lines
_smt_txt_clr (color) : Color for hidden SSMT label text
_val_ses (int) : Current session index
_tf_ok_V (bool) : Whether timeframe is within visibility range
lbl (string) : Label text string
name (string) : SSMT timeframe name
pair (int) : Pair number (2=secondary, 3=tertiary)
_inv (bool) : Whether pair is inverse correlated
_time_limit (int) : Time limit for active status checking
ssmt_pair (string) : Name string of secondary pair
ssmt_pair2 (string) : Name string of tertiary pair
Returns: Tuple:
run_ssmt(cfg, qcisd_state, _tf_ok, _tf_ok_V, _val_ses, name, _show_smt, _smt_clr, _hsmt_clr, _smt_txt_clr, _show_hsmt, X_SSMT, shortname, _time_limit, xo0, xc0, xh0, xl0, xt0, yo0, yc0, yh0, yl0, yt0, ssmt_pair, ssmt_pair2, arr_inv, triad_ok, showsmt2)
Orchestrator that calls f_main_process + f_ssmt + f_hidden_ssmt for each timeframe.
This replaces f_allrun from the monolithic indicator.
Parameters:
cfg (SSMTConfig) : SSMTConfig settings
qcisd_state (QCISDState) : QCISDState arrays
_tf_ok (bool) : Whether timeframe is within valid range
_tf_ok_V (bool) : Whether timeframe is within visibility range
_val_ses (int) : Current session index value
name (string) : SSMT timeframe name ("90m", "Daily", "Weekly", etc.)
_show_smt (bool) : Whether to show normal SSMT
_smt_clr (color) : Color for normal SSMT lines
_hsmt_clr (color) : Color for hidden SSMT lines
_smt_txt_clr (color) : Color for SSMT label text
_show_hsmt (bool) : Whether to show hidden SSMT
X_SSMT (array) : The bool array (size 14) for this timeframe's SSMT state
shortname (string) : Short name for labels (e.g. "90m", "D", "W")
_time_limit (int) : Time limit for active status (dayStart or 0)
xo0 (float) : Secondary asset open
xc0 (float) : Secondary asset close
xh0 (float) : Secondary asset high
xl0 (float) : Secondary asset low
xt0 (int) : Secondary asset time
yo0 (float) : Tertiary asset open
yc0 (float) : Tertiary asset close
yh0 (float) : Tertiary asset high
yl0 (float) : Tertiary asset low
yt0 (int) : Tertiary asset time
ssmt_pair (string) : Name of secondary pair
ssmt_pair2 (string) : Name of tertiary pair
arr_inv (array) : Array of inversion flags
triad_ok (bool) : Whether triad mode is active
showsmt2 (bool) : Whether to show SMT pair 2
run_alert(cfg, a_con1, a_con3, number, M_SSMT, W_SSMT, D_SSMT, Q_SSMT, Y_SSMT, N_SSMT, m_SSMT, newMo, newW, newD, newN, newm, newQ, newY)
Multi-condition alert logic for SSMT signals.
Parameters:
cfg (SSMTConfig) : SSMTConfig settings
a_con1 (string) : First alert condition string (e.g. "W Bull SSMT")
a_con3 (string) : Second alert condition string
number (string) : Alert number string ("1", "2", "3", "4")
M_SSMT (array) : Monthly SSMT state array (size 14)
W_SSMT (array) : Weekly SSMT state array
D_SSMT (array) : Daily SSMT state array
Q_SSMT (array) : Quarterly SSMT state array
Y_SSMT (array) : Yearly SSMT state array
N_SSMT (array) : 90m SSMT state array
m_SSMT (array) : Micro SSMT state array
newMo (bool) : Whether monthly session just changed
newW (bool) : Whether weekly session just changed
newD (bool) : Whether daily session just changed
newN (bool) : Whether 90m session just changed
newm (bool) : Whether micro session just changed
newQ (bool) : Whether quarterly session just changed
newY (bool) : Whether yearly session just changed
calc_psp_adaptive(_show, xc0, xo0, xt0, yc0, yo0, yt0, arr_inv, triad_ok, dyad_ok, showsmt1, showsmt2)
PSP (Price Sync Pattern) adaptive bar-by-bar divergence calculation.
Parameters:
_show (bool) : Whether PSP is enabled and visible
xc0 (float) : Secondary asset close
xo0 (float) : Secondary asset open
xt0 (int) : Secondary asset time
yc0 (float) : Tertiary asset close
yo0 (float) : Tertiary asset open
yt0 (int) : Tertiary asset time
arr_inv (array) : Inversion flags array
triad_ok (bool) : Whether triad mode is active
dyad_ok (bool) : Whether dyad mode is active
showsmt1 (bool) : Whether SMT pair 1 is shown
showsmt2 (bool) : Whether SMT pair 2 is shown
Returns: Tuple:
get_sb_color_norm(_arr, _c_bull, _c_bear, _c_sand, _c_neut)
Computes the status bar color for a given SSMT timeframe array.
Parameters:
_arr (array) : The SSMT bool array (size 14) for this timeframe
_c_bull (color) : Bullish color
_c_bear (color) : Bearish color
_c_sand (color) : Sandwich/conflict color
_c_neut (color) : Neutral color
Returns: The computed color for the status bar cell
process_qcisd(qcisd_state, cfg)
QCISD global processor and mitigation loop. Handles confirmation, invalidation,
retest detection, and cleanup of QCISD levels.
Parameters:
qcisd_state (QCISDState) : QCISDState arrays to process
cfg (SSMTConfig) : SSMTConfig settings
SSMTConfig
Configuration settings for SSMT detection, drawing, alerts, and QCISD engine.
Fields:
hidelines (series bool) : Whether to hide SSMT divergence lines on chart
hidelabels (series bool) : Whether to hide SSMT divergence labels on chart
normstyle (series string) : Line style for normal SSMT lines (line.style_solid, etc.)
hiddstyle (series string) : Line style for hidden SSMT lines
i_font (series string) : Text size for SSMT labels ("tiny", "small", "normal", "large", "huge")
alerttype (series string) : Type of SSMT to show/alert ("All", "Normal", "Hidden")
ssmt_history_limit (series int) : Maximum number of SSMT lines/labels to keep
show_qcisd (series bool) : Whether QCISD engine is enabled
bias_input (series string) : Market bias for QCISD ("Bullish", "Bearish", "Neutral")
qcisd_bull_clr (series color) : Color for bullish QCISD levels
qcisd_bear_clr (series color) : Color for bearish QCISD levels
qcisd_pend_clr (series color) : Color for pending (threatened) QCISD levels
qcisd_text_clr (series color) : Color for QCISD label text
qcisd_width (series int) : Line width for QCISD levels
qcisd_lbl_size (series string) : Text size for QCISD labels (size.tiny, size.small, size.normal)
qcisd_mono (series bool) : Whether to use monospace font for QCISD labels
qcisd_max_limit (series int) : Maximum number of QCISD lines to draw
qcisd_trigger (series string) : HP-QCISD alert trigger mode ("All", "Confirmation", "Invalidation", "Retest", "Off")
qcisd_norm_trigger (series string) : Normal QCISD alert trigger mode
QCISDState
Holds the global QCISD state arrays. Passed by reference so library functions can modify in place.
Fields:
g_q_lines (array) : Array of QCISD line drawings
g_q_lbls (array) : Array of QCISD label drawings
g_q_dirs (array) : Array of directions (1=bullish, -1=bearish)
g_q_hp (array) : Array of high-probability flags
g_q_conf (array) : Array of confirmation flags
g_q_names (array) : Array of SSMT source names (e.g. "90m", "Daily")
g_q_pairs (array) : Array of pair names
SessionState
Holds all computed session and quarter values for the current bar.
Fields:
val_sesm (series int) : Micro session index (0-63)
val_ses (series int) : 90m session index (0-15)
val_sesD (series int) : Daily session index (0-3)
val_sesW (series int) : Weekly session index (0-6)
val_sesMo (series int) : Monthly session index (0-4)
val_sesQ (series int) : Quarterly session index (0-3)
val_sesY (series int) : Yearly session index (0-3)
newMo (series bool) : Whether monthly session just changed
newW (series bool) : Whether weekly session just changed
newD (series bool) : Whether daily session just changed
newN (series bool) : Whether 90m session just changed
newm (series bool) : Whether micro session just changed
newQ (series bool) : Whether quarterly session just changed
newY (series bool) : Whether yearly session just changed
dayStart (series int) : Timestamp of the current day start Library

EKAssetCorrelationLibrary "EKAssetCorrelation"
Full asset correlation library for SMT divergence detection. Drop-in replacement for fstarcapital/AssetCorrelationUtils/11.
detectIndicesFutures(ticker)
Detects Index Futures (NQ/ES/YM/RTY + micro variants)
Parameters:
ticker (string) : The ticker string to check (typically syminfo.ticker)
Returns: AssetPairing with secondary and tertiary assets configured
detectMetalsFutures(ticker)
Detects Metal Futures (GC/SI/HG + micro variants)
Parameters:
ticker (string) : The ticker string to check
Returns: AssetPairing with secondary and tertiary assets configured
detectMetalsFuturesQuad(ticker)
Detects Metal Futures in Quad Mode (Gold->SI/XAUEUR/XAUGBP, Silver->GC/XAGEUR/XAGGBP)
Parameters:
ticker (string) : The ticker string to check
Returns: AssetPairing with futures secondary + GXT cross-pairs as tertiary/quaternary (empty for copper)
detectMetalsFuturesGxt(ticker)
Detects Metal Futures in GXT Mode (Gold->XAUEUR/XAUGBP, Silver->XAGEUR/XAGGBP)
Parameters:
ticker (string) : The ticker string to check
Returns: AssetPairing with GXT secondary and tertiary assets (empty for copper)
detectForexFutures(ticker)
Detects Forex Futures (6E/6B + micro variants)
Parameters:
ticker (string) : The ticker string to check
Returns: AssetPairing with secondary and tertiary assets configured
detectEnergyFutures(ticker)
Detects Energy Futures (CL/RB/HO + micro variants)
Parameters:
ticker (string) : The ticker string to check
Returns: AssetPairing with secondary and tertiary assets configured
detectTreasuryFutures(ticker)
Detects Treasury Futures (ZB/ZF/ZN)
Parameters:
ticker (string) : The ticker string to check
Returns: AssetPairing with secondary and tertiary assets configured
detectCryptoFutures(ticker)
Detects CME Crypto Futures (BTC/ETH + micro variants)
Parameters:
ticker (string) : The ticker string to check
Returns: AssetPairing with secondary and tertiary assets configured
detectCADFutures(ticker)
Detects CAD Forex Futures (6C + micro variants)
Parameters:
ticker (string) : The ticker string to check
Returns: AssetPairing with secondary and tertiary assets configured
detectForexCFD(ticker, tickerId)
Detects Forex CFD pairs (EUR/GBP/DXY, USD/JPY/CHF triads)
Parameters:
ticker (string) : The ticker string to check
tickerId (string) : The full ticker ID (syminfo.tickerid) for primary asset
Returns: AssetPairing with secondary and tertiary assets configured
detectCrypto(ticker, tickerId)
Detects major Crypto assets (BTC, ETH, SOL, XRP, alts)
Parameters:
ticker (string) : The ticker string to check
tickerId (string) : The full ticker ID for primary asset
Returns: AssetPairing with secondary and tertiary assets configured
detectMetalsCFD(ticker, tickerId)
Detects Metals CFD (XAU/XAG/Copper + EUR/GBP cross-pairs)
Parameters:
ticker (string) : The ticker string to check
tickerId (string) : The full ticker ID for primary asset
Returns: AssetPairing with secondary and tertiary assets configured
detectMetalsCFDGxt(ticker, tickerId)
Detects Metals CFD in GXT Mode (XAUUSD->XAUEUR/XAUGBP, XAGUSD->XAGEUR/XAGGBP)
Parameters:
ticker (string) : The ticker string to check
tickerId (string) : The full ticker ID for primary asset
Returns: AssetPairing with GXT secondary and tertiary assets (empty for copper)
detectMetalsCFDQuad(ticker, tickerId)
Detects Metals CFD in Quad Mode (XAUUSD->XAGUSD/XAUEUR/XAUGBP, etc.)
Parameters:
ticker (string) : The ticker string to check
tickerId (string) : The full ticker ID for primary asset
Returns: AssetPairing with quad assets (empty for copper)
detectIndicesCFD(ticker, tickerId)
Detects Indices CFD (NAS100/SP500/DJ30)
Parameters:
ticker (string) : The ticker string to check
tickerId (string) : The full ticker ID for primary asset
Returns: AssetPairing with secondary and tertiary assets configured
detectEUStocks(ticker, tickerId)
Detects EU Stock Indices (GER40/EU50) - Dyad only
Parameters:
ticker (string) : The ticker string to check
tickerId (string) : The full ticker ID for primary asset
Returns: AssetPairing with secondary asset configured (tertiary empty for dyad)
getDefaultFallback(tickerId)
Returns default fallback assets (chart ticker only, no correlation)
Parameters:
tickerId (string) : The full ticker ID for primary asset
Returns: AssetPairing with chart ticker as primary, empty secondary/tertiary (no correlation)
applySessionModifierWithBackadjust(tickerStr, sessionType)
Applies futures session modifier to ticker WITH back adjustment
Parameters:
tickerStr (string) : The ticker to modify
sessionType (string) : The session type (syminfo.session)
Returns: Modified ticker string with session and backadjustment.on applied
applySessionModifierNoBackadjust(tickerStr, sessionType)
Applies futures session modifier to ticker WITHOUT back adjustment
Parameters:
tickerStr (string) : The ticker to modify
sessionType (string) : The session type (syminfo.session)
Returns: Modified ticker string with session and backadjustment.off applied
isTriadMode(pairing)
Checks if a pairing represents a valid triad (3 assets)
Parameters:
pairing (AssetPairing) : The AssetPairing to check
Returns: True if tertiary is non-empty (triad mode), false for dyad
getAssetTicker(tickerId)
Extracts clean ticker string from full ticker ID
Parameters:
tickerId (string) : The full ticker ID (e.g., "BITGET:BTCUSDT.P")
Returns: Clean ticker string (e.g., "BTCUSDT.P")
resolveTriad(chartTickerId, pairing)
Resolves triad asset assignments with proper inversion flags
Parameters:
chartTickerId (string) : The current chart's ticker ID (syminfo.tickerid)
pairing (AssetPairing) : The detected AssetPairing
Returns: Tuple
resolveDyad(chartTickerId, pairing)
Resolves dyad asset assignment with proper inversion flag
Parameters:
chartTickerId (string) : The current chart's ticker ID
pairing (AssetPairing) : The detected AssetPairing (dyad: tertiary is empty)
Returns: Tuple
resolveQuad(chartTickerId, pairing)
Resolves quad asset assignments with proper inversion flags (4 assets)
Parameters:
chartTickerId (string) : The current chart's ticker ID (syminfo.tickerid)
pairing (AssetPairing) : The detected AssetPairing with quaternary populated
Returns: Tuple
resolveAssets(ticker, tickerId, assetType, sessionType, useBackadjust, gxtMode, quadMode)
Main auto-detection entry point. Detects asset category and returns fully resolved config.
Parameters:
ticker (string) : The ticker string to check (typically syminfo.ticker)
tickerId (string) : The full ticker ID (typically syminfo.tickerid)
assetType (string) : The asset type (typically syminfo.type)
sessionType (string) : The session type for futures (typically syminfo.session)
useBackadjust (bool) : Whether to apply back adjustment for futures session alignment
gxtMode (bool) : When true, metals use currency-cross triads instead of standard metal correlations
quadMode (bool) : When true, metals use futures secondary + GXT cross-pairs as tertiary/quaternary. Takes priority over gxtMode for metals.
Returns: AssetConfig with fully resolved assets, inversion flags, and detection status
resolveCurrentChart(gxtMode, quadMode)
Simplified auto-detection using current chart's syminfo values
Parameters:
gxtMode (bool) : When true, metals use currency-cross triads instead of standard metal correlations
quadMode (bool) : When true, metals use futures secondary + GXT cross-pairs as tertiary/quaternary
Returns: AssetConfig with fully resolved assets, inversion flags, and detection status
AssetPairing
Core asset pairing structure for triad/dyad configurations
Fields:
primary (series string) : The primary (chart) asset ticker ID
secondary (series string) : The secondary correlated asset ticker ID
tertiary (series string) : The tertiary correlated asset ticker ID (empty for dyad)
quaternary (series string) : The quaternary correlated asset ticker ID (empty unless quad mode)
invertSecondary (series bool) : Whether secondary asset should be inverted for divergence calc
invertTertiary (series bool) : Whether tertiary asset should be inverted for divergence calc
invertQuaternary (series bool) : Whether quaternary asset should be inverted for divergence calc
AssetConfig
Full asset resolution result with mode detection and computed values
Fields:
detected (series bool) : Whether auto-detection succeeded
isTriadMode (series bool) : True if triad (3 assets), false if dyad (2 assets)
isQuadMode (series bool) : True if quad (4 assets)
primary (series string) : The resolved primary asset ticker ID
secondary (series string) : The resolved secondary asset ticker ID
tertiary (series string) : The resolved tertiary asset ticker ID (empty for dyad)
quaternary (series string) : The resolved quaternary asset ticker ID (empty unless quad mode)
invertSecondary (series bool) : Computed inversion flag for secondary asset
invertTertiary (series bool) : Computed inversion flag for tertiary asset
invertQuaternary (series bool) : Computed inversion flag for quaternary asset
assetCategory (series string) : String describing the detected asset category Library

RollingWindow█ OVERVIEW
This a Pine Script™ library to create rolling windows with arrays and matrices.
A rolling window is a first-in-first-out algorithm that stores values over chart updates, removing the oldest element as new values are added. Many programmers implement a form of this algorithm into their scripts currently like this:
var window = array.new(size = 10)
window.push(close) // Append `close` as a new element every new bar.
if 10 < window.size() // Maintain a size of 10 elements.
window.shift() // Remove oldest element.
With the RollingWindow library, you can simply call `roll()` to do the same thing like so:
var window = array.new(size = 10)
window.roll(close) // Rolling window of 10 elements updated each bar with `close`.
█ USAGE
Rolling Window Arrays
Import the RollingWindow library into your script.
import joebaus/RollingWindow/1 as rw
Create a rolling window array by calling `roll()` on an array declared with `var` bar persistence.
var window = array.new()
rw.roll(id = window, source = close, size = 3) // Alternatively: window.roll(close, 3)
Using an array with `varip` intrabar persistence lets `roll()` execute on and update elements intrabar.
varip window = array.new() // Updates intrabar.
rw.roll(id = window, source = close, size = 3) // Executes `roll()` every realtime update.
Ensure arrays are declared with `var` or `varip` keywords to store updated elements. Otherwise, applying `roll()` will not store rolled values.
id = array.new() // No `var` or `varip` keyword.
rw.roll(id, close, 3) // Only updates a single element in `id`!
New elements can be added dynamically to empty arrays with the limit set by the `roll(size)` parameter. Once the array is full, every sequential chart update rolls out , removes, the oldest element.
varip id = array.new()
// Dynamically appends up to 3 elements of `timenow`, then rolls elements.
id.roll(source = timenow, size = 3) // Use `roll()` as a method on `id`.
Arrays with initialized values and sizes can be used as a rolling window array.
var array id = array.from("Hello", "World")
string closeString = str.tostring(close, format.mintick)
id.roll(source = closeString, size = 2) // Rolls elements into `id`, up to 2 elements.
The `roll(size)` parameter becomes optional for arrays with an initialized size, because `roll()` will by default use the initialized size of the array provided if `roll(size)` is not set.
var id = array.new(size = 3) // Returns
color gradient = color.from_gradient(close, low, high, color.red, color.green)
id.roll(source = gradient) // Uses the size of `id` as the rolling window size limit.
An array with initialized values can still grow dynamically if `roll(size)` parameter is greater than the array's initial size.
var id = array.new(size = 3) // Returns
chart.point nowPoint = chart.point.now(close)
id.roll(source = nowPoint, size = 4) // Dynamically grows to
The `roll(size)` parameter must always be equal to or greater than the initial size of the array, or else `roll()` will generate a runtime error.
var id = array.new(size = 3)
id.roll(source = close, size = 2) // Generates a runtime error!
// roll(array id, float source, int size):
// `size` (2) must be greater than or equal to the size of `id` (3), or set to `na`!
When the array size and `roll(size)` parameter are both unspecified, `roll()` will dynamically size the array up to the element limit before rolling new elements.
var id = array.new()
id.roll(timenow) // Adds new elements to `id` up to the element limit, then rolls elements.
From within a conditional local scope , `roll()` can operate on arrays in a higher scope.
var id1 = array.new(size = 3)
float sma50 = ta.sma(close, 50)
float sma200 = ta.sma(close, 200)
if ta.cross(sma50, sma200) // Golden Cross condition.
id1.roll(source = str.format_time(time)) // Rolls up to 3 Golden Cross dates into `id1`.
var id2 = array.new()
footprint reqFootprint = request.footprint(100)
if not na(reqFootprint)
id2.roll(source = reqFootprint, size = 3) // Rolls up to 3 footprint objects into `id2`.
`roll()` returns the element removed from the array, allowing scripts to capture values as they are rolled out.
var id = array.new(size = 3)
float removedElement = id.roll(close, 3) // Returns the removed `close` value from the array.
if not na(removedElement)
label.new(bar_index, removedValue, str.tostring(removedValue))
Reverse Rolling Window Arrays
The roll operation can be done in reverse order using the `rollReverse()` library function; new elements are inserted at the beginning of the array instead, and old elements are removed at the end of the array.
var id1 = array.new()
id1.rollReverse(source = close, size = 3) // Dynamically sized reverse rolling window array.
varip id2 = array.new(size = 3)
id2.rollReverse(source = close) // Initialized size reverse rolling window array.
`rollReverse()` returns the element removed from the array, just like `roll()`.
var id = array.new(size = 3)
int removedValue = id.rollReverse(bar_index, 3)
Sorted Rolling Window Arrays
Rolling window arrays created with `roll()` are unsorted. To create ascending sorted rolling window arrays for `float` or `int` types, use the `rollAscendingVar()` or `rollAscendingVarip()` functions for `var` and `varip` keyword arrays respectively.
var id1 = array.new()
// Ascending sorted, dynamically sized `var` rolling window array.
id1.rollAscendingVar(source = close, size = 3)
varip id2 = array.new(size = 3)
// Ascending sorted, initialized size `varip` rolling window array.
id2.rollAscendingVarip(source = bar_index)
To create descending sorted rolling window arrays for `float` or `int` types, use the `rollDescendingVar()` or `rollDescendingVarip()` functions for `var` and `varip` keyword arrays respectively.
var id1 = array.new()
// Descending sorted, dynamically sized `var` rolling window array.
id1.rollDescendingVar(bar_index, 3)
varip id2 = array.new(3)
// Descending sorted, initialized size `varip` rolling window array.
id2.rollDescendingVarip(close)
Just like with the `array.sort()` built-in function, the ascending and descending rolling window functions do not sort `na` values.
float sma = ta.sma(50, close) // First 49 values are `na`.
var id = array.new()
id.rollAscendingVar(sma, 3) // Rolls nothing until `sma` values are not `na`.
The arrays with unsorted values should be sorted in ascending or descending order before calling the respective sorted rolling window library functions.
var array id = array.from(4, 1, 2, 3, 10, 8, 15, 7) // Unsorted initialized array.
id.sort(order.ascending) // Sort it first!
id.rollAscendingVar(bar_index)
The sorted rolling window functions can return the latest element removed from the array.
var id = array.new(size = 3)
float removedElement = id.rollAscendingVar(close, 3)
Rolling Window Matrices
The `roll()` matrix methods store a rolling window of arrays in row-major order. Rows are "rolled" by adding a new row at index `0`, and removing the oldest row at the end of the matrix.
var closeArray = array.new(10) // Array to store in the rolling window matrix.
var closeMatrix = matrix.new()
// Create a rolling window matrix with up to 3 `closeArray` rows and 10 columns (1 per element).
rw.roll(id = closeMatrix, array_id = closeArray, rows = 3)
A matrix with `varip` intrabar persistence lets `roll()` execute and update matrix rows intrabar.
varip closeArray = array.new(size = 10)
rw.roll(id = closeArray, source = time)
varip closeMatrix = matrix.new()
// Create a rolling window matrix with up to 3 `closeArray` rows and 10 elements (columns).
closeMatrix.roll(array_id = closeArray, rows = 3)
The matrix methods of `roll()` will dynamically create the necessary columns to fit all elements of `roll(array_id)` as long as the array size is greater than the number of matrix columns.
var closeArray = array.new(size = 10000)
closeArray.roll(source = close) // Creating a rolling window array.
// Size empty matrices dynamically with `roll(array_id)`.
var closeMatrix = matrix.new()
// Roll up to 100 rows of `closeArray` with 10000 elements (columns) into `closeMatrix`.
rw.roll(id = closeMatrix, array_id = closeArray, rows = 100)
The size of `array_id` can possibly be too large when dynamically adding columns to a rolling window matrix, generating a runtime error when the new column would exceed the 100,000 matrix size limit.
var closeArray = array.new()
closeArray.roll(source = close, size = 1001)
var closeMatrix = matrix.new()
rw.roll(id = closeMatrix, array_id = closeArray, rows = 100) // Generates a runtime error!
// roll(matrix id, array array_id, int rows):
// `array_id` (1001) and `rows` (100) create 100100 matrix elements!"
// Reduce the size of `array_id` or value of `rows` to stay within the 100,000 matrix size limit!
A matrix with initialized rows and columns can also be used with `roll()`. This requires that the array's size used in `roll(array_id)` must be less than or equal to the number of matrix columns.
var closeArray = array.new(100) // Initialized matrix columns require initialized array sizes.
closeArray.roll(close) // Roll 100 `close` elements into `closeArray`.
var closeMatrix = matrix.new(rows = 10, columns = 100) // 100 array elements, 100 columns.
rw.roll(id = closeMatrix, array_id = closeArray, rows = 10)
The `roll(rows)` parameter is also optional: `roll()` will use the number of rows in the initialized matrix when `roll(rows)` is not set. Plus the number of initialized matrix columns does not have to be the same size as `roll(array_id)`.
var closeArray = array.new(10) // Array initialized with 10 elements.
closeArray.roll(close)
var closeMatrix = matrix.new(rows = 10, columns = 100) // Matrix initialized with 100 columns.
closeMatrix.roll(closeArray) // No `rows` parameter, uses the initialized number of matrix rows.
Managing Drawings
An array or matrix of `line`, `linefill`, `label`, `box`, `polyline`, or `table` types can be used with `roll()` to manage the number of drawing on a chart.
var id = array.new()
// Roll a label in `id` after a bullish bar is confirmed.
if close > open and barstate.isconfirmed
chart.point nowPoint = chart.point.now(high)
label newLabel = label.new(nowPoint, "Bullish")
id.roll(newLabel, 5) // Rolls up to 5 labels in `id`, array grows up to a size of 5.
When drawings are rolled out of an array or matrix, they are deleted with the `.delete()` method of the respective type used. The library functions return `void` for drawing types, so no value is returned when an element is removed.
Library
